Skip to content

Console commands (Artisan-style)

pyvelm supports custom CLI commands like Laravel Artisan. Modules ship generators, maintenance tasks, and importers as Python classes; users run them as:

pyvelm make:module inventory
pyvelm inventory:import --file=data.csv
pyvelm list
pyvelm help make:module

Built-in framework commands (cron, init, new, db diff, db nuke, …) are listed under Core when you run pyvelm list. Console module commands (migrate, migrate:fresh, serve, make:module, …) appear under Module.

Writing a command

Subclass :class:pyvelm.console.Command and place the file in your module's commands/ directory (or register it in COMMANDS).

# app/modules/inventory/commands/import_products.py
from pyvelm.console import Command


class ImportProductsCommand(Command):
    name = "inventory:import"
    description = "Import products from a CSV file"
    signature = "inventory:import {path} {--dry-run}"
    requires_db = True  # loads PYVELM_DSN + registry before handle()

    def handle(self, path: str, dry_run: bool = False) -> int:
        self.info(f"Importing {path}…")
        if dry_run:
            self.warn("Dry run — no writes.")
        # self.env is available when requires_db=True
        return 0

Signature tokens

Token Meaning
{name} Required positional argument
{name?} Optional positional
{--flag} Boolean flag (store_true)
{--opt=} Optional option (value after =)
{--opt=default} Option with default
{name : Help text} Description shown in --help

Use a namespace in the command name (inventory:import, make:module) so app commands don't collide.

Registration

Auto-discovery (recommended): any Command subclass under <module>/commands/*.py is registered when the module is discovered.

Explicit manifest: list dotted paths in __pyvelm__.py:

COMMANDS = [
    "inventory.commands.import_products:ImportProductsCommand",
]

Discovery uses the same module roots as the web app (pyvelm.toml modules_root, PYVELM_MODULE_ROOTS, bundled base / admin / console).

Generators (bundled console module)

Command Purpose
make:module Empty addon skeleton (no models/views/menus)
make:model models/<name>.py (models.Model) + models/__init__.py import
make:view views/<stem>.py with ViewsData.make() list + form + manifest .data() entry
make:menu views/menu.py (or --append to existing)
make:command commands/<name>.py Artisan command class
make:stubs Generate .pyvelm/typing/ + merge pyrightconfig.json — see IDE typing stubs
serve Run uvicorn (--env development|production, --reload, --host, --port)
test Run pytest (--coverage, --integration, extra args after --)
migrate Install/upgrade modules (bootstrap on fresh DB; --all, --module)
migrate:fresh DEV ONLY — drop schema, then migrate (bootstrap by default)
migrate:reset DEV ONLY — drop schema (empty database)

Typical workflow:

cd my_erp
pyvelm make:module inventory
pyvelm make:model inventory.product --module=inventory
pyvelm make:view inventory.product --module=inventory
# Default: introspect stored fields → list columns + form sections
# (booleans → toggle, O2m/M2m → widget="dialog"). Use --minimal for name-only stub.
pyvelm make:menu --view=product.list --module=inventory
pyvelm make:stubs                              # IDE literals + pyrightconfig.json
pyvelm migrate                         # upgrade installed modules (deploy)
pyvelm migrate --module tasks          # one module + dependencies
pyvelm serve --reload                  # dev server (http://127.0.0.1:8000/login)
pyvelm test                                    # unit tests (pyvelm/tests in this repo)
pyvelm db autogen inventory --with-views   # migration + views for new models
docker compose restart app
# Install via /web/apps

pyvelm db autogen <module> --with-views creates list+form views for any model touched by the schema diff that does not already have a list view.

API reference

  • :class:pyvelm.console.Command — base class
  • :class:pyvelm.console.CommandContextinfo, line, warn, error
  • :func:pyvelm.loader.discover_commands — build a registry programmatically

See also CLI reference and IDE typing stubs.