Skip to main content

Small, fast, HTTP URL router

Project description

GitHub Actions Workflow Status

nanoroute

A small Python HTTP URL routing facility capable of sub-microsecond routing, typically <200ns.

Nanoroute is a C++ implementation of a modified version of the priority radix-tree algorithm pioneered by Julien Schmidt's httprouter. Like httprouter, nanorouter's underlying implementation performs no allocations in the common case, although some allocations are necessary to create the PyObjects necessary for the CPython bindings.

The nanoroute package is suitable as a building block for more fully featured HTTP frameworks. It also provides a simple WSGI interface for integration directly into WSGI application stacks.

The intended use cases are high-performance message brokers, dispatchers, and ingestion endpoints. The performance improvements of a high-speed router are unlikely to be very significant in a typical database-backed Python REST API.

Quickstart

For complete documentation, see the docs.

Installation

Nanoroute only supports Python 3.13+. It is available via PyPI, any mechanism of installing Python packages from PyPI will work:

pip install nanoroute

Registering Routes

Nanoroute provides a single class, the router, which can be used to register handlers for common HTTP verbs.

import nanoroute

app = nanoroute.router()

@app.get('/')
def root(*args, **kwargs):
  ...

@app.post('/endpoint')
def endpoint(*args, **kwargs):
  ...

The verbs supported via these convenience methods are GET, POST, PUT, and DELETE. Arbitrary sets of any valid HTTP verbs can be registered using router.route().

# Register for a single HTTP verb
@app.route('PATCH', '/object')
def handle_patch(*args, **kwargs):
  ...

# Register for a multiple HTTP verbs
@app.route(['POST', 'PUT'], '/multi-meth')
def handle_multi(*args, **kwargs):
  ...

Finally, any arbitrary object can be registered with nanoroute. The decorator syntax is merely convenient for typical usage.

# Register arbitrary object for GET '/'
app.get('/')(object())

Capturing Parameters

Two forms of parameter capture are available, segment capture and catchall. Segment captures are delimited by : and capture from the appearance of the delimiter until the following / or the end of the URL. Catchalls are delimited by * and capture the entire URL following their appearance.

Both types of parameter capture may be followed by a name, which will used as the key associated with the parameter during route lookup. Anonymous parameters act as wildcards, they have the same behavior as named parameters but the captured data is not reported during lookup.

# Captures the middle segment with the name "id"
@app.get('/user/:id/profile')
def get_profile(*args, **kwargs):
  ...

# Captures the article ID into "id", and then everything after the final "/"
# is captured as "slug" which might contain multiple path segments
@app.get('/article/:id/*slug')
def article(*args, **kwargs):
  ...

# The first path segment is a wildcard, anything may appear, but nothing is
# captured during route lookup
@app.get('/:/anonymous')
def anon(*args, **kwargs):
  ...

Captures are allowed to appear at arbtirary points in a given segment, so long as multiple captures do not appear in the same segment.

# Captures an "id" in the middle of a segment
@app.get('/user_:id/profile')
def get_profile(*args, **kwargs):
  ...

# Error: Invalid wildcard construction. Only one capture is allowed to appear
# in a given path segment
@app.get('/user_:id_:name')
def this_is_an_error(*args, **kwargs):
  ...

Captures that appear in the same place for different paths may have different names, which will be recorded appropriately.

# Captures the first segment as "foo"
@app.get('/:foo/alpha')
def alpha(*args, **kwargs):
  ...

# Captures the first segment as "bar"
@app.get('/:bar/beta')
def beta(*args, **kwargs):
  ...

Lookup

The base lookup facility is router.lookup(), which is intended for other frameworks to use as a building block. It takes a method and path as arguments and returns the registered handler and a parameter capture dictionary.

@app.get('/user/:name')
def say_hello(params):
  print(f'Hello {params['name']}!')

handler, params = app.lookup('GET', '/user/Jane')
handler(params)

