Merge pull request #3346 from volundmush/cmdhandler_refactor

Streamlined cmdhandler to support more extension.
This commit is contained in:
Griatch 2023-12-10 17:34:01 +01:00 committed by GitHub
commit 3062a31363
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 289 additions and 111 deletions

View file

@ -272,6 +272,12 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
""" """
# Determines which order command sets begin to be assembled from.
# Accounts are usually second.
cmdset_provider_order = 50
cmdset_provider_error_order = 0
cmdset_provider_type = "account"
objects = AccountManager() objects = AccountManager()
# Used by account.create_character() to choose default typeclass for characters. # Used by account.create_character() to choose default typeclass for characters.
@ -309,6 +315,19 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
def characters(self): def characters(self):
return CharactersHandler(self) return CharactersHandler(self)
def get_cmdset_providers(self) -> dict[str, "CmdSetProvider"]:
"""
Overrideable method which returns a dictionary of every kind of object which
has a cmdsethandler linked to this Account, and should participate in cmdset
merging.
Accounts have no way of being aware of anything besides themselves, unfortunately.
Returns:
dict[str, CmdSetProvider]: The CmdSetProviders linked to this Object.
"""
return {"account": self}
def at_post_add_character(self, character: "DefaultCharacter"): def at_post_add_character(self, character: "DefaultCharacter"):
""" """
Called after a character is added to this account's list of playable characters. Called after a character is added to this account's list of playable characters.
@ -1514,17 +1533,35 @@ class DefaultAccount(AccountDB, metaclass=TypeclassBase):
def at_cmdset_get(self, **kwargs): def at_cmdset_get(self, **kwargs):
""" """
Called just *before* cmdsets on this account are requested by Called just before cmdsets on this object are requested by the
the command handler. The cmdsets are available as command handler. If changes need to be done on the fly to the
`self.cmdset`. If changes need to be done on the fly to the
cmdset before passing them on to the cmdhandler, this is the cmdset before passing them on to the cmdhandler, this is the
place to do it. This is called also if the account currently place to do it. This is called also if the object currently
have no cmdsets. kwargs are usually not used unless the have no cmdsets.
cmdset is generated dynamically.
Keyword Args:
caller (obj): The object requesting the cmdsets.
current (cmdset): The current merged cmdset.
force_init (bool): If `True`, force a re-build of the cmdset. (seems unused)
**kwargs: Arbitrary input for overloads.
""" """
pass pass
def get_cmdsets(self, caller, current, **kwargs):
"""
Called by the CommandHandler to get a list of cmdsets to merge.
Args:
caller (obj): The object requesting the cmdsets.
current (cmdset): The current merged cmdset.
**kwargs: Arbitrary input for overloads.
Returns:
tuple: A tuple of (current, cmdsets), which is probably self.cmdset.current and self.cmdset.cmdset_stack
"""
return self.cmdset.current, list(self.cmdset.cmdset_stack)
def at_first_login(self, **kwargs): def at_first_login(self, **kwargs):
""" """
Called the very first time this account logs into the game. Called the very first time this account logs into the game.

View file

