Skip to main content

prosemirror-py

CI Code Coverage PyPI Package License Fellow Careers

This package provides Python implementations of the following ProseMirror packages:

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:

  • camelCase methods/properties become snake_case (e.g. nodeSize -> node_size, isBlock -> is_block, textBetween -> text_between)
  • from (a Python keyword) becomes from_ in parameter names and the Fragment.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: Uses lxml.html for HTML parsing. Text nodes are wrapped in <lxmltext> pseudo-elements since lxml doesn't represent text nodes as separate child elements. CSS selector matching uses lxml.cssselect.
  • DOMSerializer: Outputs HTML strings via custom Element and DocumentFragment classes 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_size
  • TextNode.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 as TypedDict or frozen dataclass types.
  • Union types use X | Y syntax (Python 3.10+).

Additional Conveniences

These are Python-specific additions not present in the upstream:

  • Fragment.from_json() accepts a JSON str and parses it automatically.
  • from_dom.py includes a from_html() helper to parse an HTML string directly to a ProseMirror document.
  • DOMSerializer output type is named HTMLOutputSpec (instead of DOMOutputSpec) 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.

Metadata

Release files for prosemirror 0.6.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for prosemirror 0.6.1
File Size Uploaded
prosemirror-0.6.1.tar.gz 76.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for prosemirror 0.6.1
File Interpreter ABI Platform
prosemirror-0.6.1-py3-none-any.whl Python 3 none any Details

Total release size: 144.1 kB

Release files / prosemirror-0.6.1.tar.gz

Download URL prosemirror-0.6.1.tar.gz
Size 76.3 kB
Tags Source
SHA-256 checksum
How to use checksums
eec45a4f35d03ddb58700199292646c34b334bbad385a21679fc7a8d181cd272
BLAKE2b-256 checksum
How to use checksums
d68a36cd9224f70441fd66d428f7cad650923d949d0d9395cbae82aab6b78df5
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / prosemirror-0.6.1-py3-none-any.whl

Download URL prosemirror-0.6.1-py3-none-any.whl
Size 67.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f9d38313a72af1c579cbb87beac3a765f399416afc4567cb016aca53ec4cdae6
BLAKE2b-256 checksum
How to use checksums
8c6bff4f2362320c53ea989ef32e4ac978233b65ffb2e6e3131aea7860c740cb
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

This release

0.6.1 This release

2 release files

0.6.0

2 release files

0.5.2

2 release files

0.5.1

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.7

2 release files

0.3.5

2 release files

0.3.3

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3

2 release files

0.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page