Skip to content

Commit 0e41325

Browse files
authored
Merge pull request #387 from reagento/develop
Develop
2 parents 9c8606c + 68975bc commit 0e41325

File tree

64 files changed

+1162
-1094
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+1162
-1094
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ An extremely flexible and configurable data model conversion library.
2525

2626
Install
2727
```bash
28-
pip install adaptix==3.0.0b10
28+
pip install adaptix==3.0.0b11
2929
```
3030

3131
Use for model loading and dumping.

docs/changelog/changelog_body.rst

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,79 @@
11
----------------------------------------------------
22

33

4+
.. _v3.0.0b11:
5+
6+
`3.0.0b11 <https://github.com/reagento/adaptix/tree/v3.0.0b11>`__ -- 2025-05-09
7+
===============================================================================
8+
9+
.. _v3.0.0b11-Features:
10+
11+
Features
12+
--------
13+
14+
15+
- Completely redesigned error rendering system.
16+
All errors related to loader, dumper, and converter generation now utilize a new compact and intuitive display format.
17+
Error messages have also been substantially improved for clarity.
18+
19+
.. code-block:: text
20+
:caption: Old error example
21+
22+
| adaptix.AggregateCannotProvide: Cannot create loader for model. Loaders for some fields cannot be created (1 sub-exception)
23+
| Location: `Book`
24+
+-+---------------- 1 ----------------
25+
| adaptix.AggregateCannotProvide: Cannot create loader for model. Cannot fetch InputNameLayout (1 sub-exception)
26+
| Location: `Book.author: Person`
27+
+-+---------------- 1 ----------------
28+
| adaptix.CannotProvide: Required fields ['last_name'] are skipped
29+
| Location: `Book.author: Person`
30+
+------------------------------------
31+
32+
The above exception was the direct cause of the following exception:
33+
34+
Traceback (most recent call last):
35+
...
36+
adaptix.ProviderNotFoundError: Cannot produce loader for type <class '__main__.Book'>
37+
Note: The attached exception above contains verbose description of the problem
38+
39+
40+
.. code-block:: text
41+
:caption: New error example
42+
43+
Traceback (most recent call last):
44+
...
45+
adaptix.ProviderNotFoundError: Cannot produce loader for type <class '__main__.Book'>
46+
× Cannot create loader for model. Loaders for some fields cannot be created
47+
│ Location: ‹Book›
48+
╰──▷ Cannot create loader for model. Cannot fetch `InputNameLayout`
49+
│ Location: ‹Book.author: Person›
50+
╰──▷ Required fields ['last_name'] are skipped
51+
52+
.. _v3.0.0b11-Breaking Changes:
53+
54+
Breaking Changes
55+
----------------
56+
57+
- Custom iterable subclasses are no longer supported.
58+
To use them, register via the internal (temporary) API with IterableProvider.
59+
- The ``Retort.replace`` method now requires ``Omitted`` instead of ``None`` to skip parameter values.
60+
- Removed the ``hide_traceback`` parameter from ``Retort`` and ``Retort.replace``.
61+
Error rendering is now controlled via the ``error_renderer`` parameter.
62+
Pass ``None`` to display raw Python ``ExceptionGroup`` traces.
63+
64+
.. _v3.0.0b11-Bug Fixes:
65+
66+
Bug Fixes
67+
---------
68+
69+
- Fixed incorrect classification of parametrized generic Pydantic models as iterables
70+
(due to Pydantic model instances being inherently iterable).
71+
- Corrected hint generation errors during model conversion.
72+
- Fixed handling of parametrized TypeAlias.
73+
74+
----------------------------------------------------
75+
76+
477
.. _v3.0.0b10:
578

679
`3.0.0b10 <https://github.com/reagento/adaptix/tree/v3.0.0b10>`__ -- 2025-04-13

docs/common/installation.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ Just use pip to install the library
22

33
.. code-block:: text
44
5-
pip install adaptix==3.0.0b10
5+
pip install adaptix==3.0.0b11
66
77
88
Integrations with 3-rd party libraries are turned on automatically,
@@ -20,5 +20,5 @@ So, this is valid installation variants:
2020

