Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simpler Test Data #110

Merged
merged 65 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from 56 commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
f604d35
use cast instead of type: ignore
meksor Jul 8, 2024
1ba7bed
add updateinfo migration
meksor Jul 12, 2024
06b80b2
rework tests to use fixtures and cli option
meksor Jul 12, 2024
aca7337
adjust core tests
meksor Jul 12, 2024
694543b
adjust data layer tests
meksor Jul 12, 2024
d51934f
adjust benchmark tests
meksor Aug 6, 2024
f14b214
remove create_filter_test_data
meksor Aug 6, 2024
2fead3b
remove old code/comments
meksor Aug 6, 2024
1d4020d
create deployment instead of trying to build docker img
meksor Aug 6, 2024
921ee8c
adjust pytest wf
meksor Aug 6, 2024
befc4a4
add medium test data
meksor Aug 12, 2024
c9db7de
split check_dsn and make_engine
meksor Aug 12, 2024
2374382
use medium test data where it makes sense
meksor Aug 12, 2024
03a8483
make postgres_dsn its own cli option
meksor Aug 12, 2024
323cb2e
run one backend per test run
meksor Aug 12, 2024
8fb67a6
Merge branch 'main' into enhancement/simpler-test-data
meksor Aug 12, 2024
0c67899
fix typo
meksor Aug 12, 2024
ba0fd99
remove copy disclaimer
meksor Aug 12, 2024
2022438
Merge branch 'main' into enhancement/simpler-test-data
meksor Aug 12, 2024
ccd0dc7
backends -> backend
meksor Aug 12, 2024
db47480
add type:ignore
meksor Aug 12, 2024
93ddb09
change url to localhost
meksor Aug 12, 2024
4d27515
fix conflict resolution mixstakes
meksor Aug 12, 2024
52956fc
fix more conf. res. mistakes
meksor Aug 12, 2024
b076194
try to start psotgres only when it is needed
meksor Aug 12, 2024
f6f7d08
fix gha syntax
meksor Aug 12, 2024
849d1bd
group tests into groups of two
meksor Aug 12, 2024
3810ab8
group default matrix as well
meksor Aug 12, 2024
c0f8414
all platforms at once seems to be most efficient
meksor Aug 12, 2024
06d1c4e
make iamc tests a little bit faster
meksor Aug 12, 2024
4a609d5
use explicit core import
meksor Aug 12, 2024
a9dd19d
remove run.set_as_default()
meksor Aug 12, 2024
face797
remove more set_as_default
meksor Aug 12, 2024
867be4f
remove runs.set_as_default_version from opt data layer tests
meksor Aug 12, 2024
5484801
remove extra comment
meksor Aug 12, 2024
13950a7
missed a correct conflict res.
meksor Aug 12, 2024
ea15e7b
fix broken psql tests
meksor Aug 12, 2024
2be9c41
remove extra comment
meksor Aug 12, 2024
b3d41ae
fix typo
meksor Aug 12, 2024
cb83db7
simplify conftest.py
meksor Aug 12, 2024
cbbe9e9
forgot sqlite_platform
meksor Aug 12, 2024
53f83f4
more performance refactoring
meksor Aug 12, 2024
75d27a7
reset is now called in conftest.py
meksor Aug 12, 2024
a6396ed
reset after the test instead of before?
meksor Aug 12, 2024
2126e53
rev reset after
meksor Aug 12, 2024
0895d46
split up reset()
meksor Aug 12, 2024
2ffea54
split reset for all fixtures
meksor Aug 12, 2024
9128792
refactor reset()
meksor Aug 12, 2024
0182c7f
more reset refactoring
meksor Aug 12, 2024
4f73122
avoid duplicate setup/td
meksor Aug 12, 2024
ecf94b7
try class scope for big td
meksor Aug 12, 2024
96fe9b8
switch close and td
meksor Aug 12, 2024
700911f
NullPool is the answer for test runs
meksor Aug 12, 2024
90e73cf
more efficient run filter test
meksor Aug 12, 2024
423b76f
touchups and docstrings
meksor Aug 13, 2024
effc539
we can only use class scope for immutable pltf. fixtures
meksor Aug 13, 2024
6ae4da8
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
460ed27
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
93c776d
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
7636130
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
4d9aae7
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
d68620c
Update tests/data/test_optimization_indexset.py
meksor Aug 13, 2024
9a9bbc5
Update tests/data/test_optimization_table.py
meksor Aug 13, 2024
183da6c
Update tests/data/test_optimization_table.py
meksor Aug 13, 2024
4baae99
Update tests/data/test_optimization_table.py
meksor Aug 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/deploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: deploy

