SDK Functions

Every name listed here is exported from moo.sdk and importable in verb code (from moo.sdk import lookup, create, ...). For methods defined on the Object model and accessible as obj.<name>(...), see Objects in the DjangoMOO Database. For Property and Verb field reference, see Properties on Objects and Verbs on Objects.

Object lifecycle and 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

moo.sdk.create(name, *a, **kw)

Creates and returns a new object whose parents are parents and whose owner is as described below. Provided parents are valid Objects with derive permission, otherwise PermissionError is raised. After the new object is created, its initialize verb, if any, is called with no arguments.

The owner of the new object is either the programmer (if owner is not provided), or the provided owner, if the caller has permission to entrust the object.

If the intended owner of the new object has a property named ownership_quota and the value of that property is an integer, then create() treats that value as a quota. If the quota is less than or equal to zero, then the quota is considered to be exhausted and create() raises QuotaError instead of creating an object. Otherwise, the quota is decremented and stored back into the ownership_quota property as a part of the creation of the new object.

Parameters:
  • name (str) – canonical name

  • owner (Object) – owner of the Object being created

  • location (Object) – where to create the Object

  • parents (list[Object]) – a list of parents for the Object

  • obvious (bool) – whether the object appears in room contents listings (default False)

Returns:

the new object

Return type:

Object

Raises:
  • PermissionError – if the caller is not allowed to derive from the parent

  • QuotaError – if the caller has a quota and it has been exceeded

moo.sdk.players()

Return a list of all player avatar Objects.

Returns:

Objects that are player avatars

Return type:

list[Object]

moo.sdk.connected_players(within=None)

Return a list of player avatars whose last_connected_time property was updated within the given within window (default: 5 minutes).

The last_connected_time value is precached into the session-level property cache on every returned Object so subsequent get_property calls incur no extra queries.

Parameters:

within (timedelta) – recency window; defaults to timedelta(minutes=5)

Returns:

Objects whose avatars have connected recently

Return type:

list[Object]

moo.sdk.owned_objects(player_obj)

Return a QuerySet of all Objects owned by player_obj, ordered by name.

Parameters:

player_obj – the owner Object

Return type:

QuerySet

moo.sdk.owned_objects_by_pks(pk_list)

Return a QuerySet of Objects with PKs in pk_list, ordered by name.

Used by continuation tasks (e.g. audit_batch) where the target player is not in scope but the remaining PK list was passed as args[0].

Parameters:

pk_list – list of integer Object PKs

Return type:

QuerySet

moo.sdk.ensure_player_record(obj)

Ensure a Player row exists for obj with user=None.

Used by NPC initialization so the avatar reports is_player() == True to the parser while is_connected() == False causes tell() to silently drop (no connection). Idempotent: returns the existing Player if one already references this avatar.

Parameters:

obj – the avatar Object

Returns:

the Player row (created or pre-existing)

Raises:

UserError – if the current caller is not a wizard

moo.sdk.remove_player_record(obj)

Delete any anonymous (user=None) Player rows pointing at obj.

Counterpart to ensure_player_record(); used by the $npc.recycle verb. Rows tied to a real User are left alone — those belong to a human player and removing them is not this helper’s job.

Parameters:

obj – the avatar Object

Returns:

number of Player rows deleted

Raises:

UserError – if the current caller is not a wizard

Tasks and continuations

moo.sdk.invoke(*args, verb=None, callback=None, delay=0, periodic=False, cron=None, _caller=None, _player=None, **kwargs)

Asynchronously execute a Verb, optionally returning the result to another Verb. This is often a better alternative than using __call__-syntax to invoke a verb directly, since Verbs invoked this way will each have their own timeout.

Parameters:
  • verb (Verb) – the Verb to execute

  • callback (Verb) – an optional callback Verb to receive the result

  • delay (int) – seconds to wait before executing, cannot be used with cron

  • periodic (bool) – should this task continue to repeat? cannot be used with cron

  • cron (str | None) – a crontab expression to schedule Verb execution

  • _caller – explicit caller override; falls back to context.caller. Used by transaction.on_commit callbacks that fire after the ContextManager has exited and cleared the contextvars.

  • _player – explicit player override; falls back to context.player. Same reason as _caller.

  • args – positional arguments for the Verb, if any

  • kwargs – keyword arguments for the Verb, if any

