A python library for interacting with HAL+JSON APIs
—
Support for HAL templated links with parameter expansion and URI template processing. RestNavigator handles RFC 6570 URI templates transparently, allowing dynamic URL construction from templates.
Navigate templated links by providing required parameters to expand URLs dynamically.
class PartialNavigator:
def __call__(self, **kwargs) -> 'HALNavigator':
"""
Expand template parameters to create a full HALNavigator.
Parameters:
- **kwargs: Template variable values
Returns:
HALNavigator for the expanded URI
"""
def expand_uri(self, **kwargs) -> str:
"""
Expand template to URI string without creating navigator.
Parameters:
- **kwargs: Template variable values
Returns:
Expanded URI string
"""
def expand_link(self, **kwargs) -> 'Link':
"""
Expand template to Link object.
Parameters:
- **kwargs: Template variable values
Returns:
Link object with expanded URI
"""
@property
def variables(self) -> set:
"""
Set of template variable names required for expansion.
Returns:
Set of variable names
"""
@property
def template_uri(self) -> str:
"""
The templated URI string before expansion.
Returns:
Original template URI
"""# Discover templated links
api_links = api.links()
user_template = api_links['ht:user'] # Returns PartialNavigator
# Check if it's a template
if hasattr(user_template, 'variables'):
print("Template variables:", user_template.variables)
print("Template URI:", user_template.template_uri)
# Expand template with parameters
user = user_template(name='john_doe')
# Alternative expansion methods
user_uri = user_template.expand_uri(name='john_doe')
user_link = user_template.expand_link(name='john_doe')# Single parameter template
# Template: "/users/{id}"
user = api['users'](id=123)
# Multiple parameter template
# Template: "/users/{id}/posts{?page,limit}"
posts = api['users'](id=123)['posts'](page=2, limit=10)
# Optional parameters (query strings)
# Template: "/search{?q,type,sort}"
search_results = api['search'](q='python', type='repositories')
# Complex template with path and query parameters
# Template: "/repos/{owner}/{repo}/issues{?state,labels,sort}"
issues = api['repos'](owner='octocat', repo='Hello-World')['issues'](
state='open',
labels='bug,enhancement',
sort='created'
)# Check required variables before expansion
search_template = api['search']
required_vars = search_template.variables
print(f"Required parameters: {required_vars}")
# Partial expansion (providing only some parameters)
try:
# This might fail if 'q' is required
incomplete = api['search'](type='users') # Missing 'q' parameter
except Exception as e:
print("Template expansion error:", e)
# Safe expansion with validation
def safe_template_expand(template, **kwargs):
required = template.variables
provided = set(kwargs.keys())
missing = required - provided
if missing:
raise ValueError(f"Missing required parameters: {missing}")
return template(**kwargs)
# Usage
user = safe_template_expand(api['users'], id=123)# Get the raw template URI
template = api['users']
print("Template:", template.template_uri) # "/users/{id}"
# Expand to URI without creating navigator
user_uri = template.expand_uri(id=123) # "/users/123"
# Build full URL
full_url = api.session.base_url + user_uri
# Create Link object
link = template.expand_link(id=123)
print("Link URI:", link.uri)
print("Link properties:", link.props)# Chain templated navigations
user_posts = api['users'](id=123)['posts'](page=1)
# Equivalent to
user = api['users'](id=123)
posts = user['posts'](page=1)
# Mixed template and regular navigation
# api['users'](id=123)['posts', 'comments', 0]
specific_comment = api['users'](id=123)['posts']['comments'][0]# Store template for reuse
user_template = api['users']
# Create multiple users from same template
users = []
for user_id in [1, 2, 3, 4, 5]:
user = user_template(id=user_id)
users.append(user)
# Template with default values simulation
def get_user_posts(user_id, page=1, limit=20):
return api['users'](id=user_id)['posts'](page=page, limit=limit)
# Dynamic template parameter building
search_params = {'q': 'python'}
if include_forks:
search_params['fork'] = 'true'
if language:
search_params['language'] = language
results = api['search'](**search_params)# Inspect all templated links in a resource
def find_templates(navigator):
templates = {}
for rel, link in navigator.links().items():
if hasattr(link, 'variables'):
templates[rel] = {
'uri': link.template_uri,
'variables': link.variables
}
return templates
# Usage
api_templates = find_templates(api)
for rel, info in api_templates.items():
print(f"{rel}: {info['uri']} (vars: {info['variables']})")Install with Tessl CLI
npx tessl i tessl/pypi-restnavigator