@ -41,6 +41,7 @@ from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.internet.task import deferLater from twisted.internet.task import deferLater
from evennia.commands.command import InterruptCommand from evennia.commands.command import InterruptCommand
from evennia.commands.cmdset import CmdSet
from evennia.utils import logger, utils from evennia.utils import logger, utils
from evennia.utils.utils import string_suggestions from evennia.utils.utils import string_suggestions
@ -150,9 +151,13 @@ _GET_INPUT = None
# helper functions # helper functions
def err_helper(raw_string, cmdid=None):
if cmdid is not None:
return raw_string, {"cmdid": cmdid}
return raw_string
def _msg_err(receiver, stringtuple): def _msg_err(receiver, stringtuple, cmdid=None):
""" """
Helper function for returning an error to the caller. Helper function for returning an error to the caller.
@ -168,19 +173,16 @@ def _msg_err(receiver, stringtuple):
tracestring = format_exc() tracestring = format_exc()
logger.log_trace() logger.log_trace()
if _IN_GAME_ERRORS: if _IN_GAME_ERRORS:
receiver.msg( out = string.format(
string.format( traceback=tracestring, errmsg=stringtuple[0].strip(), timestamp=timestamp
traceback=tracestring, errmsg=stringtuple[0].strip(), timestamp=timestamp ).strip()
).strip()
)
else: else:
receiver.msg( out = string.format(
string.format( traceback=tracestring.splitlines()[-1],
traceback=tracestring.splitlines()[-1], errmsg=stringtuple[1].strip(),
errmsg=stringtuple[1].strip(), timestamp=timestamp,
timestamp=timestamp, ).strip()
).strip() receiver.msg(err_helper(out, cmdid=cmdid))
)
def _process_input(caller, prompt, result, cmd, generator): def _process_input(caller, prompt, result, cmd, generator):
@ -280,10 +282,39 @@ class ErrorReported(Exception):
# Helper function # Helper function
def generate_cmdset_providers(called_by, session=None):
cmdset_providers = dict()
cmdset_providers.update(called_by.get_cmdset_providers())
if session and session is not called_by:
cmdset_providers.update(session.get_cmdset_providers())
cmdset_providers_list = list(cmdset_providers.values())
cmdset_providers_list.sort(key=lambda x: getattr(x, "cmdset_provider_order", 0))
# sort the dictionary by priority. This can be done because Python now cares about dictionary insert order.
cmdset_providers = {c.cmdset_provider_type: c for c in cmdset_providers_list}
if not cmdset_providers:
raise RuntimeError("cmdhandler: no command objects found.")
# the caller will be the one to receive messages and excert its permissions.
# we assign the caller with preference 'bottom up'
caller = cmdset_providers_list[-1]
cmdset_providers_errors_list = sorted(
cmdset_providers_list, key=lambda x: getattr(x, "cmdset_provider_error_order", 0)
)
# The error_to is the default recipient for errors. Tries to make sure an account
# does not get spammed for errors while preserving character mirroring.
error_to = cmdset_providers_errors_list[-1]
return cmdset_providers, cmdset_providers_list, cmdset_providers_errors_list, caller, error_to
@inlineCallbacks @inlineCallbacks
def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string): def get_and_merge_cmdsets(
caller, cmdset_providers, callertype, raw_string, report_to=None, cmdid=None
):
""" """
Gather all relevant cmdsets and merge them. Gather all relevant cmdsets and merge them.
@ -293,12 +324,11 @@ def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string)
when the user is not logged in, this will be a Session, when being OOC when the user is not logged in, this will be a Session, when being OOC
it will be an Account and when puppeting an object this will (often) be it will be an Account and when puppeting an object this will (often) be
a Character Object. In the end it depends on where the cmdset is stored. a Character Object. In the end it depends on where the cmdset is stored.
session (Session or None): The Session associated with caller, if any. cmdset_providers (list): A list of sorted objects which provide cmdsets.
account (Account or None): The calling Account associated with caller, if any.
obj (Object or None): The Object associated with caller, if any.
callertype (str): This identifies caller as either "account", "object" or "session" callertype (str): This identifies caller as either "account", "object" or "session"
to avoid having to do this check internally. to avoid having to do this check internally.
raw_string (str): The input string. This is only used for error reporting. raw_string (str): The input string. This is only used for error reporting.
report_to (Object, optional): If given, this object will receive error messages
Returns: Returns:
cmdset (Deferred): This deferred fires with the merged cmdset cmdset (Deferred): This deferred fires with the merged cmdset
@ -366,81 +396,52 @@ def get_and_merge_cmdsets(caller, session, account, obj, callertype, raw_string)
raise ErrorReported(raw_string) raise ErrorReported(raw_string)
@inlineCallbacks @inlineCallbacks
def _get_cmdsets(obj): def _get_cmdsets(obj, current):
""" """
Helper method; Get cmdset while making sure to trigger all Helper method; Get cmdset while making sure to trigger all
hooks safely. Returns the stack and the valid options. hooks safely. Returns the stack and the valid options.
""" """
try: try:
yield obj.at_cmdset_get() yield obj.at_cmdset_get(caller=caller, current=current)
except Exception: except Exception:
_msg_err(caller, _ERROR_CMDSETS) _msg_err(caller, _ERROR_CMDSETS)
raise ErrorReported(raw_string) raise ErrorReported(raw_string)
try: try:
returnValue((obj.cmdset.current, list(obj.cmdset.cmdset_stack))) returnValue(obj.get_cmdsets(caller=caller, current=current))
except AttributeError: except AttributeError:
returnValue(((None, None, None), [])) returnValue(((None, None, None), []))
local_obj_cmdsets = [] local_obj_cmdsets = []
if callertype == "session":
# we are calling the command from the session level current_cmdset = CmdSet()
report_to = session object_cmdsets = list()
current, cmdsets = yield _get_cmdsets(session) for cmdobj in cmdset_providers:
if account: # this automatically implies logged-in current, cur_cmdsets = yield _get_cmdsets(cmdobj, current_cmdset)
pcurrent, account_cmdsets = yield _get_cmdsets(account) if current:
cmdsets += account_cmdsets current_cmdset = current_cmdset + current
current = current + pcurrent if cur_cmdsets:
if obj: object_cmdsets += cur_cmdsets
ocurrent, obj_cmdsets = yield _get_cmdsets(obj) match cmdobj.cmdset_provider_type:
current = current + ocurrent case "object":
cmdsets += obj_cmdsets
if not current.no_objs: if not current.no_objs:
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj) local_obj_cmdsets = yield _get_local_obj_cmdsets(cmdobj)
if current.no_exits: if current.no_exits:
# filter out all exits # filter out all exits
local_obj_cmdsets = [ local_obj_cmdsets = [
cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet" cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"
] ]
cmdsets += local_obj_cmdsets object_cmdsets += local_obj_cmdsets
elif callertype == "account":
# we are calling the command from the account level
report_to = account
current, cmdsets = yield _get_cmdsets(account)
if obj:
ocurrent, obj_cmdsets = yield _get_cmdsets(obj)
current = current + ocurrent
cmdsets += obj_cmdsets
if not current.no_objs:
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj)
if current.no_exits:
# filter out all exits
local_obj_cmdsets = [
cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"
]
cmdsets += local_obj_cmdsets
elif callertype == "object":
# we are calling the command from the object level
report_to = obj
current, cmdsets = yield _get_cmdsets(obj)
if not current.no_objs:
local_obj_cmdsets = yield _get_local_obj_cmdsets(obj)
if current.no_exits:
# filter out all exits
local_obj_cmdsets = [
cmdset for cmdset in local_obj_cmdsets if cmdset.key != "ExitCmdSet"
]
cmdsets += yield local_obj_cmdsets
else:
raise Exception("get_and_merge_cmdsets: callertype %s is not valid." % callertype)
# weed out all non-found sets # weed out all non-found sets
cmdsets = yield [cmdset for cmdset in cmdsets if cmdset and cmdset.key != "_EMPTY_CMDSET"] cmdsets = yield [
cmdset for cmdset in object_cmdsets if cmdset and cmdset.key != "_EMPTY_CMDSET"
]
# report cmdset errors to user (these should already have been logged) # report cmdset errors to user (these should already have been logged)
yield [ yield [
report_to.msg(cmdset.errmessage) for cmdset in cmdsets if cmdset.key == "_CMDSET_ERROR" report_to.msg(err_helper(cmdset.errmessage, cmdid=cmdid))
for cmdset in cmdsets
if cmdset.key == "_CMDSET_ERROR"
] ]
if cmdsets: if cmdsets:
@ -550,9 +551,10 @@ def cmdhandler(
default Evennia. default Evennia.
""" """
cmdid = kwargs.get("cmdid", None)
@inlineCallbacks @inlineCallbacks
def _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, account): def _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, account, cmdset_providers):
""" """
Helper function: This initializes and runs the Command Helper function: This initializes and runs the Command
instance once the parser has identified it as either a normal instance once the parser has identified it as either a normal
@ -568,6 +570,7 @@ def cmdhandler(
cmdset (CmdSet): Command sert the command belongs to (if any).. cmdset (CmdSet): Command sert the command belongs to (if any)..
session (Session): Session of caller (if any). session (Session): Session of caller (if any).
account (Account): Account of caller (if any). account (Account): Account of caller (if any).
cmdset_providers (dict): Dictionary of all cmdset-providing objects.
Returns: Returns:
deferred (Deferred): this will fire with the return of the deferred (Deferred): this will fire with the return of the
@ -586,6 +589,7 @@ def cmdhandler(
cmd.cmdstring = cmdname # deprecated cmd.cmdstring = cmdname # deprecated
cmd.args = args cmd.args = args
cmd.cmdset = cmdset cmd.cmdset = cmdset
cmd.cmdset_providers = cmdset_providers.copy()
cmd.session = session cmd.session = session
cmd.account = account cmd.account = account
cmd.raw_string = unformatted_raw_string cmd.raw_string = unformatted_raw_string
@ -655,25 +659,15 @@ def cmdhandler(
finally: finally:
_COMMAND_NESTING[called_by] -= 1 _COMMAND_NESTING[called_by] -= 1
session, account, obj = session, None, None (
if callertype == "session": cmdset_providers,
session = called_by cmdset_providers_list,
account = session.account cmdset_providers_list_error,
obj = session.puppet caller,
elif callertype == "account": error_to,
account = called_by ) = generate_cmdset_providers(called_by, session=session)
if session:
obj = yield session.puppet account = cmdset_providers.get("account", None)
elif callertype == "object":
obj = called_by
else:
raise RuntimeError("cmdhandler: callertype %s is not valid." % callertype)
# the caller will be the one to receive messages and excert its permissions.
# we assign the caller with preference 'bottom up'
caller = obj or account or session
# The error_to is the default recipient for errors. Tries to make sure an account
# does not get spammed for errors while preserving character mirroring.
error_to = obj or session or account
try: # catch bugs in cmdhandler itself try: # catch bugs in cmdhandler itself
try: # catch special-type commands try: # catch special-type commands
@ -691,7 +685,7 @@ def cmdhandler(
else: else:
# no explicit cmdobject given, figure it out # no explicit cmdobject given, figure it out
cmdset = yield get_and_merge_cmdsets( cmdset = yield get_and_merge_cmdsets(
caller, session, account, obj, callertype, raw_string caller, cmdset_providers_list, callertype, raw_string, cmdid=cmdid
) )
if not cmdset: if not cmdset:
# this is bad and shouldn't happen. # this is bad and shouldn't happen.
@ -764,7 +758,9 @@ def cmdhandler(
cmd = copy(cmd) cmd = copy(cmd)
# A normal command. # A normal command.
ret = yield _run_command(cmd, cmdname, args, raw_cmdname, cmdset, session, account) ret = yield _run_command(
cmd, cmdname, args, raw_cmdname, cmdset, session, account, cmdset_providers
)
returnValue(ret) returnValue(ret)
except ErrorReported as exc: except ErrorReported as exc:
@ -780,17 +776,24 @@ def cmdhandler(
if syscmd: if syscmd:
ret = yield _run_command( ret = yield _run_command(
syscmd, syscmd.key, sysarg, unformatted_raw_string, cmdset, session, account syscmd,
syscmd.key,
sysarg,
unformatted_raw_string,
cmdset,
session,
account,
cmdset_providers,
) )
returnValue(ret) returnValue(ret)
elif sysarg: elif sysarg:
# return system arg # return system arg
error_to.msg(exc.sysarg) error_to.msg(err_helper(exc.sysarg, cmdid=cmdid))
except NoCmdSets: except NoCmdSets:
# Critical error. # Critical error.
logger.log_err("No cmdsets found: %s" % caller) logger.log_err("No cmdsets found: %s" % caller)
error_to.msg(_ERROR_NOCMDSETS) error_to.msg(err_helper(_ERROR_NOCMDSETS, cmdid=cmdid))
except Exception: except Exception:
# We should not end up here. If we do, it's a programming bug. # We should not end up here. If we do, it's a programming bug.

View file

@ -10,7 +10,7 @@ from django.db.models import Max, Min, Q
import evennia import evennia
from evennia import InterruptCommand from evennia import InterruptCommand
from evennia.commands.cmdhandler import get_and_merge_cmdsets from evennia.commands.cmdhandler import get_and_merge_cmdsets, generate_cmdset_providers
from evennia.locks.lockhandler import LockException from evennia.locks.lockhandler import LockException
from evennia.objects.models import ObjectDB from evennia.objects.models import ObjectDB
from evennia.prototypes import menus as olc_menus from evennia.prototypes import menus as olc_menus
@ -3122,8 +3122,16 @@ class CmdExamine(ObjManipCommand):
def _get_cmdset_callback(current_cmdset): def _get_cmdset_callback(current_cmdset):
self.msg(self.format_output(obj, current_cmdset).strip()) self.msg(self.format_output(obj, current_cmdset).strip())
(
command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = generate_cmdset_providers(obj, session=session)
get_and_merge_cmdsets( get_and_merge_cmdsets(
obj, session, account, objct, mergemode, self.raw_string obj, command_objects_list, mergemode, self.raw_string, error_to
).addCallback(_get_cmdset_callback) ).addCallback(_get_cmdset_callback)
else: else:

View file

@ -1020,8 +1020,16 @@ class TestGetAndMergeCmdSets(TwistedTestCase, BaseEvenniaTest):
a = self.cmdset_a a = self.cmdset_a
a.no_channels = True a.no_channels = True
self.set_cmdsets(self.session, a) self.set_cmdsets(self.session, a)
(
command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = cmdhandler.generate_cmdset_providers(self.session)
deferred = cmdhandler.get_and_merge_cmdsets( deferred = cmdhandler.get_and_merge_cmdsets(
self.session, self.session, None, None, "session", "" self.session, [self.session], "session", "", error_to
) )
def _callback(cmdset): def _callback(cmdset):
@ -1036,8 +1044,16 @@ class TestGetAndMergeCmdSets(TwistedTestCase, BaseEvenniaTest):
a = self.cmdset_a a = self.cmdset_a
a.no_channels = True a.no_channels = True
self.set_cmdsets(self.account, a) self.set_cmdsets(self.account, a)
(
command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = cmdhandler.generate_cmdset_providers(self.account)
deferred = cmdhandler.get_and_merge_cmdsets( deferred = cmdhandler.get_and_merge_cmdsets(
self.account, None, self.account, None, "account", "" self.account, command_objects_list, "account", "", error_to
) )
# get_and_merge_cmdsets converts to lower-case internally. # get_and_merge_cmdsets converts to lower-case internally.
@ -1053,7 +1069,17 @@ class TestGetAndMergeCmdSets(TwistedTestCase, BaseEvenniaTest):
def test_from_object(self): def test_from_object(self):
self.set_cmdsets(self.obj1, self.cmdset_a) self.set_cmdsets(self.obj1, self.cmdset_a)
deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") (
command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = cmdhandler.generate_cmdset_providers(self.obj1)
deferred = cmdhandler.get_and_merge_cmdsets(
self.obj1, command_objects_list, "object", "", error_to
)
# get_and_merge_cmdsets converts to lower-case internally. # get_and_merge_cmdsets converts to lower-case internally.
def _callback(cmdset): def _callback(cmdset):
@ -1069,8 +1095,16 @@ class TestGetAndMergeCmdSets(TwistedTestCase, BaseEvenniaTest):
a.no_exits = True a.no_exits = True
a.no_channels = True a.no_channels = True
self.set_cmdsets(self.obj1, a, b, c, d) self.set_cmdsets(self.obj1, a, b, c, d)
(
deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = cmdhandler.generate_cmdset_providers(self.obj1)
deferred = cmdhandler.get_and_merge_cmdsets(
self.obj1, command_objects_list, "object", "", error_to
)
def _callback(cmdset): def _callback(cmdset):
self.assertTrue(cmdset.no_exits) self.assertTrue(cmdset.no_exits)
@ -1087,7 +1121,17 @@ class TestGetAndMergeCmdSets(TwistedTestCase, BaseEvenniaTest):
b.duplicates = True b.duplicates = True
d.duplicates = True d.duplicates = True
self.set_cmdsets(self.obj1, a, b, c, d) self.set_cmdsets(self.obj1, a, b, c, d)
deferred = cmdhandler.get_and_merge_cmdsets(self.obj1, None, None, self.obj1, "object", "") (
command_objects,
command_objects_list,
command_objects_list_error,
caller,
error_to,
) = cmdhandler.generate_cmdset_providers(self.obj1, session=None)
deferred = cmdhandler.get_and_merge_cmdsets(
self.obj1, command_objects_list, "object", "", error_to
)
def _callback(cmdset): def _callback(cmdset):
self.assertEqual(len(cmdset.commands), 9) self.assertEqual(len(cmdset.commands), 9)

View file

@ -204,6 +204,12 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
""" """
# Determines which order command sets begin to be assembled from.
# Objects are usually third.
cmdset_provider_order = 100
cmdset_provider_error_order = 100
cmdset_provider_type = "object"
# Used for sorting / filtering in inventories / room contents. # Used for sorting / filtering in inventories / room contents.
_content_types = ("object",) _content_types = ("object",)
@ -256,6 +262,22 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
""" """
return self.sessions.count() return self.sessions.count()
def get_cmdset_providers(self) -> dict[str, "CmdSetProvider"]:
"""
Overrideable method which returns a dictionary of every kind of object which
has a cmdsethandler linked to this Object, and should participate in cmdset
merging.
Objects might be aware of an Account. Otherwise, just themselves, by default.
Returns:
dict[str, CmdSetProvider]: The CmdSetProviders linked to this Object.
"""
out = {"object": self}
if self.account:
out["account"] = self.account
return out
@property @property
def is_superuser(self): def is_superuser(self):
""" """
@ -1601,12 +1623,28 @@ class DefaultObject(ObjectDB, metaclass=TypeclassBase):
have no cmdsets. have no cmdsets.
Keyword Args: Keyword Args:
caller (Session, Object or Account): The caller requesting caller (obj): The object requesting the cmdsets.
this cmdset. current (cmdset): The current merged cmdset.
force_init (bool): If `True`, force a re-build of the cmdset. (seems unused)
**kwargs: Arbitrary input for overloads.
""" """
pass pass
def get_cmdsets(self, caller, current, **kwargs):
"""
Called by the CommandHandler to get a list of cmdsets to merge.
Args:
caller (obj): The object requesting the cmdsets.
current (cmdset): The current merged cmdset.
**kwargs: Arbitrary input for overloads.
Returns:
tuple: A tuple of (current, cmdsets), which is probably self.cmdset.current and self.cmdset.cmdset_stack
"""
return self.cmdset.current, list(self.cmdset.cmdset_stack)
def at_pre_puppet(self, account, session=None, **kwargs): def at_pre_puppet(self, account, session=None, **kwargs):
""" """
Called just before an Account connects to this object to puppet Called just before an Account connects to this object to puppet

View file

@ -46,6 +46,12 @@ class ServerSession(_BASE_SESSION_CLASS):
""" """
# Determines which order command sets begin to be assembled from.
# Sessions are usually first.
cmdset_provider_order = 0
cmdset_provider_error_order = 50
cmdset_provider_type = "session"
def __init__(self): def __init__(self):
""" """
Initiate to avoid AttributeErrors down the line Initiate to avoid AttributeErrors down the line
@ -64,6 +70,25 @@ class ServerSession(_BASE_SESSION_CLASS):
cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set) cmdset_storage = property(__cmdset_storage_get, __cmdset_storage_set)
def get_cmdset_providers(self) -> dict[str, "CmdSetProvider"]:
"""
Overrideable method which returns a dictionary of every kind of object which
has a cmdsethandler linked to this ServerSession, and should participate in cmdset
merging.
In all normal cases, that's the Session itself, and possibly an account and puppeted
object.
Returns:
dict[str, CmdSetProvider]: The CmdSetProviders linked to this Object.
"""
out = {"session": self}
if self.account:
out["account"] = self.account
if self.puppet:
out["object"] = self.puppet
return out
@property @property
def id(self): def id(self):
return self.sessid return self.sessid
@ -376,12 +401,35 @@ class ServerSession(_BASE_SESSION_CLASS):
def at_cmdset_get(self, **kwargs): def at_cmdset_get(self, **kwargs):
""" """
A dummy hook all objects with cmdsets need to have Called just before cmdsets on this object are requested by the
command handler. If changes need to be done on the fly to the
cmdset before passing them on to the cmdhandler, this is the
place to do it. This is called also if the object currently
have no cmdsets.
Keyword Args:
caller (obj): The object requesting the cmdsets.
current (cmdset): The current merged cmdset.
force_init (bool): If `True`, force a re-build of the cmdset. (seems unused)
**kwargs: Arbitrary input for overloads.
""" """
pass pass
def get_cmdsets(self, caller, current, **kwargs):
"""
Called by the CommandHandler to get a list of cmdsets to merge.
Args:
caller (obj): The object requesting the cmdsets.
current (cmdset): The current merged cmdset.
**kwargs: Arbitrary input for overloads.
Returns:
tuple: A tuple of (current, cmdsets), which is probably self.cmdset.current and self.cmdset.cmdset_stack
"""
return self.cmdset.current, list(self.cmdset.cmdset_stack)
# Mock db/ndb properties for allowing easy storage on the session # Mock db/ndb properties for allowing easy storage on the session
# (note that no databse is involved at all here. session.db.attr = # (note that no databse is involved at all here. session.db.attr =
# value just saves a normal property in memory, just like ndb). # value just saves a normal property in memory, just like ndb).