Object-oriented paths
Project description
pathable
About
Pathable provides a small set of "path" objects for traversing hierarchical data (mappings, lists, and other subscriptable trees) using a familiar path-like syntax.
It’s especially handy when you want to:
- express deep lookups as a single object (and pass it around)
- build paths incrementally (
p / "a" / 0 / "b") - safely probe (
exists(),get(...)) or strictly require segments (//)
Key features
- Intuitive path-based navigation for nested data (e.g., dicts/lists)
- Pluggable accessor layer for custom backends
- Pythonic, chainable API for concise and readable code
- Per-instance (bounded LRU) cached lookup accessor for repeated reads of the same tree
Quickstart
from pathable import LookupPath
data = {
"parts": {
"part1": {"name": "Part One"},
"part2": {"name": "Part Two"},
}
}
root = LookupPath.from_lookup(data)
name = (root / "parts" / "part2" / "name").read_value()
assert name == "Part Two"
Usage
from pathable import LookupPath
data = {
"parts": {
"part1": {"name": "Part One"},
"part2": {"name": "Part Two"},
}
}
p = LookupPath.from_lookup(data)
# Concatenate path segments with /
parts = p / "parts"
# Check membership (mapping keys or list indexes)
assert "part2" in parts
# Read a value
assert (parts / "part2" / "name").read_value() == "Part Two"
# Iterate children as paths
for child in parts:
print(child, child.read_value())
# Work with keys/items
print(list(parts.keys()))
print({k: v.read_value() for k, v in parts.items()})
# Safe access
print(parts.get("missing", default=None))
# Strict access (raises KeyError if missing)
must_exist = parts // "part2"
# "Open" yields the current value as a context manager
with parts.open() as parts_value:
assert isinstance(parts_value, dict)
# Optional metadata
print(parts.stat())
Filesystem example
Pathable can also traverse the filesystem via an accessor.
from pathlib import Path
from pathable import FilesystemPath
root_dir = Path(".")
p = FilesystemPath.from_path(root_dir)
readme = p / "README.md"
if readme.exists():
content = readme.read_value() # bytes
print(content[:100])
Core concepts
BasePathis a pure path (segments + separator) with/joining.AccessorPathis aBasePathbound to aNodeAccessor, enablingread_value(),exists(),keys(), iteration, etc.FilesystemPathis anAccessorPathspecialized for filesystem objects.LookupPathis anAccessorPathspecialized for mapping/list lookups.
Notes on parsing:
- A segment like
"a/b"is split into parts using the separator. Nonesegments are ignored."."segments are ignored (relative no-op).- Operations like
relative_to()andis_relative_to()also respect the instance separator.
Equality and ordering:
- Two
BasePathinstances are equal if theirpartsare equal. The separator is presentation only —BasePath("a", separator="/") == BasePath("a", separator="."). - Two
AccessorPathinstances are equal if they have equalpartsand their accessors compare equal under the accessor's own__eq__. A plainBasePathis never equal to anAccessorPath. - Path parts are type-sensitive (
0is not equal to"0"). - Ordering is address-based: separator is not part of the order, and it remains deterministic across mixed part types. For
AccessorPath, different bindings with the samepartsmay compare ordering-equivalent while remaining unequal.
Identity and lifecycle:
- Build one accessor per resource and reuse it for every path you derive.
LookupPath.from_lookup(data)constructs a fresh accessor on each call, which is convenient for one-off use but defeats the cache when called repeatedly over the same data:
from pathable import LookupPath
from pathable.accessors import LookupAccessor
# Construct the accessor once, reuse it.
accessor = LookupAccessor(data)
root = LookupPath(accessor)
# Every path derived from `accessor` shares its cache.
a = root / "parts" / "part1"
b = root / "parts" / "part1"
assert a == b
path.is_same_binding(other)is a stricter version of==that additionally requires both paths to share the same accessor instance (object identity), not just an==-equal one. Use it when you need to verify cache attribution or detect accidental accessor swaps.
Lookup caching:
LookupPathuses a per-instance LRU cache (default maxsize: 128) on its accessor.- You can control it via
path.accessor.clear_cache(),path.accessor.disable_cache(), andpath.accessor.enable_cache(maxsize=...). path.accessor.nodeis immutable; to point at a different tree, create a newLookupPath/accessor.
Installation
Recommended way (via pip):
pip install pathable
Alternatively you can download the code and install from the repository:
pip install -e git+https://github.com/p1c2u/pathable.git#egg=pathable
Benchmarks
Benchmarks live in tests/benchmarks/ and produce JSON reports.
Local run (recommended as modules):
poetry run python -m tests.benchmarks.bench_parse --output reports/bench-parse.json
poetry run python -m tests.benchmarks.bench_lookup --output reports/bench-lookup.json
Quick sanity run:
poetry run python -m tests.benchmarks.bench_parse --quick --output reports/bench-parse.quick.json
poetry run python -m tests.benchmarks.bench_lookup --quick --output reports/bench-lookup.quick.json
Compare two results (fails if candidate is >20% slower in any scenario):
poetry run python -m tests.benchmarks.compare_results \
--baseline reports/bench-before.json \
--candidate reports/bench-after.json \
--tolerance 0.20
CI (on-demand):
- GitHub Actions workflow
Benchmarksruns viaworkflow_dispatchand uploads the JSON artifacts.
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 pathable-0.6.0.tar.gz.
File metadata
- Download URL: pathable-0.6.0.tar.gz
- Upload date:
- Size: 19.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58
|
|
| MD5 |
1515f586cea1c43aa94d2049ff111c1d
|
|
| BLAKE2b-256 |
66f35a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986
|
Provenance
The following attestation bundles were made for pathable-0.6.0.tar.gz:
Publisher:
python-publish.yml on p1c2u/pathable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pathable-0.6.0.tar.gz -
Subject digest:
6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58 - Sigstore transparency entry: 1574437818
- Sigstore integration time:
-
Permalink:
p1c2u/pathable@8913cc5ca5e035dc952e9e21bc142ed8e2f58c6a -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/p1c2u
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@8913cc5ca5e035dc952e9e21bc142ed8e2f58c6a -
Trigger Event:
release
-
Statement type:
File details
Details for the file pathable-0.6.0-py3-none-any.whl.
File metadata
- Download URL: pathable-0.6.0-py3-none-any.whl
- Upload date:
- Size: 19.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566
|
|
| MD5 |
5fa379781b441f0dc477cde09d1f2c81
|
|
| BLAKE2b-256 |
a2e86d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b
|
Provenance
The following attestation bundles were made for pathable-0.6.0-py3-none-any.whl:
Publisher:
python-publish.yml on p1c2u/pathable
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pathable-0.6.0-py3-none-any.whl -
Subject digest:
82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566 - Sigstore transparency entry: 1574437847
- Sigstore integration time:
-
Permalink:
p1c2u/pathable@8913cc5ca5e035dc952e9e21bc142ed8e2f58c6a -
Branch / Tag:
refs/tags/0.6.0 - Owner: https://github.com/p1c2u
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@8913cc5ca5e035dc952e9e21bc142ed8e2f58c6a -
Trigger Event:
release
-
Statement type: