Skip to main content

Marcato

Marcato by Analog converts HTML to Markdown with one parser and one narrow dependency: lxml.

pip install marcato
from marcato import html_to_markdown

markdown = html_to_markdown("<h1>Hello</h1><p>Good to meet you.</p>")

Marcato preserves common document structure including headings, links, lists, tables, code, images, advisory blocks, and authored inline emphasis. Its single rendering policy defines an intentional GFM-compatible presentation.

Input and rendering contract

html_to_markdown() accepts plain text, an HTML fragment, a full HTML document, or an empty string. Malformed HTML remains supported when lxml can recover it; unrecoverable markup raises ValueError. Given the same input, Marcato version, and supported lxml version, recovery and rendering are deterministic. Non-string input raises TypeError.

Presentation

Marcato uses ATX headings (# through ######), one stable - marker for unordered lists at every depth, inline links, and unwrapped prose. Literal asterisks and underscores are escaped, while broad punctuation escaping stays off; generated links, images, tables, and other Markdown constructs always escape the grammar boundaries they own.

Normal-flow horizontal whitespace collapses across inline child boundaries, including around omitted hidden content and text synthesized from authored accessibility metadata. Browser-collapsible whitespace at the document edges is omitted. Preformatted content, Markdown indentation, and interior hard line breaks remain unchanged.

Blocks and regions

Marcato preserves boundaries between standard HTML block containers, including main, article, section, aside, header, footer, nav, figure, and address. These containers are structurally transparent: they separate their authored contents without adding labels or deciding which page regions matter. Callers remain responsible for selecting the HTML region Marcato receives.

Block containers authored inside a link retain their separation without terminating the Markdown link label. Phrasing containers such as span, abbr, and time remain inline and do not introduce new boundaries.

Code languages

For a block <pre> element, Marcato emits a syntax-highlighting language when it has one direct <code> child whose classes contain exactly one language-TOKEN entry. TOKEN must begin with an ASCII letter or digit and may then contain ASCII letters, digits, _, ., +, #, or -. Other class names are not interpreted as languages, and unsafe or ambiguous values are omitted.

Marcato chooses backtick or tilde fences that cannot collide with the code content.

Links and images

Marcato serializes authored link, image, and video destinations without interpreting or rewriting them. It retains compact inline Markdown for simple destinations and uses the equivalent angle-bracket form when whitespace, control characters, or unbalanced parentheses would otherwise corrupt the construct. Titles, link text, and image alt text are escaped at their Markdown grammar boundaries so page-authored characters cannot replace a destination or terminate a link early.

Matching link text is emitted as an autolink only when the destination is an eligible absolute URI. Relative and empty destinations retain ordinary inline link syntax. These rules are serialization only: Marcato does not resolve, allowlist, fetch, or assess destinations.

An icon-only local link whose fragment target is declared by its surrounding heading is treated as that heading's permalink affordance. Marcato keeps the heading text once instead of synthesizing a duplicate link from accessibility metadata. Visible heading links and icon-only links to other targets remain authored content.

When a local link that remains in the Markdown points to a custom target declared by a heading or its descendants, Marcato preserves that relationship with a GFM custom anchor immediately before the heading. Unreferenced IDs, targets outside headings, and suppressed permalink affordances add no anchor markup.

Tables

Marcato emits a GFM table when its cells contain inline content, it has at most one header row, and it does not use spanning or nested tables. Pipes in cell content are escaped, <br> remains an inline break, and shorter rows receive empty trailing cells so every authored value remains in the table. Headerless tables receive an empty header rather than assigning header semantics to the first authored row.

A table with colspan, rowspan, multiple header rows, block content in a cell, or another table inside it cannot be represented faithfully as a GFM table. Marcato emits its caption followed by each non-empty cell as ordinary Markdown blocks in source order. This linear fallback preserves authored content without inventing a rectangular relationship. A page-authored accessible name still fills an otherwise silent cell.

Compatibility

Marcato's rendered Markdown is its primary public contract. Within a 0.x minor line, patch releases do not intentionally change rendered bytes for an existing HTML input. Intentional rendering changes, including bug fixes that alter output, begin a new minor line.

The same Marcato and lxml versions produce deterministic output. Marcato bounds the lxml major versions it supports; dependency-induced output drift within that range is treated as a compatibility defect.

Attribution

Marcato originated as an lxml port of markdownify 1.2.3 by Matthew Tretter and has since diverged. The complete upstream copyright and MIT license notice ships in THIRD-PARTY-NOTICES.md.

Marcato itself is licensed under the MIT License.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

marcato-0.3.0.tar.gz (39.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

marcato-0.3.0-py3-none-any.whl (28.7 kB view details)

Uploaded Python 3

File details

Details for the file marcato-0.3.0.tar.gz.

File metadata

  • Download URL: marcato-0.3.0.tar.gz
  • Upload date:
  • Size: 39.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for marcato-0.3.0.tar.gz
Algorithm Hash digest
SHA256 4ec063b06e4c9020eb90dbde3fd50c5ff844196f8b0c6a03b58574ecb8e13263
MD5 3f285a4c73ad94c306b37d65599f229b
BLAKE2b-256 6940fcd4f0a78d46b422f8fa91adbc32504aab6800833177cf87b30c457b5962

See more details on using hashes here.

Provenance

The following attestation bundles were made for marcato-0.3.0.tar.gz:

Publisher: release-marcato-python.yml on getanalog/monorepo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file marcato-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: marcato-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 28.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for marcato-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 5e588e8c48272b43d0cf15c34844a5c6c119d8ab771c41e459781cec5e5c0133
MD5 f6aa131e8c190afb937bf459dd457617
BLAKE2b-256 601a2e90e3cca0e30ebed8b883b59d7284d6ef60e8c757dcafdb6ca14352c38c

See more details on using hashes here.

Provenance

The following attestation bundles were made for marcato-0.3.0-py3-none-any.whl:

Publisher: release-marcato-python.yml on getanalog/monorepo

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 files

0.2.0

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page