Returns:

a PeriodicTask instance or None if the task is a one-shot

Return type:

Optional[PeriodicTask]

moo.sdk.cancel_scheduled_task(pk)

Delete a django_celery_beat.PeriodicTask row by primary key.

Used by daemon lifecycle verbs ($daemon.disable, $daemon.recycle) to remove the PT created by invoke() (periodic=True). Idempotent: returns False if no PT with that pk exists.

Parameters:

pk (int) – the PeriodicTask.pk returned by invoke()

Return type:

bool

Returns:

True if a row was deleted, False if not found

Raises:

UserError – if the current caller is not a wizard

moo.sdk.get_scheduled_task_info(pk)

Look up runtime stats for a scheduled task created by invoke().

Returns None if no PeriodicTask exists with the given pk (useful for orphan-pointer detection on daemon Objects).

Parameters:

pk (int) – the PeriodicTask.pk to inspect

Return type:

dict | None

Returns:

dict with enabled, last_run_at, total_run_count, interval_seconds (or None), and task (the Celery task name) — or None if not found.

Raises:

UserError – if the current caller is not a wizard

moo.sdk.task_time_low(threshold=0.5)

Return True if the current task’s remaining time is at or below threshold seconds.

Always returns False when there is no task-time limit (e.g. in tests or interactive shells without a configured limit).

Parameters:

threshold – seconds remaining before considering time low (default 0.5)

Return type:

bool

moo.sdk.schedule_continuation(remaining_items, verb, msg=None)

Schedule a continuation task carrying the PKs of remaining_items and notify the current player.

Intended for use inside long-running verbs that iterate over many objects. The continuation verb (e.g. audit_batch, reload_batch) receives args[0] as the list of PKs and dispatches on verb_name.

Usage:

for i, item in enumerate(items):
    if task_time_low():
        schedule_continuation(items[i:], this.get_verb("audit_batch"))
        return
    # ... process item
Parameters:
  • remaining_items – iterable of Objects (or any model with .pk)

  • verb – Verb instance to invoke for the continuation

  • msg – optional override for the progress message shown to the player

moo.sdk.set_task_perms(who)

Set the task permissions to those of who for the duration of the with-block. :type who: :param who: the Object whose permissions to assume :type who: Object

moo.sdk.moo_eval(code_string)

Evaluate arbitrary Python code in the RestrictedPython sandbox.

The code runs with the same environment as verb code, with standard verb variables (this, _, context) automatically available.

Parameters:

code_string (str) – Python code to evaluate

Returns:

The result of the evaluation

moo.sdk.invoked_verb_name(default=None)

Return the verb name as the player typed it (lowercased), or default when there is no active parser context.

Verbs called via moo.sdk.invoke(), async callbacks, or test harnesses have no parser; pass the sandbox-injected verb_name as default to fall back to the verb’s defined name.

Return type:

str | None

Output and full-screen UIs

moo.sdk.write(obj, message)

Send an asynchronous message to the user.

Parameters:
  • obj (Object) – the Object to write to

  • message (Any) – any pickle-able object

moo.sdk.open_editor(obj, initial_content, callback_verb, *args, content_type='text', title=None)

Request the connected SSH client to open a full-screen text editor. When the user saves, the edited text is passed to callback_verb as args[0], followed by any extra positional arguments supplied here. If the user cancels, the callback is not invoked.

Parameters:
  • obj – the player Object whose client should open the editor

  • initial_content (str) – text to pre-populate the editor buffer

  • callback_verb – Verb to invoke with the edited text as args[0]

  • args – additional arguments forwarded to the callback verb as args[1:]

  • content_type (str) – “python”, “json”, or “text” (default); controls syntax highlighting

moo.sdk.open_paginator(obj, content, content_type='text')

Request the connected SSH client to open a full-screen read-only paginator. The user can scroll through the content and press Q to quit.

In raw-mode sessions (MUD clients, line-oriented terminals) the SSH server intercepts the paginator event and writes the content directly to the terminal instead of opening the interactive UI.

Parameters:
  • obj – the player Object whose client should open the paginator

  • content (str) – text to display

  • content_type (str) – “python”, “json”, or “text” (default); controls syntax highlighting

