Skip to main content

The lightweight Python library for safe, simple, dot-notation access to nested dicts and lists. Effortlessly get, set, and delete values deep in your complex JSON, API responses, and config files without verbose error-checking or handling KeyError exceptions.

Project description

Nested Utils

License Tests Python PyPI Documentation

The lightweight Python library for safe, simple, dot-notation access to nested dicts and lists. Effortlessly get, set, and delete values deep in your complex JSON, API responses, and config files without verbose error-checking or handling KeyError exceptions.

OG Image

Why nestedutils?

Working with deeply nested data (like JSON API responses) often leads to verbose, error-prone boilerplate:

# The Standard Way: Verbose and hard to read
user_name = None
if data and "users" in data and len(data["users"]) > 0:
    user = data["users"][0]
    if "profile" in user:
        user_name = user["profile"].get("name")

# With nestedutils: Clean, safe, and readable
from nestedutils import get_at

user_name = get_at(data, "users.0.profile.name")

Features

  • Simple Path Syntax: Use dot-notation strings ("a.b.c") or lists (["a", "b", "c"]) to navigate nested structures
  • Mixed Data Types: Seamlessly work with dictionaries, lists, and tuples (read-only for tuples)
  • List Index Support: Access list elements using numeric indices, including negative indices
  • Auto-creation: Automatically create missing intermediate containers when setting values
  • Flexible Fill Strategies: Control how missing containers are created with different fill strategies
  • Type Safety: Comprehensive error handling with descriptive error messages and error codes
  • Safety Limits: Built-in protection against excessive nesting (max depth: 100) and oversized lists (max index: 10,000)
  • Zero Dependencies: Pure Python implementation with no external dependencies

Use Cases

  • JSON API Responses: Safely extract values from complex, unpredictable JSON responses without dozens of checks.
  • Configuration Management: Easily read and modify deeply nested settings in configuration dictionaries.
  • Data Transformation: Rapidly remap data from one complex structure to another using get_at and set_at.

Installation

pip install nestedutils

Quick Start

from nestedutils import get_at, set_at, delete_at, exists_at

# Create a nested structure
data = {}

# Set values using dot-notation
set_at(data, "user.name", "John")
set_at(data, "user.age", 30)
set_at(data, "user.hobbies.0", "reading")
set_at(data, "user.hobbies.1", "coding")

# Access values
name = get_at(data, "user.name")  # "John"
age = get_at(data, "user.age")    # 30
first_hobby = get_at(data, "user.hobbies.0")  # "reading"

# Check if path exists
if exists_at(data, "user.name"):
    print("User name exists!")

# Delete values
delete_at(data, "user.age")

API Reference

get_at(data, path, default=None)

Retrieve a value from a nested data structure.

Parameters:

  • data: The data structure to navigate (dict, list, tuple, or nested combinations)
  • path: Path to the value (string with dot notation or list of keys/indices)
  • default: Value to return if path doesn't exist (default: None)

Returns: The value at the path, or default if not found

Examples:

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

data = {"items": [{"name": "apple"}, {"name": "banana"}]}
get_at(data, "items.1.name")  # "banana"
get_at(data, "items.-1.name")  # "banana" (negative index)

set_at(data, path, value, fill_strategy="auto")

Set a value in a nested data structure, creating intermediate containers as needed.

Parameters:

  • data: The data structure to modify (must be mutable: dict or list)
  • path: Path where to set the value (string with dot notation or list of keys/indices)
  • value: The value to set
  • fill_strategy: How to fill missing containers (default: "auto")
    • "auto": Intelligently creates {} for dict keys, [] for list indices, and None for sparse list gaps
    • "none": Fills missing list items with None
    • "dict": Always creates dictionaries
    • "list": Always creates lists

Note: Positive indices can extend lists (filling gaps as needed), but negative indices can only modify existing elements.

Examples:

data = {}
set_at(data, "user.profile.name", "Alice")
# Creates: {"user": {"profile": {"name": "Alice"}}}

data = {}
set_at(data, "items.0.name", "Item 1")
# Creates: {"items": [{"name": "Item 1"}]}

data = {}
set_at(data, "items.5", "Item 6", fill_strategy="none")
# Creates: {"items": [None, None, None, None, None, "Item 6"]}

data = [1, 2, 3]
set_at(data, "5", 99)  # Extends list with None gaps
# Creates: [1, 2, 3, None, None, 99]

data = [1, 2, 3]
set_at(data, "-1", 100)  # Updates existing last element
# Creates: [1, 2, 100]

exists_at(data, path)

Check if a path exists in a nested data structure.

