Bootstrapping Reference

A bootstrap is a Python package that populates an empty database with the objects, properties, and verbs that make up a DjangoMOO world. Without one, a fresh database has no System Object, no Wizard, and nothing to log into.

For a step-by-step walkthrough of writing your own dataset, see Building a Persistent World from Scratch. For task recipes (sync after edits, verb replacement, idempotent updates, debugging), see Bootstrap Recipes.

Available datasets

default — the production game world. Loaded with:

docker compose run webapp manage.py moo_init --bootstrap default

Lives at moo/bootstrap/default/. The package contains an orchestrator bootstrap.py plus numbered sub-scripts (000_initialize.py, 010_core_classes.py, …, 999_finalize.py) executed in sorted order. The package’s __init__.py is intentionally empty so importing the package (e.g. for test discovery) does not run database setup; the orchestrator is invoked explicitly by moo_init via bootstrap.py. Verb sources live in the sibling moo/bootstrap/default/verbs/ package, organised by root-class name; tests live in moo/bootstrap/default/tests/.

A second example bootstrap, derived from the original Infocom Zork I source, ships separately with the companion moo-agent project. That package contributes moo.bootstrap.zork1 via the same moo.* namespace and is loadable through moo_init --bootstrap zork1 when moo-agent is installed alongside django-moo; the translator that produced the dataset and the importer reference docs live there too.

test — a minimal dataset used by the pytest t_init fixture in moo/conftest.py. It is not loadable via moo_init. It exists as a flat module (moo/bootstrap/test.py) and creates only what the unit tests need to exercise the verb execution engine.

What initialization gives you

