markupsafe

Escapes untrusted strings for safe use in HTML and XML markup, preventing injection attacks in templated output.

Library
PyPI
v3.0.3
696stars
BSD 3-Clause License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
45/100Fair
Development Activity0
Maintenance20
Community80
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
80/100Excellent
Architecture85
Code Quality90
Innovation55
Learning Curve90

MarkupSafe implements a Markup string type that escapes special HTML/XML characters (&, <, >, ', ") so untrusted input can be embedded in a page without opening the door to injection attacks. The escape() function converts any value to a Markup instance with the dangerous characters replaced by their entity equivalents, while Markup itself is a str subclass whose methods (concatenation, %-formatting, .format(), .join(), and more) automatically escape any new content mixed in, so a value marked safe stays safe through further string operations.

As the escaping engine underneath Jinja and Flask, MarkupSafe sees enormous real-world usage — hundreds of millions of downloads a week — while staying tiny and dependency-free. Performance-critical paths are implemented as a C extension (_speedups.c) with a pure-Python fallback (_native.py) for platforms where the extension can’t be built, so the same API works everywhere without sacrificing speed where it matters.

What You Get

  • A Markup class — a str subclass that marks its contents as pre-escaped/safe for HTML or XML output
  • An escape() function that HTML-escapes any input (or delegates to an object’s __html__() method) and returns a Markup instance
  • Escape-aware string operations — concatenation, % formatting, .format()/.format_map(), .join(), .replace(), and more — that automatically escape newly introduced content
  • escape_silent() for safely handling None values without producing the literal string “None”
  • soft_str() to convert non-string values to str while preserving already-safe Markup instances
  • A compiled C extension for fast escaping with a transparent pure-Python fallback

Common Use Cases

  • Escaping user-submitted text before rendering it inside HTML templates (Jinja, Flask, or custom renderers)
  • Building template engines or template-like string composition where mixed safe/unsafe content needs consistent escaping
  • Preventing cross-site scripting (XSS) when interpolating dynamic values into HTML fragments or email bodies
  • Marking trusted, pre-sanitized HTML (e.g. from a WYSIWYG editor) as safe so it isn’t double-escaped
  • Formatting HTML strings with %-style or .format() interpolation where every substituted value must be escaped automatically

Under The Hood

Architecture The public surface lives entirely in src/markupsafe/__init__.py: a module-level escape() function and a Markup class that subclasses str. escape() special-cases plain str inputs for speed, defers to an object’s __html__() method when present (the convention shared with Jinja and Flask), and otherwise stringifies and escapes the value, always returning a Markup instance. Markup overrides every str method that can introduce new content — __add__, __radd__, __mod__, .join(), .format(), .replace(), .split(), and more — routing each argument through escape() before delegating to the underlying str implementation, and wrapping the result back in Markup. This closes the obvious escape hatches: once a value is wrapped, ordinary string operations can’t silently reintroduce unescaped content. The actual character substitution is isolated behind a single _escape_inner(s) function, imported first from a compiled _speedups C extension and falling back to an equivalent pure-Python implementation in _native.py via a try/except import — a two-tier strategy that keeps the public API identical regardless of which backend is active.

Tech Stack A pure standard-library implementation with zero runtime dependencies: string.Formatter is subclassed (EscapeFormatter) to make .format()/.format_map() escape-aware, and html.unescape backs the unescape() helper. The performance-sensitive escape loop is implemented twice — once in C (src/markupsafe/_speedups.c, built via setuptools as a compiled extension distributed as platform wheels) and once in plain Python (_native.py) — so the package works even where no C toolchain or matching wheel is available. Packaging uses pyproject.toml with a setuptools>=77 build backend, uv for dependency/lock management (uv.lock), and cibuildwheel configuration for cross-platform wheel builds including free-threaded CPython targets.

Code Quality Tests live under tests/ (test_markupsafe.py, test_escape.py, test_leak.py, test_exception_custom_html.py) and are run with pytest, parametrized heavily to cover interpolation, formatting, and edge cases like custom __html__ objects; a dedicated test_leak.py checks for reference leaks in the C extension. The codebase is fully typed (py.typed marker present) and checked with both mypy --strict and pyright, linted and auto-fixed with ruff (bugbear, pyflakes, isort, pyupgrade rules enabled), and enforced via pre-commit hooks. GitHub Actions workflows (tests.yaml, pre-commit.yaml, publish.yaml) run the test matrix and style checks on every change, and tox orchestrates testing across Python 3.10 through 3.14 (including free-threaded builds) plus a dedicated parallel environment for thread-safety checks.

What Makes It Unique MarkupSafe’s distinguishing choice is treating “safe for HTML” as a first-class string subtype rather than a convention or a manually-called sanitizer function — because Markup overrides the mutating string methods themselves, safety composes correctly through ordinary string operations instead of requiring every call site to remember to escape. Combined with the __html__() protocol, this lets template engines, ORMs, and any object with HTML-rendering logic interoperate through a shared, implicit contract. The dual C/pure-Python backend is a pragmatic rather than novel design, but it lets a library used on the hot path of nearly every Jinja/Flask request stay both universally installable and fast.

Used by 9 apps in this directory

Python
100%
Apache 2.0

Agno

Devops · AI Development · Automation

41,969

Build, run, and manage agent platforms with a full production stack — SDK, runtime, and control plane included.

View details
93
Repo Health
87
Technical
66
Dependency
Built with
Python100%
Updated today
Python
59%
Apache 2.0

argilla

AI Development · Data Engineering

5,088

Collaborate on high-quality AI training data with a self-hosted annotation platform built for LLMs, NLP, and multimodal models.

View details
65
Repo Health
81
Technical
61
Dependency
Built with
Python59%
Jupyter Notebook21%
Updated 6 days ago
Python
67%
Apache 2.0

GPT Researcher

Productivity · AI Assistants

29,203

The pioneering open-source autonomous AI agent that conducts deep, multi-source research and produces citation-backed reports exceeding 2,000 words — faster and more reliably than any human researcher.

View details
91
Repo Health
91
Technical
64
Dependency
Built with
Python67%
TypeScript20%
Updated 3 days ago
TypeScript
91%
Apache 2.0

Helicone

Monitoring · AI Development · Analytics

6,115

An open-source AI gateway and LLM observability platform that routes requests to 100+ models while logging cost, latency, and full traces for every call.

View details
65
Repo Health
81
Technical
66
Dependency
Built with
TypeScript91%
Updated 4 days ago
Python
84%
Apache 2.0

knowhere

AI Development · Developer Tools

2,752

Transform messy, unstructured documents into persistent, navigable memory that AI agents can actually use.

View details
83
Repo Health
75
Technical
69
Dependency
Built with
Python84%
HTML15%
Updated today
Python
60%
MIT

LibrePhotos

File Storage

8,054

Self-hosted photo library with AI-powered face recognition, semantic search, and automatic event albums — no cloud required.

View details
82
Repo Health
78
Technical
66
Dependency
Built with
Python60%
TypeScript36%
Updated 2 days ago
Go
61%
Apache 2.0

OSV.dev

Security

2,902

Google's open-source vulnerability database that maps CVEs to exact package versions across 50+ ecosystems with a public API and data dumps.

View details
89
Repo Health
82
Technical
70
Dependency
Built with
Go61%
Python26%
Updated 2 days ago
Python
45%
Other

Redash

Analytics · Data Engineering

28,767

Redash lets anyone connect to 35+ SQL and NoSQL data sources, write a query in the browser, and turn the result into a shared dashboard — no separate BI suite required.

View details
85
Repo Health
74
Technical
60
Dependency
Built with
Python45%
JavaScript31%
TypeScript17%
Updated yesterday
Python
97%
MPL 2.0

Taiga Back

Project Management · Developer Tools

848

Self-hosted agile project management backend with Scrum, Kanban, issue tracking, and a full REST API — built on Django and PostgreSQL.

View details
66
Repo Health
74
Technical
64
Dependency
Built with
Python97%
Updated 3 weeks ago

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search