Contributing
June 3, 2026 · View on GitHub
Thanks for improving tiptap_python_utils.
Before you open a pull request
Please open an issue first at github.com/tugkanpilka/tiptap-python-utils/issues and describe what you want to change. This lets us align on the approach before you invest time in a PR — especially for anything that touches the public API, the round-trip invariant, or the identity model.
Local setup
python -m venv .venv
. .venv/bin/activate
python -m pip install -e ".[dev]"
pyproject.toml sets pythonpath = ["src"], so pytest works without
installing. Python ≥ 3.9. No runtime dependencies.
Running tests
pytest -q # full suite
pytest tests/test_content.py -q # single file
pytest tests/test_content.py::test_name -q # single test
Architecture
The package is layered: contract (key/kind/policy primitives) → model
(immutable AST with a registry of node classes; unknown kinds round-trip via
Unknown) → codec (raw I/O in raw.py, hydration in reader.py, dump in
writer.py) → walk & tree (traversal + path-based replacement on the
immutable tree) → select (fluent Selection — the single home for mutation)
→ content (public facade) → text / tasks / shared (user-facing
workflows built on Content). All nodes are @dataclass(frozen=True);
mutations return new instances.
A core invariant is the lossless round trip: parsing must never silently
drop fields. Node.extra stores unknown top-level keys, Node.present records
which structural keys appeared in the raw input, and unknown node kinds become
Unknown. When editing Node.raw() or its subclasses, do not lose extra or
violate present semantics — tests/test_content.py exercises round-trip
cases. See CLAUDE.md for the full architecture notes.
Release checklist
- Update
CHANGELOG.md. - Bump the version in
pyproject.toml. - Run
pytest -q. - Run
python -m build. - Run
python -m twine check dist/*. - Tag the release as
vX.Y.Z.
See docs/release.md for the trusted-publishing setup and the full publish flow.