Skip to content

API Reference

nestedutils.access

get_at

get_at(data: Any, path: Union[str, List[Any]], *, default: Any = MISSING) -> Any

Retrieve a value from a nested data structure.

Navigates through nested dictionaries, lists, and tuples using a path specified as either a dot-notation string or a list of keys/indices. By default, raises PathError if the path does not exist. Use the default parameter to return a value instead of raising. Supports negative indexing for lists and tuples.

Parameters:

Name Type Description Default
data Any

The data structure to navigate (dict, list, tuple, or nested combinations).

required
path Union[str, List[Any]]

Path to the value. Accepts either a dot-separated string (e.g., "a.b.0.name") or a list of keys/indices (e.g., ["a", "b", 0, "name"]). Indices may be integers or strings representing integers (including negative indices).

required
default Any

Value to return if the path does not exist. If not provided, raises PathError for missing paths.

MISSING

Returns:

Type Description
Any

The value at the specified path, or default if the path does not exist and

Any

default is provided.

Raises:

Type Description
PathError

If the path is malformed, empty, contains empty keys, or path doesn't exist (when default is not provided).

Examples:

data = {"a": {"b": {"c": 5}}}
get_at(data, "a.b.c")  # Returns: 5

# Missing paths raise by default
get_at(data, "a.b.d")  # Raises: PathError

# Explicit default for optional values
get_at(data, "a.b.d", default=99)  # Returns: 99

data = {"items": [{"name": "apple"}, {"name": "banana"}]}
get_at(data, "items.1.name")  # Returns: 'banana'

get_at(data, "items.-1.name")  # Returns: 'banana'

get_at(data, "items.-5.name", default="not found")  # Returns: 'not found'

data = (10, 20, 30)
get_at(data, "-1")  # Returns: 30

set_at

set_at(data: Any, path: Union[str, List[Any]], value: Any, *, create: bool = False) -> None

Set a value in a nested data structure.

Navigates to the specified path and sets the value. By default (create=False), raises PathError if any intermediate key is missing. With create=True, automatically creates missing intermediate containers (dicts for string keys, lists for numeric keys).

List indexing rules: - Positive indices can append (index == len(list)) but NOT create gaps (index > len(list)) - Negative indices can only modify existing elements - Out-of-bounds negative indices raise PathError - Index cannot exceed MAX_LIST_SIZE (10000)

Parameters:

Name Type Description Default
data Any

The mutable data structure to modify (dict or list). The root container must already exist and be mutable.

required
path Union[str, List[Any]]

Path where to set the value. Accepts either a dot-separated string (e.g., "a.b.0.name") or a list of keys/indices (e.g., ["a", "b", 0, "name"]).

required
value Any

The value to set at the specified path.

required
create bool

If True, automatically create missing intermediate containers. If False (default), raise PathError if path doesn't exist.

False

Returns:

Type Description
None

None. The function modifies data in place.

Raises:

Type Description
PathError

If path is malformed, path doesn't exist (when create=False), attempts to modify tuple, uses out-of-bounds negative index, or would create sparse list.

Examples:

# create=False (default) - path must exist
data = {"user": {"profile": {}}}
set_at(data, "user.profile.name", "Alice")  # OK - path exists
# data is now: {'user': {'profile': {'name': 'Alice'}}}

data = {}
set_at(data, "user.name", "Bob")  # PathError - path doesn't exist

# create=True - auto-create missing parts
data = {}
set_at(data, "user.profile.name", "Alice", create=True)
# data is now: {'user': {'profile': {'name': 'Alice'}}}

# List operations - sequential only (no gaps)
data = {}
set_at(data, "items.0", "first", create=True)  # OK - creates list
# data is now: {'items': ['first']}

set_at(data, "items.1", "second", create=True)  # OK - appends
# data is now: {'items': ['first', 'second']}

set_at(data, "items.5", "x", create=True)  # PathError - would create gap

# Negative indices - modify existing only
data = {"items": [1, 2, 3]}
set_at(data, "items.-1", 99)  # OK - modifies last element
# data is now: {'items': [1, 2, 99]}

set_at(data, "items.-5", 0)  # PathError - out of bounds

# Modifying existing nested structures
data = {"a": [{"x": 1}]}
set_at(data, "a.0.y", 2, create=True)  # Adds key to existing dict
# data is now: {'a': [{'x': 1, 'y': 2}]}

delete_at

