Configuration¶
The func CLI uses a layered configuration system. Settings can come from CLI flags, environment variables, project files, or a global config file — with a clear precedence chain determining which value wins.
Precedence Chain¶
Settings are resolved from highest to lowest priority. The first non-empty value wins:
┌─────────────────────────────────────────────────┐
│ 1. CLI flags (--require-file-import, etc.) │ ← Highest priority
├─────────────────────────────────────────────────┤
│ 2. FUNCTUALIZE_* environment variables │
├─────────────────────────────────────────────────┤
│ 3. pyproject.toml [tool.functualize] │
├─────────────────────────────────────────────────┤
│ 4. .functualize.toml (only if no pyproject) │
├─────────────────────────────────────────────────┤
│ 5. ~/.config/functualize/config.toml │
├─────────────────────────────────────────────────┤
│ 6. Built-in defaults (all unset/None) │ ← Lowest priority
└─────────────────────────────────────────────────┘
See also:
examples/standalone/config_lab/for a working precedence demo.
Global Config File¶
The global config provides persistent user-level preferences that apply across all projects.
Location¶
Falls back to ~/.config/functualize/config.toml when $XDG_CONFIG_HOME is unset or empty.
Schema¶
[discovery]
require_file_prefix = "job_" # optional
require_file_postfix = "_task" # optional
require_file_import = "functualize" # optional
require_file_marker = "__functualize__" # optional
require_job_decorators = ["job", "workflow"] # optional
require_job_prefix = "run_" # optional
require_job_postfix = "_job" # optional
extra_directories = ["~/.config/functualize/jobs"] # optional, max 20 entries
exclude_patterns = ["**/test_*.py"] # optional, max 50 entries
[cli]
output = "rich" # "rich" | "plain" | "json"
show_timing = false
[aliases]
d = "deploy"
m = "migrate"
r = "run_tests"
All fields are optional. When absent, no constraint is applied (baseline convention mode).
See Global Config Directory for the full directory layout.
Project Config¶
pyproject.toml¶
The primary project-level config location:
[tool.functualize]
jobs_directories = ["jobs"]
[tool.functualize.discovery]
require_file_import = "functualize"
exclude_patterns = ["**/test_*.py", "**/migrations/**"]
.functualize.toml (Alternative)¶
For non-Python projects or when you prefer a standalone config file:
# .functualize.toml — root-level keys (no [tool.functualize] nesting)
jobs_directories = ["jobs"]
[discovery]
require_file_import = "functualize"
exclude_patterns = ["**/test_*.py"]
Rules:
- If pyproject.toml contains [tool.functualize], .functualize.toml is ignored
- If pyproject.toml lacks [tool.functualize] (or doesn't exist), .functualize.toml is used
- Both formats support the same settings with the same semantics
Key placement: the directory-list keys (jobs_directories,
extra_directories, exclude_patterns, import_libs) are accepted both at
the top level and under [discovery]; the top-level location is checked
first. jobs_directories is consumed by the CLI's project discovery
(auto_discover) — relative paths resolve against the config file's own
directory, and entries from every config layer in the upward walk contribute
(nearest layer first).
See also:
examples/standalone/showcase/for a.functualize.tomlexample.
Environment Variables¶
Override any setting via environment variables using the FUNCTUALIZE_ prefix.
Naming Convention¶
The env var name is constructed by uppercasing the full key path with underscores:
Examples¶
| Config Key | Environment Variable |
|---|---|
[discovery].require_file_import |
FUNCTUALIZE_DISCOVERY_REQUIRE_FILE_IMPORT |
[discovery].require_file_prefix |
FUNCTUALIZE_DISCOVERY_REQUIRE_FILE_PREFIX |
[cli].output |
FUNCTUALIZE_CLI_OUTPUT |
[cli].show_timing |
FUNCTUALIZE_CLI_SHOW_TIMING |
Top-Level Keys¶
Recognized top-level (non-section) keys map directly, without a section segment:
| Config Key | Environment Variable |
|---|---|
dotenv |
FUNCTUALIZE_DOTENV |
dotenv_path |
FUNCTUALIZE_DOTENV_PATH |
import_libs |
FUNCTUALIZE_IMPORT_LIBS |
jobs_directories |
FUNCTUALIZE_JOBS_DIRECTORIES |
extra_directories |
FUNCTUALIZE_EXTRA_DIRECTORIES |
Boolean Values¶
Boolean env vars accept (case-insensitive): true, 1, false, 0.
Empty Values¶
An empty string is treated as unset — resolution continues to the next level:
List Values¶
List-typed settings (like exclude_patterns) use comma separation in env vars:
List Merge Behavior¶
When list values (like exclude_patterns, extra_directories) exist at multiple levels, they are concatenated and deduplicated:
- Project-level entries come first
- Global-level entries are appended
- Duplicates are removed (project-level entry is retained)
# pyproject.toml
[tool.functualize.discovery]
exclude_patterns = ["**/test_*.py"]
# ~/.config/functualize/config.toml
[discovery]
exclude_patterns = ["**/test_*.py", "**/migrations/**"]
# Resolved: ["**/test_*.py", "**/migrations/**"]
# (duplicate removed, project-level retained)
func builtin config Commands¶
Built-in commands for inspecting and editing your configuration.
func builtin config show¶
Display the fully resolved configuration with source annotations:
$ func builtin config show
[discovery]
require_file_import = "functualize" # source: pyproject.toml
exclude_patterns = ["**/test_*.py"] # source: global config
[cli]
output = "rich" # source: default
show_timing = false # source: default
[aliases]
d = "deploy" # source: global config
func builtin config path¶
Show config file paths with their status:
$ func builtin config path
✓ used /home/user/project/pyproject.toml
✗ missing /home/user/project/.functualize.toml
○ found /home/user/.config/functualize/config.toml
Status indicators:
- ✓ used — file exists and contributed values
- ○ found — file exists but all values were overridden by higher-priority sources
- ✗ missing — file does not exist
func builtin config edit¶
Open the global config file in your editor:
Editor resolution order:
1. $EDITOR
2. $VISUAL
3. Platform default (vi on POSIX, notepad on Windows)
If the config file doesn't exist, func builtin config edit creates the directory and a template file before opening the editor.
If no editor can be resolved, an error message is displayed and the command exits with a non-zero code.
Error Handling¶
The configuration system follows warn and continue for ambient config, fail fast for explicit actions:
| Condition | Behavior |
|---|---|
| Global config missing | Proceed with defaults (silent) |
| Global config unreadable (permissions) | Warning to stderr, proceed with defaults |
| TOML syntax error | Warning to stderr with file + line, proceed with defaults |
| Unrecognized section in config | Silently ignored |
| Unrecognized key in recognized section | Warning to stderr, key ignored |
| Type mismatch for recognized key | Warning to stderr, schema default substituted |
Invalid [cli].output value |
Warning, falls back to "rich" |
| Invalid alias key (pattern mismatch) | Warning, alias skipped |
| Invalid UTF-8 in config file | Warning, proceed with defaults |
--dotenv-file points to missing file |
Error + non-zero exit (explicit = required) |
.env missing with auto-load |
Proceed silently (opportunistic) |
Dotenv Support¶
The CLI can auto-load .env files for environment variable injection.
# pyproject.toml — top-level keys, NOT a [tool.functualize.config] section
[tool.functualize]
dotenv = true # enable auto-loading
# dotenv_path = ".env.local" # optional: explicit path
CLI Flags¶
func --dotenv-file .env.local deploy # explicit file (required to exist)
func --no-dotenv deploy # suppress all .env loading
Environment Variables¶
export FUNCTUALIZE_DOTENV=true # enable auto-loading
export FUNCTUALIZE_DOTENV_PATH=.env.local # explicit path (opportunistic)
Behavior¶
dotenv = false(default): no.envloading unless--dotenv-fileis provideddotenv = true: auto-loads.envfrom CWD (missing file = no error)dotenv_path = "...": loads the named file (missing file = warning, no error)--dotenv-file: overrides all other settings, file must exist--no-dotenv: suppresses all loading regardless of config- Loaded values never override variables already set in the shell (
override=False)
Ordering caveat: dotenv settings are read from the resolved CLI config, so the
.envfile itself cannot influence CLI config resolution (e.g. aFUNCTUALIZE_CLI_OUTPUTvalue inside.envdoes not affect the current invocation's output format). Job config resolution throughEnvSourcedoes see.envvalues — the file is loaded before the resolution chain is built at app boot. - Loading happens after flag parsing, before job execution