The DjangoMOO Runtime

DjangoMOO verb code runs inside a RestrictedPython sandbox with a constrained runtime environment. This page documents the global names available to a verb, the context object, and the execution-environment limits the sandbox enforces. For the conceptual model and full set of sandbox guards, see Verb Sandbox Security Reference.

Names available in every verb

Six names are injected into the verb’s local scope by the compiler. Linters will flag them as undefined — add a # pylint: disable=undefined-variable at the top of every verb file.

Name

Type

Description

this

Object

The object the verb was matched on. With --dspec this, this is the direct object. With --dspec any or none, this is the caller.

passthrough

callable

Calls the same verb on the next ancestor up the parent chain (the MOO super()). Pass any received args through: passthrough(*args, **kwargs).

_

Object

The System Object (pk=1). Used for _.string_utils, _.gripe_recipients, and other globals. The single attribute name beginning with _ that the sandbox allows.

args

list

Positional arguments when the verb is invoked as a method. Empty when invoked from the command parser.

kwargs

dict

Keyword arguments when invoked as a method. Empty from the parser.

verb_name

str

The exact alias the caller used to invoke the verb. Never assign to a local named verb_name — Python scoping makes the whole function treat it as a local, and reads before the assignment raise UnboundLocalError.

The context object

from moo.sdk import context exposes a module-level proxy whose attributes track the state of the currently executing session. The proxy is implemented over contextvars.ContextVar, so each Celery task gets its own isolated copy.

When a player sends a command, the task runner opens a ContextManager scope:

with code.ContextManager(caller, output.append, task_id=task_id) as ctx:
    parse.interpret(ctx, line)

parse.interpret then attaches a Parser instance, making the full session state available to whatever verb dispatches.

Attributes

_Context.player = None

The Object that originated the command. Stays anchored to the session initiator across nested verb calls. Use this for “who is acting” logic.

_Context.caller = None

The Object whose verb code is currently executing. Shifts as verbs invoke other verbs. Permission checks evaluate against this, not player.

_Context.parser = None

The Parser for the current command. None when a Celery task re-invokes a verb without an active player command (e.g. scheduled invoke() calls).

_Context.writer = None

Callable that print() ends up calling. Sends a single string to the originating player’s connection.

_Context.task_id = None

The Celery task ID for the current execution.

_Context.task_time = None

TaskTime(elapsed, time_limit, remaining) namedtuple in seconds. None when no time limit is configured. Used for time-aware continuation.

_Context.caller_stack = []

Stack of caller frames accumulated as verbs invoke sub-verbs. The returned list is a copy; mutating it does not affect the live stack.

context.player and context.caller are the same object at the start of a command. They diverge as verbs invoke other verbs: caller shifts to the new executor, while player stays the original session initiator. See How Permissions Work in Verbs for why this matters.

Execution environment

Verb code is compiled by RestrictedPython and executed inside a controlled globals dict. Practical effects on what verb code can do:

  • The print() builtin routes through context.writer and ends up on the initiator’s connection. Output is buffered for the duration of the verb task; consecutive print(..., end="") calls are coalesced into one writer invocation so a verb can stream a phrase with multiple calls without producing fragmented lines on the client.

  • Attribute names beginning with _ raise AttributeError (the global _ reference to the System Object is the single allowed exception).

  • Augmented assignment to a subscript (d["k"] += 1) is rejected — use a plain variable instead. Augmented assignment on locals (x += 1) works.

  • Only the builtins in settings.ALLOWED_BUILTINS are available (all, any, dict, enumerate, getattr, hasattr, list, max, min, set, sorted, sum, PermissionError).

  • Only the modules in settings.ALLOWED_MODULES may be imported (moo.sdk, hashlib, re, datetime, time). Wizard-owned verbs additionally get moo.core.models.{object,verb,property}.

  • return may appear at any depth in the verb body, not just at function end.

  • Each verb invocation finishes within settings.CELERY_TASK_TIME_LIMIT (default 3 seconds) or the worker terminates the task. Synchronous calls to other verbs share that budget.

For the full sandbox model — including model-layer permission checks, QuerySet method whitelists, and the str.format block — see Verb Sandbox Security Reference.

Looking up objects

There is no LambdaMOO-style #N literal syntax in Python. To obtain an object reference inside verb code, use lookup():

moo.sdk.lookup(x, return_first=True)

Lookup an object within the current site by PK, name, or alias.

PK lookups are site-scoped: an integer PK that belongs to a different universe raises NoSuchObjectError the same as a missing object would. Internal system code that genuinely needs cross-site access should use Object.global_objects directly rather than going through this function.

Parameters:
  • x (Union[int, str]) – lookup value

  • return_first (bool) – when True (default), return the first match or raise NoSuchObjectError; when False, return a list of all matches (may be empty)

Returns:

the result of the lookup, or a list when return_first is False

Return type:

Object | list[Object]

Raises:

NoSuchObjectError – when a result cannot be found and return_first is True

lookup() accepts an object PK, an exact name, or any of an object’s aliases. It raises NoSuchObjectError if nothing matches — that exception is a UserError and propagates cleanly to the player when uncaught (see Creating MOO Verbs).