Yet another URL library - comprehensive URL parsing and manipulation for Python
Comprehensive query string manipulation with support for multiple values per parameter, various input formats, and MultiDict integration for robust parameter handling.
YARL provides flexible type definitions for query parameters to support various input formats.
from typing import Union, Sequence, Mapping, SupportsInt
SimpleQuery = Union[str, SupportsInt, float]
"""Simple query value types: string, integer-like, or float"""
QueryVariable = Union[SimpleQuery, Sequence[SimpleQuery]]
"""Query variable: single value or sequence of values for multi-value parameters"""
Query = Union[
None,
str,
Mapping[str, QueryVariable],
Sequence[tuple[str, QueryVariable]]
]
"""Query parameter input types: None, string, mapping, or sequence of tuples"""Methods for modifying query parameters in URLs, supporting various input formats and merge strategies.
def with_query(self, query: Query = None, **kwargs: QueryVariable) -> "URL":
"""
Return URL with completely replaced query parameters.
Args:
query (Query, optional): New query parameters
**kwargs: Query parameters as keyword arguments
Returns:
URL: New URL with replaced query parameters
Examples:
url.with_query({'key': 'value'})
url.with_query([('key', 'value1'), ('key', 'value2')])
url.with_query('key=value&other=data')
url.with_query(key='value', other='data')
"""
def extend_query(self, query: Query = None, **kwargs: QueryVariable) -> "URL":
"""
Return URL with additional query parameters appended to existing ones.
Args:
query (Query, optional): Additional query parameters
**kwargs: Additional query parameters as keyword arguments
Returns:
URL: New URL with extended query parameters
Examples:
# Add to existing parameters
url.extend_query({'new_key': 'value'})
url.extend_query(new_key='value')
"""
def update_query(self, query: Query = None, **kwargs: QueryVariable) -> "URL":
"""
Return URL with updated query parameters (merge strategy).
Existing parameters are preserved unless explicitly overridden.
Args:
query (Query, optional): Query parameters to update/add
**kwargs: Query parameters as keyword arguments
Returns:
URL: New URL with updated query parameters
Examples:
# Update existing 'limit', add new 'sort'
url.update_query({'limit': 100, 'sort': 'name'})
"""
def without_query_params(self, *query_params: str) -> "URL":
"""
Return URL without specified query parameters.
Args:
*query_params (str): Names of parameters to remove
Returns:
URL: New URL with specified parameters removed
Examples:
url.without_query_params('limit', 'offset')
"""Low-level functions for processing query parameters into string format.
def query_var(v: SimpleQuery) -> str:
"""
Convert a query variable to its string representation.
Args:
v (SimpleQuery): Value to convert (str, int, float, or SupportsInt)
Returns:
str: String representation of the value
Raises:
ValueError: For float('inf') or float('nan')
TypeError: For unsupported types
"""
def get_str_query(*args, **kwargs) -> str | None:
"""
Convert various query input formats to query string.
Args:
*args: Single query argument (Query type)
**kwargs: Query parameters as keyword arguments
Returns:
str | None: Query string or None if no parameters
Raises:
ValueError: If both args and kwargs provided
"""
def get_str_query_from_sequence_iterable(
items: Iterable[tuple[Union[str, istr], QueryVariable]]
) -> str:
"""
Convert sequence of (key, value) pairs to query string.
Supports multi-value parameters where value can be a sequence.
Args:
items: Iterable of (key, value) pairs
Returns:
str: URL-encoded query string
"""
def get_str_query_from_iterable(
items: Iterable[tuple[Union[str, istr], SimpleQuery]]
) -> str:
"""
Convert iterable of (key, value) pairs to query string.
Values must be simple (not sequences).
Args:
items: Iterable of (key, value) pairs
Returns:
str: URL-encoded query string
"""Access query parameters from URL objects.
@property
def query(self) -> MultiDictProxy[str]:
"""
Query parameters as MultiDictProxy.
Provides dict-like access with support for multiple values per key.
Returns:
MultiDictProxy[str]: Read-only multi-value dictionary of parameters
"""
@property
def raw_query_string(self) -> str:
"""Raw (encoded) query string without leading '?'"""
@property
def query_string(self) -> str:
"""Decoded query string without leading '?'"""
@property
def path_qs(self) -> str:
"""Decoded path + query string combination"""
@property
def raw_path_qs(self) -> str:
"""Raw path + query string combination"""from yarl import URL
base_url = URL('https://api.example.com/users')
# Add query parameters
search_url = base_url.with_query({'q': 'john', 'active': True, 'limit': 10})
print(search_url) # https://api.example.com/users?q=john&active=True&limit=10
# Using keyword arguments
same_url = base_url.with_query(q='john', active=True, limit=10)
print(same_url) # https://api.example.com/users?q=john&active=True&limit=10from yarl import URL
url = URL('https://api.example.com/search')
# Multiple values for same parameter using list
multi_url = url.with_query({'tags': ['python', 'web', 'api'], 'active': True})
print(multi_url) # https://api.example.com/search?tags=python&tags=web&tags=api&active=True
# Using sequence of tuples for precise control
tuple_url = url.with_query([
('tags', 'python'),
('tags', 'web'),
('sort', 'date'),
('order', 'desc')
])
print(tuple_url) # https://api.example.com/search?tags=python&tags=web&sort=date&order=descfrom yarl import URL
url = URL('https://api.example.com/data')
# Different parameter types
typed_url = url.with_query({
'string_param': 'text value',
'int_param': 42,
'float_param': 3.14,
'bool_param': True,
'list_param': [1, 2, 3]
})
print(typed_url)
# https://api.example.com/data?string_param=text+value&int_param=42&float_param=3.14&bool_param=True&list_param=1&list_param=2&list_param=3
# Note: boolean True becomes 'True', False becomes 'False'from yarl import URL
# Start with existing parameters
base_url = URL('https://api.example.com/users?active=true&limit=10')
# Extend with additional parameters
extended = base_url.extend_query({'sort': 'name', 'order': 'asc'})
print(extended) # https://api.example.com/users?active=true&limit=10&sort=name&order=asc
# Update existing parameters
updated = base_url.update_query({'limit': 50, 'offset': 20})
print(updated) # https://api.example.com/users?active=true&limit=50&offset=20
# Remove specific parameters
cleaned = extended.without_query_params('sort', 'order')
print(cleaned) # https://api.example.com/users?active=true&limit=10from yarl import URL
# From query string
url = URL('https://example.com/').with_query('name=john&age=30&tags=dev&tags=python')
print(url.query) # MultiDictProxy with parsed parameters
# Access individual parameters
print(url.query['name']) # 'john'
print(url.query.getlist('tags')) # ['dev', 'python']
# Get all parameter names and values
for key in url.query:
values = url.query.getlist(key)
print(f"{key}: {values}")from yarl import URL
url = URL('https://api.example.com/')
# Valid numeric types
valid_url = url.with_query({
'page': 1,
'size': 50,
'ratio': 1.5
})
# Invalid types raise errors
try:
invalid_url = url.with_query({'infinity': float('inf')})
except ValueError as e:
print(f"Error: {e}") # float('inf') is not supported
try:
invalid_url = url.with_query({'nan': float('nan')})
except ValueError as e:
print(f"Error: {e}") # float('nan') is not supportedfrom yarl import URL
# Complex query building
api_url = URL('https://api.example.com/search')
# Build complex search query
search_params = {
'q': 'python web framework',
'categories': ['tutorials', 'documentation'],
'difficulty': ['beginner', 'intermediate'],
'published_after': '2023-01-01',
'sort': 'relevance',
'page': 1,
'per_page': 20
}
search_url = api_url.with_query(search_params)
print(search_url)
# Pagination through query updates
page_2 = search_url.update_query({'page': 2})
page_3 = search_url.update_query({'page': 3, 'per_page': 50})
# Filter refinement by extending
refined = search_url.extend_query({'language': 'en', 'format': 'json'})from yarl import URL
url = URL('https://example.com/')
# Special characters are automatically encoded
special_chars = url.with_query({
'message': 'Hello, World!',
'symbols': '$%&=+',
'unicode': 'café résumé',
'spaces': 'value with spaces'
})
print(special_chars.raw_query_string) # Shows encoded version
print(special_chars.query_string) # Shows decoded version
# Access the raw vs decoded values
print(special_chars.query['unicode']) # 'café résumé' (decoded)Install with Tessl CLI
npx tessl i tessl/pypi-yarl