Quickstart¶
This guide walks you through functualize's three modes of use — from a single file to a full framework project. Start simple, graduate when you need more.
Prerequisites
Python 3.11+ and functualize installed. See Installation if you haven't set up yet.
Mode 1: Single-File Script¶
The fastest way to start. One file, one job, zero configuration.
Create a job file¶
from functualize.job import RunContext, Log
def deploy(rc: RunContext):
"""Deploy the application to production."""
rc.log("Starting deployment...")
rc.log("Deployment complete")
Run it¶
That's it. No pyproject.toml, no project structure, no registration. Functualize imports the file, finds deploy, and runs it with a full RunContext.
List available functions¶
If your file has multiple jobs, run without a function name to see what's available:
from functualize.job import RunContext, Log
def deploy(rc: RunContext):
"""Deploy the application to production."""
rc.log("Deploying...")
def rollback(rc: RunContext):
"""Roll back the last deployment."""
rc.log("Rolling back...")
def healthcheck(rc: RunContext):
"""Check service health."""
rc.log("All services healthy")
Available functions in jobs.py:
deploy — Deploy the application to production.
healthcheck — Check service health.
rollback — Roll back the last deployment.
Mode 2: Project Directory with Auto-Discovery¶
When you have multiple job files, organize them in a jobs/ directory. Functualize discovers the modules you point it at and registers each function as a CLI command.
Create the structure¶
from functualize.job import RunContext, Log
def deploy(rc: RunContext):
"""Deploy the application."""
rc.log("Deploying to production...")
from functualize.job import RunContext, Log
def migrate(rc: RunContext):
"""Run database migrations."""
rc.log("Running migrations...")
Tell functualize where the jobs live by declaring the directory in pyproject.toml:
How func finds jobs from the current directory¶
By default func scans only the current directory (scan_depth = 0) — it does
not recurse into subdirectories. Run from myproject/, the jobs/ folder is therefore
not discovered unless you tell functualize about it. There are three ways to make
func deploy work from myproject/:
- Declare the directory (recommended):
jobs_directories = ["jobs"]inpyproject.toml(above), the same key in a.functualize.toml, or place the jobs under a.functualize/jobs/convention directory. - Recurse the working directory: set
[discovery] scan_depth = 1inpyproject.toml, or pass the flagfunc --discovery-depth 1 deploy.scan_depthis clamped to the range0–5. - Run from inside
jobs/:cd jobs && func deploy— the.pyfiles are then at the top level of the current directory, so the defaultscan_depth = 0finds them.
Run from the project directory¶
Functualize discovers the jobs/ directory (via the jobs_directories setting above)
and registers each public function as a CLI command. The command name is the function
name, so def deploy becomes func deploy and def migrate becomes func migrate.
(With the directory declared, func healthcheck also works if healthcheck.py
defines a healthcheck function.)
Scaffold a new job¶
Instead of creating files manually, use the scaffold command:
This creates jobs/backup.py with the correct template and imports.
Mode 3: Full FunctualizeApp Project¶
For production applications that need custom configuration, plugins, and a dedicated CLI command, scaffold a complete project.
Scaffold a new project¶
This generates:
my-platform/
├── pyproject.toml
├── config.base.toml
└── src/
└── my_platform/
├── __init__.py
├── main.py
└── jobs/
├── __init__.py
└── sample_job.py
Install and run¶
The entry point¶
The generated main.py wires everything together:
from functualize.app import FunctualizeApp, JobSources, ConfigSources, classic
app = FunctualizeApp(
name="my-platform",
job_sources=JobSources(directories=["my_platform.jobs"]),
config_sources=classic(),
)
def run() -> None:
"""Console script entry point."""
app.run()
The pyproject.toml maps your project name to this entry point:
Use presets for production deployments¶
Swap the configuration strategy based on your deployment target:
from functualize.app import FunctualizeApp, JobSources, ConfigSources, twelve_factor
app = FunctualizeApp(
name="my-platform",
job_sources=JobSources(directories=["my_platform.jobs"]),
config_sources=twelve_factor(dotenv=True), # Env vars only, no config files
)
| Preset | Strategy | Best for |
|---|---|---|
classic() |
CLI → Env → Config files → Defaults | Local dev, desktop tools |
twelve_factor() |
CLI → Env → Defaults (no files) | Docker, Kubernetes, Heroku |
env_only() |
CLI → Env → Defaults (dotenv on) | Serverless, minimal setups |
remote_first() |
CLI → Vault → Env → Files → Defaults | AWS Secrets Manager, Bitwarden |
remote_first() needs a provider plugin and a vault key
Config values declared as aws-sm://prod/db-password resolve from an
encrypted local vault that func builtin vault sync fills — reads never
touch the network. Selecting the preset with no remote provider registered
raises at construction rather than quietly resolving from local files. See
Remote Configuration.
You can also write your own preset — any function returning ConfigSources works:
from functualize.app import ConfigSources
def my_custom_preset(**kwargs) -> ConfigSources:
return ConfigSources(dotenv=True, file_pattern=r"^settings\.\w+\.toml$")
Exposing a Global CLI Command (uv project)¶
When you scaffold a project with func builtin scaffold init, the pyproject.toml already includes a [project.scripts] entry that makes your app available as a named command after installation.
Here's how it works:
This tells Python's packaging system: "when this package is installed, create a my-platform executable that calls my_platform.main:run()."
After uv sync or pip install -e ., the command is available globally in your environment:
Adding it to an existing uv project¶
If you already have a pyproject.toml, add the entry point manually:
Where myapp/main.py contains:
from functualize.app import FunctualizeApp, JobSources
app = FunctualizeApp(
name="my-cli",
job_sources=JobSources(directories=["myapp.jobs"]),
)
def run() -> None:
app.run()
Then reinstall:
Scaffolding Commands¶
Functualize provides scaffolding for common operations:
| Command | What it does |
|---|---|
func builtin scaffold init <name> |
Create a new project with full structure |
func builtin scaffold add job <name> |
Add a job file to the current project (or CWD in bare mode) |
func builtin scaffold add plugin <name> |
Add a plugin file |
func builtin scaffold add tui-screen <name> |
Add a TUI screen component |
Scaffold a new project¶
Add a job to an existing project¶
Add a job in bare mode (no project)¶
When you're outside a project context, scaffold creates a standalone job file:
mkdir scripts && cd scripts
func builtin scaffold add job backup
# Creates backup.py in the current directory
What's Next¶
| Topic | Guide |
|---|---|
| Understand the generated project layout | Project Structure |
| Configure jobs with typed parameters | Job Configuration |
| Set up layered config resolution | Configuration System |
| Add more jobs and understand discovery | Jobs & Auto-Discovery |
| Extend with plugins | Plugins Guide |
| Choose a usage mode | Modes Guide |