Skip to main content

pathable

Package version Python versions License

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

  • BasePath is a pure path (segments + separator) with / joining.
  • AccessorPath is a BasePath bound to a NodeAccessor, enabling read_value(), exists(), keys(), iteration, etc.
  • FilesystemPath is an AccessorPath specialized for filesystem objects.
  • LookupPath is an AccessorPath specialized for mapping/list lookups.

Notes on parsing:

  • A segment like "a/b" is split into parts using the separator.
  • None segments are ignored.
  • "." segments are ignored (relative no-op).
  • Operations like relative_to() and is_relative_to() also respect the instance separator.

Equality and ordering:

  • Two BasePath instances are equal if their parts are equal. The separator is presentation only — BasePath("a", separator="/") == BasePath("a", separator=".").
  • Two AccessorPath instances are equal if they have equal parts and their accessors compare equal under the accessor's own __eq__. A plain BasePath is never equal to an AccessorPath.
  • Path parts are type-sensitive (0 is 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 same parts may 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:

  • LookupPath uses a per-instance LRU cache (default maxsize: 128) on its accessor.
  • You can control it via path.accessor.clear_cache(), path.accessor.disable_cache(), and path.accessor.enable_cache(maxsize=...).
  • path.accessor.node is immutable; to point at a different tree, create a new LookupPath/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 Benchmarks runs via workflow_dispatch and uploads the JSON artifacts.

Release files for pathable 0.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pathable 0.6.0
File Size Uploaded
pathable-0.6.0.tar.gz 19.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pathable 0.6.0
File Interpreter ABI Platform
pathable-0.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 38.0 kB

Release files / pathable-0.6.0.tar.gz

Download URL pathable-0.6.0.tar.gz
Size 19.0 kB
Tags Source
SHA-256 checksum
How to use checksums
6404b8b82aef5ff0fd478934137128b99b12212ba35afdde5525ca4f8388ea58
BLAKE2b-256 checksum
How to use checksums
66f35a20387de9bcd0607871bfc2198ee0e15836da7baa4592ccd7f24c27c986
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 19, 2026.

Transparency log

Release files / pathable-0.6.0-py3-none-any.whl

Download URL pathable-0.6.0-py3-none-any.whl
Size 19.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
82c4ca6c98c502ad12e0d4e9779b6210afee93c38990988c8c5d1b49bdcdf566
BLAKE2b-256 checksum
How to use checksums
a2e86d75ffd9784bce2e93d1ae4415649427e39a53bb172d4672b2b59c6f0a7b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on May 19, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page