CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-uritemplate

Implementation of RFC 6570 URI Templates for Python applications

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

index.mddocs/

uritemplate

A Python implementation of RFC 6570 URI Templates that enables creation and expansion of URI templates according to the official specification. Provides both class-based and functional interfaces for creating dynamic URIs from templates and variable substitutions.

Package Information

  • Package Name: uritemplate
  • Language: Python
  • Installation: pip install uritemplate

Core Imports

from uritemplate import URITemplate, expand, partial, variables

Alternative imports for specific functionality:

from uritemplate import URITemplate  # Class-based approach
from uritemplate import expand        # Direct expansion function
from uritemplate import partial       # Partial expansion function
from uritemplate import variables     # Variable extraction function

Internal types and classes (for advanced usage):

from uritemplate.orderedset import OrderedSet
from uritemplate.variable import URIVariable, VariableValue, VariableValueDict, ScalarVariableValue

Basic Usage

from uritemplate import URITemplate, expand

# Basic template expansion using the URITemplate class
template = URITemplate('https://api.github.com/users/{username}/repos')
uri = template.expand(username='octocat')
print(uri)  # https://api.github.com/users/octocat/repos

# Direct expansion using the expand function
uri = expand('https://api.github.com/users/{username}/repos', username='octocat')
print(uri)  # https://api.github.com/users/octocat/repos

# Multiple variables with different expansion operators
api_template = URITemplate('https://api.github.com{/endpoint}{?page,per_page}')
uri = api_template.expand(endpoint='repos', page=2, per_page=50)
print(uri)  # https://api.github.com/repos?page=2&per_page=50

# Using dictionary for variable values  
variables_dict = {'username': 'octocat', 'repo': 'Hello-World'}
repo_template = URITemplate('https://api.github.com/repos/{username}/{repo}')
uri = repo_template.expand(variables_dict)
print(uri)  # https://api.github.com/repos/octocat/Hello-World

Architecture

The uritemplate package follows a simple, focused design with two main interfaces:

  • Functional Interface: Direct functions (expand, partial, variables) for immediate operations
  • Class-based Interface: URITemplate class for reusable template objects with parsing optimization

Core components:

  • URITemplate: Main template class that parses and expands URI templates
  • URIVariable: Internal variable representation supporting all RFC 6570 operators
  • OrderedSet: Custom ordered set implementation for maintaining variable name order
  • Operator: Enum defining URI template expansion operators (simple, reserved, fragment, etc.)

Capabilities

Template Expansion

Expand URI templates with variable substitutions using either the functional or class-based interface.

def expand(
    uri: str,
    var_dict: Optional[VariableValueDict] = None,
    **kwargs: VariableValue
) -> str:
    """
    Expand the URI template with the given parameters.
    
    Parameters:
    - uri: The templated URI to expand
    - var_dict: Optional dictionary with variables and values
    - **kwargs: Alternative way to pass arguments
    
    Returns:
    Expanded URI string
    """
class URITemplate:
    def __init__(self, uri: str):
        """
        Initialize a URI template.
        
        Parameters:
        - uri: The URI template string
        """
        
    def expand(
        self,
        var_dict: Optional[VariableValueDict] = None,
        **kwargs: VariableValue
    ) -> str:
        """
        Expand the template with the given parameters.
        
        Parameters:
        - var_dict: Optional dictionary with variables and values
        - **kwargs: Alternative way to pass arguments
        
        Returns:
        Expanded URI string
        """

Partial Template Expansion

Partially expand templates, leaving unresolved variables as template expressions for later expansion.

def partial(
    uri: str,
    var_dict: Optional[VariableValueDict] = None,
    **kwargs: VariableValue
) -> URITemplate:
    """
    Partially expand the template with the given parameters.
    
    If all parameters for the template are not given, return a
    partially expanded template.
    
    Parameters:
    - uri: The templated URI to expand
    - var_dict: Optional dictionary with variables and values
    - **kwargs: Alternative way to pass arguments
    
    Returns:
    URITemplate instance with partially expanded template
    """
class URITemplate:
    def partial(
        self,
        var_dict: Optional[VariableValueDict] = None,
        **kwargs: VariableValue
    ) -> URITemplate:
        """
        Partially expand the template with the given parameters.
        
        Parameters:
        - var_dict: Optional dictionary with variables and values
        - **kwargs: Alternative way to pass arguments
        
        Returns:
        URITemplate instance with partially expanded template
        """

Usage Example

from uritemplate import partial, URITemplate

# Partial expansion with function
template_str = 'https://api.github.com/repos/{owner}/{repo}{/path}{?page,per_page}'
partial_template = partial(template_str, owner='octocat', repo='Hello-World')
print(str(partial_template))  # https://api.github.com/repos/octocat/Hello-World{/path}{?page,per_page}

