confit 0.13.0.dev0__tar.gz → 0.13.0.dev1__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 (32) hide show
  1. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/PKG-INFO +1 -1
  2. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/_version.py +1 -1
  3. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/registry.py +7 -3
  4. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/typing.py +18 -5
  5. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit.egg-info/PKG-INFO +1 -1
  6. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_validate.py +44 -0
  7. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/LICENSE +0 -0
  8. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/README.md +0 -0
  9. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/__init__.py +0 -0
  10. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/cli.py +0 -0
  11. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/config.py +0 -0
  12. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/draft.py +0 -0
  13. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/errors.py +0 -0
  14. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/__init__.py +0 -0
  15. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/collections.py +0 -0
  16. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/eval.py +0 -0
  17. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/random.py +0 -0
  18. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/settings.py +0 -0
  19. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit/utils/xjson.py +0 -0
  20. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit.egg-info/SOURCES.txt +0 -0
  21. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit.egg-info/dependency_links.txt +0 -0
  22. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit.egg-info/requires.txt +0 -0
  23. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/confit.egg-info/top_level.txt +0 -0
  24. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/pyproject.toml +0 -0
  25. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/setup.cfg +0 -0
  26. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_as_list.py +0 -0
  27. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_cli.py +0 -0
  28. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_config_instance.py +0 -0
  29. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_draft.py +0 -0
  30. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_registry.py +0 -0
  31. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_typing.py +0 -0
  32. {confit-0.13.0.dev0 → confit-0.13.0.dev1}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: confit
3
- Version: 0.13.0.dev0
3
+ Version: 0.13.0.dev1
4
4
  Summary: Smart configuration framework
5
5
  Author-email: Perceval Wajsburt <perceval.wajsburt@aphp.fr>, Thomas Petit-Jean <thomas.petitjean@aphp.fr>, Adam Remaki <adam.remaki@aphp.fr>, Alice Calliger <alice.calliger@aphp.fr>
6
6
  License: Copyright 2023 Assistance Publique - Hôpitaux de Paris
@@ -4,7 +4,7 @@ import subprocess
4
4
  from importlib import metadata
5
5
  from pathlib import Path
6
6
 
7
- _BASE_VERSION = "0.13.0.dev0"
7
+ _BASE_VERSION = "0.13.0.dev1"
8
8
 
9
9
 
10
10
  def get_version(base_version: str = _BASE_VERSION) -> str:
@@ -26,6 +26,7 @@ from confit.errors import (
26
26
  SignatureError,
27
27
  remove_lib_from_traceback,
28
28
  )
29
+ from confit.typing import legacy_validator_schema
29
30
  from confit.utils.settings import is_debug
30
31
 
31
32
  # Reuse evaluated annotations, evaluating AsList[int] again can create a different class
@@ -48,7 +49,11 @@ def make_wrapper(raw_function, callee, config, invoker):
48
49
  extras_name = next(
49
50
  (name for name, p in parameters.items() if p.kind == p.VAR_KEYWORD), None
50
51
  )
51
- callee_name = callee.__module__ + "." + callee.__qualname__
52
+ callee_name = (
53
+ f"{callee.__module__}.{callee.__qualname__}"
54
+ if callee.__module__
55
+ else callee.__qualname__
56
+ )
52
57
  # Keep Field defaults, ordinary defaults stay omitted through stacked decorators