delete_at(data: Any, path: Union[str, List[Any]], *, allow_list_mutation: bool = False) -> Any

Delete a value from a nested data structure and return it.

Removes the item at the specified path. For dictionaries, the key-value pair is removed. For lists, deletion is disabled by default to prevent accidental index shifting that could break subsequent code.

Parameters:

Name Type Description Default
data Any

The mutable data structure to modify (dict or list).

required
path Union[str, List[Any]]

Path to the value to delete. Accepts either a dot-separated string (e.g., "a.b.0") or a list of keys/indices (e.g., ["a", "b", 0]).

required
allow_list_mutation bool

If True, allows deletion from lists using list.pop(). Defaults to False to prevent unintended side effects.

False

Returns:

Type Description
Any

The deleted value.

Raises:

Type Description
PathError

If the path does not exist, is malformed, attempts deletion from a tuple, attempts list deletion without allow_list_mutation=True, or uses an out-of-bounds index.

Examples:

data = {"a": {"b": 1, "c": 2}}
delete_at(data, "a.b")  # Returns: 1
# data is now: {'a': {'c': 2}}

data = {"items": [1, 2, 3]}
delete_at(data, "items.1", allow_list_mutation=True)  # Returns: 2
# data is now: {'items': [1, 3]}

delete_at(data, "items.-1", allow_list_mutation=True)  # Returns: 3
# data is now: {'items': [1]}

# Without allow_list_mutation=True, list deletion raises PathError
delete_at(data, "items.0")  # Raises: PathError: List deletion disabled...

exists_at

exists_at(data: Any, path: Union[str, List[Any]]) -> bool

Check if a path exists in a nested data structure.

Navigates through nested dictionaries, lists, and tuples. Returns True if the full path exists and is accessible, False otherwise (including out-of-bounds indices). Supports negative indexing for lists and tuples.

This function never raises PathError for missing paths - it returns False instead. PathError is only raised for malformed paths.

Parameters:

Name Type Description Default
data Any

The data structure to navigate (dict, list, tuple, or nested combinations).

required
path Union[str, List[Any]]

Path to check. Accepts either a dot-separated string (e.g., "a.b.0.name") or a list of keys/indices (e.g., ["a", "b", 0, "name"]).

required

Returns:

Type Description
bool

True if the path exists and is accessible, False otherwise.

Raises:

Type Description
PathError

Only if the path format is invalid (empty, malformed, exceeds max depth).

Examples:

data = {"a": {"b": {"c": 5}}}
exists_at(data, "a.b.c")  # Returns: True
exists_at(data, "a.b.d")  # Returns: False

data = {"items": [{"name": "apple"}, {"name": "banana"}]}
exists_at(data, "items.1.name")  # Returns: True
exists_at(data, "items.-1.name")  # Returns: True
exists_at(data, "items.-5.name")  # Returns: False
exists_at(data, "items.10.name")  # Returns: False

data = (10, 20, 30)
exists_at(data, "2")  # Returns: True
exists_at(data, "5")  # Returns: False

# Even None values return True if path exists
data = {"a": {"b": None}}
exists_at(data, "a.b")  # Returns: True
exists_at(data, "a.b.c")  # Returns: False (can't navigate into None)

nestedutils.introspection

Introspection utilities for nested data structures.

This module provides functions to inspect and analyze nested data structures without modifying them. All functions are pure and support dict, list, and tuple. Other container types (set, frozenset, deque, etc.) are treated as leaf values.

get_depth

get_depth(data: Any) -> int

Return the maximum nesting depth of a nested structure.

Supports dict, list, and tuple. Other types are treated as leaves (depth 0).

Parameters:

Name Type Description Default
data Any

Any nested structure (dict, list, tuple, or primitive).

required

Returns:

Type Description
int

Integer depth. Primitives return 0, empty containers return 1.

Examples:

>>> get_depth(42)
0
>>> get_depth({})
1
>>> get_depth({"a": 1})
1
>>> get_depth({"a": {"b": 1}})
2
>>> get_depth({"a": {"b": {"c": 1}}})
3
>>> get_depth([1, [2, [3]]])
3

count_leaves

count_leaves(data: Any) -> int

Count the total number of leaf values in a nested structure.

A leaf is any value that is not a dict, list, or tuple. Empty containers count as 0 leaves.

Supports dict, list, and tuple. Other container types (set, frozenset, etc.) are treated as single leaf values.

Parameters:

Name Type Description Default
data Any

