confit 0.12.0__tar.gz → 0.13.0.dev0__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.
- {confit-0.12.0 → confit-0.13.0.dev0}/PKG-INFO +2 -2
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/_version.py +5 -2
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/cli.py +11 -13
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/config.py +3 -10
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/draft.py +10 -39
- confit-0.13.0.dev0/confit/errors.py +265 -0
- confit-0.13.0.dev0/confit/registry.py +534 -0
- confit-0.13.0.dev0/confit/typing.py +42 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit.egg-info/PKG-INFO +2 -2
- {confit-0.12.0 → confit-0.13.0.dev0}/confit.egg-info/requires.txt +1 -1
- {confit-0.12.0 → confit-0.13.0.dev0}/pyproject.toml +1 -1
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_as_list.py +19 -36
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_cli.py +9 -16
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_config_instance.py +27 -31
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_validate.py +102 -89
- confit-0.12.0/confit/errors.py +0 -362
- confit-0.12.0/confit/registry.py +0 -675
- confit-0.12.0/confit/typing.py +0 -58
- {confit-0.12.0 → confit-0.13.0.dev0}/LICENSE +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/README.md +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/__init__.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/__init__.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/collections.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/eval.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/random.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/settings.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit/utils/xjson.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit.egg-info/SOURCES.txt +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit.egg-info/dependency_links.txt +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/confit.egg-info/top_level.txt +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/setup.cfg +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_draft.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_registry.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_typing.py +0 -0
- {confit-0.12.0 → confit-0.13.0.dev0}/tests/test_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: confit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.13.0.dev0
|
|
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
|
|
@@ -20,7 +20,7 @@ Description-Content-Type: text/markdown
|
|
|
20
20
|
License-File: LICENSE
|
|
21
21
|
Requires-Dist: catalogue>=2.0.7
|
|
22
22
|
Requires-Dist: lark>=1.1.5
|
|
23
|
-
Requires-Dist: pydantic
|
|
23
|
+
Requires-Dist: pydantic<3,>=2.1.1
|
|
24
24
|
Requires-Dist: pydantic-core>=0.20
|
|
25
25
|
Requires-Dist: PyYAML
|
|
26
26
|
Requires-Dist: griffe
|
|
@@ -4,7 +4,7 @@ import subprocess
|
|
|
4
4
|
from importlib import metadata
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
_BASE_VERSION = "0.
|
|
7
|
+
_BASE_VERSION = "0.13.0.dev0"
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
def get_version(base_version: str = _BASE_VERSION) -> str:
|
|
@@ -36,10 +36,13 @@ def get_version(base_version: str = _BASE_VERSION) -> str:
|
|
|
36
36
|
except (OSError, subprocess.CalledProcessError):
|
|
37
37
|
tag = None
|
|
38
38
|
|
|
39
|
+
development_version = (
|
|
40
|
+
base_version if ".dev" in base_version else f"{base_version}.dev0"
|
|
41
|
+
)
|
|
39
42
|
return (
|
|
40
43
|
base_version
|
|
41
44
|
if tag in {base_version, f"v{base_version}"}
|
|
42
|
-
else f"{
|
|
45
|
+
else f"{development_version}+g{commit_hash}"
|
|
43
46
|
)
|
|
44
47
|
|
|
45
48
|
|
|
@@ -18,8 +18,10 @@ from typing import (
|
|
|
18
18
|
get_origin,
|
|
19
19
|
)
|
|
20
20
|
|
|
21
|
+
from pydantic.fields import FieldInfo
|
|
22
|
+
|
|
21
23
|
from .config import Config, merge_from_disk
|
|
22
|
-
from .errors import ConfitValidationError
|
|
24
|
+
from .errors import ConfitValidationError
|
|
23
25
|
from .registry import VisibleDeprecationWarning, validate_arguments
|
|
24
26
|
from .utils.random import set_seed
|
|
25
27
|
from .utils.settings import is_debug
|
|
@@ -370,19 +372,13 @@ class Cli:
|
|
|
370
372
|
if default_config is not None and merge_with_default_config:
|
|
371
373
|
config = Config(default_config).merge(config)
|
|
372
374
|
|
|
373
|
-
|
|
374
|
-
# For example model.date becomes script.model.date when model is a parameter.
|
|
375
|
-
model_fields = (
|
|
376
|
-
validated.model.model_fields
|
|
377
|
-
if hasattr(validated.model, "model_fields")
|
|
378
|
-
else validated.model.__fields__
|
|
379
|
-
)
|
|
375
|
+
parameters = inspect.signature(fn).parameters
|
|
380
376
|
for key, value in parse_overrides(overrides).items():
|
|
381
377
|
if "." not in key:
|
|
382
378
|
parts = (name, key)
|
|
383
379
|
else:
|
|
384
380
|
parts = key.split(".")
|
|
385
|
-
if parts[0] in
|
|
381
|
+
if parts[0] in parameters and parts[0] not in config:
|
|
386
382
|
parts = (name, *parts)
|
|
387
383
|
current = config
|
|
388
384
|
if parts[0] not in current:
|
|
@@ -392,9 +388,11 @@ class Cli:
|
|
|
392
388
|
current[parts[-1]] = value
|
|
393
389
|
|
|
394
390
|
try:
|
|
395
|
-
default_seed =
|
|
396
|
-
if default_seed
|
|
391
|
+
default_seed = parameters["seed"].default if "seed" in parameters else None
|
|
392
|
+
if isinstance(default_seed, FieldInfo):
|
|
397
393
|
default_seed = default_seed.get_default()
|
|
394
|
+
elif default_seed is inspect.Parameter.empty:
|
|
395
|
+
default_seed = None
|
|
398
396
|
seed = Config.resolve(
|
|
399
397
|
config.get(name, {}).get("seed", default_seed),
|
|
400
398
|
registry=registry,
|
|
@@ -414,8 +412,8 @@ class Cli:
|
|
|
414
412
|
)
|
|
415
413
|
return validated(**resolved_config, config_meta=config_meta)
|
|
416
414
|
return validated(**resolved_config)
|
|
417
|
-
except
|
|
418
|
-
e
|
|
415
|
+
except ConfitValidationError as e:
|
|
416
|
+
e = e.with_path((name,))
|
|
419
417
|
if is_debug() or e.__cause__ is not None:
|
|
420
418
|
raise e
|
|
421
419
|
print("Validation error:", str(e))
|
|
@@ -15,7 +15,6 @@ from confit.errors import (
|
|
|
15
15
|
ConfitValidationError,
|
|
16
16
|
CyclicReferenceError,
|
|
17
17
|
MissingReference,
|
|
18
|
-
patch_errors,
|
|
19
18
|
remove_lib_from_traceback,
|
|
20
19
|
)
|
|
21
20
|
from confit.utils.collections import flatten_sections, join_path, split_path
|
|
@@ -118,13 +117,9 @@ class Config(dict):
|
|
|
118
117
|
current = current[part]
|
|
119
118
|
|
|
120
119
|
current.clear()
|
|
121
|
-
errors = []
|
|
122
120
|
for k, v in parser.items(section):
|
|
123
121
|
current[loads(k)] = loads(v)
|
|
124
122
|
|
|
125
|
-
if errors:
|
|
126
|
-
raise ConfitValidationError(errors=errors)
|
|
127
|
-
|
|
128
123
|
if resolve:
|
|
129
124
|
return config.resolve(registry=registry)
|
|
130
125
|
|
|
@@ -464,11 +459,9 @@ class Config(dict):
|
|
|
464
459
|
# we need to do it here
|
|
465
460
|
Config._store_resolved(resolved, cfg)
|
|
466
461
|
except ConfitValidationError as e:
|
|
467
|
-
e =
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
name=getattr(e, "name", None),
|
|
471
|
-
).with_traceback(remove_lib_from_traceback(e.__traceback__))
|
|
462
|
+
e = e.with_path(loc).with_traceback(
|
|
463
|
+
remove_lib_from_traceback(e.__traceback__)
|
|
464
|
+
)
|
|
472
465
|
if not is_debug():
|
|
473
466
|
e.__cause__ = None
|
|
474
467
|
e.__suppress_context__ = True
|
|
@@ -11,22 +11,12 @@ from typing import (
|
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
import pydantic
|
|
14
|
+
from pydantic_core import core_schema
|
|
14
15
|
from typing_extensions import ParamSpec, Protocol
|
|
15
16
|
|
|
16
|
-
from confit.errors import
|
|
17
|
-
ConfitValidationError,
|
|
18
|
-
ErrorWrapper,
|
|
19
|
-
patch_errors,
|
|
20
|
-
to_legacy_error,
|
|
21
|
-
)
|
|
17
|
+
from confit.errors import ConfitValidationError
|
|
22
18
|
from confit.typing import cast
|
|
23
19
|
|
|
24
|
-
if pydantic.VERSION >= "2":
|
|
25
|
-
from pydantic_core import core_schema
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
PYDANTIC_V1 = pydantic.VERSION.split(".")[0] == "1"
|
|
29
|
-
|
|
30
20
|
P = ParamSpec("P")
|
|
31
21
|
R = TypeVar("R", covariant=True)
|
|
32
22
|
|
|
@@ -94,14 +84,9 @@ class MetaDraft(type):
|
|
|
94
84
|
|
|
95
85
|
def validate(cls, value, config=None):
|
|
96
86
|
if not isinstance(value, Draft):
|
|
97
|
-
raise ConfitValidationError(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
exc=TypeError(f"Expected {cls}, got {value.__class__}"),
|
|
101
|
-
loc=(),
|
|
102
|
-
),
|
|
103
|
-
],
|
|
104
|
-
model=cls,
|
|
87
|
+
raise ConfitValidationError.from_exception(
|
|
88
|
+
TypeError(f"Expected {cls}, got {value.__class__}"),
|
|
89
|
+
source=cls,
|
|
105
90
|
name=cls.__name__,
|
|
106
91
|
)
|
|
107
92
|
actual = value._func
|
|
@@ -115,23 +100,13 @@ class MetaDraft(type):
|
|
|
115
100
|
else: # pragma: no cover
|
|
116
101
|
cast(Union[Type[cls.type_], Callable[..., cls.type_]], actual)
|
|
117
102
|
except pydantic.ValidationError as e:
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
ErrorWrapper(
|
|
122
|
-
exc=TypeError(f"Expected {cls}, got {Draft[actual]}"),
|
|
123
|
-
loc=e.raw_errors[0]._loc,
|
|
124
|
-
),
|
|
125
|
-
],
|
|
126
|
-
model=cls,
|
|
103
|
+
raise ConfitValidationError.from_exception(
|
|
104
|
+
TypeError(f"Expected {cls}, got {Draft[actual]}"),
|
|
105
|
+
source=cls,
|
|
127
106
|
name=cls.__name__,
|
|
128
|
-
)
|
|
129
|
-
raise e
|
|
107
|
+
).with_path(e.errors()[0]["loc"])
|
|
130
108
|
return value
|
|
131
109
|
|
|
132
|
-
def __get_validators__(cls):
|
|
133
|
-
yield cls.validate
|
|
134
|
-
|
|
135
110
|
def __get_pydantic_core_schema__(cls, source, handler):
|
|
136
111
|
return core_schema.no_info_plain_validator_function(cls.validate)
|
|
137
112
|
|
|
@@ -183,11 +158,7 @@ class Draft(Generic[R], metaclass=MetaDraft):
|
|
|
183
158
|
try:
|
|
184
159
|
res = self._func(**{**kwargs, **self._kwargs})
|
|
185
160
|
except ConfitValidationError as e:
|
|
186
|
-
raise
|
|
187
|
-
patch_errors(e.raw_errors, self._loc, self._kwargs),
|
|
188
|
-
model=e.model,
|
|
189
|
-
name=getattr(e, "name", None),
|
|
190
|
-
).with_traceback(e.__traceback__)
|
|
161
|
+
raise e.with_path(self._loc).with_traceback(e.__traceback__)
|
|
191
162
|
return res
|
|
192
163
|
|
|
193
164
|
def _raise_draft_error(self):
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
import warnings
|
|
3
|
+
from textwrap import indent
|
|
4
|
+
from typing import Callable, Tuple, Union
|
|
5
|
+
|
|
6
|
+
import pydantic
|
|
7
|
+
|
|
8
|
+
from confit.utils.collections import join_path
|
|
9
|
+
from confit.utils.settings import is_debug
|
|
10
|
+
from confit.utils.xjson import Reference
|
|
11
|
+
|
|
12
|
+
Loc = Tuple[Union[int, str]]
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MissingReference(Exception):
|
|
16
|
+
"""
|
|
17
|
+
Raised when one or multiple references cannot be resolved.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, ref: Reference):
|
|
21
|
+
"""
|
|
22
|
+
Parameters
|
|
23
|
+
----------
|
|
24
|
+
ref: Reference
|
|
25
|
+
The reference that could not be resolved.
|
|
26
|
+
"""
|
|
27
|
+
self.ref = ref
|
|
28
|
+
super().__init__()
|
|
29
|
+
|
|
30
|
+
def __str__(self):
|
|
31
|
+
"""
|
|
32
|
+
String representation of the exception
|
|
33
|
+
"""
|
|
34
|
+
return "Could not interpolate the following reference: {}".format(self.ref)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class CyclicReferenceError(Exception):
|
|
38
|
+
"""
|
|
39
|
+
Raised when a cyclic reference is detected.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self, path: Loc):
|
|
43
|
+
"""
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
path: Loc
|
|
47
|
+
The path of the cyclic reference
|
|
48
|
+
"""
|
|
49
|
+
self.path = path
|
|
50
|
+
super().__init__()
|
|
51
|
+
|
|
52
|
+
def __str__(self):
|
|
53
|
+
"""
|
|
54
|
+
String representation of the exception
|
|
55
|
+
"""
|
|
56
|
+
return "Cyclic reference detected at {}".format(join_path(self.path))
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def remove_lib_from_traceback(tb):
|
|
60
|
+
"""
|
|
61
|
+
Remove the lib folder from the traceback
|
|
62
|
+
"""
|
|
63
|
+
# compare package to module in f_globals
|
|
64
|
+
if is_debug():
|
|
65
|
+
return tb
|
|
66
|
+
if tb is not None and tb.tb_frame.f_globals.get("__package__") == __package__:
|
|
67
|
+
return remove_lib_from_traceback(tb.tb_next)
|
|
68
|
+
if tb is None or tb.tb_next is None:
|
|
69
|
+
return tb
|
|
70
|
+
tb.tb_next = remove_lib_from_traceback(tb.tb_next)
|
|
71
|
+
return tb
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class ConfitValidationError(ValueError):
|
|
75
|
+
"""
|
|
76
|
+
Structured validation errors with paths added by config and pipeline resolution
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
def __init__(self, errors, *, name=None, source=None, model=None):
|
|
80
|
+
if model is not None:
|
|
81
|
+
warnings.warn(
|
|
82
|
+
"Use source instead of model", DeprecationWarning, stacklevel=2
|
|
83
|
+
)
|
|
84
|
+
source = model
|
|
85
|
+
self.source = source
|
|
86
|
+
self.name = name or getattr(source, "__qualname__", None)
|
|
87
|
+
# Old EDS-NLP callers can aggregate lists of errors inside another list
|
|
88
|
+
self.details = [
|
|
89
|
+
detail
|
|
90
|
+
for error in errors
|
|
91
|
+
for detail in (
|
|
92
|
+
ConfitValidationError(error).details
|
|
93
|
+
if isinstance(error, list)
|
|
94
|
+
else [error]
|
|
95
|
+
)
|
|
96
|
+
]
|
|
97
|
+
super().__init__(self.details)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def from_exception(cls, error, *, source=None, name=None, signature=None):
|
|
101
|
+
"""
|
|
102
|
+
Normalize Pydantic errors before config resolution adds its parent path
|
|
103
|
+
"""
|
|
104
|
+
if isinstance(error, cls):
|
|
105
|
+
return cls(
|
|
106
|
+
error.errors(), source=source or error.source, name=name or error.name
|
|
107
|
+
)
|
|
108
|
+
if not isinstance(error, pydantic.ValidationError):
|
|
109
|
+
return cls(
|
|
110
|
+
[
|
|
111
|
+
{
|
|
112
|
+
"loc": (),
|
|
113
|
+
"msg": str(error),
|
|
114
|
+
"type": "value_error",
|
|
115
|
+
"ctx": {"error": error},
|
|
116
|
+
}
|
|
117
|
+
],
|
|
118
|
+
source=source,
|
|
119
|
+
name=name,
|
|
120
|
+
)
|
|
121
|
+
details = []
|
|
122
|
+
for detail in error.errors(include_url=False):
|
|
123
|
+
loc = detail["loc"]
|
|
124
|
+
if signature is not None and loc and isinstance(loc[0], int):
|
|
125
|
+
names = [
|
|
126
|
+
name
|
|
127
|
+
for name, p in signature.parameters.items()
|
|
128
|
+
if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD)
|
|
129
|
+
]
|
|
130
|
+
if loc[0] < len(names):
|
|
131
|
+
loc = (names[loc[0]], *loc[1:])
|
|
132
|
+
nested = detail.get("ctx", {}).get("error")
|
|
133
|
+
if isinstance(nested, cls):
|
|
134
|
+
target = source
|
|
135
|
+
# Only typed construction joins paths, separate calls keep their name
|
|
136
|
+
try:
|
|
137
|
+
for part in loc:
|
|
138
|
+
target = (
|
|
139
|
+
target.model_fields[part].annotation
|
|
140
|
+
if hasattr(target, "model_fields")
|
|
141
|
+
else inspect.signature(target).parameters[part].annotation
|
|
142
|
+
)
|
|
143
|
+
except (KeyError, AttributeError, TypeError, ValueError):
|
|
144
|
+
target = None
|
|
145
|
+
if target is not None and target is nested.source:
|
|
146
|
+
details.extend(nested.with_path(loc).errors())
|
|
147
|
+
continue
|
|
148
|
+
details.append({**detail, "loc": loc})
|
|
149
|
+
return cls(details, source=source, name=name)
|
|
150
|
+
|
|
151
|
+
def with_path(self, path, *, name=None):
|
|
152
|
+
"""
|
|
153
|
+
Return an error with a parent path without modifying shared component errors
|
|
154
|
+
"""
|
|
155
|
+
return type(self)(
|
|
156
|
+
[{**detail, "loc": (*path, *detail["loc"])} for detail in self.details],
|
|
157
|
+
source=self.source,
|
|
158
|
+
name=name or self.name,
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
@classmethod
|
|
162
|
+
def combine(cls, errors, *, name=None):
|
|
163
|
+
"""
|
|
164
|
+
Collect failures from sibling components in their original order
|
|
165
|
+
"""
|
|
166
|
+
return cls([detail for error in errors for detail in error.errors()], name=name)
|
|
167
|
+
|
|
168
|
+
def errors(self):
|
|
169
|
+
return [dict(detail) for detail in self.details]
|
|
170
|
+
|
|
171
|
+
def __str__(self):
|
|
172
|
+
lines = []
|
|
173
|
+
for detail in self.details:
|
|
174
|
+
original = detail.get("ctx", {}).get("error")
|
|
175
|
+
original = original if isinstance(original, BaseException) else None
|
|
176
|
+
msg = str(original) if original is not None else detail["msg"]
|
|
177
|
+
if original is None:
|
|
178
|
+
msg = (
|
|
179
|
+
"field required"
|
|
180
|
+
if detail["type"].startswith("missing")
|
|
181
|
+
else msg[:1].lower() + msg[1:]
|
|
182
|
+
)
|
|
183
|
+
if "input" in detail and not detail["type"].startswith("missing"):
|
|
184
|
+
value = detail["input"]
|
|
185
|
+
try:
|
|
186
|
+
preview = repr(value)
|
|
187
|
+
except Exception:
|
|
188
|
+
preview = object.__repr__(value)
|
|
189
|
+
preview = preview[:50] + "..." if len(preview) > 50 else preview
|
|
190
|
+
msg += f", got {preview} ({type(value).__name__})"
|
|
191
|
+
lines.append(
|
|
192
|
+
"-> " + ".".join(map(str, detail["loc"])) + "\n" + indent(msg, " ")
|
|
193
|
+
)
|
|
194
|
+
count = len(self.details)
|
|
195
|
+
name = f" for {self.name}()" if self.name is not None else ""
|
|
196
|
+
return (
|
|
197
|
+
f"{count} validation error{'s' if count != 1 else ''}{name}\n"
|
|
198
|
+
+ "\n".join(lines)
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def raw_errors(self):
|
|
203
|
+
warnings.warn(
|
|
204
|
+
"Use errors() instead of raw_errors", DeprecationWarning, stacklevel=2
|
|
205
|
+
)
|
|
206
|
+
return self.errors()
|
|
207
|
+
|
|
208
|
+
@raw_errors.setter
|
|
209
|
+
def raw_errors(self, errors):
|
|
210
|
+
warnings.warn(
|
|
211
|
+
"Use with_path or combine instead of assigning raw_errors",
|
|
212
|
+
DeprecationWarning,
|
|
213
|
+
stacklevel=2,
|
|
214
|
+
)
|
|
215
|
+
self.details = type(self)(errors).details
|
|
216
|
+
|
|
217
|
+
@property
|
|
218
|
+
def model(self):
|
|
219
|
+
warnings.warn("Use source instead of model", DeprecationWarning, stacklevel=2)
|
|
220
|
+
return self.source
|
|
221
|
+
|
|
222
|
+
@model.setter
|
|
223
|
+
def model(self, model):
|
|
224
|
+
warnings.warn("Use source instead of model", DeprecationWarning, stacklevel=2)
|
|
225
|
+
self.source = model
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class SignatureError(TypeError):
|
|
229
|
+
def __init__(self, func: Callable):
|
|
230
|
+
message = f"{func} must not have positional only args or duplicated kwargs"
|
|
231
|
+
super().__init__(message)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def patch_errors(errors, path=(), values=None, model=None, drop_names=()):
|
|
235
|
+
"""
|
|
236
|
+
Adapt old EDS-NLP error calls to ConfitValidationError methods
|
|
237
|
+
"""
|
|
238
|
+
warnings.warn(
|
|
239
|
+
"Use ConfitValidationError.from_exception and with_path "
|
|
240
|
+
"instead of patch_errors",
|
|
241
|
+
DeprecationWarning,
|
|
242
|
+
stacklevel=2,
|
|
243
|
+
)
|
|
244
|
+
is_exception = isinstance(errors, (pydantic.ValidationError, ConfitValidationError))
|
|
245
|
+
error = (
|
|
246
|
+
ConfitValidationError.from_exception(errors, source=model)
|
|
247
|
+
if is_exception
|
|
248
|
+
else ConfitValidationError(errors, source=model)
|
|
249
|
+
)
|
|
250
|
+
error = error.with_path(path)
|
|
251
|
+
if drop_names:
|
|
252
|
+
error = ConfitValidationError(
|
|
253
|
+
[
|
|
254
|
+
{
|
|
255
|
+
**detail,
|
|
256
|
+
"loc": tuple(
|
|
257
|
+
part for part in detail["loc"] if part not in drop_names
|
|
258
|
+
),
|
|
259
|
+
}
|
|
260
|
+
for detail in error.errors()
|
|
261
|
+
],
|
|
262
|
+
source=error.source,
|
|
263
|
+
name=error.name,
|
|
264
|
+
)
|
|
265
|
+
return error if is_exception else error.errors()
|