53
58
  omitted_defaults = {
54
59
  name
@@ -252,8 +257,7 @@ def validate_arguments(
252
257
  steps.append(old_get_pydantic_core_schema(*args, **kwargs))
253
258
  elif old_get_validators is not None:
254
259
  steps.extend(
255
- core_schema.no_info_plain_validator_function(fn)
256
- for fn in old_get_validators()
260
+ legacy_validator_schema(fn) for fn in old_get_validators()
257
261
  )
258
262
  steps.append(
259
263
  core_schema.no_info_plain_validator_function(post_validate)
@@ -4,12 +4,28 @@ from typing import TypeVar
4
4
 
5
5
  from pydantic import BaseModel
6
6
  from pydantic.type_adapter import ConfigDict, TypeAdapter
7
- from pydantic_core import core_schema
7
+ from pydantic_core import PydanticCustomError, core_schema
8
8
  from typing_extensions import is_typeddict
9
9
 
10
10
  T = TypeVar("T")
11
11
 
12
12
 
13
+ def legacy_validator_schema(validator):
14
+ """
15
+ Keep TypeError as a validation failure for custom get_validators hooks
16
+ """
17
+
18
+ def validate(value):
19
+ try:
20
+ return validator(value)
21
+ except TypeError as error:
22
+ raise PydanticCustomError(
23
+ "type_error", "{error}", {"error": error}
24
+ ) from error
25
+
26
+ return core_schema.no_info_plain_validator_function(validate)
27
+
28
+
13
29
  class Validatable:
14
30
  @classmethod
15
31
  def __get_validators__(cls):
@@ -18,10 +34,7 @@ class Validatable:
18
34
  @classmethod
19
35
  def __get_pydantic_core_schema__(cls, source, handler):
20
36
  return core_schema.chain_schema(
21
- [
22
- core_schema.no_info_plain_validator_function(v)
23
- for v in cls.__get_validators__()
24
- ]
37
+ [legacy_validator_schema(v) for v in cls.__get_validators__()]
25
38
  )
26
39
 
27
40
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: confit
3
- Version: 0.13.0.dev0
3
+ Version: 0.13.0.dev1
4
4
  Summary: Smart configuration framework
5
5
  Author-email: Perceval Wajsburt <perceval.wajsburt@aphp.fr>, Thomas Petit-Jean <thomas.petitjean@aphp.fr>, Adam Remaki <adam.remaki@aphp.fr>, Alice Calliger <alice.calliger@aphp.fr>
6
6
  License: Copyright 2023 Assistance Publique - Hôpitaux de Paris
@@ -60,6 +60,13 @@ def test_validate_decorator():
60
60
  validated = validate_arguments()(GoodModel)
61
61
  assert validated("3").value == 3
62
62
 
63
+ scope = {"validate_arguments": validate_arguments}
64
+ exec("@validate_arguments\ndef fn(value: int): return value", scope)
65
+ assert scope["fn"]("3") == 3
66
+ with pytest.raises(ConfitValidationError) as exc:
67
+ scope["fn"]("invalid")
68
+ assert str(exc.value).startswith("1 validation error for fn()\n-> value\n")
69
+
63
70
 
64
71
  def test_custom_validators_v1():
65
72
  @validate_arguments()
@@ -151,6 +158,43 @@ def test_custom_validators_validatable():
151
158
  )
152
159
 
153
160
 
161
+ @pytest.mark.parametrize("base", [Validatable, object])
162
+ def test_custom_validator_type_error(base):
163
+ error = TypeError("Invalid value")
164
+
165
+ @validate_arguments
166
+ class Model(base):
167
+ def __init__(self, value: int):
168
+ self.value = value
169
+
170
+ @classmethod
171
+ def __get_validators__(cls):
172
+ yield cls.validate
173
+
174
+ @classmethod
175
+ def validate(cls, value):
176
+ raise error
177
+
178
+ @validate_arguments
179
+ def fn(values: list[Model]):
180
+ raise error
181
+
182
+ with pytest.raises(ConfitValidationError) as exc:
183
+ fn([1])
184
+ assert exc.value.errors() == [
185
+ {
186
+ "loc": ("values", 0),
187
+ "msg": "Invalid value",
188
+ "type": "type_error",
189
+ "input": 1,
190
+ "ctx": {"error": error},
191
+ }
192
+ ]
193
+ with pytest.raises(TypeError) as exc:
194
+ fn([])
195
+ assert exc.value is error
196
+
197
+
154
198
  def test_literals():
155
199
  @validate_arguments()
156
200
  def test(val: Literal["ok", "ko"]):
File without changes
File without changes
File without changes
File without changes