pydantic-json-patch 0.2.0__tar.gz → 0.4.0__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.2.0 → pydantic_json_patch-0.4.0}/PKG-INFO +10 -1
- {pydantic_json_patch-0.2.0 → pydantic_json_patch-0.4.0}/README.md +9 -0
- {pydantic_json_patch-0.2.0 → pydantic_json_patch-0.4.0}/pyproject.toml +1 -1
- {pydantic_json_patch-0.2.0 → pydantic_json_patch-0.4.0}/src/pydantic_json_patch/models.py +28 -20
- {pydantic_json_patch-0.2.0 → pydantic_json_patch-0.4.0}/src/pydantic_json_patch/__init__.py +0 -0
- {pydantic_json_patch-0.2.0 → pydantic_json_patch-0.4.0}/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.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: Pydantic models for implementing JSON Patch.
|
|
5
5
|
Author: Jonathan Sharpe
|
|
6
6
|
Author-email: Jonathan Sharpe <mail@jonrshar.pe>
|
|
@@ -68,6 +68,15 @@ AddOp(op='add', path='/foo/bar', value=123)
|
|
|
68
68
|
'{"op":"add","path":"/foo/bar","value":123}'
|
|
69
69
|
```
|
|
70
70
|
|
|
71
|
+
The operations that take a value (`AddOp`, `ReplaceOp`, and `TestOp`) are generic, so you can parameterize them with a specific value type:
|
|
72
|
+
|
|
73
|
+
```python
|
|
74
|
+
>>> from pydantic_json_patch import ReplaceOp
|
|
75
|
+
>>> op = ReplaceOp[str].create(path="/foo/bar", value="hello")
|
|
76
|
+
>>> op
|
|
77
|
+
ReplaceOp[str](op='replace', path='/foo/bar', value='hello')
|
|
78
|
+
```
|
|
79
|
+
|
|
71
80
|
Additionally, there are two compound models:
|
|
72
81
|
|
|
73
82
|
- `Operation` is the union of all the operators; and
|
|
@@ -36,6 +36,15 @@ AddOp(op='add', path='/foo/bar', value=123)
|
|
|
36
36
|
'{"op":"add","path":"/foo/bar","value":123}'
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
The operations that take a value (`AddOp`, `ReplaceOp`, and `TestOp`) are generic, so you can parameterize them with a specific value type:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
>>> from pydantic_json_patch import ReplaceOp
|
|
43
|
+
>>> op = ReplaceOp[str].create(path="/foo/bar", value="hello")
|
|
44
|
+
>>> op
|
|
45
|
+
ReplaceOp[str](op='replace', path='/foo/bar', value='hello')
|
|
46
|
+
```
|
|
47
|
+
|
|
39
48
|
Additionally, there are two compound models:
|
|
40
49
|
|
|
41
50
|
- `Operation` is the union of all the operators; and
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import typing as tp
|
|
3
3
|
from collections.abc import Sequence
|
|
4
|
-
from functools import cached_property
|
|
4
|
+
from functools import cached_property, lru_cache
|
|
5
5
|
|
|
6
6
|
import typing_extensions as tx
|
|
7
7
|
from pydantic import BaseModel, ConfigDict, Field, ValidationInfo, model_validator
|
|
8
8
|
|
|
9
9
|
_JSON_POINTER = re.compile(r"^(?:/(?:[^/~]|~[01])+)*$")
|
|
10
10
|
|
|
11
|
+
T = tx.TypeVar("T", default=tp.Any)
|
|
11
12
|
|
|
12
13
|
# region base models
|
|
13
14
|
|
|
@@ -21,8 +22,7 @@ class _BaseOp(BaseModel):
|
|
|
21
22
|
@classmethod
|
|
22
23
|
def create(cls, *, path: str | Sequence[str], **kwargs) -> tx.Self:
|
|
23
24
|
(op,) = tp.get_args(cls.model_fields["op"].annotation)
|
|
24
|
-
|
|
25
|
-
return cls(op=op, path=pointer, **kwargs)
|
|
25
|
+
return cls(op=op, path=cls._dump_pointer(path), **kwargs)
|
|
26
26
|
|
|
27
27
|
op: tp.Literal["add", "copy", "move", "remove", "replace", "test"]
|
|
28
28
|
"""The operation being represented."""
|
|
@@ -35,25 +35,33 @@ class _BaseOp(BaseModel):
|
|
|
35
35
|
"""The decoded tokens in the 'path' JSON pointer."""
|
|
36
36
|
return self._load_pointer(self.path)
|
|
37
37
|
|
|
38
|
+
@classmethod
|
|
39
|
+
def _dump_pointer(cls, pointer: Sequence[str]) -> str:
|
|
40
|
+
if isinstance(pointer, str):
|
|
41
|
+
return pointer
|
|
42
|
+
return "/".join(["", *(cls._encode_token(token) for token in pointer)])
|
|
43
|
+
|
|
38
44
|
@staticmethod
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
)
|
|
45
|
+
@lru_cache
|
|
46
|
+
def _decode_token(token: str) -> str:
|
|
47
|
+
return token.replace("~1", "/").replace("~0", "~")
|
|
43
48
|
|
|
44
49
|
@staticmethod
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
+
@lru_cache
|
|
51
|
+
def _encode_token(token: str) -> str:
|
|
52
|
+
return token.replace("~", "~0").replace("/", "~1")
|
|
53
|
+
|
|
54
|
+
@classmethod
|
|
55
|
+
@lru_cache
|
|
56
|
+
def _load_pointer(cls, pointer: str) -> tuple[str, ...]:
|
|
57
|
+
return tuple(cls._decode_token(token) for token in pointer.split("/")[1:])
|
|
50
58
|
|
|
51
59
|
|
|
52
60
|
class _FromOp(_BaseOp):
|
|
53
61
|
@classmethod
|
|
54
62
|
def create(
|
|
55
63
|
cls, *, path: str | Sequence[str], from_: str | Sequence[str]
|
|
56
|
-
) -> tx.Self: #
|
|
64
|
+
) -> tx.Self: # ty: ignore[invalid-method-override] -- deliberately narrows **kwargs to named params
|
|
57
65
|
pointer = from_ if isinstance(from_, str) else cls._dump_pointer(from_)
|
|
58
66
|
return super().create(path=path, **{"from": pointer})
|
|
59
67
|
|
|
@@ -78,12 +86,12 @@ class _FromOp(_BaseOp):
|
|
|
78
86
|
return self._load_pointer(self.from_)
|
|
79
87
|
|
|
80
88
|
|
|
81
|
-
class _ValueOp(_BaseOp):
|
|
89
|
+
class _ValueOp(_BaseOp, tp.Generic[T]):
|
|
82
90
|
@classmethod
|
|
83
|
-
def create(cls, *, path: str | Sequence[str], value:
|
|
91
|
+
def create(cls, *, path: str | Sequence[str], value: T) -> tx.Self: # ty: ignore[invalid-method-override] -- deliberately narrows **kwargs to named params
|
|
84
92
|
return super().create(path=path, value=value)
|
|
85
93
|
|
|
86
|
-
value:
|
|
94
|
+
value: T = Field(examples=[42])
|
|
87
95
|
"""The value to use in the operation."""
|
|
88
96
|
|
|
89
97
|
|
|
@@ -92,7 +100,7 @@ class _ValueOp(_BaseOp):
|
|
|
92
100
|
# region public models
|
|
93
101
|
|
|
94
102
|
|
|
95
|
-
class AddOp(_ValueOp):
|
|
103
|
+
class AddOp(_ValueOp[T], tp.Generic[T]):
|
|
96
104
|
op: tp.Literal["add"]
|
|
97
105
|
|
|
98
106
|
|
|
@@ -106,17 +114,17 @@ class MoveOp(_FromOp):
|
|
|
106
114
|
|
|
107
115
|
class RemoveOp(_BaseOp):
|
|
108
116
|
@classmethod
|
|
109
|
-
def create(cls, *, path: str | Sequence[str]) -> tx.Self: #
|
|
117
|
+
def create(cls, *, path: str | Sequence[str]) -> tx.Self: # ty: ignore[invalid-method-override] -- deliberately narrows **kwargs to named params
|
|
110
118
|
return super().create(path=path)
|
|
111
119
|
|
|
112
120
|
op: tp.Literal["remove"]
|
|
113
121
|
|
|
114
122
|
|
|
115
|
-
class ReplaceOp(_ValueOp):
|
|
123
|
+
class ReplaceOp(_ValueOp[T], tp.Generic[T]):
|
|
116
124
|
op: tp.Literal["replace"]
|
|
117
125
|
|
|
118
126
|
|
|
119
|
-
class TestOp(_ValueOp):
|
|
127
|
+
class TestOp(_ValueOp[T], tp.Generic[T]):
|
|
120
128
|
op: tp.Literal["test"]
|
|
121
129
|
|
|
122
130
|
|
|
File without changes
|
|
File without changes
|