uritemplate

Parses and expands RFC 6570 URI Templates in Python, from simple substitution to every reserved, label, path, and query-string operator.

Library
PyPI
v4.2.0
246stars
BSD-3-Clause OR Apache-2.0

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
61/100Good
Development Activity60
Maintenance36
Community68
Maturity60
Momentum20

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
78/100Good
Architecture88
Code Quality92
Innovation78
Learning Curve55

uritemplate is a small, focused Python library that implements RFC 6570 URI Templates end to end. It parses template strings like https://api.github.com/users/{user}/gists{/gist_id} into a URITemplate object, then expands them against a dict or keyword arguments into a fully resolved URI, handling every operator level the RFC defines: basic {var} substitution, reserved (+) and fragment (#) expansion, dot-prefixed label (.) and path segment (/) expansion, path-style (;) and form-style query (?, &) expansion, plus list/dict values, explode (*) modifiers, and prefix-length (:N) truncation.

The library grew out of the requests/GitHub-API ecosystem, and its docstring examples still expand GitHub API templates directly. It’s now widely used as the templating layer underneath OpenAPI/Swagger-generated clients and other REST SDKs that need to build parameterized request URLs from a spec instead of hand-formatting strings. The core is three small modules (template.py, variable.py, orderedset.py), fully typed (checked with mypy --strict, ships a py.typed marker), and dual-licensed under Apache-2.0/BSD-3-Clause so it can be vendored into virtually any project without license friction.

What You Get

  • URITemplate class - Parses a template string once so re-expansion against new variables is cheap, with __eq__/__hash__ support for use as dict keys.
  • Full RFC 6570 operator coverage - Handles simple, reserved (+), fragment (#), label (.), path segment (/), path-style (;), and query-string (?, &) expansions, including their explode (*) and prefix (:N) modifiers.
  • partial() for incremental expansion - Expands only the variables supplied and returns a new URITemplate with the rest still templated, useful when building a URL across multiple call sites.
  • variables() introspection - Extracts the ordered set of variable names referenced by a template without expanding it, letting callers validate inputs before making a request.
  • Type-safe API - Ships a py.typed marker and is checked with mypy --strict, so consuming code gets accurate type hints for template variables and return values.

Common Use Cases

  • REST/OpenAPI client generation - SDK generators embed RFC 6570 templates from an OpenAPI spec and use uritemplate to expand path and query parameters into a real request URL at call time.
  • Following GitHub API hypermedia links - GitHub’s API returns url/_links fields as RFC 6570 templates (e.g. {/gist_id}, {?page,per_page}) rather than plain strings; uritemplate expands them into the next request URL.
  • Building parameterized query URLs safely - Expanding a {?q,page}-style query template with list or dict values instead of hand-building a query string with manual percent-encoding.
  • Vendoring a small, dependency-free URL templating layer - With zero runtime dependencies and a permissive dual license, projects embed it directly rather than pulling in a heavier HTTP client just for URL construction.

Under The Hood

Architecture The package is organized as three cooperating modules under uritemplate/: template.py defines URITemplate, which on construction regex-parses {...} expressions out of the URI string (template_re = re.compile("{([^}]+)}")) and builds one URIVariable (variable.py) per expression; variable.py does the heavy lifting — URIVariable.parse() splits the comma-joined variable list, detects operator prefixes, explode and prefix modifiers, and default values (an extension beyond the RFC), then URIVariable.expand() dispatches to one of four expansion strategies selected by the Operator enum’s expansion_separator(), reserved_characters(), and quote() methods. orderedset.py is a small, self-contained OrderedSet used only to track variable names in declaration order. api.py is a thin functional facade (expand, partial, variables) over the class API. There is no dependency injection, plugin surface, external I/O, or persistent state — the whole library is stateless beyond template parsing, so the Operator enum and the parsing regex are effectively the only places a change could ripple outward.

Tech Stack Pure Python 3.9+ standard library only — re, urllib.parse, enum, string, collections.abc, typing, weakref — with zero runtime third-party dependencies. Packaging uses classic setuptools via setup.py/setup.cfg (the pyproject.toml here only configures black/isort, not build metadata). Test and dev tooling includes pytest, a tox.ini matrix spanning Python 3.8 through 3.14 plus a pep8 environment (black, flake8, mypy --strict), and a pre-commit config chaining isort, black, pyupgrade, mypy, and gitlint. Docs are built with Sphinx and published via Read the Docs; CI runs on GitHub Actions, with a dedicated release tox environment using twine to publish to PyPI.

Code Quality Test coverage is unusually rigorous for a library this size: test_uritemplate.py uses a metaclass (RFCTemplateExamples) to programmatically generate unittest cases for every RFC 6570 example across expansion levels 1-4, and test_from_fixtures.py separately replays the official RFC 6570 Level 4 conformance fixtures from tests/fixtures/spec-examples.json and spec-examples-by-section.json — effectively a full RFC compliance suite rather than plain unit tests. Type safety is strict (mypy --strict in both tox.ini and pre-commit, with a py.typed marker shipped), naming is consistent snake_case with descriptive docstrings on every public method, and error handling is minimal because the domain has few failure modes — an invalid prefix modifier raises a plain ValueError from int() rather than a custom exception type, the one soft spot. CI exercises the full tox matrix across Python 3.8-3.14.

API Design The public surface is deliberately tiny — three module-level functions (expand, partial, variables) plus one class (URITemplate) — so getting started needs no configuration: from uritemplate import URITemplate; URITemplate(uri).expand(**kwargs). Parameter passing accepts both a var_dict and **kwargs simultaneously, with documented override semantics matching how callers actually have data (a dict from a spec, plus one or two overrides). partial() is a genuinely useful ergonomic touch missing from many comparable implementations in other languages — it lets you expand a subset of variables and get back a still-valid URITemplate for the rest, useful when composing URLs across layers that each own a different subset of variables. Documentation is RFC-example-driven, effective for anyone already familiar with RFC 6570 but offering little hand-holding for someone who isn’t, since there’s no narrative getting-started guide beyond the README.

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