on:
push:
tags: ["v*"]
release:
types: ["published"]

jobs:
create_deployment:
timeout-minutes: 15
runs-on: ubuntu-latest
permissions:
id-token: write
steps:
- if: github.event_name == 'release'
uses: chrnorm/deployment-action@v2
name: Create Testing Deployment
with:
token: ${{ github.token }}
environment: testing
- if: github.event_name != 'release'
uses: chrnorm/deployment-action@v2
name: Create Development Deployment
with:
token: ${{ github.token }}
environment: development
60 changes: 0 additions & 60 deletions .github/workflows/docker.yaml

This file was deleted.

19 changes: 14 additions & 5 deletions .github/workflows/pytest.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# copied from https://github.com/marketplace/actions/install-poetry-action
name: test

on: pull_request
Expand All @@ -18,30 +17,40 @@ jobs:
- "16"
pandas-version:
- false
backend:
- "sqlite,rest-sqlite,postgres,rest-postgres"
include:
# with pyarrow
- python-version: "3.12"
with-pyarrow: true
postgres-version: "16"
pandas-version: false
backend: "sqlite,rest-sqlite"
# pgsql 15
- python-version: "3.12"
with-pyarrow: false
postgres-version: "15"
pandas-version: false
backend: "postgres,rest-postgres"

# pandas 2.1.3
- python-version: "3.11"
with-pyarrow: true
postgres-version: "16"
backend: "sqlite,rest-sqlite"
pandas-version: "2.1.3"
# pandas 2.0.0
- python-version: "3.10"
with-pyarrow: true
postgres-version: "16"
backend: "sqlite,rest-sqlite"
pandas-version: "2.0.0"


name: py${{ matrix.python-version }} | with-pyarrow=${{ matrix.with-pyarrow }} | pgsql=${{ matrix.postgres-version }} | pandas=${{ matrix.pandas-version }}
name: py${{ matrix.python-version }} | backend=${{ matrix.backend }} | with-pyarrow=${{ matrix.with-pyarrow }} | pgsql=${{ matrix.postgres-version }} | pandas=${{ matrix.pandas-version }}
runs-on: ubuntu-latest
services:
postgres:
image: postgres:${{ matrix.postgres-version }}
image: ${{ contains(matrix.backend, 'postgres') && format('postgres:{0}', matrix.postgres-version) || '' }}
env:
POSTGRES_PASSWORD: postgres
POSTGRES_DB: test
Expand Down Expand Up @@ -120,7 +129,7 @@ jobs:
- name: Run tests
run: |
source .venv/bin/activate
pytest --cov-report xml:.coverage.xml --cov-report term --cov=ixmp4 -rsx --benchmark-skip
pytest --backend ${{ matrix.backend }} --postgres-dsn "postgresql://postgres:postgres@localhost:5432/test" --cov-report xml:.coverage.xml --cov-report term --cov=ixmp4 -rsx --benchmark-skip