moo.sdk.can_open_editor()

True if the current player’s client can display an editor.

Either the player is in rich mode (prompt_toolkit TUI), or their client advertises support for the GMCP Editor package via Core.Supports.Set (the djangomoo Mudlet bridge does this; the server hands the edit off to the client’s preferred local editor over GMCP). Verbs that open the editor should gate on this rather than get_client_mode() == "raw" so bridge-equipped MUD clients are not forced onto the inline with "..." fallback.

Return type:

bool

Session settings and client capabilities

moo.sdk.get_client_mode()

Return the current player’s shell mode.

Returns "rich" (prompt_toolkit TUI, the default) or "raw" (line- based I/O for traditional MUD clients that cannot handle cursor control).

Verbs use this to short-circuit editor-opening code paths in raw mode and suggest the inline @edit ... with "..." form instead. Most verbs should call can_open_editor() instead, which also returns True for raw-mode clients that advertise GMCP Editor support (e.g. the djangomoo Mudlet bridge).

Return type:

str

moo.sdk.get_wrap_column()

Return the effective wrap column for the current player.

Reads the player’s wrap_column property. If it is "auto" (or the property is not set), returns the terminal_width session setting, falling back to 80 if the terminal width is not known.

moo.sdk.get_session_setting(key, default=None)

Get a session-specific output setting for the current player.

Session settings are stored per-user and cleared on disconnect. Used by PREFIX, SUFFIX, OUTPUTPREFIX/SUFFIX, and the a11y verb.

Checks the in-process _session_settings dict first (authoritative in the SSH server process and in tests), then falls back to the Django cache so Celery workers — which run in a separate process — can also read the value.

Parameters:
  • key – setting name (‘output_prefix’, ‘output_suffix’, ‘quiet_mode’, ‘color_system’)

  • default – value to return if setting is not found

Returns:

setting value or default

moo.sdk.set_session_setting(key, value)

Set a session-specific output setting for the current player.

Session settings are stored per-user and cleared on disconnect. Used by PREFIX, SUFFIX, OUTPUTPREFIX/SUFFIX, and the a11y verb.

Writes to the Django cache (accessible cross-process from Celery workers) and also publishes a session_setting event to the player’s Kombu queue so the SSH server’s process_messages() loop can update its own registry.

Parameters:
  • key – setting name (‘output_prefix’, ‘output_suffix’, ‘quiet_mode’, ‘color_system’)

  • value – setting value

Out-of-band MUD-client protocols

moo.sdk.send_gmcp(obj, module, data=None)

Send a GMCP event to obj’s SSH channel.

GMCP (Generic MUD Communication Protocol) is the canonical OOB channel for structured MUD events. Clients that negotiated GMCP receive IAC SB GMCP <module> <json> IAC SE; clients that did not see it as zero bytes on the wire (the SSH server skips the emit if the capability flag is false).

Example:

send_gmcp(player, "Char.Vitals", {"hp": 50, "maxhp": 100})
send_gmcp(player, "Room.Info", {"num": 12, "name": "A dim hall"})
send_gmcp(player, "Core.Ping")  # no payload
Parameters:
  • obj – the Object (player avatar) to send the GMCP event to

  • module (str) – GMCP module/package name, e.g. "Char.Vitals"

  • data – JSON-serializable value, or None for an empty event

moo.sdk.play_sound(obj, name, volume=100, priority=10)

Play a sound on obj’s client.

Prefers GMCP Client.Media.Play when the client negotiated GMCP; falls back to the inline MSP !!SOUND(...) marker if the client negotiated MSP; no-ops otherwise.

DjangoMOO does not bundle any sound assets. Sound pack authors are expected to provide filenames that their client-side pack can resolve.

Parameters:
  • obj – the player to play the sound on

  • name (str) – filename (client-resolvable), e.g. "door.wav"

  • volume (int) – 0–100

  • priority (int) – higher numbers preempt lower-priority sounds

moo.sdk.room_info_payload(room)

Build the IRE-style GMCP Room.Info payload for room.

Returns {"num", "name", "exits"} with values stringified per the Achaea/Aardwolf/Mudlet-generic-mapper convention. Exit keys are normalized to short codes (n, ne, u, …); custom-named exits ("ladder", "portal") round-trip unchanged so they still appear on the client.

