ab-pydantic-patch 1.3.2__tar.gz → 1.3.3__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.
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/PKG-INFO +1 -1
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/pyproject.toml +1 -1
- ab_pydantic_patch-1.3.3/src/ab_core/pydantic_patch/orm_clone.py +103 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/.gitignore +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/LICENSE +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/README.md +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/cache.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/classproperty.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/computed_field_type_hints.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/errors.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/field_type_hints.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/fields.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/forward_references.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/operation.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/orm_type_hints.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/payload.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/payload_types.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/transform.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/type_hints.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/types.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/pydantic_examples/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/pydantic_examples/pydantic_computed_fields.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/pydantic_examples/pydantic_discriminated_union_api_schema.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/project.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/project_milestone.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/project_task.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/quote_line_item.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/task_comment.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/models/user.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/sqlmodel_computed_fields.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/sqlmodel_patch.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/sqlmodel_examples/sqlmodel_self_referencing_tree.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/api.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/operation.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/orm_patch.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/api.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/operation.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/api.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/operation.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/api.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/operation.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pydantic_jsonb.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/__init__.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/api.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/config.py +0 -0
- {ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/operation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ab-pydantic-patch
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.3
|
|
4
4
|
Summary: Python Pydantic support of TypeScript-style utility types, including Partial, Required, Pick, and Omit. Useful for PATCH endpoints driven from BaseModel / SQLModel classes.
|
|
5
5
|
Author-email: Matt Coulter <mattcoul7@gmail.com>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,7 +28,7 @@ description = "Python Pydantic support of TypeScript-style utility types, includ
|
|
|
28
28
|
name = "ab-pydantic-patch"
|
|
29
29
|
readme = "README.md"
|
|
30
30
|
requires-python = ">=3.12,<4.0"
|
|
31
|
-
version = "1.3.
|
|
31
|
+
version = "1.3.3"
|
|
32
32
|
|
|
33
33
|
[project.optional-dependencies]
|
|
34
34
|
orm = [
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Utility for cloning SQLModel ORM objects without copying SQLAlchemy instance state."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from sqlalchemy import inspect
|
|
8
|
+
from sqlalchemy.orm.attributes import NO_VALUE
|
|
9
|
+
from sqlmodel import SQLModel
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def recursive_clone_scalar[T: SQLModel](
|
|
13
|
+
obj: T,
|
|
14
|
+
*,
|
|
15
|
+
include_primary_keys: bool = False,
|
|
16
|
+
include_foreign_keys: bool = True,
|
|
17
|
+
_memo: dict[int, SQLModel] | None = None,
|
|
18
|
+
) -> T:
|
|
19
|
+
"""Clone a SQLModel ORM object graph without copying SQLAlchemy instance state.
|
|
20
|
+
|
|
21
|
+
- Copies mapped column values.
|
|
22
|
+
- Copies already-loaded relationships.
|
|
23
|
+
- Recursively clones related SQLModel objects.
|
|
24
|
+
- Handles cycles/shared objects via memo.
|
|
25
|
+
- Does not use model_dump(), so relationships are preserved.
|
|
26
|
+
- Does not copy _sa_instance_state.
|
|
27
|
+
"""
|
|
28
|
+
if _memo is None:
|
|
29
|
+
_memo = {}
|
|
30
|
+
|
|
31
|
+
obj_id = id(obj)
|
|
32
|
+
if obj_id in _memo:
|
|
33
|
+
return _memo[obj_id] # type: ignore[return-value]
|
|
34
|
+
|
|
35
|
+
state = inspect(obj)
|
|
36
|
+
mapper = state.mapper
|
|
37
|
+
cls = type(obj)
|
|
38
|
+
|
|
39
|
+
column_values: dict[str, Any] = {}
|
|
40
|
+
|
|
41
|
+
primary_key_column_keys = {column.key for column in mapper.primary_key}
|
|
42
|
+
|
|
43
|
+
foreign_key_column_keys = {column.key for column in mapper.columns if column.foreign_keys}
|
|
44
|
+
|
|
45
|
+
for column_attr in mapper.column_attrs:
|
|
46
|
+
key = column_attr.key
|
|
47
|
+
|
|
48
|
+
if not include_primary_keys and key in primary_key_column_keys:
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if not include_foreign_keys and key in foreign_key_column_keys:
|
|
52
|
+
continue
|
|
53
|
+
|
|
54
|
+
attr_state = state.attrs[key]
|
|
55
|
+
value = attr_state.loaded_value
|
|
56
|
+
|
|
57
|
+
if value is NO_VALUE:
|
|
58
|
+
# Avoid triggering lazy loads / missing state.
|
|
59
|
+
continue
|
|
60
|
+
|
|
61
|
+
column_values[key] = value
|
|
62
|
+
|
|
63
|
+
clone = cls(**column_values)
|
|
64
|
+
_memo[obj_id] = clone
|
|
65
|
+
|
|
66
|
+
for relationship in mapper.relationships:
|
|
67
|
+
key = relationship.key
|
|
68
|
+
|
|
69
|
+
attr_state = state.attrs[key]
|
|
70
|
+
value = attr_state.loaded_value
|
|
71
|
+
|
|
72
|
+
if value is NO_VALUE:
|
|
73
|
+
# Relationship was not populated, so don't accidentally lazy-load it.
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
if value is None:
|
|
77
|
+
setattr(clone, key, None)
|
|
78
|
+
continue
|
|
79
|
+
|
|
80
|
+
if relationship.uselist:
|
|
81
|
+
cloned_items = [
|
|
82
|
+
recursive_clone_scalar(
|
|
83
|
+
item,
|
|
84
|
+
include_primary_keys=include_primary_keys,
|
|
85
|
+
include_foreign_keys=include_foreign_keys,
|
|
86
|
+
_memo=_memo,
|
|
87
|
+
)
|
|
88
|
+
for item in value
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
setattr(clone, key, cloned_items)
|
|
92
|
+
|
|
93
|
+
else:
|
|
94
|
+
cloned_related = recursive_clone_scalar(
|
|
95
|
+
value,
|
|
96
|
+
include_primary_keys=include_primary_keys,
|
|
97
|
+
include_foreign_keys=include_foreign_keys,
|
|
98
|
+
_memo=_memo,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
setattr(clone, key, cloned_related)
|
|
102
|
+
|
|
103
|
+
return clone
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/__init__.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/cache.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/classproperty.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/errors.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/fields.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/operation.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/payload.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/payload_types.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/transform.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/type_hints.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/core/types.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/examples/__init__.py
RENAMED
|
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
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/omit/operation.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/__init__.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/api.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/partial/operation.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/patch/operation.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pick/operation.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/pydantic_jsonb.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/__init__.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/api.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/config.py
RENAMED
|
File without changes
|
{ab_pydantic_patch-1.3.2 → ab_pydantic_patch-1.3.3}/src/ab_core/pydantic_patch/required/operation.py
RENAMED
|
File without changes
|