CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-restnavigator

A python library for interacting with HAL+JSON APIs

Pending
Overview
Eval results
Files

utilities.mddocs/

Utilities

Enhanced collections and helper functions for HAL processing. RestNavigator provides specialized data structures and utilities that support CURIE prefixes, link disambiguation, and HAL-specific data manipulation.

Capabilities

Enhanced Collections

Specialized collection classes that provide HAL-specific functionality for managing links and data.

class CurieDict(dict):
    def __init__(self, default_curie: str, d: dict):
        """
        Dictionary supporting CURIE prefixes for link relations.
        
        Parameters:
        - default_curie: Default CURIE prefix for unprefixed relations
        - d: Initial dictionary data
        """
    
    @property
    def default_curie(self) -> str:
        """Default CURIE prefix"""
class LinkList(list):
    def get_by(self, prop: str, val, raise_exc: bool = False):
        """
        Get single item by property value.
        
        Parameters:
        - prop: Property name to search by
        - val: Property value to match
        - raise_exc: Whether to raise exception if not found
        
        Returns:
        First matching item or None
        """
    
    def getall_by(self, prop: str, val) -> list:
        """
        Get all items matching property value.
        
        Parameters:
        - prop: Property name to search by
        - val: Property value to match
        
        Returns:
        List of all matching items
        """
    
    def named(self, name: str):
        """
        Get item by name property (HAL standard).
        
        Parameters:
        - name: Name value to search for
        
        Returns:
        Item with matching name property
        """
    
    def append_with(self, obj, **properties) -> None:
        """
        Add item with metadata properties.
        
        Parameters:
        - obj: Object to add to list
        - **properties: Metadata properties to associate
        """

Core Utility Functions

Essential helper functions for URL processing, data manipulation, and HAL-specific operations.

def fix_scheme(url: str) -> str:
    """
    Add http:// scheme if missing, validate scheme.
    
    Parameters:
    - url: URL to fix
    
    Returns:
    URL with proper scheme
    
    Raises:
    WileECoyoteException: For invalid schemes
    ZachMorrisException: For multiple schemes
    """

def normalize_getitem_args(args) -> list:
    """
    Normalize __getitem__ arguments to list.
    
    Parameters:
    - args: Arguments from __getitem__ call
    
    Returns:
    Normalized list of arguments
    """

def namify(root_uri: str) -> str:
    """
    Convert URI to readable API name.
    
    Parameters:
    - root_uri: API root URI
    
    Returns:
    Human-readable API name
    """

def objectify_uri(relative_uri: str) -> str:
    """
    Convert URI to object notation string.
    
    Parameters:
    - relative_uri: Relative URI path
    
    Returns:
    Object notation representation
    """

def parse_media_type(media_type: str) -> tuple:
    """
    Parse HTTP media type header.
    
    Parameters:
    - media_type: Media type string from HTTP header
    
    Returns:
    Tuple of (type, subtype, parameters)
    """

def getpath(d: dict, json_path: str, default=None, sep: str = '.') -> any:
    """
    Get nested dictionary value by path.
    
    Parameters:
    - d: Dictionary to search
    - json_path: Dot-separated path to value
    - default: Default value if path not found
    - sep: Path separator character
    
    Returns:
    Value at path or default
    """

def getstate(d: dict) -> dict:
    """
    Deep copy dict removing HAL keys (_links, _embedded).
    
    Parameters:
    - d: Dictionary to clean
    
    Returns:
    Clean copy without HAL metadata
    """

Usage Examples

Working with CurieDict

# CurieDict automatically handles CURIE prefixes
links = CurieDict('ex', {})

# These are equivalent when default_curie is 'ex'
links['ex:users'] = user_navigator
links['users'] = user_navigator  # Automatically becomes 'ex:users'

# Access with or without CURIE
user_nav = links['users']      # Works
user_nav = links['ex:users']   # Also works

# IANA standard relations have precedence
links['next'] = next_page      # Standard 'next' relation
links['ex:next'] = custom_next # Custom next relation
print(links['next'])          # Returns standard 'next', not 'ex:next'

Working with LinkList

# LinkList for managing HAL link arrays
links = LinkList()

# Add links with properties
links.append_with(widget1_nav, name='widget1', profile='widget')
links.append_with(widget2_nav, name='widget2', profile='widget')  
links.append_with(gadget_nav, name='gadget1', profile='gadget')

# Find by property
widget1 = links.get_by('name', 'widget1')
gadget = links.get_by('profile', 'gadget')

