paths
tit.paths ¶
BIDS-compliant path management for TI-Toolbox.
Provides a singleton :class:PathManager that resolves all file and
directory paths within a BIDS-structured TI-Toolbox project. Paths cover
subject anatomical data, SimNIBS derivatives, FastSurfer outputs, analysis
results, and optimization runs.
Public API¶
PathManager
Central path resolver — instantiated as a singleton via
:func:get_path_manager.
get_path_manager
Return (and optionally initialise) the global PathManager singleton.
reset_path_manager
Destroy the singleton so the next call creates a fresh instance.
Examples¶
from tit.paths import get_path_manager pm = get_path_manager("/data/project") # doctest: +SKIP pm.list_simnibs_subjects() # doctest: +SKIP ['001', '002'] pm.m2m("001") # doctest: +SKIP '/data/project/derivatives/SimNIBS/sub-001/m2m_001'
See Also¶
tit.constants : Project-wide constants used for directory/file names.
PathManager ¶
PathManager(project_dir: str | None = None)
BIDS-compliant path resolution for TI-Toolbox projects.
Provides methods to resolve file and directory paths within a BIDS-structured project, including subject anatomical data, SimNIBS derivatives, FastSurfer outputs, optimization runs, and analysis results.
The project directory can be set explicitly or auto-detected from the
PROJECT_DIR / PROJECT_DIR_NAME environment variables (useful
inside Docker containers).
Parameters¶
project_dir : str or None, optional Root directory of the BIDS project. If None, the directory is auto-detected from environment variables on first access.
Attributes¶
project_dir : str or None
Resolved project root, or None if not yet set / detected.
project_dir_name : str or None
Basename of :attr:project_dir.
See Also¶
get_path_manager : Obtain the global singleton instance. reset_path_manager : Destroy the singleton for testing or re-init.
Source code in tit/paths.py
project_dir
property
writable
¶
project_dir: str | None
Project root directory, auto-detected from environment if unset.
project_dir_name
property
¶
project_dir_name: str | None
Basename of :attr:project_dir, or the PROJECT_DIR_NAME env var.
freesurfer ¶
freesurfer() -> str
Path to <project>/derivatives/freesurfer/ (legacy, read-only).
TI-Toolbox no longer writes here -- FastSurfer --seg_only
replaced recon-all -- but every atlas reader still discovers
existing recon-all output so projects processed with older
releases keep working.
Source code in tit/paths.py
cache ¶
ensure_cache ¶
scene_cache ¶
mask_cache ¶
extensions_config ¶
extensions_config() -> str
Path to the extensions.json configuration file.
Uses the user-level config directory so that extension preferences persist across projects and container restarts.
Source code in tit/paths.py
user_config_dir
staticmethod
¶
user_config_dir() -> str
Path to the user-level config directory.
Returns the directory that persists across projects and container
restarts. Inside Docker this is /root/.config/ti-toolbox
(mounted from the host by the Electron launcher). Outside Docker
the platform-native config directory is used:
- macOS:
~/.config/ti-toolbox - Linux:
$XDG_CONFIG_HOME/ti-toolbox(default~/.config) - Windows:
%APPDATA%/ti-toolbox
The directory is created if it does not exist.
Returns¶
str Absolute path to the user config directory.
Source code in tit/paths.py
stats_output ¶
Path to a specific statistics output directory.
Parameters¶
analysis_type : str
Type of statistical analysis (e.g., "permutation").
analysis_name : str
Name of the analysis run.
Returns¶
str Absolute path to the output directory.
Source code in tit/paths.py
sub ¶
Path to derivatives/SimNIBS/sub-{sid}/.
Parameters¶
sid : str
Subject identifier (without sub- prefix).
Returns¶
str Absolute path to the subject's SimNIBS directory.
Source code in tit/paths.py
m2m ¶
eeg_positions ¶
rois ¶
masks ¶
Path to the user-supplied custom mask directory for sid.
Any integer label volume placed here (*.nii.gz/*.nii/*.mgz,
in the subject space m2m_{sid} was built from) is auto-discovered as
a targetable volume atlas by the optimiser ROI pickers.
Source code in tit/paths.py
t1 ¶
segmentation ¶
tissue_labeling ¶
leadfields ¶
forward ¶
Path to the EEG source-forward directory for sid.
Holds the MNE forward solution, source space, head<->MRI transform,
fsaverage morph, and the point-electrode leadfield they derive from
(derivatives/SimNIBS/sub-{sid}/forward/). Distinct from
:meth:leadfields, which the optimizer uses for modeled-electrode
stimulation leadfields.
Source code in tit/paths.py
simulations ¶
logs ¶
tissue_analysis_output ¶
bids_subject ¶
bids_datatype ¶
Path to <project>/sub-{sid}/{datatype}/ for any BIDS datatype.
bids_anat ¶
bids_dwi ¶
sourcedata_subject ¶
fastsurfer_subject ¶
fastsurfer_mri ¶
Path to derivatives/fastsurfer/sub-{sid}/mri/.
Holds aparc.DKTatlas+aseg.deep.mgz (plus the .nii.gz copy and
the *_labels.txt sidecar :mod:tit.pre.fastsurfer writes).
Source code in tit/paths.py
freesurfer_subject ¶
freesurfer_mri ¶
qsiprep_subject ¶
qsirecon_subject ¶
ex_search ¶
m_ex_search ¶
flex_search ¶
simulation ¶
sim_fsaverage ¶
Path to the fsaverage field-map cache for a simulation.
derivatives/SimNIBS/sub-{sid}/Simulations/{sim}/fsaverage/ -- holds
the .npz field projection produced by
:func:tit.source.project_fields_to_fsaverage. Co-located with the
simulation it derives from (and mirroring SimNIBS's native
map_to_fsavg layout), a sibling of TI/ and high_Frequency/ --
distinct from :meth:forward, which is EEG source reconstruction.
Source code in tit/paths.py
ti_mesh ¶
ti_mesh_dir ¶
ti_central_surface ¶
Path to the TI central cortical surface mesh.
mti_central_surface ¶
Path to the mTI central cortical surface mesh.
mTI runs write their own central surface under mTI/mesh/surfaces/;
the TI/mesh/surfaces/ copy is written only by 2-pair TI runs.
Source code in tit/paths.py
mti_mesh_dir ¶
analysis_dir ¶
Path to the analysis directory for a given analysis space.
Parameters¶
sid : str
Subject identifier.
sim : str
Simulation name.
space : str
Analysis space — "mesh" or "voxel".
Returns¶
str
Absolute path to Analyses/Mesh/ or Analyses/Voxel/.
Source code in tit/paths.py
sourcedata_dicom ¶
Path to DICOM source data for sid and modality.
ex_search_run ¶
m_ex_search_run ¶
flex_search_run ¶
flex_electrode_positions ¶
Path to electrode_positions.json for a flex-search run.
flex_manifest ¶
ensure ¶
list_bids_subjects ¶
Subject ids with a raw BIDS folder (<project>/sub-*), naturally sorted.
list_fastsurfer_subjects ¶
Subject ids with a derivatives/fastsurfer/sub-* folder, naturally sorted.
list_freesurfer_subjects ¶
Subject ids with a derivatives/freesurfer/sub-* folder (legacy).
Kept so projects carrying old recon-all output still list those
subjects; nothing in the toolbox writes this tree any more.
Source code in tit/paths.py
list_simnibs_subjects ¶
List subject IDs that have a SimNIBS head-model (m2m) folder.
Returns¶
list of str
Naturally sorted subject identifiers (without the sub- prefix).
Returns an empty list if the SimNIBS directory does not exist.
Source code in tit/paths.py
list_simulations ¶
List simulation folder names for a subject.
Parameters¶
sid : str Subject identifier.
Returns¶
list of str
Alphabetically sorted simulation directory names. Returns an
empty list if the Simulations/ directory does not exist.
Source code in tit/paths.py
list_eeg_caps ¶
List EEG cap CSV filenames for a subject.
Parameters¶
sid : str Subject identifier.
Returns¶
list of str
Sorted CSV filenames found in the eeg_positions/ directory.
Source code in tit/paths.py
list_flex_search_runs ¶
List flex-search run directories containing result metadata.
Only directories that contain flex_meta.json or
electrode_positions.json are included.
Parameters¶
sid : str Subject identifier.
Returns¶
list of str Sorted run directory names.
Source code in tit/paths.py
spherical_analysis_name
staticmethod
¶
Build a canonical folder name for a spherical ROI analysis.
Parameters¶
x, y, z : float
Centre coordinates of the sphere (mm).
radius : float
Sphere radius (mm).
coordinate_space : str
"MNI" or "subject".
Returns¶
str
Folder name, e.g. "sphere_x0.00_y0.00_z0.00_r5.0_MNI".
Source code in tit/paths.py
spherical_union_analysis_name
classmethod
¶
Build a folder name for a union of several spherical ROIs.
Parameters¶
spheres : sequence of tuple of float
One (x, y, z, r) per sphere.
coordinate_space : str
"MNI" or "subject".
Returns¶
str
Folder name, e.g. "spheres2_x-40.00_y-20.00_z5.00_r5.0+
x40.00_y-20.00_z5.00_r5.0_subject". Long unions are shortened
to the first sphere plus a hash of the rest so the name stays
within filesystem limits.
Source code in tit/paths.py
cortical_analysis_name
classmethod
¶
cortical_analysis_name(*, whole_head: bool, region: str | None, atlas_name: str | None = None, atlas_path: str | None = None) -> str
Build a canonical folder name for a cortical/atlas analysis.
Parameters¶
whole_head : bool
If True, the analysis covers the whole head (no region filter).
region : str or None
Atlas region label(s). Multiple regions are +-separated.
atlas_name : str or None, optional
Human-readable atlas name.
atlas_path : str or None, optional
Filesystem path to the atlas file (used as fallback for naming).
Returns¶
str
Folder name, e.g. "cortical_precentral_DK40" or
"whole_head_DK40".
Raises¶
ValueError If whole_head is False and region is empty or None.
Source code in tit/paths.py
analysis_output_dir ¶
analysis_output_dir(*, sid: str, sim: str, space: str, analysis_type: str, coordinates=None, radius=None, coordinate_space: str = 'subject', spheres=None, whole_head: bool = False, region: str | None = None, atlas_name: str | None = None, atlas_path: str | None = None) -> str
Return the analysis output directory path (does not create it).
Delegates to :meth:spherical_analysis_name or
:meth:cortical_analysis_name depending on analysis_type.
Parameters¶
sid : str
Subject identifier.
sim : str
Simulation name.
space : str
Analysis space ("mesh" or "voxel").
analysis_type : str
"spherical" or "cortical".
coordinates : sequence of float or None, optional
(x, y, z) centre for spherical analysis.
radius : float or None, optional
Sphere radius in mm (required when analysis_type is
"spherical").
coordinate_space : str, optional
"MNI" or "subject". Default is "subject".
spheres : sequence of tuple of float or None, optional
One (x, y, z, r) per sphere when several spheres are unioned
into a single ROI. Takes precedence over coordinates/radius.
whole_head : bool, optional
Whether cortical analysis covers the whole head.
region : str or None, optional
Atlas region label(s) for cortical analysis.
atlas_name : str or None, optional
Atlas name for cortical analysis.
atlas_path : str or None, optional
Atlas file path for cortical analysis.
Returns¶
str Absolute path to the analysis output directory.
Raises¶
ValueError If required parameters for the chosen analysis_type are missing or invalid.
See Also¶
spherical_analysis_name : Naming convention for spherical ROIs. cortical_analysis_name : Naming convention for cortical/atlas ROIs.
Source code in tit/paths.py
1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 | |
natural_key ¶
Sort key: sub-2 before sub-10 (digits compared numerically).
resolve_resources_dir ¶
resolve_resources_dir() -> str
Resolve the resources/ directory tit's resource-bearing modules read from.
Resolution order, first match wins:
TIT_RESOURCES_DIRenv var, if set and an existing directory -- the override a native (non-Docker) launcher or a packaged install can set explicitly. This is also the only way to point at resources/ from a wheel install today: a wheel only ships thetitpackage itself (pyproject.toml's[tool.setuptools.packages.find] include = ["tit*"]excludes the top-levelresources/tree entirely), so a packaged app with no override and no/ti-toolboxpresent has no resources/ directory to find -- moving these files inside the package and reading them viaimportlib.resourcesis the real fix, tracked as an N1 follow-up, not attempted here./ti-toolbox/resources-- the Docker image's own layout (the container copies the checkout to/ti-toolbox); kept working unchanged for the existing container path.<repo_root>/resources-- checkout-relative,repo_rootbeing two levels above this file (tit/paths.py->tit/-> repo root). What a native run from a git checkout has on disk today. Returned even when it doesn't exist, so callers get a stable, informative path for error messages instead ofNone.
Examples¶
resolve_resources_dir() # doctest: +SKIP '/Users/.../TI-toolbox/resources'
Source code in tit/paths.py
resolve_resource_path ¶
resolve_resources_dir() joined with parts.
Examples¶
resolve_resource_path("amv", "GSN-256.csv") # doctest: +SKIP '/Users/.../TI-toolbox/resources/amv/GSN-256.csv'
Source code in tit/paths.py
is_valid_subject_id ¶
validate_subject_id ¶
Return sid unchanged, or raise ValueError naming the rule it broke.
Called by every :class:PathManager accessor that puts a subject id in a path, so a
traversal cannot reach the filesystem however it entered the process — an API body, a
config file, or a script argument.
Source code in tit/paths.py
is_within ¶
True if path resolves inside root (containment check for created directories).
Source code in tit/paths.py
is_valid_name ¶
validate_name ¶
Return value unchanged, or raise ValueError naming the rule it broke.
what names the field in the message ("simulation", "montage", ...).
This is the allowlist half of the sanitizer contract (see
dev/security/SECURITY_MASTER_DOCUMENT.md); :func:resolve_under is the
containment half, applied where the component becomes a path.
Source code in tit/paths.py
resolve_under ¶
Join parts under root and return the path, or raise if it escapes root.
The lexical half of the sanitizer contract: the normalised join must start
with <root>/, so .., an absolute part and an empty result are all
refused. Pure string work, no filesystem access, which is why every
:class:PathManager accessor can afford it. Symlinks are not followed here:
a subject directory linked from another disk keeps resolving for scripts,
and it is :func:resolve_within -- at the I/O boundary -- that decides
whether the link may be read.
Raises ValueError, the same class :func:validate_subject_id and
:func:validate_name raise, so one except ValueError at a route
boundary covers all three.
Examples¶
resolve_under("/p", "derivatives", "sub-01") '/p/derivatives/sub-01' resolve_under("/p", "../etc/passwd") Traceback (most recent call last): ValueError: path '/etc/passwd' escapes '/p'
Source code in tit/paths.py
resolve_within ¶
path with symlinks resolved, or raise if it then lies outside jail.
The physical half of the sanitizer contract, applied right before a read,
listing or write: realpath(path) must start with realpath(jail)/.
A symlink planted inside the project therefore cannot lead outside it,
while a project-local link (a run directory linked from elsewhere in the
same project) still works. The value returned is the resolved path, so
what is checked is exactly what is opened. Equal to :func:is_within in
what it accepts, but it returns the path and raises ValueError.
Examples¶
resolve_within("/p", "/p/derivatives/x") # doctest: +SKIP '/p/derivatives/x'
Source code in tit/paths.py
resolve_leaf_within ¶
path with its parent resolved and its leaf kept, or raise if either escapes jail.
For writes and deletes: :func:resolve_within follows the leaf, so
replacing or unlinking what it returns would touch the target of a
planted link instead of the link itself. This form resolves only the
parent, keeps the leaf's own name, and checks both the entry and what the
leaf points at, so the caller replaces or unlinks the alias -- never the
file behind it.
Source code in tit/paths.py
dot_dir ¶
cache_dir ¶
<project>/.ti-toolbox/cache/<parts...> (not created).
Source code in tit/paths.py
scene_cache_dir_for ¶
<project>/.ti-toolbox/cache/scene/sub-<id>/ (not created).
mask_cache_dir_for ¶
ensure_bidsignore ¶
ensure_bidsignore(project_dir: str) -> None
Append :data:DOT_BIDSIGNORE_LINE to the project's .bidsignore once.
Idempotent, and never rewrites a line a user put there by hand. A failure (read-only project, for instance) is swallowed: the only consequence is a bids-validator warning, and refusing to serve a scene over it would be worse.
Source code in tit/paths.py
migrate_legacy_caches ¶
Move pre-.ti-toolbox cache locations into the dot-directory.
Each move is a plain rename, and only ever when the destination does not exist yet, so a half-migrated project can never overwrite a fresh cache. A rename that fails (cross-device, permissions, another process mid-move) is ignored: the legacy directory is then simply left where it is and the new cache is rebuilt, which costs time but never correctness.
Returns the (old, new) pairs actually moved -- for tests and logging.
Source code in tit/paths.py
ensure_cache_dir ¶
Create <project>/.ti-toolbox/cache/<parts...> and return it.
The first call for a project also migrates the legacy cache locations,
drops the README that says the tree is disposable, and lists the
dot-directory in .bidsignore.
Source code in tit/paths.py
get_path_manager ¶
get_path_manager(project_dir: str | None = None) -> PathManager
Return the global :class:PathManager singleton.
Creates a new instance on the first call. If project_dir is provided,
the singleton's :attr:~PathManager.project_dir is (re)set.
Parameters¶
project_dir : str or None, optional
Project root directory (the BIDS root holding sub-* and
derivatives/). Must exist. When None (default), the
existing value is kept; if none was set, it is auto-detected from
the PROJECT_DIR environment variable or, inside the container,
/mnt/<PROJECT_DIR_NAME> -- which is why scripts and notebooks
run in the app need no argument.
Returns¶
PathManager
The shared singleton instance. Every path accessor
(pm.m2m(sid), pm.simulations(sid), ...) raises
RuntimeError("Project directory not set") if no project root
could be resolved.
Raises¶
ValueError If project_dir is given but is not an existing directory.
Examples¶
from tit import get_path_manager pm = get_path_manager("/data/project") # doctest: +SKIP pm.list_simnibs_subjects() # doctest: +SKIP ['ernie', '101'] pm.m2m("ernie") # doctest: +SKIP '/data/project/derivatives/SimNIBS/sub-ernie/m2m_ernie' get_path_manager() is pm # the same singleton on later calls # doctest: +SKIP True
See Also¶
reset_path_manager : Destroy the singleton for testing or re-init.
Source code in tit/paths.py
reset_path_manager ¶
Destroy the singleton so the next call creates a fresh instance.
Primarily used in test fixtures to prevent cross-test contamination.
See Also¶
get_path_manager : Obtain the singleton instance.