Safely navigate through unsafe data structures: dictionaries, tuples, lists, strings, and even custom objects.
Inspired by Ruby’s dig
.
pip install py-data-digger
Sometimes, you just don’t want to deal with Python exceptions when accessing nested lists and dictionaries.
If the data isn’t there, you just want to move on — no if
s, no try
-except
s!
from py_data_digger import dig
components: list | None = dig(nasty_dict, "machines", 0, "engine", "components")
When web scraping or working with APIs, you often deal with deeply nested structures like this:
nasty_dict = {
"machines": [
{
"machine_id": "1234567890",
"engine": {
"id": "321abcde",
"name": "Motor XPTO",
"components": [
{"id": "0942323", "name": "Cog"},
{"id": "1642723", "name": "Piston"},
{"id": "8412321", "name": "Bar", "extras": ["Foo"]},
],
},
}
]
}
Let’s say we want to extract the list of engine components.
components = nasty_dict["machines"][0]["engine"]["components"]
This can raise KeyError
, IndexError
, or TypeError
if any part of the structure is missing or malformed.
machines = nasty_dict.get("machines")
machine = next(iter(machines), None) if machines else None
engine = machine.get("engine") if machine else None
components = engine.get("components") if engine else None
Safe, yes — but tedious and hard to read.
With dig()
, you can safely access deeply nested data in a concise and readable way:
from py_data_digger import dig
components = dig(nasty_dict, "machines", 0, "engine", "components")
If any part of the path is missing, dig()
returns None
— and you move on:
components = dig(nasty_dict, "machines", 0, "engine_2", "components")
if components is None:
return None
If you want stricter error handling, use seek()
. It behaves like dig()
, but raises an error if the path doesn’t exist.
from py_data_digger import seek
components = seek(nasty_dict, "machines", 0, "engine_2", "components")
SeekError: Data digger can't go any further: KeyError
Path traveled: dict -> machines -> 0 -> engine_2
No more dealing with multiple exception types — just catch SeekError
.
You can also dig through custom object attributes, such as dataclasses
, Pydantic models, or any plain Python objects.
Use the dig_objects=True
or seek_objects=True
flag:
from py_data_digger import dig, seek
person = Person(name='John Doe')
my_dict = {
'item_with_object': person
}
dig(my_dict, 'item_with_object', 'name', dig_objects=True)
# → 'John Doe'
dig(my_dict, 'item_with_object', 'age', dig_objects=True)
# → None
seek(my_dict, 'item_with_object', 'name', seek_objects=True)
# → 'John Doe'
seek(my_dict, 'item_with_object', 'age', seek_objects=True)
# → SeekError
pip install py-data-digger
Feel free to open issues or pull requests. Feedback is always welcome!