detquantlib 5.9.0__tar.gz → 5.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {detquantlib-5.9.0 → detquantlib-5.9.1}/PKG-INFO +1 -1
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/README.md +25 -16
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/isolation.py +20 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/pytest_plugin.py +18 -2
- {detquantlib-5.9.0 → detquantlib-5.9.1}/pyproject.toml +1 -1
- {detquantlib-5.9.0 → detquantlib-5.9.1}/LICENSE.txt +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/README.md +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/battery.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/wind_turbine.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/definitions.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/energy.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/helpers.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/price.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/azure_client.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/helpers.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/snowflake_client.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/tennet/tennet_client.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/dates/dates.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/forecasting/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/forecasting/forecasting.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/cases.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/comparison.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/config.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/errors.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/recording.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/tables.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/logging.py +0 -0
- {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/utils.py +0 -0
|
@@ -54,7 +54,9 @@ A run goes through the following steps (see `run_snapshot_case()` in `pytest_plu
|
|
|
54
54
|
3. In replay and update modes, the network is blocked and the local `.env` file is ignored, so
|
|
55
55
|
that local runs behave exactly like CI (which has no credentials).
|
|
56
56
|
4. The model runs through its real entry point, with the time frozen if the test case says so,
|
|
57
|
-
and with its logging isolated from the rest of the pytest process.
|
|
57
|
+
and with its logging isolated from the rest of the pytest process. The test only prints the
|
|
58
|
+
name and description of the test case: everything the model prints or logs to the console is
|
|
59
|
+
written to `tests/snapshot/.actual/CaseXX/console.txt` instead.
|
|
58
60
|
5. The outputs are compared with the snapshot (replay mode), or replace it (update and record
|
|
59
61
|
modes).
|
|
60
62
|
|
|
@@ -114,6 +116,10 @@ created by that run, which are also detached afterwards. The exception hooks rep
|
|
|
114
116
|
`detquantlib.utils.setup_logger` are restored as well. The working directory and the
|
|
115
117
|
`INPUT_DIR` / `OUTPUT_DIR` environment variables are restored after each test.
|
|
116
118
|
|
|
119
|
+
The model's console output (stdout, stderr, and console log handlers created by the model) is
|
|
120
|
+
muted and written to `tests/snapshot/.actual/CaseXX/console.txt`, also when the model crashes.
|
|
121
|
+
The run log written to the outputs folder is not affected.
|
|
122
|
+
|
|
117
123
|
### Output Comparison
|
|
118
124
|
|
|
119
125
|
Each file of the outputs folder is compared with its snapshot according to its type:
|
|
@@ -324,20 +330,6 @@ ignore = ["plots/*.png"]
|
|
|
324
330
|
| `atol` | `config.atol` | Absolute tolerance of numerical comparisons |
|
|
325
331
|
| `ignore` | `[]` | Additional output files not compared (glob patterns) |
|
|
326
332
|
|
|
327
|
-
### Creating a Test Case
|
|
328
|
-
|
|
329
|
-
`create_case(inputs_dir, cases_root)` creates the folder of a new test case, with the lowest free
|
|
330
|
-
case number. It copies the given inputs folder (typically the project's default `Inputs` folder)
|
|
331
|
-
and writes a `case.toml` file to complete, which freezes the time at the moment of creation. Then
|
|
332
|
-
record the new test case:
|
|
333
|
-
|
|
334
|
-
```cmd
|
|
335
|
-
pytest tests/snapshot --snapshot-record -k Case02
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
Test cases can also be created manually: create the folder with a `case.toml` file and an `Inputs`
|
|
339
|
-
folder, then record it. Freeze the time _before_ recording if the model uses the current date.
|
|
340
|
-
|
|
341
333
|
`print_cases(cases_root)` lists the test cases and their descriptions:
|
|
342
334
|
|
|
343
335
|
```
|
|
@@ -348,6 +340,23 @@ folder, then record it. Freeze the time _before_ recording if the model uses the
|
|
|
348
340
|
This is another description
|
|
349
341
|
```
|
|
350
342
|
|
|
343
|
+
### Creating a Test Case
|
|
344
|
+
|
|
345
|
+
Step 1: Execute `create_case(inputs_dir, cases_root)`. The function handles the following actions:
|
|
346
|
+
- Creates the folder of a new test case, with the lowest free case number.
|
|
347
|
+
- Copies the given inputs folder (typically the project's default `Inputs` folder).
|
|
348
|
+
- Writes a `case.toml` file to complete, which freezes the time at the moment of creation.
|
|
349
|
+
|
|
350
|
+
Alternatively, test cases can also be created manually: create the folder with a `case.toml`
|
|
351
|
+
file and an `Inputs` folder. Freeze the time (_before_ recording) if the model uses the current
|
|
352
|
+
date.
|
|
353
|
+
|
|
354
|
+
Step 2: Record the new test case with the following command:
|
|
355
|
+
|
|
356
|
+
```cmd
|
|
357
|
+
pytest tests/snapshot --snapshot-record -k Case02
|
|
358
|
+
```
|
|
359
|
+
|
|
351
360
|
## Day-to-Day Usage
|
|
352
361
|
|
|
353
362
|
```cmd
|
|
@@ -364,7 +373,7 @@ pytest tests/snapshot --snapshot-record -k Case01 # re-record a single test
|
|
|
364
373
|
`--snapshot-record` locally, with database access. Review the changes to `db_cache` (queries,
|
|
365
374
|
data) and `ExpectedOutputs`.
|
|
366
375
|
- **A test fails in CI**: download the `snapshot-actual-outputs` artifact of the workflow run to
|
|
367
|
-
inspect the produced outputs.
|
|
376
|
+
inspect the produced outputs and the model's console output (`console.txt`).
|
|
368
377
|
|
|
369
378
|
## The DB Cache
|
|
370
379
|
|
|
@@ -12,6 +12,7 @@ import logging
|
|
|
12
12
|
import socket
|
|
13
13
|
import sys
|
|
14
14
|
import threading
|
|
15
|
+
from pathlib import Path
|
|
15
16
|
from typing import Any
|
|
16
17
|
|
|
17
18
|
# Internal modules
|
|
@@ -118,6 +119,25 @@ def frozen_time(moment: Any):
|
|
|
118
119
|
traveller.stop()
|
|
119
120
|
|
|
120
121
|
|
|
122
|
+
@contextlib.contextmanager
|
|
123
|
+
def muted_console(console_path: Path):
|
|
124
|
+
"""
|
|
125
|
+
Mutes everything the model prints or logs to the console: stdout and stderr (including the
|
|
126
|
+
console handlers created by the model's logging setup) are written to a file instead, kept
|
|
127
|
+
for inspection.
|
|
128
|
+
|
|
129
|
+
Args:
|
|
130
|
+
console_path: File receiving the console output of the run
|
|
131
|
+
|
|
132
|
+
Yields:
|
|
133
|
+
Nothing; the model runs inside the context
|
|
134
|
+
"""
|
|
135
|
+
console_path.parent.mkdir(parents=True, exist_ok=True)
|
|
136
|
+
with open(console_path, "w", encoding="utf-8") as f:
|
|
137
|
+
with contextlib.redirect_stdout(f), contextlib.redirect_stderr(f):
|
|
138
|
+
yield
|
|
139
|
+
|
|
140
|
+
|
|
121
141
|
@contextlib.contextmanager
|
|
122
142
|
def isolated_logging(monkeypatch):
|
|
123
143
|
"""
|
|
@@ -31,11 +31,17 @@ import pytest
|
|
|
31
31
|
from detquantlib.snapshot_testing.cases import SnapshotCase
|
|
32
32
|
from detquantlib.snapshot_testing.comparison import compare_dirs, write_snapshot
|
|
33
33
|
from detquantlib.snapshot_testing.config import SnapshotConfig
|
|
34
|
-
from detquantlib.snapshot_testing.isolation import
|
|
34
|
+
from detquantlib.snapshot_testing.isolation import (
|
|
35
|
+
block_network,
|
|
36
|
+
frozen_time,
|
|
37
|
+
isolated_logging,
|
|
38
|
+
muted_console,
|
|
39
|
+
)
|
|
35
40
|
from detquantlib.snapshot_testing.recording import DbRecorder, install_db_patches
|
|
36
41
|
|
|
37
42
|
MODES = ("replay", "update", "record")
|
|
38
43
|
OUTPUTS_DIRNAME = "Outputs"
|
|
44
|
+
CONSOLE_FILENAME = "console.txt"
|
|
39
45
|
|
|
40
46
|
|
|
41
47
|
def pytest_addoption(parser: pytest.Parser):
|
|
@@ -133,6 +139,9 @@ def run_snapshot_case(
|
|
|
133
139
|
if mode not in MODES:
|
|
134
140
|
raise ValueError(f"Unknown snapshot mode '{mode}'. Accepted values: {', '.join(MODES)}.")
|
|
135
141
|
print(f"\nRunning snapshot test case '{case.name}' ({mode} mode) ...")
|
|
142
|
+
print("Case description:")
|
|
143
|
+
for line in (case.description or "(no description)").splitlines():
|
|
144
|
+
print(f" {line}".rstrip())
|
|
136
145
|
record = mode == "record"
|
|
137
146
|
update = mode in ("record", "update")
|
|
138
147
|
ignore = [*config.ignore_patterns, *case.ignore]
|
|
@@ -158,7 +167,14 @@ def run_snapshot_case(
|
|
|
158
167
|
monkeypatch.delenv(var, raising=False)
|
|
159
168
|
|
|
160
169
|
# 4. Run the model through its real entry point
|
|
161
|
-
|
|
170
|
+
# Everything the model prints or logs to the console goes to .actual/CaseXX/console.txt.
|
|
171
|
+
# Exit order matters: the run's log handlers are flushed before the console file closes.
|
|
172
|
+
console_path = config.actual_dir / case.name / CONSOLE_FILENAME
|
|
173
|
+
with (
|
|
174
|
+
muted_console(console_path),
|
|
175
|
+
frozen_time(case.frozen_time) as real_time,
|
|
176
|
+
isolated_logging(monkeypatch),
|
|
177
|
+
):
|
|
162
178
|
recorder.real_time = real_time # live DB calls (record mode) see the real clock
|
|
163
179
|
entry_point.run(input_dir=case.inputs_dir, output_dir=output_dir)
|
|
164
180
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|