Skip to content

Path Management Module

Centralized path definitions with type-safe access and automatic directory creation.

Quick Start

from src.shared_services.path_management.api import get_path, get_path_str
from src.shared_services.constants.paths import OAuth
from src.shared_services.rendering.icons.icon_paths import Icons

# Get Path object (for file operations)
config_dir = get_path(OAuth.Config.Directory)
config_dir.mkdir(parents=True, exist_ok=True)

# Get string path (for Qt widgets, etc.)
icon_path = get_path_str(Icons.Logos.GehaLogoPNG)
pixmap = QPixmap(icon_path)

Defining Paths

Paths are defined in src/shared_services/constants/paths.py:

from src.shared_services.path_management.path_types import PathDef

class MyModule:
    class Config:
        Directory = PathDef("my_module/config", is_directory=True)
        SettingsFile = PathDef("my_module/config/settings.json")

PathDef Options

Parameter Type Description
relative_path str Path relative to app data directory
is_directory bool True if path is a directory
auto_create bool Auto-create directory on first access
base str Base directory ("app_data", "resources", etc.)

Files

File Purpose
api.py Public API (get_path(), get_path_str())
path_manager.py PathManager singleton implementation
path_types.py PathDef dataclass
exceptions.py Custom exceptions

Path Definitions Location

  • Global paths: src/shared_services/constants/paths.py
  • Module-specific: {module}/paths.py (for module-internal paths)

Features

  • Type-safe path definitions
  • Automatic directory creation
  • Centralized path management
  • Support for multiple base directories

API Reference

src.shared_services.path_management.api

Public API for path management.

USAGE

from src.shared_services.path_management.api import get_path, get_path_str, initialize_paths from src.shared_services.constants.paths import Icons, Styles

At app startup (once)

initialize_paths()

Anywhere in code - use get_path() for pathlib operations

style_path = get_path(Styles.Dark) content = style_path.read_text()

Use get_path_str() for APIs expecting strings (Qt, subprocess, etc.)

icon = QIcon(get_path_str(Icons.Logos.GitHub))

NOTE

Both functions ONLY accept PathDef objects from constants/paths.py. Raw strings are not supported by design.

COEXISTENCE WITH OLD SYSTEM

During migration, the old PathHelper/resource_path system can coexist: - Both systems use the same data/ directory structure - New code should use get_path()/get_path_str() with PathDef constants - Old code continues to work until migrated - No coordination needed - they resolve to the same locations

clear_app_temp()

Remove all contents of the .app_temp directory on shutdown.

Skips the -INFO.md marker file. Best-effort: silently catches exceptions so shutdown is never blocked.

Source code in src\shared_services\path_management\api.py
def clear_app_temp() -> None:
    """Remove all contents of the .app_temp directory on shutdown.

    Skips the -INFO.md marker file. Best-effort: silently catches
    exceptions so shutdown is never blocked.
    """
    import shutil

    if _manager is None:
        return

    app_temp_dir = _manager.data_root / ".app_temp"
    if not app_temp_dir.is_dir():
        return

    for child in app_temp_dir.iterdir():
        if child.name == "-INFO.md":
            continue
        try:
            if child.is_dir():
                shutil.rmtree(child)
            else:
                child.unlink()
        except Exception:
            pass

get_path(path_def)

Resolve a PathDef to an absolute Path.

Use for pathlib operations: file I/O, existence checks, path manipulation. For APIs expecting strings (Qt, subprocess), use get_path_str() instead.

Parameters:

Name Type Description Default
path_def PathDef

A PathDef from src.shared_services.constants.paths

required

Returns:

Type Description
Path

Absolute Path to the resource.

Raises:

Type Description
TypeError

If path_def is not a PathDef instance.

PathNotInitializedError

If path manager not initialized.

Example

from src.shared_services.constants.paths import Styles from src.shared_services.path_management.api import get_path

Read file content

stylesheet = get_path(Styles.Dark).read_text()

Check existence

if get_path(Config.UserSettings).exists(): ...

This will raise TypeError

path = get_path(".app_data/styles/dark.qss") # WRONG!

Source code in src\shared_services\path_management\api.py
def get_path(path_def: "PathDef") -> Path:
    """
    Resolve a PathDef to an absolute Path.

    Use for pathlib operations: file I/O, existence checks, path manipulation.
    For APIs expecting strings (Qt, subprocess), use get_path_str() instead.

    Args:
        path_def: A PathDef from src.shared_services.constants.paths

    Returns:
        Absolute Path to the resource.

    Raises:
        TypeError: If path_def is not a PathDef instance.
        PathNotInitializedError: If path manager not initialized.

    Example:
        from src.shared_services.constants.paths import Styles
        from src.shared_services.path_management.api import get_path

        # Read file content
        stylesheet = get_path(Styles.Dark).read_text()

        # Check existence
        if get_path(Config.UserSettings).exists():
            ...

        # This will raise TypeError
        path = get_path(".app_data/styles/dark.qss")  # WRONG!
    """
    if _manager is None:
        raise PathNotInitializedError(
            "Path manager not initialized. Call initialize_paths() first."
        )

    return _manager.resolve(path_def)

get_path_manager()

Get the PathManager instance.

For advanced usage only. Prefer get_path() for normal operations.

Raises:

Type Description
PathNotInitializedError

If not initialized.

