pytest-csv-params 2.2.6__tar.gz → 2.3.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 (22) hide show
  1. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/PKG-INFO +9 -2
  2. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/README.md +8 -1
  3. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/exception.py +8 -0
  4. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/parametrize.py +50 -0
  5. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/version.py +20 -3
  6. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pyproject.toml +5 -3
  7. pytest_csv_params-2.3.1/pytest_csv_params/object.py +58 -0
  8. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/plugin.py +19 -7
  9. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/types.py +12 -0
  10. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/.gitignore +0 -0
  11. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/LICENSE.txt +0 -0
  12. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/__init__.py +0 -0
  13. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/cmdline.py +0 -0
  14. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/configure.py +0 -0
  15. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/plugin.py +0 -0
  16. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/_ptcsvp/varname.py +0 -0
  17. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/__init__.py +0 -0
  18. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/decorator.py +0 -0
  19. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/dialect.py +0 -0
  20. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/exception.py +0 -0
  21. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/shortcut.py +0 -0
  22. {pytest_csv_params-2.2.6 → pytest_csv_params-2.3.1}/pytest_csv_params/warning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: pytest-csv-params
3
- Version: 2.2.6
3
+ Version: 2.3.1
4
4
  Summary: Pytest plugin for Test Case Parametrization with CSV files
5
5
  Project-URL: repository, https://git.codebau.dev/pytest-plugins/pytest-csv-params
6
6
  Project-URL: homepage, https://git.codebau.dev/pytest-plugins/pytest-csv-params
@@ -84,6 +84,12 @@ uv add --group dev pytest-csv-params
84
84
  **Detailed documentation can be found under