DjangoMOO names cardinal exits "<direction> from <room>" — e.g. "east from grand foyer" — to keep them globally unique. We check the exit’s exact name, then its first word, then its aliases for a direction match before falling back to the raw name.

Parameters:

room – an Object representing a room

Return type:

dict

Returns:

a dict suitable for send_gmcp(player, "Room.Info", payload)

Server administration

moo.sdk.boot_player(obj)

Disconnect the given player from the MOO server.

Publishes a disconnect event to the player’s Kombu message queue, which the SSH server’s process_messages() loop picks up and exits cleanly.

Permission: the caller must be the player being booted, or a wizard.

Parameters:

obj – the player Object to disconnect

moo.sdk.server_info()

Return a dict with server version and process statistics.

Keys: version, python, pid, memory_mb (may be None on platforms where resource is unavailable).

Return type:

dict

Mail Functions

moo.sdk.send_message(sender, recipients, subject, body)

Create a message and deliver it to all recipients.

Parameters:
  • sender – the sending Object (must be a player)

  • recipients (list) – list of recipient Objects (must be players)

  • subject (str) – message subject line

  • body (str) – message body text

Returns:

plain message value for the created message

moo.sdk.get_mailbox(player, include_deleted=False)

Return player’s received messages as plain mailbox values, newest first.

Parameters:
  • player – the recipient Object

  • include_deleted (bool) – if True, include soft-deleted messages

Return type:

list

Returns:

list of MailboxEntry values

moo.sdk.get_message(player, n)

Return the nth message (1-based) in the player’s non-deleted mailbox, or None.

Parameters:
  • player – the recipient Object

  • n (int) – 1-based message index

Returns:

MailboxEntry or None

moo.sdk.mark_read(player, n)

Mark message n as read. Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.delete_message(player, n)

Soft-delete message n. Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.undelete_message(player, n)

Restore the nth deleted message (1-based among deleted-only list). Returns True if successful, False if n is out of range.

Return type:

bool

moo.sdk.count_unread(player)

Return the count of unread, non-deleted messages for the player.

Return type:

int

moo.sdk.get_mail_stats(player)

Return a dict with total, unread, and deleted message counts for the player. All counts are computed in two queries.

Return type:

dict

Accounts and identity

The Player row is the durable account behind an avatar; these resolve between an avatar and its account.

moo.sdk.account_for(avatar_obj)

Return the Player account for an avatar Object, or None.

Parameters:

avatar_obj – a player avatar Object

Returns:

the owning Player account, or None if the object is not an avatar

moo.sdk.avatars_of(account)

Return the list of avatar Objects an account owns.

Today an account carries a single avatar; this returns it as a one-element list (empty if the avatar has been recycled) so callers are already shaped for the later multi-avatar model.

Parameters:

account – a Player account

Returns:

list of avatar Objects

moo.sdk.account_id_for(avatar_obj)

Return the stable account id for an avatar, or None.

The cheap form used on the provenance hot path (E): one indexed lookup, no avatar materialization beyond the id.

Parameters:

avatar_obj – a player avatar Object

Returns:

the account pk, or None

moo.sdk.current_account()

Return the account for the current context.player, or None.

Provenance and tagged output

Every published message carries a server-computed provenance triple, recorded always; sanctioned output forms carry a structural kind tag the client renders so a user line cannot present as a system line or another actor.

moo.sdk.notify(obj, message, kind='text')

Send a structurally-tagged message to a player.

The tagged-output counterpart to moo.sdk.write(): the kind is applied server-side and carried in the envelope so it cannot be spoofed by embedding markup in message. Like write, this is a privileged primitive (wizard-owned verbs only); the sanctioned say/emote/ system verbs route through it. The system kind additionally requires a wizard initiator, so an ordinary user’s verb cannot wear it.

Parameters:
  • obj – the player avatar Object to write to

  • message – the payload (typically a string)

  • kind – one of OUTPUT_KINDS

Raises:

UserError – on a bad kind, a non-wizard caller, or a non-wizard initiator attempting a privileged kind

moo.sdk.current_provenance()

Return the always-on provenance triple for the running task.

