tpcp 2.2.0__tar.gz → 2.2.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 (38) hide show
  1. {tpcp-2.2.0 → tpcp-2.2.1}/PKG-INFO +1 -1
  2. {tpcp-2.2.0 → tpcp-2.2.1}/pyproject.toml +1 -1
  3. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/__init__.py +1 -1
  4. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/testing/_regression_utils.py +65 -15
  5. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/validate/_validate.py +11 -13
  6. {tpcp-2.2.0 → tpcp-2.2.1}/README.md +0 -0
  7. {tpcp-2.2.0 → tpcp-2.2.1}/skills/tpcp/tpcp-basics/SKILL.md +0 -0
  8. {tpcp-2.2.0 → tpcp-2.2.1}/skills/tpcp/tpcp-builder/SKILL.md +0 -0
  9. {tpcp-2.2.0 → tpcp-2.2.1}/skills/tpcp/tpcp-datasets/SKILL.md +0 -0
  10. {tpcp-2.2.0 → tpcp-2.2.1}/skills/tpcp/tpcp-multiprocessing/SKILL.md +0 -0
  11. {tpcp-2.2.0 → tpcp-2.2.1}/skills/tpcp/tpcp-optimization/SKILL.md +0 -0
  12. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_algorithm.py +0 -0
  13. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_algorithm_utils.py +0 -0
  14. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_base.py +0 -0
  15. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_cli.py +0 -0
  16. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_dataset.py +0 -0
  17. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_hash.py +0 -0
  18. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_optimize.py +0 -0
  19. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_parameters.py +0 -0
  20. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_pipeline.py +0 -0
  21. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_utils/__init__.py +0 -0
  22. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_utils/_general.py +0 -0
  23. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/_utils/_score.py +0 -0
  24. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/caching.py +0 -0
  25. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/exceptions.py +0 -0
  26. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/misc/__init__.py +0 -0
  27. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/misc/_class_utils.py +0 -0
  28. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/misc/_typed_iterator.py +0 -0
  29. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/optimize/__init__.py +0 -0
  30. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/optimize/_optimize.py +0 -0
  31. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/optimize/optuna.py +0 -0
  32. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/parallel.py +0 -0
  33. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/testing/__init__.py +0 -0
  34. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/testing/_algorithm_test_mixin.py +0 -0
  35. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/types.py +0 -0
  36. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/validate/__init__.py +0 -0
  37. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/validate/_cross_val_helper.py +0 -0
  38. {tpcp-2.2.0 → tpcp-2.2.1}/src/tpcp/validate/_scorer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: tpcp
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: Pipeline and Dataset helpers for complex algorithm evaluation.
5
5
  Author: Arne Küderle, Robert Richer, Raul C. Sîmpetru, Björn Eskofier
6
6
  Author-email: Arne Küderle <arne.kuederle@fau.de>, Robert Richer <robert.richer@fau.de>, Raul C. Sîmpetru <raul.simpetru@fau.de>, Björn Eskofier <bjoern.eskofier@fau.de>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "tpcp"
3
- version = "2.2.0"
3
+ version = "2.2.1"
4
4
  description = "Pipeline and Dataset helpers for complex algorithm evaluation."
