Skip to main content

pycodetags

Store structured data in Python comments, next to the code it describes.

pycodetags grew out of PEP-350 and the many homegrown ways people write TODOs in code comments: a label, a description, perhaps an owner or a deadline. It generalizes that idea into a library for reading, writing, and querying comment records.

Use it for tasks, requirements, design notes, or metadata that your own tools consume. The core handles the records; your application decides what they mean.

# DATA: Customer export policy
# owner=analytics retention_days=30
# Remove generated exports after their retention period.
def export_customers():
    pass

That comment contains a tag (DATA), a title, named properties, and a body. It stays readable in the source file while also being available as data to Python programs.

Install

pip install pycodetags

Supports Python 3.9–3.15. For command-line use in a separate environment:

pipx install pycodetags

Read and write records

This example uses TDG, one of the built-in comment formats:

from dataclasses import replace
from pycodetags import loads, dumps

record = loads(
    "# DATA: Customer export policy\n"
    "# owner=analytics retention_days=30\n"
    "# Remove generated exports after their retention period.\n",
    schema="TDG",
)

print(record.title)                            # Customer export policy
print(record.custom_fields["retention_days"])  # "30" (a string)

updated = replace(record, title="Customer export retention policy")
print(dumps(updated))

Properties are read as strings. The library preserves custom properties; it doesn't apply your application's business rules or perform actions described by a comment. loads reads one record; loads_all reads multiple records.

Work with source files

Choose a schema and the source folders in your project's pyproject.toml:

[tool.pycodetags]
schema = "TDG"
src = ["src"]

Replace src with your Python source folder. Save the opening example as src/exports.py, then inspect it from Python:

from pycodetags import inspect_file

for record in inspect_file("src/exports.py"):
    print(record.code_tag, record.title, record.custom_fields)

Or collect the project's records as JSON:

pycodetags data --format json

Select the schema in configuration or pass schema= to the API. The library does not guess which convention an unfamiliar comment uses.

Comment formats

TDG puts properties immediately after the title, followed by the description:

# TODO: Explain invalid dates
# assignee=alice priority=high
# Include the row number in the error message.

Extended PEP-350 puts properties in a closing metadata block:

# TODO: Explain invalid dates
# Include the row number in the error message.
# <assignee=alice priority=high>

Use schema = "PEP350" to read the second form. In both formats, the first line supplies the title and subsequent description lines supply the body. A PEP-350 tag ends at its closing >; a TDG tag ends at a non-comment line, another recognized tag, or the end of the file.

Both built-in schemas recognize tags such as TODO, BUG, NOTE, REQ, and DATA. You can also define a schema with your own tag names and fields. See the core reference for custom schemas, quoting, multiline comments, and projects that use different formats in different files.

Edit records in place

Use records read from a source file to update or delete its comments:

from dataclasses import replace
from pycodetags import inspect_file, update_tags

path = "src/exports.py"
record = inspect_file(path)[0]
updated = replace(record, title="Customer export retention policy")
update_tags(path, [(record, updated)])

delete_tags removes selected records; apply_mutations combines updates and deletes in one file. Edits preserve surrounding code and reject records read before the file changed. Read the file again before making another batch of edits. Writes require one writer at a time; they are not transactions across multiple source files.

Repeated queries

For repeated lookups, build a SQLite snapshot of the configured source folders:

from pathlib import Path
from pycodetags import TagIndex

index = TagIndex(Path("."))
index.refresh()
records = index.query_snapshot(file_path="src/exports.py")

Queries read the last refreshed snapshot. Call refresh() after changing source files. Refresh reads the selected files but reparses only changed files; the source remains authoritative. See the core reference for scan exclusions, local IDs, and lookups by ID or tracker URL.

Task reports

For task validation and text, HTML, and changelog reports, install the issue-tracker plugin:

pip install pycodetags-issue-tracker
pycodetags issues --format text

Plugins can provide schemas and application-specific behavior. The core library can be used on its own.

More information

Metadata

Release files for pycodetags 0.8.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 pycodetags 0.8.1
File Size Uploaded
pycodetags-0.8.1.tar.gz 143.3 kB Details

Built distribution (wheel)

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

Total release size: 224.5 kB

Release files / pycodetags-0.8.1.tar.gz

Download URL pycodetags-0.8.1.tar.gz
Size 143.3 kB
Tags Source
SHA-256 checksum
How to use checksums
e7f7602f17009b28a30e6f0dd0dc95bfbb45f18ccafb44272a16cd343c93f661
BLAKE2b-256 checksum
How to use checksums
aa67568e950a0d7623bad3ccaf9d3013597f3d399c995218ede9f75a86384b34
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / pycodetags-0.8.1-py3-none-any.whl

Download URL pycodetags-0.8.1-py3-none-any.whl
Size 81.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
1b7ae0089339c0424384aea2bde2d63db37146f8a15b87971aca88c1a9e6c073
BLAKE2b-256 checksum
How to use checksums
61cbf86dc32482e20d040837555a2adbf3ecbf243e05ad4b02be4772c0c7f779
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.8.1 This release

2 release files

0.8.0

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.2

2 release files

0.1.1

2 release files

0.1.0

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