See moo.core.current_provenance(). Re-exported here so verb authors import it from moo.sdk like the rest of the API.

moo.sdk.capture_provenance_stack()

Return the full caller stack as a list of provenance frames.

The expensive form, for a report or audit only — the hot path uses the single-frame current_provenance(). Each frame is {"origin", "this", "verb", "owner"} with object pks.

Returns:

list of frame dicts, outermost first

moo.sdk.resolve_provenance_account(provenance)

Resolve a provenance triple’s owner object to its account id.

The report/audit-time step that turns the cheap object-pk record into an account-keyed one (G), so moderation keys to the human, not a discardable avatar.

Parameters:

provenance – a triple from current_provenance(), or None

Returns:

the owning Player account id, or None

Flood limiting

moo.sdk.broadcast_allowed(account_id)

Charge one broadcast line to account_id and report whether it is allowed.

Returns True (and does not charge) when the limit is disabled or no account is known. Otherwise increments the account’s fixed-window counter and returns False once the budget is exceeded.

Parameters:

account_id – the initiating Player account id

Return type:

bool

Returns:

True if the line may be sent, False if the account is over budget

moo.sdk.broadcast_limit()

The effective per-window broadcast line budget (0 disables).

Return type:

int

moo.sdk.broadcast_window()

The effective fixed-window length in seconds.

Return type:

int

Moderation: suspend and ban

Staff-gated sanctions keyed to the durable account; neither can target staff.

moo.sdk.suspend(account, duration=None, hours=None, reason='')

Suspend an account, blocking login until the deadline elapses.

Parameters:
  • account – the Player account to suspend

  • duration – a datetime.timedelta, or None for open-ended

  • hours – convenience for verb authors (who cannot import datetime in the sandbox); converted to a duration. Ignored if duration is given.

  • reason – optional free-text reason (recorded in the audit log)

Raises:
  • PermissionError – if the caller is not staff

  • UserError – if the target is missing or is a staff account

moo.sdk.unsuspend(account, reason='')

Lift a suspension, returning the account to active.

Parameters:

account – the Player account to reinstate

Raises:

PermissionError – if the caller is not staff

moo.sdk.ban(account, reason='')

Ban an account and blacklist its durable identity / last site.

The status flag stops this account; the Blacklist row stops the same human from re-registering under a fresh account.

Parameters:

account – the Player account to ban

Raises:
  • PermissionError – if the caller is not staff

  • UserError – if the target is missing or is a staff account

moo.sdk.is_blacklisted(identity, site=None)

True if a durable identity is banned (globally, or on the given site).

Parameters:
  • identity – the durable identifier to check

  • site – optional Site to scope the check to

Return type:

bool

moo.sdk.account_login_blocked(account)

Return a reason string if this account may not log in, else None.

The single chokepoint the SSH login path consults — composes the account’s own status (suspended/banned) with the identity blacklist.

Parameters:

account – the Player account attempting to log in

Onboarding: guests and registration

moo.sdk.provision_guest(name, site=None, user=None)

Create a non-persistent guest avatar + account.

The avatar is parented to $guest and given its own ownership_quota of 0 so moo.sdk.create() refuses (the class default is not enough — create reads the quota with recurse=False).

Parameters:
  • name – display name for the guest avatar

  • site – the Site to provision on (defaults to the active site)

  • user – an optional Django User to tie the guest to (usually None)

Returns:

(account, avatar)

moo.sdk.remove_guest(account)

Reap a guest account and its avatar (the non-persistence side).

No-op for a non-guest account, so it is safe to call unconditionally on disconnect.

Parameters:

account – the guest Player account to remove

Returns:

True if a guest was removed

moo.sdk.is_guest(account)

True if an account is in the guest tier.

Return type:

bool

moo.sdk.register(account, identity)

Bind a durable identity to an account and promote it out of guest.

Runs the pluggable verifier, refuses a banned identity (H), records the identity, and lifts a guest account to active.

Parameters:
  • account – the Player account to register

  • identity – the candidate durable identifier

Raises:

UserError – on a missing account, a rejected identity, a banned one, or an identity already claimed on this site

moo.sdk.require_registered(account)

Raise unless an account has bound a durable identity (the build gate).

Build-granting code calls this so build rights require accountability.

