uritemplate
Parses and expands RFC 6570 URI Templates in Python, from simple substitution to every reserved, label, path, and query-string operator.
Repository Health
Technical Analysis
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
URITemplatewith 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.typedmarker and is checked withmypy --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/_linksfields 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.
Used by 2 apps in this directory
Helicone
Monitoring · AI Development · Analytics
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.
SWIRL
Search · Databases · Data Engineering
Federated AI search and RAG across 100+ enterprise sources—no data extraction, no vector database required.