Pathable: Object-Oriented Paths

raw JSON →
0.5.0 verified Tue May 12 auth: no python install: verified quickstart: verified

Pathable is a Python library that provides object-oriented paths for traversing hierarchical data structures like dictionaries and lists, as well as file systems. It offers an intuitive, chainable API for deep lookups, incremental path building, and safe probing of data. The library is actively maintained with frequent releases, with version 0.5.0 being the latest stable release.

pip install pathable
error ModuleNotFoundError: No module named 'pathable'
cause The 'pathable' library is not installed in your Python environment or is not accessible on the Python path.
fix
Install the library using pip: pip install pathable
error KeyError: '<missing_key>'
cause You are attempting to access a path segment that does not exist in the underlying data structure using strict access (e.g., the `//` operator or `.read_value()` on a non-existent path).
fix
Use safe access methods like .get('<key>', default=None) to provide a default value if the key is missing, or check for existence with .exists() before strict access. For example, change p // 'a' // 'c' to (p / 'a' / 'c').get(default=None).
error AttributeError: 'LookupPath' object has no attribute '<deprecated_method>'
cause You are trying to use a method that has been deprecated or removed in `pathable` version 0.5.0 (e.g., `iter`, `iteritems`, `content`, `get`, `getkey`).
fix
Migrate to the newer, supported API. For example, instead of p.get('key'), use (p / 'key').read_value() or direct subscriptable access p['key'] (if the path is already resolved to the appropriate level), or p.read_value() after building the path. For iteration, use standard Python iteration directly on the path object, e.g., for child in p:.
breaking `BaseAccessor` was replaced by `NodeAccessor`. Code directly using `BaseAccessor` will fail.
fix Update all imports and references from `BaseAccessor` to `NodeAccessor`. Review your code for areas where custom accessors might need adaptation to the new `NodeAccessor` interface.
breaking `AccessorPath` became generic. This change impacts type hints and potentially runtime behavior where type arguments for `AccessorPath` were not previously expected or provided.
fix Review type hints for `AccessorPath` instances. If you have custom subclasses or functions expecting specific types, you may need to add or adjust generic type parameters (e.g., `AccessorPath[SomeType]`).
gotcha The `pyrsistent` dependency was removed in version 0.5.0b3. While an internal optimization for `pathable`, if your project implicitly relied on `pyrsistent` being installed as a transitive dependency of `pathable`, it will no longer be available automatically.
fix If your project still requires `pyrsistent`, you must explicitly add `pip install pyrsistent` to your project's dependencies.
breaking The static method `LookupPath.from_lookup` has been removed or renamed.
fix Update your code to use the current method for constructing `LookupPath` instances from lookup data. This may involve using the class constructor directly (e.g., `LookupPath(data)`) or a different factory method introduced in the new API.
python os / libc status wheel install import disk
3.10 alpine (musl) - - 0.04s 17.9M
3.10 slim (glibc) - - 0.04s 18M
3.11 alpine (musl) - - 0.08s 19.7M
3.11 slim (glibc) - - 0.10s 20M
3.12 alpine (musl) - - 0.07s 11.6M
3.12 slim (glibc) - - 0.09s 12M
3.13 alpine (musl) - - 0.06s 11.2M
3.13 slim (glibc) - - 0.09s 12M
3.9 alpine (musl) - - 0.03s 17.3M
3.9 slim (glibc) - - 0.03s 18M

This example demonstrates how to use `LookupPath` to navigate and access values within nested dictionary structures using a pathlib-like syntax. It shows basic navigation, value reading, existence checks, and safe access with a default fallback.

from pathable import LookupPath

data = {
    "parts": {
        "part1": {"name": "Part One"},
        "part2": {"name": "Part Two"}
    }
}

root = LookupPath.from_lookup(data)

# Navigate using slash operator
name_path = root / "parts" / "part2" / "name"
name = name_path.read_value()

print(f"Value found: {name}")
assert name == "Part Two"

# Check existence
assert (root / "parts" / "part1").exists()
assert not (root / "non_existent").exists()

# Safe access with .get()
part3_name = (root / "parts").get("part3", default=None)
print(f"Value with .get(): {part3_name}")
assert part3_name is None