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.2.0.tar.gz (38.5 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.2.0-py3-none-any.whl (28.5 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for marcato-0.2.0.tar.gz
Algorithm Hash digest
SHA256 a46ce489c7bba9dff2dcad07e54a09a082a93f67d54c009049207b527de4773d
MD5 bed412ee7421aa89d88b43f568360d15
BLAKE2b-256 a30835bcbd5f83b1898cbe4d3c665d8a87b84b6b72d87a052190230fafbd1ed9

See more details on using hashes here.

Provenance

The following attestation bundles were made for marcato-0.2.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.2.0-py3-none-any.whl.

File metadata

  • Download URL: marcato-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 28.5 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.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 47aace4becce8d8d9988ea5f44fcd9195016cfecbb118d41c15c14b85e6eaa5f
MD5 b6ea66945a37896a1a780b3ba13a7988
BLAKE2b-256 0d9e8adb070d046c136d9f1c02f3efa4ec0671cb17660e5410d55c1c13f824e6

See more details on using hashes here.

Provenance

The following attestation bundles were made for marcato-0.2.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

0.3.0

2 files

This release

0.2.0 This release

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