Source code in src\shared_services\path_management\api.py
def get_path_manager() -> PathManager:
    """
    Get the PathManager instance.

    For advanced usage only. Prefer get_path() for normal operations.

    Raises:
        PathNotInitializedError: If not initialized.
    """
    if _manager is None:
        raise PathNotInitializedError(
            "Path manager not initialized. Call initialize_paths() first."
        )
    return _manager

get_path_str(path_def)

Resolve a PathDef to an absolute path string.

Use for APIs expecting strings: Qt classes, subprocess, os.path functions. For pathlib operations, use get_path() instead.

Parameters:

Name Type Description Default
path_def PathDef

A PathDef from src.shared_services.constants.paths

required

Returns:

Type Description
str

Absolute path as string.

Raises:

Type Description
TypeError

If path_def is not a PathDef instance.

PathNotInitializedError

If path manager not initialized.

Example

from src.shared_services.constants.paths import Icons from src.shared_services.path_management.api import get_path_str

Qt classes expect strings

icon = QIcon(get_path_str(Icons.Logos.AppLogo)) pixmap = QPixmap(get_path_str(Icons.Actions.Save))

QFile, QSettings, etc.

file = QFile(get_path_str(Config.UserSettings))

Source code in src\shared_services\path_management\api.py
def get_path_str(path_def: "PathDef") -> str:
    """
    Resolve a PathDef to an absolute path string.

    Use for APIs expecting strings: Qt classes, subprocess, os.path functions.
    For pathlib operations, use get_path() instead.

    Args:
        path_def: A PathDef from src.shared_services.constants.paths

    Returns:
        Absolute path as string.

    Raises:
        TypeError: If path_def is not a PathDef instance.
        PathNotInitializedError: If path manager not initialized.

    Example:
        from src.shared_services.constants.paths import Icons
        from src.shared_services.path_management.api import get_path_str

        # Qt classes expect strings
        icon = QIcon(get_path_str(Icons.Logos.AppLogo))
        pixmap = QPixmap(get_path_str(Icons.Actions.Save))

        # QFile, QSettings, etc.
        file = QFile(get_path_str(Config.UserSettings))
    """
    return str(get_path(path_def))

get_vendor_path(relative_path)

Resolve a path relative to the top-level vendor directory.

Use for locating bundled third-party binaries (e.g. WeasyPrint). The vendor directory lives outside the data directory so it can be excluded from antivirus scanning independently.

Parameters:

Name Type Description Default
relative_path str

Path relative to vendor root (e.g. "weasyprint-windows/dist/weasyprint.exe").

required

Returns:

Type Description
Path

Absolute Path to the vendor resource.

Raises:

Type Description
PathNotInitializedError

If path manager not initialized.

Example

exe = get_vendor_path("weasyprint-windows/dist/weasyprint.exe")

Source code in src\shared_services\path_management\api.py
def get_vendor_path(relative_path: str) -> Path:
    """
    Resolve a path relative to the top-level vendor directory.

    Use for locating bundled third-party binaries (e.g. WeasyPrint).
    The vendor directory lives outside the data directory so it can be
    excluded from antivirus scanning independently.

    Args:
        relative_path: Path relative to vendor root
            (e.g. "weasyprint-windows/dist/weasyprint.exe").

    Returns:
        Absolute Path to the vendor resource.

    Raises:
        PathNotInitializedError: If path manager not initialized.

    Example:
        exe = get_vendor_path("weasyprint-windows/dist/weasyprint.exe")
    """
    if _manager is None:
        raise PathNotInitializedError(
            "Path manager not initialized. Call initialize_paths() first."
        )

    return _manager.resolve_vendor(relative_path)

get_vendor_path_str(relative_path)

Resolve a vendor path to an absolute path string.

Convenience wrapper around get_vendor_path() for APIs expecting strings.

Source code in src\shared_services\path_management\api.py
def get_vendor_path_str(relative_path: str) -> str:
    """
    Resolve a vendor path to an absolute path string.

    Convenience wrapper around get_vendor_path() for APIs expecting strings.
    """
    return str(get_vendor_path(relative_path))

initialize_paths(mode=None, app_version=None, reset=False)

Initialize the path management system.

Call once at application startup, before any get_path() calls.

Parameters:

Name Type Description Default
mode str | None

"dev" or "deploy". Auto-detected if None.

None
app_version str | None

Current app version. Uses global_app_configs if None.

None
reset bool

Force re-initialization (for testing).

False

Raises:

Type Description
PathError

If initialization fails.

Example
In main.py

from src.shared_services.path_management.api import initialize_paths

def main(): initialize_paths() # ... rest of app startup

Source code in src\shared_services\path_management\api.py
def initialize_paths(
    mode: str | None = None,
    app_version: str | None = None,
    reset: bool = False,
) -> None:
    """
    Initialize the path management system.

    Call once at application startup, before any get_path() calls.

    Args:
        mode: "dev" or "deploy". Auto-detected if None.
        app_version: Current app version. Uses global_app_configs if None.
        reset: Force re-initialization (for testing).

    Raises:
        PathError: If initialization fails.

    Example:
        # In main.py
        from src.shared_services.path_management.api import initialize_paths

        def main():
            initialize_paths()
            # ... rest of app startup
    """
    global _manager

    if _manager is not None and not reset:
        return

    _manager = PathManager(mode=mode, app_version=app_version)
    _manager.initialize()

is_initialized()

Check if the path manager has been initialized.

Source code in src\shared_services\path_management\api.py
def is_initialized() -> bool:
    """Check if the path manager has been initialized."""
    return _manager is not None and _manager.is_initialized