urlops
Clean URL handling made easy.
Overview
urlops provides a simple, clean API for working with URLs. It offers immutable URL objects with intuitive operations that make URL manipulation feel natural.
Features
- Intuitive API - Natural operations like
/for path joining - Immutable design - All operations return new instances
- Multi-value query parameters - Proper handling of complex query strings
- Custom exceptions - Only uses urlops-specific exceptions, never built-in Python exceptions
- Simple and focused - Core functionality without unnecessary complexity
Installation
From PyPI (when available)
pip install urlops
From Source
# Clone the repository
git clone https://github.com/RVV-karma/urlops.git
cd urlops
# Install in development mode
pip install -e .
# Or install with development dependencies
pip install -e ".[dev]"
Requirements
- Python 3.8 or higher
- No external dependencies (pure Python implementation)
Development Setup
# Clone and setup
git clone https://github.com/RVV-karma/urlops.git
cd urlops
# Install development dependencies
make dev-install
# Run tests
make test
# Run linting and formatting
make lint
make format
Quick Start
from urlops import URL, parse, join
# Create a URL
url = URL("https://example.com/api/users?page=1&sort=name")
# Access components
print(url.scheme) # "https"
print(url.host) # "example.com"
print(url.port) # None
print(url.path) # URLPath("/api/users")
print(url.query) # QueryParams("page=1&sort=name")
print(url.fragment) # ""
# Path operations (intuitive syntax)
new_url = url / "123" / "profile"
print(new_url) # "https://example.com/api/users/123/profile"
# Query parameter manipulation
query = url.query
print(query.get("page")) # "1"
print(query.get_all("tag")) # [] (empty for non-existent keys)
# Immutable modification
new_url = url.with_host("api.example.com").with_port(8080)
print(new_url) # "https://api.example.com:8080/api/users?page=1&sort=name"
Detailed Examples
URL Creation and Parsing
from urlops import URL, parse
# Different ways to create URLs
url1 = URL("https://example.com/path")
url2 = parse("https://example.com/path") # Factory function
url3 = URL("//example.com/path") # Protocol-relative
url4 = URL("/relative/path") # Relative URL
# Check URL properties
print(url1.is_absolute()) # True
print(url4.is_absolute()) # False
print(url1.is_valid()) # True
Path Operations
from urlops import URL
url = URL("https://example.com/api/v1")
# Path joining with /
new_url = url / "users" / "123"
print(new_url) # "https://example.com/api/v1/users/123"
# Path properties
path = new_url.path
print(path.name) # "123"
print(path.stem) # "123" (no extension)
print(path.suffix) # "" (no extension)
print(path.parent) # URLPath("/api/v1/users")
# Path modification
new_path = path.with_name("profile.json")
print(new_path) # URLPath("/api/v1/users/profile.json")
# File-like operations
file_path = URL("https://example.com/docs/guide.pdf").path
print(file_path.stem) # "guide"
print(file_path.suffix) # ".pdf"
Query Parameter Handling
from urlops import URL
# Create URL with query parameters
url = URL("https://api.example.com/search?q=python&category=web&tag=api&tag=rest")
# Access query parameters
query = url.query
print(query.get("q")) # "python"
print(query.get("category")) # "web"
print(query.get_all("tag")) # ["api", "rest"]
print(query.keys()) # ["q", "category", "tag"]
print(len(query)) # 4 (total key-value pairs)
# Modify query parameters (immutable)
new_query = query.add("page", "2").set("q", "javascript")
print(new_query) # "q=javascript&category=web&tag=api&tag=rest&page=2"
# Remove parameters
clean_query = new_query.remove("tag")
print(clean_query) # "q=javascript&category=web&page=2"
# Builder pattern
final_query = query.with_(page="3", limit="10")
print(final_query) # "q=python&category=web&tag=api&tag=rest&page=3&limit=10"
URL Modification
from urlops import URL
base_url = URL("https://example.com/api")
# Modify different components
api_url = base_url.with_scheme("https").with_host("api.example.com").with_port(8080)
print(api_url) # "https://api.example.com:8080/api"
# Path operations
users_url = api_url / "users" / "123"
print(users_url) # "https://api.example.com:8080/api/users/123"
# Query and fragment
final_url = users_url.with_query("format=json").with_fragment("profile")
print(final_url) # "https://api.example.com:8080/api/users/123?format=json#profile"
Factory Functions
from urlops import parse, join
# Parse function
url = parse("https://example.com/api/v1")
# Join function for building URLs
base = parse("https://api.example.com")
api_url = join(base, "v1", "users", "123")
print(api_url) # "https://api.example.com/v1/users/123"
Error Handling
from urlops import URL, ParseError, ValidationError, TypeError
try:
# Invalid URL
url = URL("invalid://url")
except ValidationError as e:
print(f"Invalid URL: {e}")
try:
# Invalid port
url = URL("https://example.com:99999")
except ValidationError as e:
print(f"Invalid port: {e}")
try:
# Double slash in path
url = URL("https://example.com//invalid")
except ValidationError as e:
print(f"Invalid path: {e}")
try:
# Wrong type
url = URL(123)
except TypeError as e:
print(f"Type error: {e}")
Real-World Examples
from urlops import URL
# API endpoint construction
base_api = URL("https://api.github.com")
user_endpoint = base_api / "users" / "octocat"
repos_endpoint = user_endpoint / "repos"
# Add query parameters
search_url = repos_endpoint.with_query("type=public&sort=updated")
print(search_url) # "https://api.github.com/users/octocat/repos?type=public&sort=updated"
# CDN URL construction
cdn_base = URL("https://cdn.example.com")
asset_url = cdn_base / "images" / "logo.png"
print(asset_url) # "https://cdn.example.com/images/logo.png"
# Web scraping URL handling
scrape_url = URL("https://example.com/articles/2024/01/15")
article_id = scrape_url.path.name # "15"
year = scrape_url.path.parent.name # "2024"
Development Status
This project is currently in Stable (1.0.5). The core functionality is complete and ready for production use.
Contributing
Contributions are welcome! Please see our TODO.md for current development tasks.
Repository
This project is hosted on GitHub: https://github.com/RVV-karma/urlops
License
MIT License - see LICENSE for details.
Metadata
Release files for urlops 1.0.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| urlops-1.0.5.tar.gz | 21.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| urlops-1.0.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 33.3 kB
Release files / urlops-1.0.5.tar.gz
| Download URL | urlops-1.0.5.tar.gz |
|---|---|
| Size | 21.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
bb1465701b7ae51d5985c315320322ebf1976ce90ddfbc0885aaa6ac953d4795
|
|
BLAKE2b-256 checksum How to use checksums |
dfd5e808014634a7fc265a9b4d88f4446a5fbb996c91d41dfa31570e32d33d25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.8.18
|
Release files / urlops-1.0.5-py3-none-any.whl
| Download URL | urlops-1.0.5-py3-none-any.whl |
|---|---|
| Size | 11.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ed99a5fb4e55528f06ab2e725bf5f4b029aee6c68d95e28a14939a6a3a8405af
|
|
BLAKE2b-256 checksum How to use checksums |
f9fe66a223c961616813389ad7fe1ea56f2f002d368d7c762a86f799a62fc918
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.1.0 CPython/3.8.18
|
Security for urlops 1.0.5
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.