Translate Janelia file share paths to and from an unambiguous janelia:// URI, so code can refer to a share without depending on OS-specific mount guesses.
Janelia file shares are mounted at different paths on Linux, Mac, and Windows, and on macOS two different shares can even collide on the same guessed mount point (e.g. /Volumes/<name>). This package gives you two functions to move between a local path and a stable, host-qualified identity that's never ambiguous.
uv add janelia-pathlibOr, to track the latest unreleased commit:
uv add "janelia-pathlib @ git+https://github.com/JaneliaSciComp/janelia_pathlib"Requires Python 3.11+. Zero runtime dependencies beyond platformdirs.
from janelia_pathlib import to_janelia_uri, from_janelia_uri
# Canonicalize a local path to its unambiguous janelia:// identity
uri = to_janelia_uri("/labs/alpha/alphalab/sub/data")
# "janelia://fileserver.example.org/alphalab/sub/data"
# Resolve a janelia:// URI back to a real, usable local path
path = from_janelia_uri(uri)
# "/Volumes/alphalab/sub/data" (on Mac), "/labs/alpha/alphalab/sub/data" (on Linux), ...A share's local mount point differs by OS, and on macOS it isn't even guaranteed to be unique: two shares with the same share name on different servers (e.g. host-a.example.org/data and host-b.example.org/data) both nominally want /Volumes/data — macOS mounts the second at /Volumes/data-1 instead. A bare path string like /Volumes/data can't tell you which share that actually is.
janelia://host/share/subpath encodes the SMB host and share name directly, which is always unique — so it's safe to store, log, or pass between machines, and always resolves back to the correct share regardless of what macOS happened to name the mountpoint.
Translate a local path in the current OS's own format to its canonical janelia://host/share/subpath form. A path in a different OS's format (e.g. a Windows UNC path passed in while running on Linux) is not recognized — only janelia:// URIs are portable across OSes; a plain path is assumed to describe the machine it's running on.
from janelia_pathlib import to_janelia_uri
# On Linux:
to_janelia_uri("/labs/alpha/alphalab/data")
# "janelia://fileserver.example.org/alphalab/data"
# On Mac, once the share is mounted:
to_janelia_uri("/Volumes/alphalab/data")
# "janelia://fileserver.example.org/alphalab/data"On macOS, resolving a /Volumes/... path requires the share to be mounted already — mac_path alone can't be trusted as a guess (two shares can collide on the same mount name; see "Why janelia://?" above), so this checks live mount state rather than guessing. If the share isn't mounted, the path isn't recognized and passes through unchanged. Use from_janelia_uri first if you need to mount it.
Linux paths always resolve without any live check, since Linux mount points are fixed and never ambiguous.
Paths that don't match any known share are returned unchanged:
to_janelia_uri("/tmp/local/scratch/file.txt")
# "/tmp/local/scratch/file.txt"Translate a janelia://host/share/subpath string to a real, usable local path.
from janelia_pathlib import from_janelia_uri
from_janelia_uri("janelia://fileserver.example.org/alphalab/data")
# "/Volumes/alphalab/data" (on Mac)
# "/labs/alpha/alphalab/data" (on Linux)On macOS, if the share isn't already mounted, this mounts it on demand (triggering the native SMB mount, which may prompt for credentials) and waits up to timeout seconds for the mount to appear, returning its real local mountpoint — not a guessed /Volumes/<share> path, which may not exist yet or may belong to a different, colliding share. Raises RuntimeError if the URI doesn't match a known share, or if mounting fails or times out.
On Linux and Windows, no mounting step is needed — shares are already available at a fixed path (Linux) or accessible directly via UNC path (Windows).
A string that isn't a janelia:// URI is returned unchanged:
from_janelia_uri("/tmp/local/scratch/file.txt")
# "/tmp/local/scratch/file.txt"Both functions round-trip: from_janelia_uri(to_janelia_uri(p)) resolves back to a valid local path for p's share, and non-Janelia paths pass through both functions untouched.
Share data is not bundled with the package — it's fetched from the Fileglancer API and cached in your user cache directory:
- macOS:
~/Library/Caches/janelia-pathlib/shares.json - Linux:
~/.cache/janelia-pathlib/shares.json - Windows:
%LOCALAPPDATA%\janelia-pathlib\Cache\shares.json
Every time to_janelia_uri/from_janelia_uri first needs share data in a process, the library tries to refresh this cache from the API (once per process — subsequent calls in the same process reuse that result, so a long-running process won't pick up newly added shares until restarted). This requires access to the Janelia network, but a failed refresh never breaks translation outright:
- If the refresh fails but a cache file already exists, the existing (possibly stale) cache is used instead, with a
UserWarning. - If the refresh fails and there's no cache file at all, share data degrades to "no known shares" (also with a warning) — paths just pass through untranslated rather than raising.
To force a refresh manually (e.g. to pick up a newly added share before your next run):
janelia-paths-fetchTo point at a custom shares file (useful for testing or air-gapped setups), set:
export JANELIA_PATHLIB_SHARES_PATH=/path/to/shares.jsonFor environments that can't reach the network at all (e.g. CI on cloud runners), disable fetching entirely:
export JANELIA_PATHLIB_NO_FETCH=1With this set, the network is never touched: the existing cache file is used as-is if present (however stale), or share data degrades to "no known shares" if not — same to_janelia_uri/from_janelia_uri behavior as a failed fetch, just without ever attempting the network call. This is also what this project's own test suite uses, pointed at a small fixture file via JANELIA_PATHLIB_SHARES_PATH, so tests never depend on (or accidentally overwrite) real network data.
The fetch also has a timeout (default 10s) so an unreachable network can't hang for long; override it with:
export JANELIA_PATHLIB_FETCH_TIMEOUT=5This project uses uv and just. To set up a development environment:
git clone https://github.com/JaneliaSciComp/janelia_pathlib
cd janelia_pathlib
just installSee CONTRIBUTING.md
for more, or run just to list all available recipes.