Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 0 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,6 @@ jobs:
fail-fast: false
matrix:
include:
- name: Linux py39
pyversion: "3.9"
- name: Linux py310
pyversion: "3.10"
- name: Linux py311
pyversion: "3.11"
- name: Linux py312
pyversion: "3.12"
- name: Linux py313
pyversion: "3.13"
- name: Linux py314
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ With `in_place=True`, mutations (and patches applied with `iapply`) write straig
pip install patchdiff # or: uv add patchdiff
```

No dependencies, Python >= 3.9.
No dependencies, Python >= 3.13.

## Learn more

Expand Down
2 changes: 1 addition & 1 deletion docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,6 @@ Or with [uv](https://docs.astral.sh/uv/):
uv add patchdiff
```

There are no dependencies. Python 3.9 or newer is required.
There are no dependencies. Python 3.13 or newer is required.

Next up: the [quick start](quick-start.md).
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Patchdiff 🔍

Based on [rfc6902](https://github.com/chbrown/rfc6902) this library provides a simple API to generate **bi-directional** diffs between composite Python data structures composed out of lists, sets, tuples and dicts. The diffs are JSON-patch compliant, and can optionally be serialized to JSON format. Patchdiff has no dependencies and works on Python 3.9 and up.
Based on [rfc6902](https://github.com/chbrown/rfc6902) this library provides a simple API to generate **bi-directional** diffs between composite Python data structures composed out of lists, sets, tuples and dicts. The diffs are JSON-patch compliant, and can optionally be serialized to JSON format. Patchdiff has no dependencies and works on Python 3.13 and up.

A single call to [`diff`][patchdiff.diff.diff] gives you the patches in **both directions**:

Expand Down
7 changes: 4 additions & 3 deletions patchdiff/pointer.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from __future__ import annotations

from typing import Any, Hashable, Iterable, cast
from collections.abc import Hashable, Iterable
from typing import Any, cast

from .types import Diffable

Expand Down Expand Up @@ -35,7 +36,7 @@ def __init__(self, tokens: Iterable[Hashable] | None = None) -> None:
self.tokens = tuple(tokens)

@staticmethod
def from_str(path: str) -> "Pointer":
def from_str(path: str) -> Pointer:
"""Parse an escaped JSON pointer string (e.g. `"/a/0/b"`) into a Pointer.

All tokens are parsed as strings; numeric list indices become
Expand Down Expand Up @@ -104,6 +105,6 @@ def evaluate(self, obj: Diffable) -> tuple[Diffable | None, Hashable, Any]:
cursor = None
return parent, key, cursor

def append(self, token: Hashable) -> "Pointer":
def append(self, token: Hashable) -> Pointer:
"""Return a new Pointer with `token` appended; self is unchanged."""
return Pointer((*self.tokens, token))
9 changes: 5 additions & 4 deletions patchdiff/produce.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@

from __future__ import annotations

from collections.abc import Callable, Hashable
from copy import deepcopy
from typing import Any, Callable, Hashable
from typing import Any
from weakref import ref

from .pointer import Pointer
Expand Down Expand Up @@ -125,7 +126,7 @@ def __init__(self):
# their memoized _location() results.
self.epoch: int = 0

def finalize(self, root: "_Proxy") -> None:
def finalize(self, root: _Proxy) -> None:
"""Put the reverse patches in reverse application order and
detach the root proxy.

Expand Down Expand Up @@ -236,7 +237,7 @@ def __init__(
self,
data: Any,
recorder: PatchRecorder,
parent: "_Proxy" | None = None,
parent: _Proxy | None = None,
key: Hashable = None,
):
self._data = data
Expand Down Expand Up @@ -340,7 +341,7 @@ def _detach_all(self) -> None:
self._proxies.clear()
self._recorder.epoch += 1

def _in_parent_chain(self, proxy: "_Proxy") -> bool:
def _in_parent_chain(self, proxy: _Proxy) -> bool:
"""True when proxy is self or an ancestor of self."""
node = self
while node is not None:
Expand Down
6 changes: 3 additions & 3 deletions patchdiff/types.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Any, Literal, TypedDict, Union
from typing import TYPE_CHECKING, Any, Literal, TypedDict

if TYPE_CHECKING:
from .pointer import Pointer

# The structures patchdiff diffs and patches. Consumers may also pass
# duck-typed container look-alikes (e.g. observ reactive proxies).
Diffable = Union[dict, list, set]
type Diffable = dict | list | set


class AddOperation(TypedDict):
Expand All @@ -33,4 +33,4 @@ class ReplaceOperation(TypedDict):
value: Any


Operation = Union[AddOperation, RemoveOperation, ReplaceOperation]
type Operation = AddOperation | RemoveOperation | ReplaceOperation
5 changes: 3 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ authors = [
{ name = "Korijn van Golen", email = "korijn@gmail.com" },
{ name = "Berend Klein Haneveld", email = "berendkleinhaneveld@gmail.com" },
]
requires-python = ">=3.9"
requires-python = ">=3.13"
readme = "README.md"
license = "MIT"
classifiers = ["Typing :: Typed"]
Expand Down Expand Up @@ -42,6 +42,7 @@ extend-select = [
"N", # pep8-naming
"T10", # flake8-debugger
"T20", # flake8-print
"UP", # pyupgrade (keep syntax modern for the supported Python floor)
"RUF", # ruff
]

Expand All @@ -56,7 +57,7 @@ include = ["patchdiff"]
[tool.ty.environment]
# Match the oldest supported Python version (requires-python), so
# that the type checker catches use of newer typing features
python-version = "3.9"
python-version = "3.13"

[build-system]
requires = ["hatchling"]
Expand Down
Loading