Any nested structure.

required

Returns:

Type Description
int

Integer count of leaf values.

Examples:

>>> count_leaves(42)
1
>>> count_leaves({})
0
>>> count_leaves({"a": 1, "b": 2})
2
>>> count_leaves({"a": {"b": 1, "c": 2}, "d": 3})
3
>>> count_leaves([1, 2, [3, 4]])
4

get_all_paths

get_all_paths(data: Any) -> List[List[Union[str, int]]]

Return all paths to leaf values in a nested structure.

Supports dict, list, and tuple. Other container types are treated as leaves.

Parameters:

Name Type Description Default
data Any

Any nested structure.

required

Returns:

Type Description
List[List[Union[str, int]]]

List of paths, where each path is a list of keys/indices.

Examples:

>>> get_all_paths({"a": 1, "b": 2})
[["a"], ["b"]]
>>> get_all_paths({"a": {"b": 1, "c": 2}})
[["a", "b"], ["a", "c"]]
>>> get_all_paths({"users": [{"name": "Alice"}, {"name": "Bob"}]})
[["users", 0, "name"], ["users", 1, "name"]]
>>> get_all_paths({})
[]
>>> get_all_paths(42)
[[]]

nestedutils.constants

Constants for nestedutils library.

MAX_DEPTH module-attribute

MAX_DEPTH = 100

Maximum allowed depth for nested paths. Paths exceeding this depth will raise a PathError.

MAX_LIST_SIZE module-attribute

MAX_LIST_SIZE = 10000

Maximum allowed list index. List indices exceeding this value will raise a PathError.

nestedutils.enums

Enumerations for nestedutils library.

This module defines error codes used throughout the nestedutils library for consistent error handling.

PathErrorCode

Bases: Enum

Error codes for path-related exceptions.

Example
from nestedutils import get_at, PathError, PathErrorCode

data = {"a": {"b": 1}}
try:
    result = get_at(data, "a.c.d")
    # get_at raises PathError for missing paths in v2.0
except PathError as e:
    if e.code == PathErrorCode.MISSING_KEY:
        print("Key not found")
INVALID_INDEX class-attribute instance-attribute
INVALID_INDEX = 'INVALID_INDEX'

Raised when a list index is invalid (non-numeric, out of bounds, or would create sparse list).

MISSING_KEY class-attribute instance-attribute
MISSING_KEY = 'MISSING_KEY'

Raised when a required key doesn't exist (in set_at with create=False or delete_at).

EMPTY_PATH class-attribute instance-attribute
EMPTY_PATH = 'EMPTY_PATH'

Raised when path is empty or contains empty keys.

IMMUTABLE_CONTAINER class-attribute instance-attribute
IMMUTABLE_CONTAINER = 'IMMUTABLE_CONTAINER'

Raised when attempting to modify an immutable container (tuple).

INVALID_PATH class-attribute instance-attribute
INVALID_PATH = 'INVALID_PATH'

Raised when path format is invalid (wrong type, exceeds max depth, etc.).

NON_NAVIGABLE_TYPE class-attribute instance-attribute
NON_NAVIGABLE_TYPE = 'NON_NAVIGABLE_TYPE'

Raised when attempting to navigate into a non-container type (e.g., None, int, str).

OPERATION_DISABLED class-attribute instance-attribute
OPERATION_DISABLED = 'OPERATION_DISABLED'

Raised when an operation is disabled by configuration (e.g., list mutation without allow_list_mutation=True).

nestedutils.exceptions

Custom exceptions for the nestedutils library.

This module defines all custom exceptions used throughout the nestedutils library. The main exception class is PathError, which includes error codes for detailed error handling.

PathError

PathError(message: str, code: Optional[PathErrorCode] = None)

Bases: Exception

Raised when a nested path cannot be navigated or modified.

Attributes:

Name Type Description
message

Human-readable error message.

code

Optional PathErrorCode for programmatic error handling.

Example
from nestedutils import set_at, PathError, PathErrorCode

data = {"a": {"b": 1}}

# set_at raises when path doesn't exist (create=False is default)
try:
    set_at(data, "a.c.d", "value")
except PathError as e:
    if e.code == PathErrorCode.MISSING_KEY:
        print(f"Path doesn't exist: {e.message}")

# get_at raises for missing paths in v2.0 - use default= for optional values
from nestedutils import get_at
result = get_at(data, "a.c.d", default="not found")
print(result)  # "not found"