Skip to content

RamonGiovane/py-data-digger

Repository files navigation

example workflow

py-data-digger

Safely navigate through unsafe data structures: dictionaries, tuples, lists, strings, and even custom objects.

Inspired by Ruby’s dig.

pip install py-data-digger

💡 Why?

TL;DR:

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 ifs, no try-excepts!

from py_data_digger import dig

components: list | None = dig(nasty_dict, "machines", 0, "engine", "components")

🧠 A more detailed example

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.


🚨 The unsafe way:

components = nasty_dict["machines"][0]["engine"]["components"]

This can raise KeyError, IndexError, or TypeError if any part of the structure is missing or malformed.


😴 The safe (but verbose) way:

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.


✅ Enter dig()

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

🚀 Need strict behavior? Use seek()

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.


🔍 Accessing object attributes

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

⚠️ Note: Use the object-access flags with caution, as attribute names may conflict with dictionary keys.


📦 Installation

pip install py-data-digger

🙌 Contributions

Feel free to open issues or pull requests. Feedback is always welcome!

About

Safely navigate through unsafe data: dicts, tuples, lists, strings and objects

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages