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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic_json_patch
3
- Version: 0.6.2
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
- **Note**: explicitly specifying the [discriminator][pydantic-discriminator] gives better results on _failed_ validation for unions of operations.
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://coveralls.io/repos/github/textbook/pydantic_json_patch/badge.svg?branch=main
221
- [coverage-page]: https://coveralls.io/github/textbook/pydantic_json_patch?branch=main
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
- **Note**: explicitly specifying the [discriminator][pydantic-discriminator] gives better results on _failed_ validation for unions of operations.
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://coveralls.io/repos/github/textbook/pydantic_json_patch/badge.svg?branch=main
189
- [coverage-page]: https://coveralls.io/github/textbook/pydantic_json_patch?branch=main
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.2"
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
- """Strip any type metadata and expand the shortened name."""
28
- name, *_ = model.__name__.partition("[")
29
- return f"JsonPatch{name}eration"
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 `add`_ operation.
138
+ """Represents the [add] operation.
127
139
 
128
- .. _add: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.1
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 `copy`_ operation.
148
+ """Represents the [copy] operation.
137
149
 
138
- .. _copy: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.5
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 `move`_ operation.
158
+ """Represents the [move] operation.
147
159
 
148
- .. _move: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.4
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 `remove`_ operation.
168
+ """Represents the [remove] operation.
157
169
 
158
- .. _remove: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.2
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 `replace`_ operation.
183
+ """Represents the [replace] operation.
172
184
 
173
- .. _replace: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.3
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 `test`_ operation.
193
+ """Represents the [test] operation.
182
194
 
183
- .. _test: https://datatracker.ietf.org/doc/html/rfc6902/#section-4.6
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 `document`_.
213
+ """Represents a full JSON Patch [document].
202
214
 
203
- .. _document: https://datatracker.ietf.org/doc/html/rfc6902/#section-3
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 _coerce_to_tuple(cls, value: tp.Any) -> tuple[tp.Any, ...]: # noqa: ANN401
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) -> list[Operation]: ...
232
+ def __getitem__(self, index: slice) -> tuple[Operation, ...]: ...
221
233
 
222
234
  def __getitem__(self, index):
223
235
  return self.root[index]