# Get all matching items
all_widgets = links.getall_by('profile', 'widget')

# Use standard HAL name property
specific_item = links.named('gadget1')  # Same as get_by('name', 'gadget1')

# Handle missing items
missing = links.get_by('name', 'nonexistent', raise_exc=False)  # Returns None

URL and Scheme Utilities

from restnavigator.utils import fix_scheme, namify, objectify_uri

# Fix URL schemes
clean_url = fix_scheme('api.example.com')  # Returns 'http://api.example.com'
clean_url = fix_scheme('https://api.example.com')  # Returns unchanged

# Generate API names
api_name = namify('https://api.github.com/v3')  # Returns 'Github'
api_name = namify('http://haltalk.herokuapp.com')  # Returns 'Haltalk'

# Convert URIs to object notation
obj_notation = objectify_uri('/users/123/posts')  # Returns 'users.123.posts'
obj_notation = objectify_uri('/api/v1/repos')     # Returns 'api.v1.repos'

Data Path Utilities

from restnavigator.utils import getpath, getstate

# Navigate nested data structures
data = {
    'user': {
        'profile': {
            'name': 'John Doe',
            'settings': {
                'theme': 'dark'
            }
        }
    }
}

# Get nested values
name = getpath(data, 'user.profile.name')  # Returns 'John Doe'
theme = getpath(data, 'user.profile.settings.theme')  # Returns 'dark'
missing = getpath(data, 'user.missing.field', 'default')  # Returns 'default'

# Custom separator
theme = getpath(data, 'user/profile/settings/theme', sep='/')  # Returns 'dark'

HAL Data Cleaning

from restnavigator.utils import getstate

# Remove HAL metadata from response data
hal_response = {
    'id': 1,
    'name': 'John Doe',
    '_links': {
        'self': {'href': '/users/1'},
        'posts': {'href': '/users/1/posts'}
    },
    '_embedded': {
        'posts': [
            {
                'id': 1,
                'title': 'First Post',
                '_links': {'self': {'href': '/posts/1'}}
            }
        ]
    }
}

# Get clean state without HAL metadata
clean_data = getstate(hal_response)
# Returns: {'id': 1, 'name': 'John Doe'}

Media Type Parsing

from restnavigator.utils import parse_media_type

# Parse content-type headers
main_type, sub_type, params = parse_media_type('application/hal+json; charset=utf-8')
# main_type: 'application'
# sub_type: 'hal+json'  
# params: {'charset': 'utf-8'}

# Handle complex media types
main_type, sub_type, params = parse_media_type('text/html; charset=utf-8; boundary=something')
# params: {'charset': 'utf-8', 'boundary': 'something'}

Navigation Argument Processing

from restnavigator.utils import normalize_getitem_args

# Normalize different argument patterns
args1 = normalize_getitem_args('single')           # Returns ['single']
args2 = normalize_getitem_args(('a', 'b'))         # Returns ['a', 'b']
args3 = normalize_getitem_args(['x', 'y', 'z'])    # Returns ['x', 'y', 'z']

# Used internally for bracket notation like:
# api['users', 'posts', 0]  # Gets normalized to ['users', 'posts', 0]

Advanced Collection Patterns

# Building dynamic link collections
def build_link_collection(api_links):
    """Build organized link collection from API links"""
    organized = {}
    
    for rel, links in api_links.items():
        if isinstance(links, list):
            link_list = LinkList()
            for link in links:
                # Extract properties from link metadata
                props = getattr(link, 'props', {})
                link_list.append_with(link, **props)
            organized[rel] = link_list
        else:
            organized[rel] = links
    
    return organized

# Custom CURIE handling
def smart_curie_lookup(curie_dict, relation):
    """Smart lookup with fallback logic"""
    # Try exact match first
    if relation in curie_dict:
        return curie_dict[relation]
    
    # Try with default CURIE
    if curie_dict.default_curie:
        full_rel = f"{curie_dict.default_curie}:{relation}"
        if full_rel in curie_dict:
            return curie_dict[full_rel]
    
    # Try without CURIE if relation includes one
    if ':' in relation:
        bare_rel = relation.split(':', 1)[1]
        if bare_rel in curie_dict:
            return curie_dict[bare_rel]
    
    return None

Install with Tessl CLI

npx tessl i tessl/pypi-restnavigator

docs

authentication.md

exceptions.md

http-operations.md

index.md

navigation.md

templated-links.md

utilities.md

tile.json