pre-commit:
name: Code quality
Expand Down
11 changes: 11 additions & 0 deletions .github/workflows/release_event.json
meksor marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"action": "released",
"ref": "refs/tags/v0.0.0",
"release": {
"name": "v0.0.0"
},
"github": {
"event_name": "release"
}

}
1 change: 1 addition & 0 deletions ixmp4/data/abstract/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ def tabulate(
self,
*,
name: str | None = None,
**kwargs,
) -> pd.DataFrame:
"""Tabulate models by specified criteria.
Expand Down
6 changes: 1 addition & 5 deletions ixmp4/data/abstract/scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,11 +85,7 @@ def list(
"""
...

def tabulate(
self,
*,
name: str | None = None,
) -> pd.DataFrame:
def tabulate(self, *, name: str | None = None, **kwargs) -> pd.DataFrame:
"""Tabulate scenarios by specified criteria.

Parameters
Expand Down
6 changes: 6 additions & 0 deletions ixmp4/data/backend/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,9 @@ def make_client(self, rest_url: str, auth: BaseAuth):
def close(self):
self.client.close()
self.executor.shutdown(cancel_futures=True)

def setup(self):
pass

def teardown(self):
pass
38 changes: 22 additions & 16 deletions ixmp4/data/backend/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

from sqlalchemy.engine import Engine, create_engine
from sqlalchemy.orm.session import sessionmaker
from sqlalchemy.pool import StaticPool
from sqlalchemy.pool import NullPool, StaticPool

from ixmp4.conf.base import PlatformInfo
from ixmp4.conf.manager import ManagerConfig, ManagerPlatformInfo
Expand Down Expand Up @@ -72,16 +72,20 @@ class SqlAlchemyBackend(Backend):
def __init__(self, info: PlatformInfo) -> None:
super().__init__(info)
logger.info(f"Creating database engine for platform '{info.name}'.")
self.make_engine(info.dsn)
dsn = self.check_dsn(info.dsn)
self.make_engine(dsn)
self.make_repositories()
self.event_handler = SqlaEventHandler(self)

def make_engine(self, dsn: str):
def check_dsn(self, dsn: str):
if dsn.startswith("postgresql://"):
logger.debug(
"Replacing the platform dsn prefix to use the new `psycopg` driver."
)
dsn = dsn.replace("postgresql://", "postgresql+psycopg://")
return dsn

def make_engine(self, dsn: str):
self.engine = cached_create_engine(dsn)
self.session = self.Session(bind=self.engine)

Expand All @@ -99,9 +103,6 @@ def make_repositories(self):
self.scenarios = ScenarioRepository(self)
self.units = UnitRepository(self)

def close(self):
self.session.close()

@contextmanager
def auth(
self,
Expand All @@ -123,20 +124,26 @@ def _create_all(self):
def _drop_all(self):
BaseModel.metadata.drop_all(bind=self.engine, checkfirst=True)

def reset(self):
self.session.commit()
self._drop_all()
def setup(self):
self._create_all()

def teardown(self):
self.session.rollback()
self._drop_all()
self.engine = None
self.session = None

def close(self):
self.session.close()
self.engine.dispose()


class SqliteTestBackend(SqlAlchemyBackend):
def __init__(self, *args, **kwargs) -> None:
super().__init__(
PlatformInfo(name="sqlite-test", dsn="sqlite:///:memory:"),
*args,
**kwargs,
)
self.reset()

def make_engine(self, dsn: str):
self.engine = create_engine(
Expand All @@ -150,11 +157,10 @@ def make_engine(self, dsn: str):
class PostgresTestBackend(SqlAlchemyBackend):
def __init__(self, *args, **kwargs) -> None:
super().__init__(
PlatformInfo(
name="postgres-test",
dsn="postgresql://postgres:postgres@localhost/test",
),
*args,
**kwargs,
)
self.reset()

def make_engine(self, dsn: str):
meksor marked this conversation as resolved.
Show resolved Hide resolved
self.engine = create_engine(dsn, poolclass=NullPool)
self.session = self.Session(bind=self.engine)
24 changes: 19 additions & 5 deletions ixmp4/data/db/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Iterator,
Tuple,
TypeVar,
cast,
)

import numpy as np
Expand Down Expand Up @@ -353,18 +354,19 @@ def yield_chunks(self, df: pd.DataFrame) -> Iterator[pd.DataFrame]:

class BulkUpserter(BulkOperator[ModelType]):
def bulk_upsert(self, df: pd.DataFrame) -> None:
# slight performance improvement on small operations
if len(df.index) < self.max_list_length:
self.bulk_upsert_chunk(df)
else:
for chunk_df in self.yield_chunks(df):
self.bulk_upsert_chunk(pd.DataFrame(chunk_df))

def bulk_upsert_chunk(self, df: pd.DataFrame) -> None:
logger.debug(f"Starting `bulk_upsert_chunk` for {len(df)} rows.")
columns = db.utils.get_columns(self.model_class)
df = df[list(set(columns.keys()) & set(df.columns))]
existing_df = self.tabulate_existing(df)
if existing_df.empty:
logger.debug(f"Inserting {len(df)} rows.")
self.bulk_insert(df, skip_validation=True)
else:
df = self.merge_existing(df, existing_df)
Expand Down Expand Up @@ -393,25 +395,37 @@ def bulk_upsert_chunk(self, df: pd.DataFrame) -> None:
)

if not insert_df.empty:
logger.debug(f"Inserting {len(insert_df)} rows.")
self.bulk_insert(insert_df, skip_validation=True)
if not update_df.empty:
logger.debug(f"Updating {len(update_df)} rows.")
self.bulk_update(update_df, skip_validation=True)

self.session.commit()

def bulk_insert(self, df: pd.DataFrame, **kwargs) -> None:
# to_dict returns a more general list[Mapping[Hashable, Unknown]]
m: list[dict[str, Any]] = df.to_dict("records") # type: ignore
if "id" in df.columns:
raise ProgrammingError("You may not insert the 'id' column.")
m = cast(list[dict[str, Any]], df.to_dict("records"))

try:
self.session.execute(db.insert(self.model_class), m)
self.session.execute(
db.insert(self.model_class),
m,
execution_options={"synchronize_session": False},
)
except IntegrityError as e:
raise self.model_class.NotUnique(*e.args)

def bulk_update(self, df: pd.DataFrame, **kwargs) -> None:
# to_dict returns a more general list[Mapping[Hashable, Unknown]]
m: list[dict[str, Any]] = df.to_dict("records") # type: ignore
self.session.bulk_update_mappings(self.model_class, m) # type: ignore
m = cast(list[dict[str, Any]], df.to_dict("records"))
self.session.execute(
db.update(self.model_class),
m,
execution_options={"synchronize_session": False},
)


class BulkDeleter(BulkOperator[ModelType]):
Expand Down
6 changes: 4 additions & 2 deletions ixmp4/data/db/events.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ def receive_do_orm_execute(self, orm_execute_state: ORMExecuteState):
if orm_execute_state.is_delete:
self.logger.debug("Operation: 'delete'")
return self.receive_delete(orm_execute_state)
else:
self.logger.debug(f"Ignoring operation: {orm_execute_state}")

def receive_select(self, oes: ORMExecuteState):
# select = cast(sql.Select, oes.statement)
Expand All @@ -105,7 +107,7 @@ def receive_insert(self, oes: ORMExecuteState):
insert = cast(sql.Insert, oes.statement)
entity = insert.entity_description
type_ = entity["type"]
self.logger.info(f"Entity: '{entity['name']}'")
self.logger.debug(f"Entity: '{entity['name']}'")

if issubclass(type_, mixins.HasCreationInfo):
creation_info = {
Expand All @@ -121,7 +123,7 @@ def receive_update(self, oes: ORMExecuteState):
update = cast(sql.Update, oes.statement)
entity = update.entity_description
type_ = entity["type"]
self.logger.info(f"Entity: '{entity['name']}'")
self.logger.debug(f"Entity: '{entity['name']}'")

if issubclass(type_, mixins.HasUpdateInfo):
update_info = {
Expand Down
Loading
Loading