Parameters:

  • data: The data structure to navigate (dict, list, tuple, or nested combinations)
  • path: Path to check (string with dot notation or list of keys/indices)

Returns: True if the path exists, False otherwise

Raises: PathError if the path format is invalid

Examples:

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

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

delete_at(data, path, allow_list_mutation=False)

Delete a value from a nested data structure.

Parameters:

  • data: The data structure to modify
  • path: Path to the value to delete
  • allow_list_mutation: If True, allows deletion from lists (default: False)

Returns: The deleted value

Raises: PathError if the path doesn't exist or deletion is not allowed

Examples:

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

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

Error Handling

The library uses PathError exceptions with error codes for different failure scenarios:

from nestedutils import PathError, PathErrorCode

try:
    set_at(data, "invalid.path", 1)
except PathError as e:
    print(e.message)  # Error message
    print(e.code)     # Error code (PathErrorCode enum)

Error Codes:

  • INVALID_PATH: Invalid path format or type
  • INVALID_INDEX: Invalid list index
  • MISSING_KEY: Key doesn't exist in dictionary
  • EMPTY_PATH: Path is empty
  • IMMUTABLE_CONTAINER: Attempted to modify a tuple
  • INVALID_FILL_STRATEGY: Invalid fill strategy value

Advanced Usage

Using List Paths

List paths are useful when keys contain dots:

data = {}
set_at(data, ["user.name", "first"], "John")
set_at(data, ["user.name", "last"], "Doe")
# Creates: {"user.name": {"first": "John", "last": "Doe"}}

Negative List Indices

Negative indices work like Python list indexing for reading and updating existing elements:

data = {"items": [10, 20, 30]}
get_at(data, "items.-1")  # 30 (last item)
set_at(data, "items.-1", 999)  # Updates last item (must exist)

Important: Negative indices can only reference existing elements. They cannot extend lists - attempting to use a negative index that's out of bounds will raise a PathError.

Working with Tuples

Tuples are read-only. You can read from them but cannot modify:

data = {"items": (1, 2, 3)}
get_at(data, "items.0")  # 1 (works)
set_at(data, "items.0", 9)  # Raises PathError (tuples are immutable)

Handling None Values

The library can navigate through None values when setting:

data = {"a": None}
set_at(data, "a.b.c", 10)
# Replaces None with container: {"a": {"b": {"c": 10}}}

Safety Limits

The library includes built-in safety limits to prevent excessive resource usage:

  • Maximum Path Depth: 100 levels (prevents deeply nested paths that could cause stack issues)
  • Maximum List Index: 10,000 (prevents creating extremely large sparse lists)

These limits help protect against accidental memory exhaustion or performance issues. If you hit these limits, you'll receive a PathError with a clear message.

Requirements

  • Python 3.8+

Changelog

See CHANGELOG.md for a detailed list of changes and version history.

License

MIT License - see LICENSE file for details.

Contributing

Contributions are welcome! Please read our Contributing Guide for details on our code of conduct, development setup, and the process for submitting pull requests.

Support

If you find this library useful, please consider:

  • Starring the repository on GitHub to help others discover it.
  • 💖 Sponsoring to support ongoing maintenance and development.

Become a Sponsor on GitHub | Support on Patreon

Links

Author

Y. Siva Sai Krishna

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

nestedutils-1.1.2.tar.gz (423.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

nestedutils-1.1.2-py3-none-any.whl (12.7 kB view details)

Uploaded Python 3

File details

Details for the file nestedutils-1.1.2.tar.gz.

File metadata

  • Download URL: nestedutils-1.1.2.tar.gz
  • Upload date:
  • Size: 423.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nestedutils-1.1.2.tar.gz
Algorithm Hash digest
SHA256 4ebfd9a8fa6cecaa33ce57ee15ad7520a30bf715201c92e6014e341ca38475b9
MD5 eb281c74a3e09f5d42d4180c80c2e6f1
BLAKE2b-256 bfdbc306397161c86d997fbea2e9ff25536f776156aace8ebd369fcbcccaea9c

See more details on using hashes here.

File details

Details for the file nestedutils-1.1.2-py3-none-any.whl.

File metadata

  • Download URL: nestedutils-1.1.2-py3-none-any.whl
  • Upload date:
  • Size: 12.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for nestedutils-1.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 94abf908e75670406d56c37da2d8c2ac92aa22073648a7016f62c893dfbe87cb
MD5 0eb73d038bf6208086823127401b982b
BLAKE2b-256 98d91b76ef4d5ff0b837eaec46e8d1389d05331f81208f396c9ce1c185b94604

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page