Parameters:

account – the Player account to check

Raises:

UserError – if the account is unregistered

Audit log

moo.sdk.record_action(action, target=None, detail='', actor=None, force=False)

Append one consequential action to the audit log.

Parameters:
  • action – one of AuditLog’s action constants

  • target – the affected Object (or None); recorded by pk and a name snapshot so the row survives the target’s deletion

  • detail – optional free-text / serialized context

  • actor – the acting Player account; defaults to the current account

  • force – record even when no human actor is resolvable (used by staff-initiated sanctions that must always be logged)

Returns:

the created AuditLog row, or None if skipped

moo.sdk.query_audit(actor=None, action=None, target=None, limit=50)

Return recent audit rows, most recent first (staff-only at the verb layer).

Parameters:
  • actor – filter to a Player account

  • action – filter to one action kind

  • target – filter to an affected Object (by pk)

  • limit – maximum rows to return

Returns:

list of AuditLog rows

Non-destructive recovery

@recycle is now a reversible soft-recycle; @destroy is the permanent delete. Soft-recycled objects are hidden from the site-scoped manager but keep their id and inbound references.

moo.sdk.soft_recycle(obj)

Soft-delete an object: hide it, but keep its id and inbound refs.

Refunds the owner’s quota immediately (so the builder can build again) and moves the object out of the world. Idempotent.

Parameters:

obj – the Object to recycle

Returns:

the recycled Object

moo.sdk.restore(obj, location=None)

Restore a soft-recycled object, re-consuming a quota slot.

Parameters:
  • obj – the recycled Object

  • location – where to place it (defaults to the owner’s location)

Returns:

the restored Object

moo.sdk.destroy(obj)

Hard-delete an object permanently (the irreversible path).

Parameters:

obj – the Object to destroy

moo.sdk.get_recycled(owner=None)

List soft-recycled objects (optionally for one owner) for recovery.

Uses global_objects because the site-scoped default manager hides recycled rows by design.

Parameters:

owner – optional owner Object to filter by

Returns:

list of recycled Objects

moo.sdk.sweep_recycled(older_than_days=30)

Hard-delete objects left recycled beyond the retention window.

The reaper’s final step. Returns the number purged.

Parameters:

older_than_days – retention window in days

Returns:

count of objects hard-deleted

Escape guarantee and connectivity

moo.sdk.guaranteed_moveto(obj, dest)

Move obj to dest, bypassing the destination’s accept/locks.

The un-trappable panic move (used by home): even a misconfigured or hostile destination cannot refuse it. enterfunc/exitfunc still run.

Parameters:
  • obj – the Object to move (typically a player)

  • dest – the destination Object

Returns:

the moved Object

moo.sdk.send_home(player)

Send a player home, guaranteed.

Tries the player’s home (or $player_start if unset) and forces the move so it cannot be blocked. This is the engine-level escape the home verb delegates to.

Parameters:

player – the player Object

Returns:

the destination the player landed in

moo.sdk.check_room_connectivity(room)

Report whether a room is a potential trap.

A build-completion guard: flags a room with no way out, and any exit whose destination has no exit leading back (a one-way trip).

Parameters:

room – the room Object to check

Returns:

{"has_exit": bool, "one_way_exits": [names], "issues": [str]}

Indexed external keys

moo.sdk.resolve_by_key(namespace, key)

Resolve an Object by its indexed external key.

O(1) via the unique (namespace, key) index — the fast path for data-generated worlds that resolve by a stable external id rather than by a full Property.value scan.

Parameters:
  • namespace – the key namespace (e.g. "zone_slug")

  • key – the external key value

Returns:

the mapped Object, or None

moo.sdk.get_or_create_by_key(namespace, key, **kwargs)

Idempotently resolve-or-create an Object keyed by an external id.

The common bootstrap pattern made fast and re-runnable: a second run with the same (namespace, key) returns the existing Object in one indexed query, never a duplicate. On first sight it creates the Object (via the standard create(), so quota/ownership/initialize all apply) and records the mapping.

Parameters:
  • namespace – the key namespace

  • key – the external key value

  • name – the Object name to create (defaults to key)

  • kwargs – forwarded to create() (parents, owner, …)

Returns:

(object, created)