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
Repositoryrow (slug=name,prefix=moo/bootstrap/<name>/verbs) used byload_verbsto locate verb sources.The full set of
Permissionrows defined bysettings.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
acceptverb returningTrue.A Django
Usernamedwizardand aPlayerrow linking the user to the Wizard Object withwizard=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:
Calls
bootstrap.initialize_dataset(name).Builds a
_namespacedict containing every name the sub-scripts need (bootstrap,lookup,wizard,sys, etc.).Discovers numbered
.pyfiles in the package viaimportlib.resources.files("moo.bootstrap.<name>").iterdir().Runs each script via
exec(compile(...), _namespace)inside acode.ContextManager(wizard, log.info, site=wizard.site)block so ownership, permissions, and the active site track to the Wizard player.Either calls
bootstrap.load_verbsdirectly at the end, or defers that to a999_finalize.pyscript.
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:
- 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 verbshebang from verb source code.- Parameters:
content – the full verb source code string
- Returns:
(names, on, dspec, ispec)tuple if a valid shebang is present, elseNone
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.