# Complete the expansion later
final_uri = partial_template.expand(path='issues', page=1)
print(final_uri)  # https://api.github.com/repos/octocat/Hello-World/issues?page=1

# Partial expansion with class
template = URITemplate('https://api.example.com{/version}/users{/user_id}{?fields}')
partial_result = template.partial(version='v1')
final_uri = partial_result.expand(user_id=123, fields='name,email')
print(final_uri)  # https://api.example.com/v1/users/123?fields=name,email

Variable Extraction

Extract all variable names from a URI template for introspection and validation.

def variables(uri: str) -> OrderedSet:
    """
    Parse the variables of the template.
    
    This returns all of the variable names in the URI Template.
    
    Parameters:
    - uri: The URI template string
    
    Returns:
    OrderedSet of variable names found in the template
    """

Usage Example

from uritemplate import variables

# Extract variables from template
template_str = 'https://api.github.com/repos/{owner}/{repo}/issues{/number}{?state,labels}'
vars_found = variables(template_str)
print(list(vars_found))  # ['owner', 'repo', 'number', 'state', 'labels']

# Check if specific variables are present
if 'owner' in vars_found and 'repo' in vars_found:
    print("Template requires owner and repo parameters")
    
# Get variable count
print(f"Template contains {len(vars_found)} variables")

URI Template Class Properties

Access template properties and metadata through the URITemplate class.

class URITemplate:
    @property
    def uri(self) -> str:
        """The original URI template string."""
        
    @property
    def variables(self) -> List[URIVariable]:
        """List of URIVariable objects representing parsed variables."""
        
    @property  
    def variable_names(self) -> OrderedSet:
        """Set of variable names in the URI template."""

Usage Example

from uritemplate import URITemplate

template = URITemplate('https://api.github.com/{endpoint}{?page,per_page}')

# Access original template string
print(template.uri)  # https://api.github.com/{endpoint}{?page,per_page}

# Get variable names
print(list(template.variable_names))  # ['endpoint', 'page', 'per_page']

# Template comparison and hashing
template1 = URITemplate('https://api.example.com/{id}')
template2 = URITemplate('https://api.example.com/{id}')
print(template1 == template2)  # True

# Templates can be used as dictionary keys
template_cache = {template1: "cached_result"}

Internal Components (Advanced Usage)

Access to lower-level components for advanced template manipulation and introspection.

class URIVariable:
    def __init__(self, var: str):
        """
        Initialize a URI variable from a template expression.
        
        Parameters:
        - var: The variable expression string (e.g., "var", "+var", "?var,x,y")
        """
        
    def expand(self, var_dict: Optional[VariableValueDict] = None) -> Mapping[str, str]:
        """
        Expand this variable using the provided variable dictionary.
        
        Parameters:
        - var_dict: Dictionary of variable names to values
        
        Returns:
        Dictionary mapping the original variable expression to its expanded form
        """
        
    # Properties
    original: str  # The original variable expression
    operator: Operator  # The expansion operator used
    variables: List[Tuple[str, Dict[str, Any]]]  # Variable names and their options
    variable_names: List[str]  # List of variable names in this expression
    defaults: Dict[str, str]  # Default values for variables

Usage Example

from uritemplate.variable import URIVariable

# Parse a complex variable expression
var = URIVariable("?var,hello,x,y")
print(var.variable_names)  # ['var', 'hello', 'x', 'y']
print(var.operator)  # Operator.form_style_query

# Expand the variable
expansion = var.expand({
    'var': 'value', 
    'hello': 'Hello World!',
    'x': '1024',
    'y': '768'
})
print(expansion)  # {'?var,hello,x,y': '?var=value&hello=Hello%20World%21&x=1024&y=768'}

Types

Main API Types

class URITemplate:
    def __init__(self, uri: str): ...
    def expand(
        self, 
        var_dict: Optional[VariableValueDict] = None, 
        **kwargs: VariableValue
    ) -> str: ...
    def partial(
        self, 
        var_dict: Optional[VariableValueDict] = None, 
        **kwargs: VariableValue
    ) -> URITemplate: ...
    
    # Properties
    uri: str  # The original URI template string
    variables: List[URIVariable]  # List of parsed variable objects
    variable_names: OrderedSet  # Set of variable names in the template
    
    # Standard methods
    def __str__(self) -> str: ...
    def __repr__(self) -> str: ...
    def __eq__(self, other: object) -> bool: ...
    def __hash__(self) -> int: ...

def expand(
    uri: str,
    var_dict: Optional[VariableValueDict] = None,
    **kwargs: VariableValue
) -> str: ...

def partial(
    uri: str,
    var_dict: Optional[VariableValueDict] = None,
    **kwargs: VariableValue
) -> URITemplate: ...

