Skip to main content

A simpler way to find your files -- a Python port of the R 'here' package.

Project description

herepath

CI PyPI Python versions License: MIT

A simpler way to find your files. A Python port of the R here package.

herepath builds paths relative to your project's root, no matter what the current working directory is. Stop writing brittle ../../data/x.csv paths or relying on where a script happens to be launched from.

from herepath import here

here("data", "penguins.csv")
# -> /home/me/myproject/data/penguins.csv   (always, from anywhere)

Installation

pip install herepath

One name everywhere: pip install herepath, import herepath, and a herepath command. (An unrelated pyhere package by another author exists on PyPI; herepath is deliberately named to avoid any confusion with it.)

For local development, from the project directory:

pip install -e ".[dev]"

How it works

On the first call to here(), herepath walks up the directory tree from the current working directory until it finds a directory matching one of these criteria (in order):

Category Markers
Explicit .here
Python pyproject.toml, setup.py, setup.cfg, Pipfile, poetry.lock, environment.yml
Editors .vscode/, .idea/, *.Rproj, _quarto.yml
Version control .git, .hg/, .svn/

The closest matching ancestor wins, so a sub-package in a monorepo resolves to its own directory. If nothing matches, the current working directory is used as a fallback. requirements.txt is deliberately not a marker: it is too often duplicated in subdirectories (docs/, tests/), which would anchor the root in the wrong place. Override the criteria with set_criteria() if you need to.

API

here(*args) -> Path

Drop-in replacement for os.path.join, anchored at the project root.

here()                       # the project root
here("data", "raw", "x.csv") # root/data/raw/x.csv
here("data/raw/x.csv")       # same: components may contain "/"

data = here("data")          # absolute
here(data)                   # returned unchanged
here(data, "x.csv")          # absolute anchor kept -> data/x.csv

The result is always an absolute Path, so it stays valid even if the working directory changes later.

i_am(path, *, uuid=None) -> Path

Declare where the current script lives, relative to the project root. This is the recommended and most robust way to fix the root. Put it near the top of a script or notebook:

from herepath import i_am, here
i_am("analysis/report.py")

here("data", "penguins.csv")

It walks up from the working directory until it finds a directory containing path, and pins that as the root. On success it prints a one-line report (here() starts at ...); pass quiet=True to suppress it. If the file can't be found, it raises FileNotFoundError with a descriptive message, which protects you from running a script in the wrong place. Pass uuid="..." to also require a unique marker string within the first 100 lines of the file, for extra safety.

set_here(path=".", verbose=True) -> Path

Create an empty .here marker file to pin a root when no other criterion applies.

reset()

Forget the cached root so it is re-detected on the next call. Handy in long-lived sessions like Jupyter notebooks (after moving files or changing directory) and in tests.

find_root(*criteria, start=".") -> Path

Lower-level escape hatch for custom root markers, in the spirit of R's rprojroot::find_root. Does not cache anything or touch the session root. Build criteria with has_file, has_dir, has_glob; a directory matches if it satisfies any of them:

from herepath import find_root, has_file, has_dir

find_root(has_file("Makefile"), has_dir(".git"))

set_criteria(*criteria) / reset_criteria()

Customise what counts as a project root for the whole session. Useful for organisations with their own markers:

from herepath import set_criteria, has_file, has_dir

set_criteria(has_file("company_project.json"), has_dir("src"))
# ... here() now uses these markers
reset_criteria()  # back to the built-in defaults

using_root(path) context manager

Temporarily pin the root, restoring the previous state on exit. Ideal in tests:

from herepath import using_root, here

with using_root(tmp_path):
    assert here("data") == tmp_path / "data"
# previous root (or auto-detection) restored here

Debugging detection: dr_here(trace=True)

When the wrong root is picked, trace=True prints the full upward search:

Searching from:
  /project/notebooks
Checking:
  /project/notebooks
  /project   <- contains a file `pyproject.toml`
Matched:
  /project

Forcing the root with HEREPATH_ROOT

Set the HEREPATH_ROOT environment variable to an existing directory to override auto-detection entirely. This is the recommended escape hatch for Docker, CI, and deployment, where the heuristics may not apply:

HEREPATH_ROOT=/app python analysis/report.py

If it points to a path that isn't a directory, here() raises ValueError so misconfiguration fails loudly. An explicit i_am() call still takes precedence.

dr_here(show_reason=True)

Print a situation report explaining where the root is and why, which is useful when here() gives unexpected results.

here() starts at /home/me/myproject
- This directory contains a file `pyproject.toml`
- Initial working directory: /home/me/myproject/analysis
- Current working directory: /home/me/myproject/analysis

When not to use herepath

herepath is for scripts, notebooks and analyses: code run from within a project tree. It is not meant for use inside an installed library, because once a package is installed the source layout no longer exists. To access data bundled with an installed package, use importlib.resources instead.

Differences from the R package

  • Returns pathlib.Path objects instead of strings.
  • The default root criteria are Python-flavoured (e.g. pyproject.toml) rather than R-flavoured (e.g. DESCRIPTION), while keeping the universal markers (.here, .git, editors).
  • The root is resolved lazily on first use (Python has no package-load hook tied to a session the way R does).

Command line

Installing the package also provides a herepath command, handy in shell scripts and Makefiles:

herepath                      # print the project root
herepath data penguins.csv    # print root/data/penguins.csv
herepath --report             # situation report (why this root?)
herepath --version

# e.g. anchor a command at the project root from any subdirectory:
cat "$(herepath data/penguins.csv)"

Development

pip install -e ".[dev]"
pre-commit install   # optional: run linters on commit

pytest               # tests
pytest --cov         # tests + coverage
ruff check .         # lint
ruff format .        # format
mypy                 # type-check

See CONTRIBUTING.md for the full guide. The project is tested on CPython 3.8 to 3.13 across Linux, macOS and Windows.

License

MIT, Jonathan Katende Pinto. Inspired by the R here package by Kirill Müller and Jennifer Bryan.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

herepath-0.1.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

herepath-0.1.0-py3-none-any.whl (14.2 kB view details)

Uploaded Python 3

File details

Details for the file herepath-0.1.0.tar.gz.

File metadata

  • Download URL: herepath-0.1.0.tar.gz
  • Upload date:
  • Size: 22.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for herepath-0.1.0.tar.gz
Algorithm Hash digest
SHA256 714ba26ad1bb9e3fe944cf4c7ca64ac444f3d2b8edde96c2fa7173106602fc28
MD5 9f7a88b986ca6d1714629fb8ec5fdfa4
BLAKE2b-256 7ca87e336fd1e14b99964528c2c31499dfbb6b9a1f09e005bdb4c2b85b553e46

See more details on using hashes here.

Provenance

The following attestation bundles were made for herepath-0.1.0.tar.gz:

Publisher: publish.yml on Pinto-Katende-Jonathan/herepath

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file herepath-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: herepath-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 14.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for herepath-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 da564dff6d3fb912b53a449a400bc05dc20f15af9cbea9db9523cd948b091da0
MD5 b8c623c359152666c82d53a4423807ab
BLAKE2b-256 58e126d44300f11a3a89e331252781738c2b961e008176bbbfe28a2d00e83634

See more details on using hashes here.

Provenance

The following attestation bundles were made for herepath-0.1.0-py3-none-any.whl:

Publisher: publish.yml on Pinto-Katende-Jonathan/herepath

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page