pydantic-json-patch 0.6.2__tar.gz → 0.6.4__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.
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/PKG-INFO +11 -5
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/README.md +10 -4
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/pyproject.toml +8 -1
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/src/pydantic_json_patch/models.py +32 -20
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/src/pydantic_json_patch/__init__.py +0 -0
- {pydantic_json_patch-0.6.2 → pydantic_json_patch-0.6.4}/src/pydantic_json_patch/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic_json_patch
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: Pydantic models for implementing JSON Patch.
|
|
5
5
|
Author: Jonathan Sharpe
|
|
6
6
|
Author-email: Jonathan Sharpe <mail@jonrshar.pe>
|
|
@@ -33,8 +33,9 @@ Description-Content-Type: text/markdown
|
|
|
33
33
|
# Pydantic JSON Patch
|
|
34
34
|
|
|
35
35
|
[![Python uv CI][ci-badge]][ci-page]
|
|
36
|
-
[![Coverage Status][coverage-badge]][coverage-page]
|
|
37
36
|
[![PyPI - Version][pypi-badge]][pypi-page]
|
|
37
|
+
[![SonarQube Cloud - Maintainability][sonar-badge]][sonar-page]
|
|
38
|
+
[![Coverage Status][coverage-badge]][coverage-page]
|
|
38
39
|
|
|
39
40
|
[Pydantic] models for implementing [JSON Patch].
|
|
40
41
|
|
|
@@ -176,7 +177,10 @@ def _(
|
|
|
176
177
|
...
|
|
177
178
|
```
|
|
178
179
|
|
|
179
|
-
**
|
|
180
|
+
**Notes**:
|
|
181
|
+
|
|
182
|
+
- Explicitly specifying the [discriminator][pydantic-discriminator] gives better results on _failed_ validation for unions of operations; and
|
|
183
|
+
- Parameterised versions of the operations will also appear in the JSON Schema as e.g. `AddOp_int_` (with the title _"JsonPatchAddOperation\[int\]"_).
|
|
180
184
|
|
|
181
185
|
## Development
|
|
182
186
|
|
|
@@ -217,8 +221,8 @@ This will auto-restart as you make changes.
|
|
|
217
221
|
|
|
218
222
|
[ci-badge]: https://github.com/textbook/pydantic_json_patch/actions/workflows/push.yml/badge.svg
|
|
219
223
|
[ci-page]: https://github.com/textbook/pydantic_json_patch/actions/workflows/push.yml
|
|
220
|
-
[coverage-badge]: https://
|
|
221
|
-
[coverage-page]: https://
|
|
224
|
+
[coverage-badge]: https://sonarcloud.io/api/project_badges/measure?project=textbook_pydantic_json_patch&metric=coverage
|
|
225
|
+
[coverage-page]: https://sonarcloud.io/summary/new_code?id=textbook_pydantic_json_patch
|
|
222
226
|
[fastapi]: https://fastapi.tiangolo.com/
|
|
223
227
|
[isort]: https://pycqa.github.io/isort/
|
|
224
228
|
[json patch]: https://datatracker.ietf.org/doc/html/rfc6902/
|
|
@@ -230,6 +234,8 @@ This will auto-restart as you make changes.
|
|
|
230
234
|
[pypi-page]: https://pypi.org/project/pydantic-json-patch/
|
|
231
235
|
[pytest]: https://docs.pytest.org/en/stable/
|
|
232
236
|
[ruff]: https://docs.astral.sh/ruff/
|
|
237
|
+
[sonar-badge]: https://sonarcloud.io/api/project_badges/measure?project=textbook_pydantic_json_patch&metric=sqale_rating
|
|
238
|
+
[sonar-page]: https://sonarcloud.io/summary/new_code?id=textbook_pydantic_json_patch
|
|
233
239
|
[swagger-example]: https://github.com/textbook/pydantic_json_patch/blob/main/docs/swagger-example.png?raw=true
|
|
234
240
|
[swagger-schemas]: https://github.com/textbook/pydantic_json_patch/blob/main/docs/swagger-schemas.png?raw=true
|
|
235
241
|
[ty]: https://docs.astral.sh/ty/
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
# Pydantic JSON Patch
|
|
2
2
|
|
|
3
3
|
[![Python uv CI][ci-badge]][ci-page]
|
|
4
|
-
[![Coverage Status][coverage-badge]][coverage-page]
|
|
5
4
|
[![PyPI - Version][pypi-badge]][pypi-page]
|
|
5
|
+
[![SonarQube Cloud - Maintainability][sonar-badge]][sonar-page]
|
|
6
|
+
[![Coverage Status][coverage-badge]][coverage-page]
|
|
6
7
|
|
|
7
8
|
[Pydantic] models for implementing [JSON Patch].
|
|
8
9
|
|
|
@@ -144,7 +145,10 @@ def _(
|
|
|
144
145
|
...
|
|
145
146
|
```
|
|
146
147
|
|
|
147
|
-
**
|
|
148
|
+
**Notes**:
|
|
149
|
+
|
|
150
|
+
- Explicitly specifying the [discriminator][pydantic-discriminator] gives better results on _failed_ validation for unions of operations; and
|
|
151
|
+
- Parameterised versions of the operations will also appear in the JSON Schema as e.g. `AddOp_int_` (with the title _"JsonPatchAddOperation\[int\]"_).
|
|
148
152
|
|
|
149
153
|
## Development
|
|
150
154
|
|
|
@@ -185,8 +189,8 @@ This will auto-restart as you make changes.
|
|
|
185
189
|
|
|
186
190
|
[ci-badge]: https://github.com/textbook/pydantic_json_patch/actions/workflows/push.yml/badge.svg
|
|
187
191
|
[ci-page]: https://github.com/textbook/pydantic_json_patch/actions/workflows/push.yml
|
|
188
|
-
[coverage-badge]: https://
|
|
189
|
-
[coverage-page]: https://
|
|
192
|
+
[coverage-badge]: https://sonarcloud.io/api/project_badges/measure?project=textbook_pydantic_json_patch&metric=coverage
|
|
193
|
+
[coverage-page]: https://sonarcloud.io/summary/new_code?id=textbook_pydantic_json_patch
|
|
190
194
|
[fastapi]: https://fastapi.tiangolo.com/
|
|
191
195
|
[isort]: https://pycqa.github.io/isort/
|
|
192
196
|
[json patch]: https://datatracker.ietf.org/doc/html/rfc6902/
|
|
@@ -198,6 +202,8 @@ This will auto-restart as you make changes.
|
|
|
198
202
|
[pypi-page]: https://pypi.org/project/pydantic-json-patch/
|
|
199
203
|
[pytest]: https://docs.pytest.org/en/stable/
|
|
200
204
|
[ruff]: https://docs.astral.sh/ruff/
|
|
205
|
+
[sonar-badge]: https://sonarcloud.io/api/project_badges/measure?project=textbook_pydantic_json_patch&metric=sqale_rating
|
|
206
|
+
[sonar-page]: https://sonarcloud.io/summary/new_code?id=textbook_pydantic_json_patch
|
|
201
207
|
[swagger-example]: https://github.com/textbook/pydantic_json_patch/blob/main/docs/swagger-example.png?raw=true
|
|
202
208
|
[swagger-schemas]: https://github.com/textbook/pydantic_json_patch/blob/main/docs/swagger-schemas.png?raw=true
|
|
203
209
|
[ty]: https://docs.astral.sh/ty/
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pydantic_json_patch"
|
|
3
|
-
version = "0.6.
|
|
3
|
+
version = "0.6.4"
|
|
4
4
|
description = "Pydantic models for implementing JSON Patch."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -49,6 +49,7 @@ dev = [
|
|
|
49
49
|
"fastapi-cli>=0.0.20",
|
|
50
50
|
"httpx>=0.28.1",
|
|
51
51
|
"isort>=7.0.0",
|
|
52
|
+
"joythief>=0.8.1",
|
|
52
53
|
"pre-commit>=4.5.1",
|
|
53
54
|
"pytest>=9.0.2",
|
|
54
55
|
"ruff>=0.15.0",
|
|
@@ -63,9 +64,15 @@ mutation = [
|
|
|
63
64
|
[tool.coverage.run]
|
|
64
65
|
omit = ["tests/**"]
|
|
65
66
|
|
|
67
|
+
[tool.coverage.html]
|
|
68
|
+
directory = "coverage/html/"
|
|
69
|
+
|
|
66
70
|
[tool.coverage.lcov]
|
|
67
71
|
output = "coverage/lcov.info"
|
|
68
72
|
|
|
73
|
+
[tool.coverage.xml]
|
|
74
|
+
output = "coverage/coverage.xml"
|
|
75
|
+
|
|
69
76
|
[tool.isort]
|
|
70
77
|
profile = "black"
|
|
71
78
|
|
|
@@ -16,7 +16,7 @@ from pydantic import (
|
|
|
16
16
|
model_validator,
|
|
17
17
|
)
|
|
18
18
|
|
|
19
|
-
_JSON_POINTER = re.compile(r"^(?:/(?:[^/~]|~[01])
|
|
19
|
+
_JSON_POINTER = re.compile(r"^(?:/(?:[^/~]|~[01])*)*$")
|
|
20
20
|
|
|
21
21
|
T = tx.TypeVar("T", default=tp.Any)
|
|
22
22
|
|
|
@@ -24,9 +24,12 @@ T = tx.TypeVar("T", default=tp.Any)
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
def _generate_title(model: type[tp.Any]) -> str:
|
|
27
|
-
"""
|
|
28
|
-
name, *
|
|
29
|
-
|
|
27
|
+
"""Prefix with 'JsonPatch' and expand 'Op' contraction."""
|
|
28
|
+
name, *rest = model.__name__.partition("[")
|
|
29
|
+
if not name.endswith("Op"):
|
|
30
|
+
msg = f"{name!r} does not end with 'Op'"
|
|
31
|
+
raise ValueError(msg)
|
|
32
|
+
return "".join(("JsonPatch", name, "eration", *rest))
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class _BaseOp(BaseModel):
|
|
@@ -108,6 +111,15 @@ class _FromOp(_BaseOp):
|
|
|
108
111
|
|
|
109
112
|
|
|
110
113
|
class _ValueOp(_BaseOp, tp.Generic[T]):
|
|
114
|
+
@classmethod
|
|
115
|
+
def __class_getitem__(
|
|
116
|
+
cls, typevar_values: type[tp.Any] | tuple[type[tp.Any], ...]
|
|
117
|
+
) -> type[BaseModel]:
|
|
118
|
+
"""Propagate docstring to generic alias."""
|
|
119
|
+
alias = super().__class_getitem__(typevar_values)
|
|
120
|
+
alias.__doc__ = cls.__doc__
|
|
121
|
+
return alias
|
|
122
|
+
|
|
111
123
|
@classmethod
|
|
112
124
|
def create(cls, *, path: str | Sequence[str], value: T) -> tx.Self: # ty: ignore[invalid-method-override] -- deliberately narrows **kwargs to named params
|
|
113
125
|
"""Return an instance of the appropriate operation."""
|
|
@@ -123,9 +135,9 @@ class _ValueOp(_BaseOp, tp.Generic[T]):
|
|
|
123
135
|
|
|
124
136
|
|
|
125
137
|
class AddOp(_ValueOp[T], tp.Generic[T]):
|
|
126
|
-
"""Represents the
|
|
138
|
+
"""Represents the [add] operation.
|
|
127
139
|
|
|
128
|
-
|
|
140
|
+
[add]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.1
|
|
129
141
|
|
|
130
142
|
"""
|
|
131
143
|
|
|
@@ -133,9 +145,9 @@ class AddOp(_ValueOp[T], tp.Generic[T]):
|
|
|
133
145
|
|
|
134
146
|
|
|
135
147
|
class CopyOp(_FromOp):
|
|
136
|
-
"""Represents the
|
|
148
|
+
"""Represents the [copy] operation.
|
|
137
149
|
|
|
138
|
-
|
|
150
|
+
[copy]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.5
|
|
139
151
|
|
|
140
152
|
"""
|
|
141
153
|
|
|
@@ -143,9 +155,9 @@ class CopyOp(_FromOp):
|
|
|
143
155
|
|
|
144
156
|
|
|
145
157
|
class MoveOp(_FromOp):
|
|
146
|
-
"""Represents the
|
|
158
|
+
"""Represents the [move] operation.
|
|
147
159
|
|
|
148
|
-
|
|
160
|
+
[move]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.4
|
|
149
161
|
|
|
150
162
|
"""
|
|
151
163
|
|
|
@@ -153,9 +165,9 @@ class MoveOp(_FromOp):
|
|
|
153
165
|
|
|
154
166
|
|
|
155
167
|
class RemoveOp(_BaseOp):
|
|
156
|
-
"""Represents the
|
|
168
|
+
"""Represents the [remove] operation.
|
|
157
169
|
|
|
158
|
-
|
|
170
|
+
[remove]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.2
|
|
159
171
|
|
|
160
172
|
"""
|
|
161
173
|
|
|
@@ -168,9 +180,9 @@ class RemoveOp(_BaseOp):
|
|
|
168
180
|
|
|
169
181
|
|
|
170
182
|
class ReplaceOp(_ValueOp[T], tp.Generic[T]):
|
|
171
|
-
"""Represents the
|
|
183
|
+
"""Represents the [replace] operation.
|
|
172
184
|
|
|
173
|
-
|
|
185
|
+
[replace]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.3
|
|
174
186
|
|
|
175
187
|
"""
|
|
176
188
|
|
|
@@ -178,9 +190,9 @@ class ReplaceOp(_ValueOp[T], tp.Generic[T]):
|
|
|
178
190
|
|
|
179
191
|
|
|
180
192
|
class TestOp(_ValueOp[T], tp.Generic[T]):
|
|
181
|
-
"""Represents the
|
|
193
|
+
"""Represents the [test] operation.
|
|
182
194
|
|
|
183
|
-
|
|
195
|
+
[test]: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.6
|
|
184
196
|
|
|
185
197
|
"""
|
|
186
198
|
|
|
@@ -198,9 +210,9 @@ Operation: tp.TypeAlias = tp.Annotated[
|
|
|
198
210
|
|
|
199
211
|
|
|
200
212
|
class JsonPatch(RootModel[Sequence[Operation]], Sequence[Operation]):
|
|
201
|
-
"""Represents a full JSON Patch
|
|
213
|
+
"""Represents a full JSON Patch [document].
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
[document]: https://datatracker.ietf.org/doc/html/rfc6902/#section-3
|
|
204
216
|
|
|
205
217
|
"""
|
|
206
218
|
|
|
@@ -208,7 +220,7 @@ class JsonPatch(RootModel[Sequence[Operation]], Sequence[Operation]):
|
|
|
208
220
|
|
|
209
221
|
@model_validator(mode="before")
|
|
210
222
|
@classmethod
|
|
211
|
-
def
|
|
223
|
+
def _coerce_seq_to_tuple(cls, value: tp.Any) -> tp.Any: # noqa: ANN401
|
|
212
224
|
if isinstance(value, Sequence) and not isinstance(value, tuple):
|
|
213
225
|
return tuple(value)
|
|
214
226
|
return value
|
|
@@ -217,7 +229,7 @@ class JsonPatch(RootModel[Sequence[Operation]], Sequence[Operation]):
|
|
|
217
229
|
def __getitem__(self, index: int) -> Operation: ...
|
|
218
230
|
|
|
219
231
|
@tp.overload
|
|
220
|
-
def __getitem__(self, index: slice) ->
|
|
232
|
+
def __getitem__(self, index: slice) -> tuple[Operation, ...]: ...
|
|
221
233
|
|
|
222
234
|
def __getitem__(self, index):
|
|
223
235
|
return self.root[index]
|
|
File without changes
|
|
File without changes
|