Polymorphic URI types for Python: AnyUri, HttpUri, FileUri, GSUri, S3Uri, AzureUri, R2Uri, B2Uri
Project description
anyuri
Polymorphic URI types for Python. AnyUri auto-dispatches to the right subclass based on the input string, works as a plain str, and integrates with Pydantic v1/v2.
The Problem
Python data pipelines routinely handle assets from multiple backends — a thumbnail might live at gs://bucket/img.jpg, a report at s3://bucket/report.pdf, and a local fixture at /tmp/test.png. Without a shared abstraction, code degrades into scattered if uri.startswith("gs://") checks, duplicated normalization logic, and type annotations that lie (str accepts anything).
Cloud storage also has a dual-representation problem: the same GCS object is gs://bucket/key in SDK calls but https://storage.googleapis.com/bucket/key in HTTP clients and CDNs. Converting between them by hand is error-prone and leaks storage-specific logic everywhere.
anyuri solves both:
- Polymorphic dispatch —
AnyUri(value)returns the right subtype for the scheme; your code branches on type, not string prefixes. - Dual representation —
as_uri()gives the canonical SDK form;str(uri)gives the HTTPS form accepted by any HTTP client. Conversion is automatic. - Zero friction — every
AnyUrisubclass is astr, so it passes to any existing API without casting or adapters.
from anyuri import AnyUri
from anyuri.providers import GSUri, S3Uri # registers cloud providers
AnyUri("https://example.com/1.jpg") # → HttpUri
AnyUri("gs://bucket/key.jpg") # → GSUri
AnyUri("s3://bucket/key.jpg") # → S3Uri
AnyUri("/local/path.jpg") # → FileUri
uri = AnyUri("gs://bucket/key.jpg")
uri.as_uri() # "gs://bucket/key.jpg"
uri.as_source() # "https://storage.googleapis.com/bucket/key.jpg"
str(uri) # "https://storage.googleapis.com/bucket/key.jpg"
uri == "https://storage.googleapis.com/bucket/key.jpg" # True
Installation
pip install anyuri # core only (HttpUri, FileUri)
pip install anyuri[gcs] # + GSUri
pip install anyuri[s3] # + S3Uri
pip install anyuri[azure] # + AzureUri
pip install anyuri[all] # all cloud providers
pip install anyuri[all,pydantic] # + Pydantic integration
Supported URI Types
| Class | Schemes accepted | as_uri() | as_source() |
|---|---|---|---|
HttpUri |
http://, https:// |
original URL | original URL |
FileUri |
/path, file:///path |
file://localhost/path |
/path |
GSUri |
gs://, https://storage.googleapis.com/ |
gs://bucket/key |
https://storage.googleapis.com/bucket/key |
S3Uri |
s3://, virtual-hosted/path-style HTTPS |
s3://bucket/key |
https://bucket.s3.amazonaws.com/key |
AzureUri |
abfs://, abfss://, https://*.blob.core.windows.net/ |
abfs://container@account.dfs.core.windows.net/path |
https://account.blob.core.windows.net/container/path |
R2Uri |
r2://, https://*.r2.cloudflarestorage.com/ |
r2://account/bucket/key |
https://account.r2.cloudflarestorage.com/bucket/key |
B2Uri |
b2://, https://f*.backblazeb2.com/file/ |
b2://bucket/key |
b2://bucket/key |
Custom URI Types
from anyuri import AnyUri
from anyuri._exceptions import UriSchemaError
@AnyUri.register
class SFTPUri(AnyUri):
def __new__(cls, value):
return str.__new__(cls, cls._validate(value))
@classmethod
def _validate(cls, value):
v = str(value)
if not v.startswith("sftp://"):
raise UriSchemaError(f"Not an SFTP URI: {v!r}")
return v
@classmethod
def validate(cls, value):
return cls(cls._validate(value))
AnyUri("sftp://host/path") # → SFTPUri
Acknowledgements
This project would not exist without the support of my wife, Agnes, whose patience and encouragement make everything possible.
Pydantic Integration
from pydantic import BaseModel
from anyuri import AnyUri
from anyuri.providers import GSUri
class Asset(BaseModel):
uri: AnyUri
Asset(uri="gs://bucket/key.jpg").uri # GSUri instance
Asset(uri="https://example.com").uri # HttpUri instance
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file anyuri-0.1.0.tar.gz.
File metadata
- Download URL: anyuri-0.1.0.tar.gz
- Upload date:
- Size: 32.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fae46bfeccc76ca112c1155a4058eb07ac42b6d405e9e8e4fd9eca79350e0496
|
|
| MD5 |
4fba24a6ea4683c8f9eca7a976f6bebe
|
|
| BLAKE2b-256 |
63d609d9e42e03e749576905a72c24927e6947e5d6d49585ae9f5ddc338fe6b4
|
Provenance
The following attestation bundles were made for anyuri-0.1.0.tar.gz:
Publisher:
release.yml on lucemia/anyuri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anyuri-0.1.0.tar.gz -
Subject digest:
fae46bfeccc76ca112c1155a4058eb07ac42b6d405e9e8e4fd9eca79350e0496 - Sigstore transparency entry: 1336768485
- Sigstore integration time:
-
Permalink:
lucemia/anyuri@2304260d8f2a791e42733be0272bc4e15beefc55 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lucemia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2304260d8f2a791e42733be0272bc4e15beefc55 -
Trigger Event:
push
-
Statement type:
File details
Details for the file anyuri-0.1.0-py3-none-any.whl.
File metadata
- Download URL: anyuri-0.1.0-py3-none-any.whl
- Upload date:
- Size: 12.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96f1abbb0ff647022c354174a77a5f54b17982d6c829bd7fbd4326980783228e
|
|
| MD5 |
8ffeea0cf67ee9e05e41b0ef5a6021c7
|
|
| BLAKE2b-256 |
03741b3c3d43d9319ab6f841849062ba6ba2576704643bf7fe25461a29037d8e
|
Provenance
The following attestation bundles were made for anyuri-0.1.0-py3-none-any.whl:
Publisher:
release.yml on lucemia/anyuri
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
anyuri-0.1.0-py3-none-any.whl -
Subject digest:
96f1abbb0ff647022c354174a77a5f54b17982d6c829bd7fbd4326980783228e - Sigstore transparency entry: 1336768610
- Sigstore integration time:
-
Permalink:
lucemia/anyuri@2304260d8f2a791e42733be0272bc4e15beefc55 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/lucemia
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@2304260d8f2a791e42733be0272bc4e15beefc55 -
Trigger Event:
push
-
Statement type: