CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-dpath

Filesystem-like pathing and searching for dictionaries

Pending
Overview
Eval results
Files

advanced-operations.mddocs/

Advanced Operations

Low-level utilities for custom path manipulation and advanced use cases, providing direct access to the underlying path walking, matching, and transformation algorithms that power dpath's high-level functions.

Capabilities

Path Walking and Traversal

Core traversal functions that provide the foundation for all dpath operations, offering fine-grained control over how nested structures are navigated.

from dpath.segments import walk, make_walkable, leaf, leafy

def walk(obj, location=()):
    """
    Walk object yielding (segments, value) pairs in breadth-first order.
    
    Parameters:
    - obj: Object to walk (dict, list, or other nested structure)
    - location (tuple): Current location tuple (used internally for recursion)
    
    Yields:
    Tuple[Tuple[PathSegment, ...], Any]: (path_segments, value) pairs
    
    Note:
    Walks right-to-left for sequences to support safe deletion patterns.
    """

def make_walkable(node):
    """
    Create iterator for (key, value) pairs regardless of node type.
    
    Parameters:
    - node: Dict, list, tuple, or other container
    
    Returns:
    Iterator[Tuple[PathSegment, Any]]: Iterator of (key, value) pairs
    """

def leaf(thing):
    """
    Check if object is a leaf (primitive type: str, int, float, bool, None, bytes).
    
    Parameters:
    - thing: Any object to test
    
    Returns:
    bool: True if object is a primitive leaf value
    """

def leafy(thing):
    """
    Check if object is leaf-like (primitive or empty container).
    
    Parameters:
    - thing: Any object to test
    
    Returns:
    bool: True if object is primitive or empty sequence/mapping
    """

Usage Examples

from dpath.segments import walk, make_walkable, leaf, leafy

data = {
    "users": {
        "john": {"age": 30, "hobbies": ["reading", "gaming"]},
        "jane": {"age": 25, "hobbies": []}
    }
}

# Walk entire structure
for path_segments, value in walk(data):
    path_str = "/".join(str(seg) for seg in path_segments)
    print(f"{path_str}: {value} (leaf: {leaf(value)})")

# Output includes:
# users: {'john': {...}, 'jane': {...}} (leaf: False)
# users/john: {'age': 30, 'hobbies': [...]} (leaf: False)  
# users/john/age: 30 (leaf: True)
# users/john/hobbies: ['reading', 'gaming'] (leaf: False)
# users/john/hobbies/0: reading (leaf: True)
# etc.

# Make any container walkable
mixed_data = [{"key": "value"}, "string", 42]
for key, value in make_walkable(mixed_data):
    print(f"Index {key}: {value}")

# Test leaf conditions
print(leaf("string"))      # True
print(leaf(42))           # True  
print(leaf([]))           # False
print(leafy([]))          # True (empty containers are leafy)
print(leafy({"a": 1}))    # False (non-empty containers)

Pattern Matching and Filtering

Advanced pattern matching utilities for implementing custom search and filter logic.

from dpath.segments import match, view, has, get

def match(segments, glob):
    """
    Check if path segments match glob pattern.
    
    Parameters:
    - segments (Path): Path segments as tuple or list
    - glob (Glob): Glob pattern as tuple or list
    
    Returns:
    bool: True if segments match the glob pattern
    
    Note:
    Supports *, **, character classes, and complex patterns.
    Converts integers to strings for comparison.
    """

def view(obj, glob):
    """
    Create filtered deep copy view of object matching glob.
    
    Parameters:
    - obj (MutableMapping): Object to create view from
    - glob (Glob): Glob pattern to match
    
    Returns:
    MutableMapping: New object containing only matching paths (deep copied)
    """

def has(obj, segments):
    """
    Check if path exists in object.
    
    Parameters:
    - obj: Object to check
    - segments: Path segments as sequence
    
    Returns:
    bool: True if path exists
    """

def get(obj, segments):
    """
    Get value at path segments (lower-level than dpath.get).
    
    Parameters:
    - obj: Object to navigate
    - segments: Path segments as sequence
    
    Returns:
    Any: Value at path
    
    Raises:
    - PathNotFound: If path doesn't exist
    """

Usage Examples

from dpath.segments import match, view, has, get