85
85
  [docs.codebau.dev/pytest-plugins/pytest-csv-params/](https://docs.codebau.dev/pytest-plugins/pytest-csv-params/)**
86
86
 
87
+ There are also a few blog posts by the author (in German):
88
+
89
+ - [`pytest-csv-params`: Renovierung und neue Features für Version 2](https://j4n.e7h.eu/articles/2026-10-08-pytest-csv-params-renovierung-neue-features-version-2 "German blog post 'pytest-csv-params: Renovierung und neue Features für Version 2'")
90
+ - [`pytest-csv-params`: Testfall-Daten als »smarte Objekte«](https://j4n.e7h.eu/articles/2026-10-13-pytest-csv-params-smart-object-feature "German blog post 'pytest-csv-params: Testfall-Daten als »smarte Objekte«'")
91
+ - [Datengetriebene (data-driven) parametrisierte Tests mit Pytest (pytest-csv-params)](https://juergen.rocks/blog/articles/data-driven-tests-mit-pytest-csv-params.html "German blog post 'Datengetriebene (data-driven) parametrisierte Tests mit Pytest (pytest-csv-params)'")
92
+
87
93
  ## Usage: Command Line Argument
88
94
 
89
95
  | Argument | Required | Description | Example |
@@ -107,7 +113,8 @@ Simply decorate your test method with `@csv_params` (`pytest_csv_params.decorato
107
113
  | `reuse_id_col` | `bool` (optional) | Allows to re-use the ID column as test data column (since 1.3.0), defaults to `False` for backwards compatibility | `True` |
108
114
  | `skip_when_missing_csv_file` | `bool` (optional) | When the CSV data cannot be loaded, the test will be skipped (since 2.1.0), defaults to `False` for backwards compatibility | `True` |
109
115
  | `skip_when_empty_csv_file` | `bool` (optional) | When the CSV data file is empty, the test will be skipped (since 2.1.0), defaults to `False` for backwards compatibility | `True` |
110
- | `parameters_as_dictionary` | `str` (optional) | Instead of using one method argument per column, all columns are provided in the dict named with this parameter (since 2.2.0) | `test_data` |
116
+ | `parameters_as_dictionary` | `str` (optional) | Instead of using one method argument per column, all columns are provided in the dict named with this parameter (since 2.2.0) | `"test_data"` |
117
+ | `parameters_as_object` | `str` or `(str, pytest_csv_params.object.TestCaseDataObjectFactory)` (optional) | Instead of using one method argument per column, all columns are provided as a object, either just named by `str`, or named by `str` and prepared with the given factory (since 2.3.0) | `"test_data"`, `("test_data", my_obj_factory)` |
111
118
 
112
119
  ## CSV Format
113
120
 
@@ -48,6 +48,12 @@ uv add --group dev pytest-csv-params
48
48
  **Detailed documentation can be found under
49
49
  [docs.codebau.dev/pytest-plugins/pytest-csv-params/](https://docs.codebau.dev/pytest-plugins/pytest-csv-params/)**
50
50
 
51
+ There are also a few blog posts by the author (in German):
52
+
53
+ - [`pytest-csv-params`: Renovierung und neue Features für Version 2](https://j4n.e7h.eu/articles/2026-10-08-pytest-csv-params-renovierung-neue-features-version-2 "German blog post 'pytest-csv-params: Renovierung und neue Features für Version 2'")
54
+ - [`pytest-csv-params`: Testfall-Daten als »smarte Objekte«](https://j4n.e7h.eu/articles/2026-10-13-pytest-csv-params-smart-object-feature "German blog post 'pytest-csv-params: Testfall-Daten als »smarte Objekte«'")
55
+ - [Datengetriebene (data-driven) parametrisierte Tests mit Pytest (pytest-csv-params)](https://juergen.rocks/blog/articles/data-driven-tests-mit-pytest-csv-params.html "German blog post 'Datengetriebene (data-driven) parametrisierte Tests mit Pytest (pytest-csv-params)'")
56
+
51
57
  ## Usage: Command Line Argument
52
58
 
53
59
  | Argument | Required | Description | Example |
@@ -71,7 +77,8 @@ Simply decorate your test method with `@csv_params` (`pytest_csv_params.decorato
71
77
  | `reuse_id_col` | `bool` (optional) | Allows to re-use the ID column as test data column (since 1.3.0), defaults to `False` for backwards compatibility | `True` |
72
78
  | `skip_when_missing_csv_file` | `bool` (optional) | When the CSV data cannot be loaded, the test will be skipped (since 2.1.0), defaults to `False` for backwards compatibility | `True` |
73
79
  | `skip_when_empty_csv_file` | `bool` (optional) | When the CSV data file is empty, the test will be skipped (since 2.1.0), defaults to `False` for backwards compatibility | `True` |
74
- | `parameters_as_dictionary` | `str` (optional) | Instead of using one method argument per column, all columns are provided in the dict named with this parameter (since 2.2.0) | `test_data` |
80
+ | `parameters_as_dictionary` | `str` (optional) | Instead of using one method argument per column, all columns are provided in the dict named with this parameter (since 2.2.0) | `"test_data"` |
81
+ | `parameters_as_object` | `str` or `(str, pytest_csv_params.object.TestCaseDataObjectFactory)` (optional) | Instead of using one method argument per column, all columns are provided as a object, either just named by `str`, or named by `str` and prepared with the given factory (since 2.3.0) | `"test_data"`, `("test_data", my_obj_factory)` |
75
82
 
76
83
  ## CSV Format
77
84
 
@@ -17,3 +17,11 @@ class PytestVersionTooOldException(RuntimeError):
17
17
 
18
18
  See the exception message for more details.
19
19
  """
20
+
21
+
22
+ class ParametrizationConfigurationException(RuntimeError):
23
+ """
24
+ This exception is thrown when the configuration is invalid.
25
+
26
+ See the exception message for more details.
27
+ """
@@ -10,6 +10,7 @@ from warnings import warn
10
10
  import pytest
11
11
  from _pytest.mark import MarkDecorator
12
12
 
13
+ from _ptcsvp.exception import ParametrizationConfigurationException
13
14
  from _ptcsvp.plugin import BASE_DIR_KEY, SKIP_EMPTY_KEY, SKIP_MISSING_KEY, Plugin
14
15
  from _ptcsvp.varname import make_name_valid
15
16
  from pytest_csv_params.dialect import CsvParamsDefaultDialect
@@ -19,6 +20,7 @@ from pytest_csv_params.exception import (
19
20
  CsvParamsDataFileInvalid,
20
21
  CsvParamsDataFileNotFound,
21
22
  )
23
+ from pytest_csv_params.object import TestCaseDataElementObject, TestCaseDataElementType
22
24
  from pytest_csv_params.types import (
23
25
  BaseDir,
24
26
  CsvDialect,
@@ -27,6 +29,7 @@ from pytest_csv_params.types import (
27
29
  DictionaryName,
28
30
  HeaderRenames,
29
31
  IdColName,
32
+ ParametersObjectConfiguration,
30
33
  SkipConfig,
31
34
  )
32
35
  from pytest_csv_params.warning import CsvParamsOnlyOneColumnIsUsedAsIdAndDataWarning
@@ -106,6 +109,7 @@ def add_parametrization( # pylint: disable=too-many-arguments
106
109
  skip_when_missing_csv_file: SkipConfig = None,
107
110
  skip_when_empty_csv_file: SkipConfig = None,
108
111
  parameters_as_dictionary: DictionaryName = None,
112
+ parameters_as_object: ParametersObjectConfiguration = None,
109
113
  ) -> MarkDecorator:
110
114
  """
111
115
  Parametrize a test function with data from a CSV file.
@@ -122,6 +126,9 @@ def add_parametrization( # pylint: disable=too-many-arguments
122
126
  :param skip_when_missing_csv_file: Let tests automatically skip when CSV source is missing (since 2.1.0)
123
127
  :param skip_when_empty_csv_file: Let tests automatically skip when CSV source does not contain any test data (since 2.1.0)
124
128
  :param parameters_as_dictionary: Instead of using one argument per field, all arguments are passed as a dictionary with the given name (since 2.2.0)
129
+ :param parameters_as_object: Instead of using one argument per field, all arguments are passed as an object with the given name, or with the given name and created by the given factory (since 2.5.0)
130
+
131
+ :raises ParametersObjectConfiguration: When parameters_as_dictionary and parameters_as_object are used at the same time
125
132
 
126
133
  :returns: :meth:`pytest.mark.parametrize` mark decorator, filled with all the data from the CSV.
127
134
  """
@@ -186,8 +193,26 @@ def add_parametrization( # pylint: disable=too-many-arguments
186
193
  id_data = None
187
194
  if id_col is not None:
188
195
  id_data = [tc_data["test_id"] for tc_data in data]
196
+ if parameters_as_dictionary is not None and parameters_as_object is not None:
197
+ raise ParametrizationConfigurationException(
198
+ "it is not possible to use parameters_as_dictionary and parameters_as_object at the same time"
199
+ )
200
+ param_header_name = "_"
189
201
  if parameters_as_dictionary is not None:
190
202
  param_header_name = make_name_valid(parameters_as_dictionary)
203
+ elif parameters_as_object is not None:
204
+ if isinstance(parameters_as_object, str):
205
+ param_header_name = make_name_valid(parameters_as_object)
206
+ elif isinstance(parameters_as_object, tuple):
207
+ if len(parameters_as_object) != 2:
208
+ raise ParametrizationConfigurationException("parameters_as_object must be a tuple of length 2")
209
+ if isinstance(parameters_as_object[0], str):
210
+ param_header_name = make_name_valid(parameters_as_object[0])
211
+ else:
212
+ raise ParametrizationConfigurationException("parameters_as_object[0] must be a string")
213
+ else:
214
+ raise ParametrizationConfigurationException("parameters_as_object must be a string or a tuple")
215
+ if parameters_as_dictionary is not None:
191
216
  list_of_dicts: list[dict[str, Any]] = []
192
217
  for tc_element in [tc_data["data"] for tc_data in data]:
193
218
  list_of_dicts.append(dict(zip(headers, tc_element)))
@@ -196,4 +221,29 @@ def add_parametrization( # pylint: disable=too-many-arguments
196
221
  [pytest.param(d) for d in list_of_dicts],
197
222
  ids=id_data,
198
223
  )
224
+ elif parameters_as_object is not None:
225
+ object_factory = lambda: TestCaseDataElementObject()
226
+ if isinstance(parameters_as_object, tuple):
227
+ object_factory = parameters_as_object[1]
228
+ list_of_objects: list[TestCaseDataElementType] = []
229
+ for tc_element in [tc_data["data"] for tc_data in data]:
230
+ try:
231
+ tc_obj = object_factory()
232
+ except TypeError:
233
+ raise ParametrizationConfigurationException(
234
+ "parameters_as_object[1] must be a TestCaseDataObjectFactory"
235
+ )
236
+ for idx, element in enumerate(tc_element):
237
+ try:
238
+ tc_obj.register_value(headers[idx], element)
239
+ except (AttributeError, TypeError):
240
+ raise ParametrizationConfigurationException(
241
+ "parameters_as_object[1] must be a TestCaseDataObjectFactory"
242
+ )
243
+ list_of_objects.append(tc_obj)
244
+ return pytest.mark.parametrize(
245
+ param_header_name,
246
+ [pytest.param(o) for o in list_of_objects],
247
+ ids=id_data,
248
+ )
199
249
  return pytest.mark.parametrize(headers, [tc_data["data"] for tc_data in data], ids=id_data)
@@ -5,7 +5,7 @@ the pytest version is recent enough (:meth:`check_pytest_version`).
5
5
  During the setup phase of the plugin (see :mod:`pytest_csv_params.plugin`) these methods are called.
6
6
  """
7
7
 
8
- import sys
8
+ import platform
9
9
  import warnings
10
10
 
11
11
  from packaging.version import parse
@@ -22,10 +22,27 @@ def check_python_version(min_version: tuple[int, int] = (3, 10), deprecation_bel
22
22
  :raises PythonVersionTooOldException: When the python version is too old/unsupported
23
23
  """
24
24
 
25
- if sys.version_info < min_version:
25
+ platform_version = platform.python_version().split(".")
26
+ version_arr = [0, 0, 0]
27
+
28
+ for i, version_part in enumerate(platform_version):
29
+ if i >= len(version_arr):
30
+ break
31
+ try:
32
+ v = int(version_part)
33
+ if v >= 0:
34
+ version_arr[i] = v
35
+ except ValueError:
36
+ pass
37
+
38
+ parsed_version = parse(".".join(map(str, version_arr)))
39
+ required_version = parse(".".join(map(str, min_version)))
40
+ deprecated_below_version = parse(".".join(map(str, deprecation_below)))
41
+
42
+ if parsed_version < required_version:
26
43
  raise PythonVersionTooOldException(f"At least Python {'.'.join(map(str, min_version))} required")
27
44
 
28
- if sys.version_info < deprecation_below:
45
+ if parsed_version < deprecated_below_version:
29
46
  warnings.warn(f"Python <{'.'.join(map(str, deprecation_below))} is deprecated", DeprecationWarning)
30
47
 
31
48
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "pytest-csv-params"
3
- version = "2.2.6"
3
+ version = "2.3.1"
4
4
  description = "Pytest plugin for Test Case Parametrization with CSV files"
5
5
  authors = [
6
6
  { name = "Juergen Edelbluth", email = "csv_params@jued.de" },
@@ -75,8 +75,10 @@ homepage = "https://git.codebau.dev/pytest-plugins/pytest-csv-params"
75
75
  [tool.pytest.ini_options]
76
76
  addopts = "--cov --cov-report=term-missing --cov-report=xml --junitxml=test-reports/pytest_csv_params.xml"
77
77
  filterwarnings=[
78
- "ignore:.*zipimport.*",
79
- "ignore:Creating a LegacyVersion has been deprecated and will be removed in the next major release",
78
+ "ignore::_pytest.warning_types.PytestRemovedIn10Warning",
79
+ "ignore:Python <3.11 is deprecated",
80
+ "ignore:Pytest <9.0 is deprecated",
81
+ "ignore::pytest_csv_params.warning.CsvParamsOnlyOneColumnIsUsedAsIdAndDataWarning",
80
82
  ]
81
83
  junit_family = "xunit2"
82
84
  junit_logging = "all"
@@ -0,0 +1,58 @@
1
+ """
2
+ This module defines the smart object as test case data object.
3
+ """
4
+
5
+ import re
6
+ from collections.abc import Callable
7
+ from re import Pattern
8
+ from typing import Any
9
+
10
+ FORBIDDEN_KEY_PATTERN: Pattern[str] = re.compile("^(__(.+)__)|(register_value)$", re.ASCII | re.DOTALL)
11
+ """
12
+ Pattern to find out if a certain key is forbidden for the registration of values to the test case data object;
13
+ basically, 'register_value' and all magic-method-alike keys are forbidden
14
+ """
15
+
16
+
17
+ class TestCaseDataElementType:
18
+ """
19
+ This class represents an abstract test case data element.
20
+ """
21
+
22
+ def register_value(self, key: str, value: Any):
23
+ """
24
+ This method registers a value for a given key and is called by the csv_params plugin in order to populate the
25
+ test case data object.
26
+ """
27
+ raise NotImplementedError("register_value is not implemented")
28
+
29
+
30
+ class TestCaseDataElementObject(TestCaseDataElementType):
31
+ """
32
+ This class represents a test case data element.
33
+ """
34
+
35
+ def register_value(self, key: str, value: Any):
36
+ """
37
+ Simply add the value as an attribute to the instance.
38
+
39
+ :param key: The key to register the value for
40
+ :param value: The value to register
41
+
42
+ :raises ValueError: If the key is forbidden for registration
43
+ """
44
+ if FORBIDDEN_KEY_PATTERN.match(key):
45
+ raise ValueError(f"Key '{key}' is forbidden for the registration of values to the test case data object")
46
+ setattr(self, key, value)
47
+
48
+ def __getattribute__(self, name: str, /) -> Any:
49
+ return object.__getattribute__(self, name)
50
+
51
+ def __getattr__(self, name: str) -> None:
52
+ raise AttributeError(f"Attribute '{name}' not found")
53
+
54
+
55
+ TestCaseDataObjectFactory = Callable[[], TestCaseDataElementType]
56
+ """
57
+ This is how a factory function has to look like to build a test case data object.
58
+ """
@@ -4,20 +4,32 @@ This module contains all the code to initialize the pytest plugin. This is the e
4
4
  `pyproject.toml` as `pytest11`.
5
5
  """
6
6
 
7
+ from _pytest.config import Config
8
+
7
9
  from _ptcsvp.cmdline import pytest_addoption as _pytest_addoption
8
10
  from _ptcsvp.configure import pytest_configure as _pytest_configure
9
11
  from _ptcsvp.configure import pytest_unconfigure as _pytest_unconfigure
10
12
  from _ptcsvp.version import check_pytest_version, check_python_version
11
13
 
12
- # Fist at all, check if the python & pytest version matches
13
- check_python_version()
14
- check_pytest_version()
14
+
15
+ # First at all, check if the python & pytest version matches
16
+ def version_check():
17
+ """
18
+ Check if the python & pytest version matches the required versions
19
+ """
20
+ check_python_version()
21
+ check_pytest_version()
22
+
15
23
 
16
24
  # Basic config
17
- pytest_configure = _pytest_configure
18
- """
19
- Hook our :meth:`_ptcsvp.configure.pytest_configure` method to setup the plugin setup
20
- """
25
+ def pytest_configure(config: Config, plugin_name: str = "csv_params"):
26
+ """
27
+ Hook our :meth:`_ptcsvp.configure.pytest_configure` method to setup the plugin setup,
28
+ after executing version checks.
29
+ """
30
+ version_check()
31
+ _pytest_configure(config, plugin_name)
32
+
21
33
 
22
34
  pytest_unconfigure = _pytest_unconfigure
23
35
  """
@@ -8,6 +8,8 @@ import csv
8
8
  from collections.abc import Callable
9
9
  from typing import Any
10
10
 
11
+ from pytest_csv_params.object import TestCaseDataObjectFactory
12
+
11
13
  DataCast = Callable[[str], Any]
12
14
  """
13
15
  A :class:`DataCast` describes how a data casting callable must be implemented. It requires one parameter of the type
@@ -77,3 +79,13 @@ DictionaryName = str | None
77
79
  The :class:`DictionaryName` describes the type of the `parameters_as_dictionary` parameter of the
78
80
  :meth:`~pytest_csv_params.decorator.csv_params` decorator. It is just an optional :class:`str`.
79
81
  """
82
+
83
+ ParametersObjectConfiguration = str | tuple[str, TestCaseDataObjectFactory] | None
84
+ """
85
+ The :class:`ParametersObjectConfiguration` describes the type of the `parameters_as_object` parameter of the
86
+ :meth:`~pytest_csv_params.decorator.csv_params` decorator.
87
+
88
+ - It can be either a :class:`str` which describes only the parameter name.
89
+ - It can be a :class:`tuple` of :class:`str` and :class:`TestCaseDataObjectFactory` to describe the name and the factory.
90
+ - Default is :obj:`None`, to disable the feature.
91
+ """