Skip to main content

Escapers

Escapers for CSV, Markdown, XML, HTML, JSON and URL, each verified against a real parser

Overview

Text that came from somewhere else — a name, a status, a free-text reason — cannot be dropped into a document as it stands. A comma reshapes a CSV row, a pipe splits a Markdown table cell, an unescaped quote ends an HTML attribute early and starts whatever follows it. An escaper takes such a value and returns one that embeds at a specific place in a specific format without changing the document's structure.

There is no general "escaped string": a value is escaped for somewhere. The same name that is safe inside a Markdown code span breaks a CSV row, and the same value quoted for CSV is meaningless inside an XML attribute. So each escaper here names the position it is for and is correct there and nowhere else. Two escapers are never applied to the same value for extra safety; where a document genuinely contains another, they nest once per layer.

Every escaper accepts any value whose str() succeeds — None included — and returns a string that can be written as UTF-8. Each is verified by round-tripping a hostile value through a real parser for its format, in the position a consumer would use it, rather than by reading the specification.

Installation

pip install vcti-escapers

In requirements.txt

vcti-escapers>=1.0.0

In pyproject.toml dependencies

dependencies = [
    "vcti-escapers>=1.0.0",
]

Quick Start

from vcti.escapers import csv_field, html_attribute, json_string, xml_text

csv_field("failed, retried")  # '"failed, retried"'
csv_field(138.0)  # '138.0'  — numbers stay bare
xml_text("a & b")  # 'a & b'
html_attribute('" onclick="')  # '" onclick="'
json_string("</script>")  # '"\\u003c/script\\u003e"'

Every escaper takes any value whose str() succeeds, None included. What absence looks like is the target's decision, so each escaper documents its own answer.

csv_field(None)      # ''    — an empty field
json_string(None)    # '""'  — an empty JSON string, since null is not one

The escapers

They form one flat pool, grouped here by format for finding rather than because a format owns them.

Format Escaper For
Markdown markdown_code An inline code span in prose — content shown literally, not obeyed
Markdown markdown_table_cell A code span in a GFM table cell, where | must also be escaped
CSV csv_field One RFC 4180 field; text quoted, numbers bare
XML xml_text Character data between tags
XML xml_attribute Inside an attribute (caller supplies the quotes)
HTML html_text Ordinary text content between tags
HTML html_attribute Inside a quoted ordinary attribute (caller supplies the quotes)
JSON json_string A string literal, quotes included, safe inside <script>
URL url_path_segment One path segment — a slash stays inside it
URL url_query_value One query parameter value, form-encoded

A format appears more than once because a format needs different escaping in different positions — that distinction is the point, and picking the wrong one is the mistake this package exists to prevent. Read each escaper's docstring before first use; several carry limits that matter.

Two worth knowing up front:

  • html_attribute is for ordinary attributes. An event handler (onclick), a URL attribute (href, src) or style needs more than escaping — a perfectly escaped javascript: URL still runs.
  • Escapers do not chain for extra safety. They nest, once per layer, when a document genuinely contains another — see docs/patterns.md.

Using them with a template engine

Escapers are plain functions, so they register wherever a template engine takes callables. Nothing here depends on a template engine or knows one exists.

from vcti.escapers import csv_field, markdown_code

filters = {"csv_field": csv_field, "markdown_code": markdown_code}
# Jinja2:  environment.filters.update(filters)

Registered under their own names, they read the same in a template as in Python — {{ item.name | csv_field }}.


Dependencies

None.


Documentation

If you want to… Read
See practical, real-world usage docs/patterns.md
Understand the architecture and design decisions docs/design.md
Navigate and understand the source docs/source-guide.md
Add an escaper docs/extending.md

Download files

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

Source Distribution

vcti_escapers-1.0.0.tar.gz (31.5 kB view details)

Uploaded Source

Built Distribution

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

vcti_escapers-1.0.0-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file vcti_escapers-1.0.0.tar.gz.

File metadata

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

File hashes

Hashes for vcti_escapers-1.0.0.tar.gz
Algorithm Hash digest
SHA256 0d9e371eb1f5f6f0fec175afdb42a4f104e32ab3a5255b50b1736daf3159008b
MD5 534a3b466b6db4c79e4401a266cfe6d5
BLAKE2b-256 0a186fb5d37c289756ae7276e1d9628f2d410f158468da70d1d975da1d53674b

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_escapers-1.0.0.tar.gz:

Publisher: release.yml on vcollab/vcti-python-escapers

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

File details

Details for the file vcti_escapers-1.0.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for vcti_escapers-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 07e700474b73a51ec6f84f2859c175f98f7836ae149759dc11e88b679591b97c
MD5 48883045ade2ccdf3cae1509bd8c70c7
BLAKE2b-256 3936ba760373b0927c56c4d88cf15006a03f5576669f954c463f3dcd5888a9c1

See more details on using hashes here.

Provenance

The following attestation bundles were made for vcti_escapers-1.0.0-py3-none-any.whl:

Publisher: release.yml on vcollab/vcti-python-escapers

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

1.0.0 This release

2 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