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
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.
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_atandset_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 setfill_strategy: How to fill missing containers (default:"auto")"auto": Intelligently creates{}for dict keys,[]for list indices, andNonefor sparse list gaps"none": Fills missing list items withNone"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 modifypath: Path to the value to deleteallow_list_mutation: IfTrue, 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 typeINVALID_INDEX: Invalid list indexMISSING_KEY: Key doesn't exist in dictionaryEMPTY_PATH: Path is emptyIMMUTABLE_CONTAINER: Attempted to modify a tupleINVALID_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.
Links
- PyPI: pypi.org/project/nestedutils
- Documentation: ysskrishna.github.io/nestedutils
- Repository: github.com/ysskrishna/nestedutils.git
- Issues: github.com/ysskrishna/nestedutils/issues
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
License
MIT License - see LICENSE file for details.
Author
Y. Siva Sai Krishna
- GitHub: @ysskrishna
- LinkedIn: ysskrishna
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file nestedutils-1.1.5.tar.gz.
File metadata
- Download URL: nestedutils-1.1.5.tar.gz
- Upload date:
- Size: 426.6 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b82a1e31e8bd1f92301d98dadaea1568b68adb3bfaf76ff7a5f3de210dd63e6
|
|
| MD5 |
c6d8bbf9963a7fd446550c3fe92b38a7
|
|
| BLAKE2b-256 |
42e0c1201ffa89795a6e155e1de71eae5637d42ac4e0fc9a76cc194e4761aca4
|
File details
Details for the file nestedutils-1.1.5-py3-none-any.whl.
File metadata
- Download URL: nestedutils-1.1.5-py3-none-any.whl
- Upload date:
- Size: 13.4 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
959d580b391127c98daf40232602b53671f0be899971f4b5c9ab0302cccc6694
|
|
| MD5 |
8e939dde7a984f6e9c7291d87435f8b5
|
|
| BLAKE2b-256 |
0d746e491455c376a1c3884a61dd7a37b531bee56f9889ca9246e80b0fd21796
|