data = {
    "api": {
        "v1": {"users": ["john", "jane"]},
        "v2": {"users": ["bob"], "admin": ["alice"]}
    }
}

# Test pattern matching directly
segments = ("api", "v1", "users")
pattern = ("api", "*", "users")
print(match(segments, pattern))  # True

pattern2 = ("api", "v2", "admin")  
print(match(segments, pattern2))  # False

# Create views with deep copying
api_v1_view = view(data, ("api", "v1", "**"))
# Returns: {"api": {"v1": {"users": ["john", "jane"]}}}

users_view = view(data, ("**", "users"))
# Returns: {"api": {"v1": {"users": ["john", "jane"]}, "v2": {"users": ["bob"]}}}

# Path existence checking
print(has(data, ("api", "v1", "users")))     # True
print(has(data, ("api", "v3", "users")))     # False
print(has(data, ("api", "v1", "users", 0)))  # True (first user exists)

# Direct path access
users = get(data, ("api", "v1", "users"))     # Returns: ["john", "jane"] 
first_user = get(data, ("api", "v1", "users", 0))  # Returns: "john"

Advanced Path Manipulation

Low-level path construction and manipulation utilities for implementing custom creators and transformers.

from dpath.segments import set, extend, types, expand, int_str, _default_creator

def set(obj, segments, value, creator=_default_creator, hints=()):
    """
    Set value at path, optionally creating missing components.
    
    Parameters:
    - obj (MutableMapping): Object to modify
    - segments: Path segments sequence
    - value: Value to set
    - creator (Creator): Function to create missing path components (default _default_creator)
    - hints (Hints): Type hints for creator function
    
    Returns:
    MutableMapping: Modified object
    """

def _default_creator(current, segments, i, hints=()):
    """
    Default creator function that creates dicts or lists based on next segment type.
    
    Parameters:
    - current: Current container being modified
    - segments: Full path segments
    - i (int): Current segment index
    - hints (Hints): Type hints for creation
    
    Creates:
    - List if next segment is numeric
    - Dict otherwise
    """

def extend(thing, index, value=None):
    """
    Extend sequence to contain at least index+1 elements.
    
    Parameters:
    - thing (MutableSequence): Sequence to extend
    - index (int): Required minimum index
    - value: Fill value for new elements (default None)
    
    Returns:
    MutableSequence: Extended sequence
    """

def types(obj, segments):
    """
    Get type information for each level of path.
    
    Parameters:
    - obj: Object to analyze
    - segments: Path segments
    
    Returns:
    Tuple[Tuple[PathSegment, type], ...]: (segment, type) pairs for each level
    """

def expand(segments):
    """
    Generate progressively longer path prefixes.
    
    Parameters:
    - segments: Full path segments
    
    Yields:
    Tuple: Path prefixes from shortest to full length
    """

def int_str(segment):
    """
    Convert integer segments to strings.
    
    Parameters:
    - segment (PathSegment): Path segment to convert
    
    Returns:
    PathSegment: String if input was int, otherwise unchanged
    """

Usage Examples

from dpath.segments import set, extend, types, expand, int_str, _default_creator

# Use default creator (automatic dict/list selection)
data = {}
set(data, ("users", 0, "name"), "john")  # Uses _default_creator
# Creates: {"users": [{"name": "john"}]} - list because next segment is 0

# Custom creator functions
def always_list_creator(current, segments, i, hints=()):
    """Creator that always makes lists instead of dicts"""
    segment = segments[i]
    if isinstance(current, list):
        extend(current, segment)
    current[segment] = []

# Use custom creator
data2 = {}
set(data2, ("items", 0, "name"), "first", creator=always_list_creator)
# Creates: {"items": [{"name": "first"}]} but with all list structure

# Extend sequences manually
my_list = ["a", "b"]
extend(my_list, 5, "empty")  # Extends to ["a", "b", "empty", "empty", "empty", "empty"]

# Analyze path types
data = {"users": [{"name": "john", "age": 30}]}
path_types = types(data, ("users", 0, "name"))
# Returns: (("users", <class 'dict'>), (0, <class 'list'>), ("name", <class 'dict'>))

# Generate path prefixes
for prefix in expand(("a", "b", "c", "d")):
    print(prefix)
# Output:
# ("a",)
# ("a", "b") 
# ("a", "b", "c")
# ("a", "b", "c", "d")

