Python implementation of core ProseMirror modules for collaborative editing
Project description
prosemirror-py
This package provides Python implementations of the following ProseMirror packages:
prosemirror-modelversion 1.25.4prosemirror-transformversion 1.11.0prosemirror-test-builderversion 1.1.1prosemirror-schema-basicversion 1.2.4prosemirror-schema-listversion 1.5.1 (node specs andwrapRangeInListonly; command functions that depend onprosemirror-stateare excluded)
The original implementation has been followed as closely as possible during translation to simplify keeping this package up-to-date with any upstream changes.
Why?
ProseMirror provides a powerful toolkit for building rich-text editors, but it's JavaScript-only. Until now, the only option for manipulating and working with ProseMirror documents from Python was to embed a JS runtime. With this translation, you can now define schemas, parse documents, and apply transforms directly via a native Python API.
Status
The full ProseMirror test suite has been translated and passes. This project only supports Python 3. The code has type annotations to support mypy or other typechecking tools.
Usage
Since this library is a direct port, the best place to learn how to use it is the official ProseMirror documentation. Here is a simple example using the included "basic" schema:
from prosemirror.transform import Transform
from prosemirror.schema.basic import schema
# Create a document containing a single paragraph with the text "Hello, world!"
doc = schema.node(
"doc", {}, [schema.node("paragraph", {}, [schema.text("Hello, world!")])]
)
# Create a Transform which will be applied to the document.
tr = Transform(doc)
# Delete the text from position 3 to 5. Adds a ReplaceStep to the transform.
tr.delete(3, 5)
# Make the first three characters bold. Adds an AddMarkStep to the transform.
tr.add_mark(1, 4, schema.mark("strong"))
# This transform can be converted to JSON to be sent and applied elsewhere.
assert [step.to_json() for step in tr.steps] == [
{"stepType": "replace", "from": 3, "to": 5},
{"stepType": "addMark", "mark": {"type": "strong"}, "from": 1, "to": 4},
]
# The resulting document can also be converted to JSON.
assert tr.doc.to_json() == {
"type": "doc",
"content": [
{
"type": "paragraph",
"content": [
{"type": "text", "marks": [{"type": "strong"}], "text": "Heo"},
{"type": "text", "text": ", world!"},
],
}
],
}
Differences from Upstream
While the translation follows the original TypeScript implementation as closely as possible, some adaptations were necessary for Python. These are documented here for reference.
Naming Conventions
Python's snake_case naming is used throughout:
camelCasemethods/properties becomesnake_case(e.g.nodeSize->node_size,isBlock->is_block,textBetween->text_between)from(a Python keyword) becomesfrom_in parameter names and theFragment.from_()static method
DOM Handling
The upstream uses browser DOM APIs. The Python port uses
lxml for parsing and a lightweight custom Element /
DocumentFragment model for serialization:
DOMParser: Useslxml.htmlfor HTML parsing. Text nodes are wrapped in<lxmltext>pseudo-elements since lxml doesn't represent text nodes as separate child elements. CSS selector matching useslxml.cssselect.DOMSerializer: Outputs HTML strings via customElementandDocumentFragmentclasses rather than creating real DOM nodes.- XML namespaces: Not supported (raises
NotImplementedError). This only affects SVG or MathML node serialization.
String Length and Slicing (UTF-16 Semantics)
JavaScript strings use UTF-16 encoding, so string.length counts UTF-16 code
units (surrogate pairs count as 2). The Python port preserves these semantics
using a text_length() helper and UTF-16 encode/decode for slicing in:
Node.node_size/TextNode.node_sizeTextNode.cut()TextNode.text_between()Fragment.findIndex()/Fragment.cut()diff.py(character-by-character comparison)
Deep Comparison
The upstream uses a custom compareDeep function for recursive comparison of
arrays/objects. The Python port uses native ==, which already performs deep
comparison of dicts and lists.
Resolve Cache
The upstream uses a WeakMap<Node, ResolveCache> for caching resolved
positions. Python uses a dict[int, _ResolveCache] keyed by id(doc) with a
weakref.ref callback to clean up entries when the document node is garbage
collected.
Type System
- TypeScript interfaces (
NodeSpec,MarkSpec,ParseOptions, etc.) are translated asTypedDictor frozendataclasstypes. - Union types use
X | Ysyntax (Python 3.10+).
Additional Conveniences
These are Python-specific additions not present in the upstream:
Fragment.from_json()accepts a JSONstrand parses it automatically.from_dom.pyincludes afrom_html()helper to parse an HTML string directly to a ProseMirror document.DOMSerializeroutput type is namedHTMLOutputSpec(instead ofDOMOutputSpec) to reflect that it produces HTML strings.
AI Disclosure
The initial version of this translation was written manually in 2019. AI is now used to help keep this translation up-to-date with upstream changes.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file prosemirror-0.6.1.tar.gz.
File metadata
- Download URL: prosemirror-0.6.1.tar.gz
- Upload date:
- Size: 76.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eec45a4f35d03ddb58700199292646c34b334bbad385a21679fc7a8d181cd272
|
|
| MD5 |
248f00201c27c63b912c9670c165c303
|
|
| BLAKE2b-256 |
d68a36cd9224f70441fd66d428f7cad650923d949d0d9395cbae82aab6b78df5
|
File details
Details for the file prosemirror-0.6.1-py3-none-any.whl.
File metadata
- Download URL: prosemirror-0.6.1-py3-none-any.whl
- Upload date:
- Size: 67.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: uv/0.10.4 {"installer":{"name":"uv","version":"0.10.4","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f9d38313a72af1c579cbb87beac3a765f399416afc4567cb016aca53ec4cdae6
|
|
| MD5 |
01b9112783f96ecd74effa9d1f0b4aac
|
|
| BLAKE2b-256 |
8c6bff4f2362320c53ea989ef32e4ac978233b65ffb2e6e3131aea7860c740cb
|