2121
.. code-block:: text
2222
23-
pip install adaptix[attrs-strict]==3.0.0b10
24-
pip install adaptix[attrs, sqlalchemy-strict]==3.0.0b10
23+
pip install adaptix[attrs-strict]==3.0.0b11
24+
pip install adaptix[attrs, sqlalchemy-strict]==3.0.0b11

docs/conf.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,12 @@
7878
"sidebar_hide_name": True,
7979
"light_logo": "adaptix-with-title-light.png",
8080
"dark_logo": "adaptix-with-title-dark.png",
81+
"light_css_variables": {
82+
"font-stack--monospace": 'Hack, SFMono-Regular, Menlo, Consolas, Monaco, "Liberation Mono", "Lucida Console", monospace',
83+
},
84+
"dark_css_variables": {
85+
"font-stack--monospace": 'Hack, SFMono-Regular, Menlo, Consolas, Monaco, "Liberation Mono", "Lucida Console", monospace',
86+
},
8187
}
8288

8389
# Add any paths that contain custom static files (such as style sheets) here,
Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
| adaptix.AggregateCannotProvide: Cannot create loader for model. Cannot fetch InputNameLayout (1 sub-exception)
2-
| Location: `User`
3-
+-+---------------- 1 ----------------
4-
| adaptix.CannotProvide: Required fields ['password_hash'] are skipped
5-
| Location: `User`
6-
+------------------------------------
7-
8-
The above exception was the direct cause of the following exception:
9-
101
Traceback (most recent call last):
112
...
12-
adaptix.NoSuitableProvider: Cannot produce loader for type <class 'docs.examples.extended_usage.fields_filtering_only.User'>
13-
Note: The attached exception above contains verbose description of the problem
3+
adaptix.ProviderNotFoundError: Cannot produce loader for type <class '__main__.User'>
4+
× Cannot create loader for model. Cannot fetch `InputNameLayout`
5+
│ Location: ‹User›
6+
╰──▷ Required fields ['password_hash'] are skipped
Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,6 @@
1-
| adaptix.AggregateCannotProvide: Cannot create loader for model. Cannot fetch InputNameLayout (1 sub-exception)
2-
| Location: `User`
3-
+-+---------------- 1 ----------------
4-
| adaptix.CannotProvide: Required fields ['password_hash'] are skipped
5-
| Location: `User`
6-
+------------------------------------
7-
8-
The above exception was the direct cause of the following exception:
9-
101
Traceback (most recent call last):
112
...
12-
adaptix.NoSuitableProvider: Cannot produce loader for type <class 'docs.examples.extended_usage.fields_filtering_skip.User'>
13-
Note: The attached exception above contains verbose description of the problem
3+
adaptix.ProviderNotFoundError: Cannot produce loader for type <class '__main__.User'>
4+
× Cannot create loader for model. Cannot fetch `InputNameLayout`
5+
│ Location: ‹User›
6+
╰──▷ Required fields ['password_hash'] are skipped

docs/loading-and-dumping/specific-types-behavior.rst

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -227,19 +227,24 @@ If there are several parents, it will be the selected class that appears first i
227227
Also, builtin dumper can work only with class type hints and ``Literal``.
228228
For example, type hints like ``LiteralString | int`` can not be dumped.
229229

230-
Iterable subclasses
230+
Iterables
231231
'''''''''''''''''''''
232232

233+
Exact list: ``list``, ``tuple``,
234+
``set``, ``frozenset``, ``collections.deque``,
235+
``Iterable``, ``Reversible``, ``Collection``, ``Sequence``,
236+
``MutableSequence``, ``Set``, ``MutableSet``.
237+
233238
If ``strict_coercion`` is enabled, the loader takes any iterable excluding ``str`` and ``Mapping``.
234239
If ``strict_coercion`` is disabled, any iterable are accepted.
235240

236-
Dumper produces the ``tuple`` (or ``list`` for list children) with dumped elements.
241+
Dumper produces the ``tuple`` (or ``list`` for mutable types) with dumped elements.
237242

238243
If you require a loader for abstract type, a minimal suitable type will be used.
239244
For type ``Iterable[int]`` retort will use ``tuple``.
240245

241-
Tuple of dynamic length like ``*tuple[int, ...]`` isn't supported yet.
242-
This doesn't applies for tuples like ``*tuple[int, str]`` (constant length tuples).
246+
Tuples with unpacking variable-length tuple like ``tuple[str, *tuple[int, ...]]`` are not supported yet.
247+
This doesn't applies for tuples like ``tuple[str, int]`` (constant length tuples).
243248

244249
Dict and Mapping
245250
'''''''''''''''''''''

