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.
Files changed (46) hide show
  1. {detquantlib-5.9.0 → detquantlib-5.9.1}/PKG-INFO +1 -1
  2. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/README.md +25 -16
  3. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/isolation.py +20 -0
  4. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/pytest_plugin.py +18 -2
  5. {detquantlib-5.9.0 → detquantlib-5.9.1}/pyproject.toml +1 -1
  6. {detquantlib-5.9.0 → detquantlib-5.9.1}/LICENSE.txt +0 -0
  7. {detquantlib-5.9.0 → detquantlib-5.9.1}/README.md +0 -0
  8. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/__init__.py +0 -0
  9. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/__init__.py +0 -0
  10. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/battery.py +0 -0
  11. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/assets/wind_turbine.py +0 -0
  12. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/__init__.py +0 -0
  13. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/definitions.py +0 -0
  14. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/energy.py +0 -0
  15. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/helpers.py +0 -0
  16. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/converters/price.py +0 -0
  17. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/__init__.py +0 -0
  18. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/azure_client.py +0 -0
  19. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/detdatabase.py +0 -0
  20. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/helpers.py +0 -0
  21. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/databases/snowflake_client.py +0 -0
  22. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/entsoe/entsoe.py +0 -0
  23. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/sftp/sftp.py +0 -0
  24. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/data/tennet/tennet_client.py +0 -0
  25. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/dates/__init__.py +0 -0
  26. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/dates/dates.py +0 -0
  27. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/figures/__init__.py +0 -0
  28. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/figures/plotly_figures.py +0 -0
  29. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/forecasting/__init__.py +0 -0
  30. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/forecasting/forecasting.py +0 -0
  31. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/outputs/__init__.py +0 -0
  32. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/outputs/outputs_interface.py +0 -0
  33. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/__init__.py +0 -0
  34. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/cases.py +0 -0
  35. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/comparison.py +0 -0
  36. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/config.py +0 -0
  37. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/errors.py +0 -0
  38. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/recording.py +0 -0
  39. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/snapshot_testing/tables.py +0 -0
  40. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/stats/__init__.py +0 -0
  41. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/stats/data_analysis.py +0 -0
  42. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/tradable_products/__init__.py +0 -0
  43. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/tradable_products/tradable_products.py +0 -0
  44. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/__init__.py +0 -0
  45. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/logging.py +0 -0
  46. {detquantlib-5.9.0 → detquantlib-5.9.1}/detquantlib/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detquantlib
3
- Version: 5.9.0
3
+ Version: 5.9.1
4
4
  Summary: An internal library containing functions and classes that can be used across Quant models.
5
5
  License-File: LICENSE.txt
6
6
  Author: Dynamic Energy Trading
@@ -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 block_network, frozen_time, isolated_logging
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
- with frozen_time(case.frozen_time) as real_time, isolated_logging(monkeypatch):
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
 
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "detquantlib"
3
- version = "5.9.0"
3
+ version = "5.9.1"
4
4
  description = "An internal library containing functions and classes that can be used across Quant models."
5
5
  authors = ["Dynamic Energy Trading"]
6
6
  readme = "README.md"
File without changes
File without changes