def variables(uri: str) -> OrderedSet: ...

Supporting Types

class OrderedSet:
    def __init__(self, iterable: Optional[Iterable[str]] = None): ...
    def add(self, key: str) -> None: ...
    def discard(self, key: str) -> None: ...
    def pop(self, last: bool = True) -> str: ...
    def __len__(self) -> int: ...
    def __contains__(self, key: object) -> bool: ...
    def __iter__(self) -> Generator[str, None, None]: ...
    def __reversed__(self) -> Generator[str, None, None]: ...

class URIVariable:
    def __init__(self, var: str): ...
    def expand(self, var_dict: Optional[VariableValueDict] = None) -> Mapping[str, str]: ...
    
    # Properties
    original: str  # The original variable expression
    operator: Operator  # The expansion operator
    variables: List[Tuple[str, Dict[str, Any]]]  # Variable names and options
    variable_names: List[str]  # List of variable names
    defaults: Dict[str, str]  # Default values for variables

class Operator(Enum):
    """RFC 6570 URI Template expansion operators."""
    default = ""  # Simple string expansion: {var}
    reserved = "+"  # Reserved expansion: {+var}
    fragment = "#"  # Fragment expansion: {#var}
    label_with_dot_prefix = "."  # Label expansion: {.var}
    path_segment = "/"  # Path segment expansion: {/var}
    path_style_parameter = ";"  # Path-style parameter expansion: {;var}
    form_style_query = "?"  # Form-style query expansion: {?var}
    form_style_query_continuation = "&"  # Form-style query continuation: {&var}

# Type aliases for variable values
ScalarVariableValue = Union[int, float, complex, str, None]
VariableValue = Union[
    Sequence[ScalarVariableValue],
    List[ScalarVariableValue], 
    Mapping[str, ScalarVariableValue],
    Tuple[str, ScalarVariableValue],
    ScalarVariableValue
]
VariableValueDict = Dict[str, VariableValue]

Advanced Usage Patterns

Complex Variable Types

from uritemplate import URITemplate

# List variables for path segments
template = URITemplate('https://api.example.com/{path*}')
uri = template.expand(path=['users', 'profile', 'settings'])
print(uri)  # https://api.example.com/users/profile/settings

# Dictionary variables for query parameters
template = URITemplate('https://api.example.com/search{?params*}')
uri = template.expand(params={'q': 'python', 'sort': 'stars', 'order': 'desc'})
print(uri)  # https://api.example.com/search?q=python&sort=stars&order=desc

# Mixed variable types
template = URITemplate('https://api.example.com/{+base}/search{?q,filters*}')
uri = template.expand(
    base='https://search.example.com/api/v1',
    q='machine learning',
    filters={'category': 'tech', 'year': '2023'}
)

Template Reuse and Performance

from uritemplate import URITemplate

class GitHubAPI:
    # Class-level template for reuse (parsed once)
    repo_template = URITemplate('https://api.github.com/repos/{owner}/{repo}')
    issues_template = URITemplate('https://api.github.com/repos/{owner}/{repo}/issues{/number}{?state,labels}')
    
    def __init__(self, owner: str, repo: str):
        self.owner = owner
        self.repo = repo
    
    def get_repo_url(self) -> str:
        return self.repo_template.expand(owner=self.owner, repo=self.repo)
    
    def get_issues_url(self, number: int = None, state: str = None) -> str:
        params = {'owner': self.owner, 'repo': self.repo}
        if number:
            params['number'] = number
        if state:
            params['state'] = state
        return self.issues_template.expand(params)

# Usage
api = GitHubAPI('octocat', 'Hello-World')
print(api.get_repo_url())  # https://api.github.com/repos/octocat/Hello-World
print(api.get_issues_url(state='open'))  # https://api.github.com/repos/octocat/Hello-World/issues?state=open

Error Handling

The uritemplate package handles various edge cases gracefully:

  • Missing variables: Unexpanded variables remain in template form during partial expansion
  • Invalid variable types: Complex objects are converted to strings when possible
  • Empty values: Empty strings and None values are handled according to RFC 6570 specification
  • Special characters: Proper URL encoding is applied based on the expansion operator used
from uritemplate import URITemplate, expand

# Missing variables in partial expansion
template = URITemplate('https://api.example.com/{service}/{version}{/endpoint}')
partial_result = template.partial(service='users')
print(str(partial_result))  # https://api.example.com/users/{version}{/endpoint}

# Empty and None values
uri = expand('https://api.example.com{/path}{?query}', path='', query=None)
print(uri)  # https://api.example.com/

# Special character encoding
uri = expand('https://api.example.com/search{?q}', q='hello world & special chars!')
print(uri)  # https://api.example.com/search?q=hello%20world%20%26%20special%20chars%21

docs

index.md

tile.json