5
5
  authors = [
6
6
  { name = "Arne Küderle", email = "arne.kuederle@fau.de" },
@@ -24,7 +24,7 @@ from tpcp._parameters import (
24
24
  )
25
25
  from tpcp._pipeline import OptimizablePipeline, Pipeline
26
26
 
27
- __version__ = "2.2.0"
27
+ __version__ = "2.2.1"
28
28
 
29
29
 
30
30
  __all__ = [
@@ -4,6 +4,7 @@ This is inspired by github.com/syrusakbary/snapshottest.
4
4
  Note that it can not be used in combination with this module!
5
5
  """
6
6
 
7
+ import difflib
7
8
  import re
8
9
  from pathlib import Path
9
10
  from typing import Optional, Union
@@ -18,6 +19,10 @@ class SnapshotNotFoundError(Exception):
18
19
  pass
19
20
 
20
21
 
22
+ class SnapshotAssertionError(AssertionError):
23
+ pass
24
+
25
+
21
26
  class PyTestSnapshotTest:
22
27
  """Perform snapshot tests in pytest.
23
28
 
@@ -82,6 +87,15 @@ class PyTestSnapshotTest:
82
87
  def _file_name_txt(self):
83
88
  return self._snapshot_folder / f"{self._test_name}.txt"
84
89
 
90
+ def _snapshot_file(self, dtype):
91
+ if dtype is pd.DataFrame:
92
+ return self._file_name_json
93
+ if dtype is np.ndarray:
94
+ return self._file_name_csv
95
+ if dtype is str:
96
+ return self._file_name_txt
97
+ raise ValueError(f"The dtype {dtype} is not supported for snapshot testing")
98
+
85
99
  @property
86
100
  def _test_name(self):
87
101
  cls_name = getattr(self.request.node.cls, "__name__", "")
@@ -143,6 +157,50 @@ class PyTestSnapshotTest:
143
157
  return value
144
158
  raise ValueError(f"The dtype {dtype} is not supported for snapshot testing")
145
159
 
160
+ @staticmethod
161
+ def _format_string_difference(value: str, prev_snapshot: str) -> str:
162
+ diff = "".join(
163
+ difflib.unified_diff(
164
+ prev_snapshot.splitlines(keepends=True),
165
+ value.splitlines(keepends=True),
166
+ fromfile="stored",
167
+ tofile="current",
168
+ )
169
+ )
170
+ if diff:
171
+ return diff
172
+ return f"stored={prev_snapshot!r}\ncurrent={value!r}"
173
+
174
+ def _format_mismatch(self, dtype, value, prev_snapshot, error: Optional[AssertionError] = None) -> str:
175
+ if dtype is str:
176
+ details = self._format_string_difference(value, prev_snapshot)
177
+ else:
178
+ details = str(error) if error else "The snapshot content does not match the current value."
179
+
180
+ return (
181
+ f"Snapshot mismatch for '{self._test_name}'.\n"
182
+ f"Snapshot file: {self._snapshot_file(dtype)}\n\n"
183
+ f"{details}\n\n"
184
+ "Run pytest with --snapshot-update to accept the new snapshot."
185
+ )
186
+
187
+ def _compare(self, value, prev_snapshot, value_dtype, **kwargs):
188
+ __tracebackhide__ = True
189
+
190
+ if isinstance(value, pd.DataFrame):
191
+ # convert datetime columns to match with the stored format
192
+ value = self._sanitize_datetime_entries(value)
193
+ assert_frame_equal(value, prev_snapshot, **kwargs)
194
+ return
195
+ if isinstance(value, np.ndarray):
196
+ np.testing.assert_array_almost_equal(value, prev_snapshot, **kwargs)
197
+ return
198
+ if isinstance(value, str):
199
+ if value != prev_snapshot:
200
+ raise SnapshotAssertionError(self._format_mismatch(value_dtype, value, prev_snapshot))
201
+ return
202
+ raise TypeError(f"The dtype {value_dtype} is not supported for snapshot testing")
203
+
146
204
  def assert_match(self, value: Union[str, pd.DataFrame, np.ndarray], name: Optional[str] = None, **kwargs):
147
205
  """Assert that the value matches the snapshot.
148
206
 
@@ -171,6 +229,7 @@ class PyTestSnapshotTest:
171
229
  There they will be passed to `assert_frame_equal` and `assert_array_almost_equal` respectively.
172
230
 
173
231
  """
232
+ __tracebackhide__ = True
174
233
  self.curr_snapshot = name or str(self.curr_snapshot_number)
175
234
  if self._update:
176
235
  self._store(value)
@@ -189,21 +248,12 @@ class PyTestSnapshotTest:
189
248
  except:
190
249
  raise
191
250
  else:
192
- if isinstance(value, pd.DataFrame):
193
- # convert datetime columns to match with the stored format
194
- value = self._sanitize_datetime_entries(value)
195
- assert_frame_equal(value, prev_snapshot, **kwargs)
196
- elif isinstance(value, np.ndarray):
197
- np.testing.assert_array_almost_equal(value, prev_snapshot, **kwargs)
198
- elif isinstance(value, str):
199
- # Display the string diff line by line as part of error message using difflib
200
- import difflib
201
-
202
- diff = difflib.ndiff(value.splitlines(keepends=True), prev_snapshot.splitlines(keepends=True))
203
- diff = "".join(diff)
204
- assert value == prev_snapshot, diff
205
- else:
206
- raise TypeError(f"The dtype {value_dtype} is not supported for snapshot testing")
251
+ try:
252
+ self._compare(value, prev_snapshot, value_dtype, **kwargs)
253
+ except SnapshotAssertionError:
254
+ raise
255
+ except AssertionError as e:
256
+ raise SnapshotAssertionError(self._format_mismatch(value_dtype, value, prev_snapshot, e)) from None
207
257
 
208
258
  self.curr_snapshot_number += 1
209
259
 
@@ -195,25 +195,23 @@ def validate(
195
195
  True/False to enable/disable a `tqdm` progress bar.
196
196
  """
197
197
  scoring_args = {"n_jobs": n_jobs, "verbose": verbose, "pre_dispatch": pre_dispatch, "progress_bar": progress_bar}
198
- # iterate over args that will be passed to Scorer
199
- for arg, value in scoring_args.items():
200
- # when a Scorer instance is provided, the respective arguments were already set
201
- if isinstance(scoring, Scorer) and not isinstance(value, _Default):
202
- raise ValueError( # noqa: TRY004
198
+ if isinstance(scoring, Scorer):
199
+ for arg, value in scoring_args.items():
200
+ if isinstance(value, _Default):
201
+ continue
202
+ raise ValueError(
203
203
  "You passed a explicit Scorer object for the scoring parameter. In this case, we expect "
204
204
  f"multiprocessing parameters ({list(scoring_args.keys())}) to be configured directly on the "
205
205
  f"Scorer instance. However, you specified {arg}={value} by passing it directly "
206
206
  "to the validate function. Instead, pass multiprocessing parameters to your Scorer during "
207
207
  "initialization or by using `set_params`."
208
208
  )
209
-
210
- # extract the value from _Default instances
211
- if isinstance(value, _Default):
212
- scoring_args[arg] = value.get_value()
213
-
214
- scoring = _validate_scorer(scoring)
215
-
216
- scoring.set_params(**scoring_args)
209
+ scoring = _validate_scorer(scoring)
210
+ else:
211
+ scoring = _validate_scorer(scoring)
212
+ scoring.set_params(
213
+ **{arg: value.get_value() if isinstance(value, _Default) else value for arg, value in scoring_args.items()}
214
+ )
217
215
 
218
216
  results = _score(
219
217
  pipeline.clone(),
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