bootstrap.initialize_dataset(name) is the entry point every bootstrap package calls first. It is idempotent and produces:

  • A Repository row (slug=name, prefix=moo/bootstrap/<name>/verbs) used by load_verbs to locate verb sources.

  • The full set of Permission rows defined by settings.DEFAULT_PERMISSIONS.

  • The lexer’s preposition table (Pattern.initializePrepositions).

  • The System Object (pk=1, name="System Object").

  • Three LambdaMOO-style sentinel objects with the lowest possible PKs: nothing (#2), ambiguous_match (#3), failed_match (#4).

  • A Wizard Object with a stub accept verb returning True.

  • A Django User named wizard and a Player row linking the user to the Wizard Object with wizard=True.

  • Wizard ownership of the System Object, the sentinels, and itself.

What it does not do — and what your bootstrap package is responsible for — is create root classes (Room, Thing, Player, etc.), starting rooms, exits, or any verbs beyond Wizard’s stub accept. The default permission application that runs on every new object is handled natively by moo.core.utils.apply_default_permissions; no set_default_permissions verb on the System Object is required.

Orchestrator pattern

Every loadable bootstrap is a Python package whose bootstrap.py entry point:

  1. Calls bootstrap.initialize_dataset(name).

  2. Builds a _namespace dict containing every name the sub-scripts need (bootstrap, lookup, wizard, sys, etc.).

  3. Discovers numbered .py files in the package via importlib.resources.files("moo.bootstrap.<name>").iterdir().

  4. Runs each script via exec(compile(...), _namespace) inside a code.ContextManager(wizard, log.info, site=wizard.site) block so ownership, permissions, and the active site track to the Wizard player.

  5. Either calls bootstrap.load_verbs directly at the end, or defers that to a 999_finalize.py script.

The package’s __init__.py is left empty; moo_init invokes bootstrap.py explicitly via load_python rather than importing the package, so test collection can import the package safely.

moo/bootstrap/default/bootstrap.py is the canonical example.

Function reference

moo.bootstrap.initialize_dataset(dataset='default', site=None)

Initialize a new dataset, or sync an existing one.

This will create the default objects and permissions for the dataset. Notably, it will create a System Object that is used to store global properties and verbs.

It will also create a Wizard user that is used to manage the system.

All operations are idempotent — safe to call on a DB that has already been initialized.

Parameters:
  • dataset (str) – The name of the dataset to initialize.

  • site (Site | None) – The site to associate the dataset with.

Returns:

The repository object for the dataset.

Return type:

Repository

moo.bootstrap.get_or_create_object(name, unique_name=False, parents=None, owner=None, location=None, site=None)

Get or create a named object. Safe to call on an already-bootstrapped database.

Parameters:
  • name (str) – The name of the object to get or create.

  • unique_name (bool) – Whether the object has a unique name constraint.

  • parents (list[Object] | None) – A list of parent objects to add if the object is newly created.

  • owner (Object | None) – The owner of the object.

  • location (Object | None) – The location of the object.

  • site (Site | None) – The site to associate the object with.

Returns:

A (object, created) tuple.

Return type:

tuple[Object, bool]

moo.bootstrap.load_verbs(repo, verb_package, replace=False)

Load the verbs from a Python package into the database and associate them with the given repository.

Verb files should start with a shebang:

#!moo [-h] [--on ON] [--dspec {this,any,none,either}] [--ispec PREP:SPEC [PREP:SPEC ...]] {verb} names [names ...]

positional arguments:
{verb}
names

options:
-h, --help            show this help message and exit
--on ON               The object to add or modify the verb on
--dspec {this,any,none,either}
                        The direct object specifier
--ispec PREP:SPEC [PREP:SPEC ...]
                        Indirect object specifiers
Parameters:
  • repo (Repository) – The repository object for the dataset.

  • verb_package (str) – The Python package to load the verbs from.

  • replace (bool) – If True, update existing verbs in place rather than skipping them.

moo.bootstrap.load_verb_source(path, system, repo, replace=False)
moo.bootstrap.parse_shebang(content)

Parse the #!moo verb shebang from verb source code.

Parameters:

content – the full verb source code string

Returns:

(names, on, dspec, ispec) tuple if a valid shebang is present, else None

get_or_create_object returns a (object, created) tuple. It only attaches parents on first creation to avoid duplicate-relationship errors. Calling it on an existing database is a no-op for the object itself, which makes top-level use safe under moo_init --sync.

load_verb_source parses a #!moo verb shebang from a single file and calls obj.add_verb(). Pass replace=True to overwrite the verb source in place; the default skips files whose verbs already exist.

load_verbs walks a Python package recursively, calling load_verb_source for every .py file with a shebang. The directory structure inside the package is purely organisational — only the --on line in each shebang determines where the verb attaches.

parse_shebang is the same parser used by both load_verb_source and @edit verb. It returns (names, on, dspec, ispec) or None if the first line is not a #!moo verb shebang. Useful when you need to extract verb metadata from user-supplied source.

Verb files in bootstrap

Every verb file begins with a shebang:

#!moo verb accept --on $room

The shebang is parsed by parse_shebang and supplies --on (the target object), the verb name(s), and optional --dspec/--ispec flags. After the shebang, the verb body follows without a function wrapper — RestrictedPython adds one at compile time.

For the full shebang grammar and verb authoring patterns, see Creating MOO Verbs.

Running bootstrap in development

Initialize a fresh database:

docker compose run webapp manage.py migrate
docker compose run webapp manage.py moo_init --bootstrap default

Sync an existing database to pick up new verbs and objects without resetting it. This is the right tool for almost every iteration cycle:

docker compose run webapp manage.py moo_init --bootstrap default --sync

If you need a true reset (destroys all data), tear down and recreate the postgres container rather than running migrate zero:

docker compose down -v
docker compose up -d
docker compose run webapp manage.py migrate
docker compose run webapp manage.py moo_init --bootstrap default

-v drops the postgres volume so the next up starts from an empty database.

Inspect the bootstrapped state from the Django shell:

docker compose run webapp manage.py shell
>>> from moo.sdk import lookup
>>> sys = lookup(1)
>>> sys.name
'System Object'
>>> sys.root_class.name
'Root Class'
>>> lookup("Wizard").is_wizard()
True

lookup() accepts an object PK, an exact name, or any of an object’s aliases. It raises NoSuchObjectError if nothing matches.