# >>> Hello Jane!

As a convenience, a WSGI application is also provided. It is directly equivalent to the following code:

def wsgi_app(environ, start_response):
  handler, params = app.lookup(environ['REQUEST_METHOD'], environ['PATH_INFO'])
  environ['nanoroute.params'] = params
  return handler(environ, start_response)

app.wsgi_app = wsgi_app

Project details


Download files

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

Source Distribution

nanoroute-2.0.0.tar.gz (22.4 kB view details)

Uploaded Source

Built Distributions

nanoroute-2.0.0-cp313-cp313-win_amd64.whl (84.4 kB view details)

Uploaded CPython 3.13 Windows x86-64

nanoroute-2.0.0-cp313-cp313-macosx_13_0_arm64.whl (27.5 kB view details)

Uploaded CPython 3.13 macOS 13.0+ ARM64

nanoroute-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl (34.7 kB view details)

Uploaded CPython 3.12 manylinux: glibc 2.39+ x86-64

File details

Details for the file nanoroute-2.0.0.tar.gz.

File metadata

  • Download URL: nanoroute-2.0.0.tar.gz
  • Upload date:
  • Size: 22.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/5.1.1 CPython/3.12.7

File hashes

Hashes for nanoroute-2.0.0.tar.gz
Algorithm Hash digest
SHA256 fa5f70946423915f26ad18f33d845f492c5d4e852b061696d3ea589e26cf4de9
MD5 a091f3c5d9f8d364ffe90d8bccd159a1
BLAKE2b-256 62d275d8fba44bda4f46dd81d0dffe0af4b6c4186c0414c346a4464498399d68

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanoroute-2.0.0.tar.gz:

Publisher: publish.yaml on nickelpro/nanoroute

Attestations:

File details

Details for the file nanoroute-2.0.0-cp313-cp313-win_amd64.whl.

File metadata

File hashes

Hashes for nanoroute-2.0.0-cp313-cp313-win_amd64.whl
Algorithm Hash digest
SHA256 8a3b8718eb310e259522ef49b47e4d321ec656f363990ece0b1acb84926a7cad
MD5 af7cced42b2c4d0f002f5508c436a3f8
BLAKE2b-256 8c1b64a03b4a199bc548d7d953460961fc5e7f09d391b67d5c58b886585b4fd3

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanoroute-2.0.0-cp313-cp313-win_amd64.whl:

Publisher: publish.yaml on nickelpro/nanoroute

Attestations:

File details

Details for the file nanoroute-2.0.0-cp313-cp313-macosx_13_0_arm64.whl.

File metadata

File hashes

Hashes for nanoroute-2.0.0-cp313-cp313-macosx_13_0_arm64.whl
Algorithm Hash digest
SHA256 f57659239b99e53495e0efb175300c8d194264743825e759fabe4d8001bd4d33
MD5 f73f65693cc6e1f9662d727c5c821497
BLAKE2b-256 6544a241d87a17b9ba78b9c81ad08e19566e0c6064d540ae805eeea7fb2334c5

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanoroute-2.0.0-cp313-cp313-macosx_13_0_arm64.whl:

Publisher: publish.yaml on nickelpro/nanoroute

Attestations:

File details

Details for the file nanoroute-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl.

File metadata

File hashes

Hashes for nanoroute-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl
Algorithm Hash digest
SHA256 7c5327a2797399f07807038ec387114b4b827e2c0f7d5e1db7ff2b1ab49033ae
MD5 a4bce964cc73079fcc78e06fe58cc62e
BLAKE2b-256 32b74ee3ad92cd3a014507c273580b25e5dc229f0658856190a37240e2ebf20c

See more details on using hashes here.

Provenance

The following attestation bundles were made for nanoroute-2.0.0-cp312-cp312-manylinux_2_39_x86_64.whl:

Publisher: publish.yaml on nickelpro/nanoroute

Attestations:

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page