docs/overview.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Installation
1616

1717
.. code-block:: text
1818
19-
pip install adaptix==3.0.0b10
19+
pip install adaptix==3.0.0b11
2020
2121
2222
Example

pyproject.toml

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = 'setuptools.build_meta'
44

55
[project]
66
name = 'adaptix'
7-
version = '3.0.0b10'
7+
version = '3.0.0b11'
88
description = 'An extremely flexible and configurable data model conversion library'
99
readme = 'README.md'
1010
requires-python = '>=3.9'
@@ -122,6 +122,9 @@ line-length = 120
122122
output-format = "concise"
123123

124124
[tool.ruff.lint]
125+
126+
allowed-confusables = ['×', '', '']
127+
125128
select = ['ALL']
126129
fixable = [
127130
'Q000',
@@ -177,7 +180,7 @@ ignore = [
177180
[tool.ruff.lint.per-file-ignores]
178181
"__init__.py" = ['F401']
179182

180-
"test_*" = ['S101', 'PLR2004', 'PLC0105', 'N806', 'FA102', 'UP035', 'UP006']
183+
"test_*" = ['S101', 'PLR2004', 'PLC0105', 'N806', 'FA102', 'UP035', 'UP006', 'E501']
181184
"tests/*/local_helpers.py" = ['S101', 'PLR2004', 'PLC0105', 'N806', 'FA102']
182185
"tests/*/data_*.py" = ['F821']
183186
"tests/tests_helpers/*" = ['INP001', 'S101']

src/adaptix/_internal/conversion/facade/retort.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@
44
from inspect import Parameter, Signature
55
from typing import Any, Callable, Optional, TypeVar, overload
66

7-
from adaptix import TypeHint
8-
9-
from ...common import Converter
7+
from ...common import Converter, TypeHint
108
from ...provider.essential import Provider
119
from ...provider.loc_stack_filtering import P
1210
from ...provider.shape_provider import BUILTIN_SHAPE_PROVIDER
11+
from ...retort.error_renderer import ErrorRenderer
1312
from ...retort.operating_retort import OperatingRetort
1413
from ...type_tools import is_generic_class
14+
from ...utils import Omittable, Omitted
1515
from ..coercer_provider import (
1616
DictCoercerProvider,
1717
DstAnyCoercerProvider,
@@ -64,10 +64,14 @@ def _calculate_derived(self) -> None:
6464
super()._calculate_derived()
6565
self._simple_converter_cache: dict[tuple[TypeHint, TypeHint, Optional[str]], Converter] = {}
6666

67-
def replace(self: AR, *, hide_traceback: Optional[bool] = None) -> AR:
67+
def replace(
68+
self: AR,
69+
*,
70+
error_renderer: Omittable[Optional[ErrorRenderer]] = Omitted(),
71+
) -> AR:
6872
with self._clone() as clone:
69-
if hide_traceback is not None:
70-
clone._hide_traceback = hide_traceback
73+
if not isinstance(error_renderer, Omitted):
74+
clone._error_renderer = error_renderer
7175
return clone
7276

7377
def extend(self: AR, *, recipe: Iterable[Provider]) -> AR:
@@ -187,7 +191,7 @@ def convert(self, src_obj: Any, dst: type[DstT], *, recipe: Iterable[Provider] =
187191
src = type(src_obj)
188192
if is_generic_class(src):
189193
raise ValueError(
190-
f"Can not infer the actual type of generic class instance ({src!r}),"
194+
f"Cannot infer the actual type of generic class instance ({src!r}),"
191195
" you have to use `get_converter` explicitly passing the type of object",
192196
)
193197

0 commit comments

Comments
 (0)