# Convert path segments for display
mixed_path = ("users", 0, "profile", 1, "email")
display_path = "/".join(int_str(seg) for seg in mixed_path)
# Returns: "users/0/profile/1/email"

Functional Operations

Higher-order functions for implementing custom operations over nested structures.

from dpath.segments import fold, foldm, leaves

def fold(obj, f, acc):
    """
    Fold (reduce) over all paths in object with read-only access.
    
    Parameters:
    - obj: Object to fold over
    - f: Function(obj, (path_segments, value), accumulator) -> bool|Any
    - acc: Initial accumulator value
    
    Returns:
    Any: Final accumulator value
    
    Note:
    If f returns False (exactly), folding stops early.
    """

def foldm(obj, f, acc):
    """
    Fold with mutation support - allows modifying obj during iteration.
    
    Parameters:
    - obj: Object to fold over (may be modified)
    - f: Function(obj, (path_segments, value), accumulator) -> bool|Any  
    - acc: Initial accumulator value
    
    Returns:
    Any: Final accumulator value
    
    Note:
    Loads all paths into memory first to support safe mutations.
    """

def leaves(obj):
    """
    Generate only leaf (path, value) pairs.
    
    Parameters:
    - obj: Object to traverse
    
    Yields:
    Tuple[Tuple[PathSegment, ...], Any]: (path_segments, leaf_value) pairs
    """

Usage Examples

from dpath.segments import fold, foldm, leaves

data = {
    "users": {
        "john": {"age": 30, "active": True},
        "jane": {"age": 25, "active": False}
    },
    "settings": {"theme": "dark"}
}

# Count all paths
def counter(obj, pair, count):
    count[0] += 1
    return True

total_paths = fold(data, counter, [0])
print(f"Total paths: {total_paths[0]}")

# Find first match with early termination
def find_age_30(obj, pair, result):
    path_segments, value = pair
    if value == 30:
        result.append(path_segments)
        return False  # Stop folding
    return True

result = fold(data, find_age_30, [])
if result:
    print(f"Found age 30 at: {result[0]}")

# Mutating fold - deactivate all users
def deactivate_users(obj, pair, count):
    path_segments, value = pair
    if (isinstance(value, dict) and 
        "active" in value and 
        len(path_segments) >= 2 and 
        path_segments[0] == "users"):
        value["active"] = False
        count[0] += 1
    return True

changes = foldm(data, deactivate_users, [0])
print(f"Deactivated {changes[0]} users")

# Process only leaf values
for path_segments, value in leaves(data):
    path_str = "/".join(str(seg) for seg in path_segments)
    print(f"Leaf {path_str}: {value}")

# Custom aggregations
def sum_numeric_leaves(obj, pair, acc):
    path_segments, value = pair  
    if isinstance(value, (int, float)):
        acc["sum"] += value
        acc["count"] += 1
    return True

result = fold(data, sum_numeric_leaves, {"sum": 0, "count": 0})
if result["count"] > 0:
    average = result["sum"] / result["count"]
    print(f"Average numeric value: {average}")

Advanced Integration Patterns

Custom Path Operations

# Implement custom search with transformation
def transform_search(obj, pattern, transformer):
    """Search and transform matching values"""
    results = {}
    
    def transform_match(obj, pair, results):
        path_segments, value = pair
        if match(path_segments, pattern):
            transformed = transformer(value)
            set(results, path_segments, transformed)
        return True
    
    fold(obj, transform_match, results)
    return results

# Usage
def uppercase_strings(value):
    return value.upper() if isinstance(value, str) else value

transformed = transform_search(data, ("**",), uppercase_strings)

Performance Optimization

# Early termination patterns
def find_first_matching_path(obj, condition):
    """Find first path matching condition"""
    def finder(obj, pair, result):
        path_segments, value = pair
        if condition(value):
            result.append(path_segments)
            return False  # Stop immediately
        return True
    
    result = fold(obj, finder, [])
    return result[0] if result else None

# Memory-efficient processing
def process_large_structure(obj, processor):
    """Process structure without loading all paths"""
    for path_segments, value in walk(obj):
        if leaf(value):
            processor(path_segments, value)

Install with Tessl CLI

npx tessl i tessl/pypi-dpath

docs

advanced-operations.md

data-manipulation.md

index.md

path-access.md

search-operations.md

tile.json