Overrides

pathbase works especially well with envstack-managed environments, where template strings, overrides, and platform-specific roots can be layered through ENVPATH. For envstack documentation, see envstack.dev.

A common pattern is:

If envstack is not installed, pathbase can still consume direct environment variables or explicit env= mappings from Python, but the examples in this guide assume envstack is the primary workflow.

Shared Production Baseline

A shared production template file might look like this:

#!/usr/bin/env envstack
include: [default]
all: &all
  SHOW_ROOT: ${ROOT}/{show}
  SEQUENCE_ROOT: ${SHOW_ROOT}/{sequence}
  SHOT_ROOT: ${SEQUENCE_ROOT}/{shot}
  STEP_ROOT: ${SHOT_ROOT}/{step}
  FILEPATH: ${STEP_ROOT}/{task}_{descriptor}_v{version:03d}.{frame:04d}.{ext}
linux:
  <<: *all
  ROOT: /mnt/projects
darwin:
  <<: *all
  ROOT: /Volumes/projects
windows:
  <<: *all
  ROOT: D:/projects

This gives every project a stable naming model without depending on whatever ambient variables happen to be present in the shell.

Project-Specific Overrides

A project can then provide its own pathbase.env in a higher-priority env directory and override only the templates that differ. Because envstack is hierarchical, the project override does not need to re-include the shared pathbase file as long as the default env directory is already present in ENVPATH.

For example, bigbuckbunny/env/pathbase.env could look like this:

#!/usr/bin/env envstack
include: [default]
all: &all
  SHOW: bigbuckbunny
  SHOW_ROOT: ${ROOT}/bigbuckbunny
  # This show uses department-specific work areas under a tasks folder.
  STEP_ROOT: ${SHOT_ROOT}/tasks/{step}
  # This show also includes the shot in the filename itself.
  FILEPATH: ${STEP_ROOT}/{shot}_{task}_{descriptor}_v{version:03d}.{frame:04d}.{ext}
linux:
  <<: *all
darwin:
  <<: *all
windows:
  <<: *all

That override keeps the shared vocabulary, pins the show root, changes the step folder layout, and changes the filename pattern for one project.

Overrides should come from envstack hierarchy, not from ad hoc shell exports.

Current and Legacy Templates

Sometimes a production changes its filepath spec over time but still needs to parse older paths. A practical pattern is to keep one canonical write template and one or more legacy read templates in the same environment.

For example:

#!/usr/bin/env envstack
include: [default]
all: &all
  SHOW_ROOT: ${ROOT}/{show}
  SEQUENCE_ROOT: ${SHOW_ROOT}/{sequence}
  SHOT_ROOT: ${SEQUENCE_ROOT}/{shot}
  STEP_ROOT: ${SHOT_ROOT}/{step}
  # Current template used for formatting new paths.
  FILEPATH: ${STEP_ROOT}/{task}_{descriptor}_v{version:03d}.{frame:04d}.{ext}
  # Older layouts kept around for parsing historical data.
  FILEPATH_V1: ${SHOT_ROOT}/{task}/{task}_{descriptor}.{frame:04d}.{ext}
  FILEPATH_V2: ${STEP_ROOT}/{task}_{descriptor}.{frame:04d}.{ext}

This gives you a simple convention:

In application code, formatting can stay explicit:

from pathbase import Template

template = Template.from_env("FILEPATH")

For parsing, pathbase parse PATH and match_template(path) can match against any compatible template present in the environment.

A few conventions help keep this maintainable:

Using the Resolved Template in Python

Once the environment is activated or resolved outside of pathbase, the application code stays simple:

import os

from pathbase import Template

template = Template.from_env("FILEPATH")

path = template.format(
    show="bigbuckbunny",
    sequence="seq001",
    shot="shot010",
    step="lighting",
    task="render",
    descriptor="beauty",
    version=1,
    frame=1001,
    ext="exr",
)

Why This Split Helps