Skip to content

Workspace

Directory layout

A pascal workspace is a standard UV workspace with a pascal.toml manifest at the root.

my-workspace/
  pascal.toml              # pascal reads this
  pyproject.toml           # UV workspace root — auto-managed by pascal
  uv.lock                  # lockfile — commit this to git
  packages/
    cart/
      pyproject.toml
      src/
        cart/
          __init__.py
      tests/
        test_cart.py
    auth/
      pyproject.toml
      src/
        auth/
          __init__.py
      tests/
        test_auth.py
  apps/
    storefront/
      pyproject.toml       # dependencies = ["cart", "auth"]
      src/
        storefront/
          __init__.py
          main.py
      tests/
        test_storefront.py

pascal.toml reference

[workspace]
name        = "my-workspace"   # required
python      = "3.12"           # required — minimum Python version
description = "My monorepo"    # optional

# Optional explicit member lists.
# If omitted, pascal auto-discovers from packages/*/pyproject.toml
# and apps/*/pyproject.toml.
packages = ["packages/cart", "packages/auth"]
apps     = ["apps/storefront"]

Auto-discovery

When packages and apps are not listed in pascal.toml, pascal scans:

  • <root>/packages/*/pyproject.toml → registered as packages
  • <root>/apps/*/pyproject.toml → registered as apps

This means adding a new directory under packages/ or apps/ is enough — no manifest update required.

Workspace root detection

Pascal walks up from the current working directory until it finds pascal.toml, the same way cargo and git find their roots. You can run pascal commands from any subdirectory inside the workspace.

Generated files

Root pyproject.toml

Auto-generated by pascal init and kept in sync by pascal sync:

[project]
name = "my-workspace"
version = "0.1.0"
requires-python = ">=3.12"

[tool.uv.workspace]
members = ["packages/*", "apps/*"]

Do not edit manually

This file is managed by pascal. Run pascal sync after changing pascal.toml to regenerate it.

Package pyproject.toml

Generated by pascal create package:

[project]
name = "cart"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

App pyproject.toml

Generated by pascal create app. Includes a [project.scripts] entry:

[project]
name = "storefront"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = []

[project.scripts]
storefront = "storefront.main:main"

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

After pascal add cart --to storefront:

[project]
name = "storefront"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = ["cart"]

[project.scripts]
storefront = "storefront.main:main"

[tool.uv.sources]
cart = { workspace = true }

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Source layout

Pascal uses the src layout for all generated packages and apps:

packages/cart/
  src/
    cart/          # importable package lives here
      __init__.py
  tests/
    test_cart.py

This keeps tests outside the importable package tree and avoids import confusion during development.

Name normalisation

Pascal normalises brick names the same way Python packaging does:

  • Hyphens and underscores are treated as equivalent
  • pascal create package my-pkg creates packages/my-pkg/ with src/my_pkg/
  • The [project] name in pyproject.toml uses hyphens (my-pkg)
  • The source directory uses underscores (my_pkg)