Refactor spawner to use prototype instead of metaprots

This commit is contained in:
Griatch 2018-04-18 23:50:12 +02:00
parent 9df0096640
commit c4d20c359b

View file

@ -122,6 +122,8 @@ from evennia.utils.ansi import strip_ansi
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination") _CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
_NON_CREATE_KWARGS = _CREATE_OBJECT_KWARGS + _PROTOTYPE_META_NAMES
_MODULE_PROTOTYPES = {} _MODULE_PROTOTYPES = {}
_MODULE_PROTOTYPE_MODULES = {} _MODULE_PROTOTYPE_MODULES = {}
_MENU_CROP_WIDTH = 15 _MENU_CROP_WIDTH = 15
@ -135,24 +137,24 @@ _MENU_ATTR_LITERAL_EVAL_ERROR = (
class PermissionError(RuntimeError): class PermissionError(RuntimeError):
pass pass
# storage of meta info about the prototype
MetaProto = namedtuple('MetaProto', ['key', 'desc', 'locks', 'tags', 'prototype'])
for mod in settings.PROTOTYPE_MODULES: for mod in settings.PROTOTYPE_MODULES:
# to remove a default prototype, override it with an empty dict. # to remove a default prototype, override it with an empty dict.
# internally we store as (key, desc, locks, tags, prototype_dict) # internally we store as (key, desc, locks, tags, prototype_dict)
prots = [(key, prot) for key, prot in all_from_module(mod).items() prots = [(prototype_key, prot) for prototype_key, prot in all_from_module(mod).items()
if prot and isinstance(prot, dict)] if prot and isinstance(prot, dict)]
_MODULE_PROTOTYPES.update( # assign module path to each prototype_key for easy reference
{key.lower(): MetaProto(
key.lower(),
prot['prototype_desc'] if 'prototype_desc' in prot else mod,
prot['prototype_lock'] if 'prototype_lock' in prot else "use:all()",
set(make_iter(
prot['prototype_tags']) if 'prototype_tags' in prot else ["base-prototype"]),
prot)
for key, prot in prots})
_MODULE_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots}) _MODULE_PROTOTYPE_MODULES.update({tup[0]: mod for tup in prots})
# make sure the prototype contains all meta info
for prototype_key, prot in prots:
prot.update({
"prototype_key": prototype_key.lower(),
"prototype_desc": prot['prototype_desc'] if 'prototype_desc' in prot else mod,
"prototype_locks": prot['prototype_locks'] if 'prototype_locks' in prot else "use:all()",
"prototype_tags": set(make_iter(prot['prototype_tags'])
if 'prototype_tags' in prot else ["base-prototype"])})
_MODULE_PROTOTYPES.update(prot)
# Prototype storage mechanisms # Prototype storage mechanisms
@ -162,24 +164,11 @@ class DbPrototype(DefaultScript):
This stores a single prototype This stores a single prototype
""" """
def at_script_creation(self): def at_script_creation(self):
self.key = "empty prototype" self.key = "empty prototype" # prototype_key
self.desc = "A prototype" self.desc = "A prototype" # prototype_desc
def build_metaproto(key='', desc='', locks='', tags=None, prototype=None): def save_db_prototype(caller, prototype, key=None, desc=None, tags=None, locks="", delete=False):
"""
Create a metaproto from combinant parts.
"""
if locks:
locks = (";".join(locks) if is_iter(locks) else locks)
else:
locks = []
prototype = dict(prototype) if prototype else {}
return MetaProto(key, desc, locks, tags, dict(prototype))
def save_db_prototype(caller, key, prototype, desc="", tags=None, locks="", delete=False):
""" """
Store a prototype persistently. Store a prototype persistently.
@ -187,13 +176,14 @@ def save_db_prototype(caller, key, prototype, desc="", tags=None, locks="", dele
caller (Account or Object): Caller aiming to store prototype. At this point caller (Account or Object): Caller aiming to store prototype. At this point
the caller should have permission to 'add' new prototypes, but to edit the caller should have permission to 'add' new prototypes, but to edit
an existing prototype, the 'edit' lock must be passed on that prototype. an existing prototype, the 'edit' lock must be passed on that prototype.
key (str): Name of prototype to store.
prototype (dict): Prototype dict. prototype (dict): Prototype dict.
desc (str, optional): Description of prototype, to use in listing. key (str): Name of prototype to store. Will be inserted as `prototype_key` in the prototype.
desc (str, optional): Description of prototype, to use in listing. Will be inserted
as `prototype_desc` in the prototype.
tags (list, optional): Tag-strings to apply to prototype. These are always tags (list, optional): Tag-strings to apply to prototype. These are always
applied with the 'db_prototype' category. applied with the 'db_prototype' category. Will be inserted as `prototype_tags`.
locks (str, optional): Locks to apply to this prototype. Used locks locks (str, optional): Locks to apply to this prototype. Used locks
are 'use' and 'edit' are 'use' and 'edit'. Will be inserted as `prototype_locks` in the prototype.
delete (bool, optional): Delete an existing prototype identified by 'key'. delete (bool, optional): Delete an existing prototype identified by 'key'.
This requires `caller` to pass the 'edit' lock of the prototype. This requires `caller` to pass the 'edit' lock of the prototype.
Returns: Returns:
@ -204,21 +194,40 @@ def save_db_prototype(caller, key, prototype, desc="", tags=None, locks="", dele
""" """
key_orig = key key_orig = key or prototype.get('prototype_key', None)
key = key.lower() if not key_orig:
locks = locks if locks else "use:all();edit:id({}) or perm(Admin)".format(caller.id) caller.msg("This prototype requires a prototype_key.")
return False
key = str(key).lower()
# we can't edit a prototype defined in a module
if key in _MODULE_PROTOTYPES:
mod = _MODULE_PROTOTYPE_MODULES.get(key, "N/A")
raise PermissionError("{} is a read-only prototype "
"(defined as code in {}).".format(key_orig, mod))
prototype['prototype_key'] = key
if desc:
desc = prototype['prototype_desc'] = desc
else:
desc = prototype.get('prototype_desc', '')
# set up locks and check they are on a valid form
locks = locks or prototype.get(
"prototype_locks", "use:all();edit:id({}) or perm(Admin)".format(caller.id))
prototype['prototype_locks'] = locks
is_valid, err = caller.locks.validate(locks) is_valid, err = caller.locks.validate(locks)
if not is_valid: if not is_valid:
caller.msg("Lock error: {}".format(err)) caller.msg("Lock error: {}".format(err))
return False return False
tags = [(tag, "db_prototype") for tag in make_iter(tags)] if tags:
tags = [(tag, "db_prototype") for tag in make_iter(tags)]
if key in _MODULE_PROTOTYPES: else:
mod = _MODULE_PROTOTYPE_MODULES.get(key, "N/A") tags = prototype.get('prototype_tags', [])
raise PermissionError("{} is a read-only prototype " prototype['prototype_tags'] = tags
"(defined as code in {}).".format(key_orig, mod))
stored_prototype = DbPrototype.objects.filter(db_key=key) stored_prototype = DbPrototype.objects.filter(db_key=key)
@ -269,7 +278,7 @@ def delete_db_prototype(caller, key):
return save_db_prototype(caller, key, None, delete=True) return save_db_prototype(caller, key, None, delete=True)
def search_db_prototype(key=None, tags=None, return_metaprotos=False): def search_db_prototype(key=None, tags=None, return_queryset=False):
""" """
Find persistent (database-stored) prototypes based on key and/or tags. Find persistent (database-stored) prototypes based on key and/or tags.
@ -278,13 +287,14 @@ def search_db_prototype(key=None, tags=None, return_metaprotos=False):
tags (str or list): Tag key or keys to query for. These tags (str or list): Tag key or keys to query for. These
will always be applied with the 'db_protototype' will always be applied with the 'db_protototype'
tag category. tag category.
return_metaproto (bool): Return results as metaprotos. return_queryset (bool): Return the database queryset.
Return: Return:
matches (queryset or list): All found DbPrototypes. If `return_metaprotos` matches (queryset or list): All found DbPrototypes. If `return_queryset`
is set, return a list of MetaProtos. is not set, this is a list of prototype dicts.
Note: Note:
This will not include read-only prototypes defined in modules. This does not include read-only prototypes defined in modules; use
`search_module_prototype` for those.
""" """
if tags: if tags:
@ -297,11 +307,9 @@ def search_db_prototype(key=None, tags=None, return_metaprotos=False):
if key: if key:
# exact or partial match on key # exact or partial match on key
matches = matches.filter(db_key=key) or matches.filter(db_key__icontains=key) matches = matches.filter(db_key=key) or matches.filter(db_key__icontains=key)
if return_metaprotos: if not return_queryset:
return [build_metaproto(match.key, match.desc, match.locks.all(), # return prototype
match.tags.get(category="db_prototype", return_list=True), return [dbprot.attributes.get("prototype", {}) for dbprot in matches]
match.attributes.get("prototype"))
for match in matches]
return matches return matches
@ -314,16 +322,16 @@ def search_module_prototype(key=None, tags=None):
tags (str or list): Tag key to query for. tags (str or list): Tag key to query for.
Return: Return:
matches (list): List of MetaProto tuples that includes matches (list): List of prototypes matching the search criterion.
prototype metadata,
""" """
matches = {} matches = {}
if tags: if tags:
# use tags to limit selection # use tags to limit selection
tagset = set(tags) tagset = set(tags)
matches = {key: metaproto for key, metaproto in _MODULE_PROTOTYPES.items() matches = {prototype_key: prototype
if tagset.intersection(metaproto.tags)} for prototype_key, prototype in _MODULE_PROTOTYPES.items()
if tagset.intersection(prototype.get("prototype_tags", []))}
else: else:
matches = _MODULE_PROTOTYPES matches = _MODULE_PROTOTYPES
@ -333,12 +341,13 @@ def search_module_prototype(key=None, tags=None):
return [matches[key]] return [matches[key]]
else: else:
# fuzzy matching # fuzzy matching
return [metaproto for pkey, metaproto in matches.items() if key in pkey] return [prototype for prototype_key, prototype in matches.items()
if key in prototype_key]
else: else:
return [match for match in matches.values()] return [match for match in matches.values()]
def search_prototype(key=None, tags=None, return_meta=True): def search_prototype(key=None, tags=None):
""" """
Find prototypes based on key and/or tags, or all prototypes. Find prototypes based on key and/or tags, or all prototypes.
@ -347,12 +356,10 @@ def search_prototype(key=None, tags=None, return_meta=True):
tags (str or list): Tag key or keys to query for. These tags (str or list): Tag key or keys to query for. These
will always be applied with the 'db_protototype' will always be applied with the 'db_protototype'
tag category. tag category.
return_meta (bool): If False, only return prototype dicts, if True
return MetaProto namedtuples including prototype meta info
Return: Return:
matches (list): All found prototype dicts or MetaProtos. If no keys matches (list): All found prototype dicts. If no keys
or tags are given, all available prototypes/MetaProtos will be returned. or tags are given, all available prototypes will be returned.
Note: Note:
The available prototypes is a combination of those supplied in The available prototypes is a combination of those supplied in
@ -363,32 +370,29 @@ def search_prototype(key=None, tags=None, return_meta=True):
""" """
module_prototypes = search_module_prototype(key, tags) module_prototypes = search_module_prototype(key, tags)
db_prototypes = search_db_prototype(key, tags, return_metaprotos=True) db_prototypes = search_db_prototype(key, tags)
matches = db_prototypes + module_prototypes matches = db_prototypes + module_prototypes
if len(matches) > 1 and key: if len(matches) > 1 and key:
key = key.lower() key = key.lower()
# avoid duplicates if an exact match exist between the two types # avoid duplicates if an exact match exist between the two types
filter_matches = [mta for mta in matches if mta.key == key] filter_matches = [mta for mta in matches
if mta.get('prototype_key') and mta['prototype_key'] == key]
if filter_matches and len(filter_matches) < len(matches): if filter_matches and len(filter_matches) < len(matches):
matches = filter_matches matches = filter_matches
if not return_meta:
matches = [mta.prototype for mta in matches]
return matches return matches
def get_protparents(): def get_protparent_dict():
""" """
Get prototype parents. These are a combination of meta-key and prototype-dict and are used when Get prototype parents.
a prototype refers to another parent-prototype.
Returns:
parent_dict (dict): A mapping {prototype_key: prototype} for all available prototypes.
""" """
# get all prototypes return {prototype['prototype_key']: prototype for prototype in search_prototype()}
metaprotos = search_prototype(return_meta=True)
# organize by key
return {metaproto.key: metaproto.prototype for metaproto in metaprotos}
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True): def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
@ -410,35 +414,29 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
tags = [tag for tag in make_iter(tags) if tag] tags = [tag for tag in make_iter(tags) if tag]
# get metaprotos for readonly and db-based prototypes # get metaprotos for readonly and db-based prototypes
metaprotos = search_module_prototype(key, tags) prototypes = search_prototype(key, tags)
metaprotos += search_db_prototype(key, tags, return_metaprotos=True)
# get use-permissions of readonly attributes (edit is always False) # get use-permissions of readonly attributes (edit is always False)
prototypes = [ display_tuples = []
(metaproto.key, for prototype in sorted(prototypes, key=lambda d: d['prototype_key']):
metaproto.desc, lock_use = caller.locks.check_lockstring(caller, prototype['locks'], access_type='use')
("{}/N".format('Y' if not show_non_use and not lock_use:
if caller.locks.check_lockstring( continue
caller, lock_edit = caller.locks.check_lockstring(caller, prototype['locks'], access_type='edit')
metaproto.locks, if not show_non_edit and not lock_edit:
access_type='use') else 'N')), continue
",".join(metaproto.tags)) display_tuples.append(
for metaproto in sorted(metaprotos, key=lambda o: o.key)] (prototype.get('prototype_key', '<unset>',
prototype['prototype_desc', ''],
"{}/{}".format('Y' if lock_use else 'N', 'Y' if lock_edit else 'N'),
",".join(prototype.get('prototype_tags', [])))))
if not prototypes: if not display_tuples:
return None
if not show_non_use:
prototypes = [metaproto for metaproto in prototypes if metaproto[2].split("/", 1)[0] == 'Y']
if not show_non_edit:
prototypes = [metaproto for metaproto in prototypes if metaproto[2].split("/", 1)[1] == 'Y']
if not prototypes:
return None return None
table = [] table = []
for i in range(len(prototypes[0])): for i in range(len(display_tuples[0])):
table.append([str(metaproto[i]) for metaproto in prototypes]) table.append([str(display_tuple[i]) for display_tuple in display_tuples])
table = EvTable("Key", "Desc", "Use/Edit", "Tags", table=table, crop=True, width=78) table = EvTable("Key", "Desc", "Use/Edit", "Tags", table=table, crop=True, width=78)
table.reformat_column(0, width=28) table.reformat_column(0, width=28)
table.reformat_column(1, width=40) table.reformat_column(1, width=40)
@ -447,22 +445,26 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
return table return table
def metaproto_to_str(metaproto): def prototype_to_str(prototype):
""" """
Format a metaproto to a nice string representation. Format a prototype to a nice string representation.
Args: Args:
metaproto (NamedTuple): Represents the prototype. metaproto (NamedTuple): Represents the prototype.
""" """
header = ( header = (
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n" "|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
"|cdesc:|n {} \n|cprototype:|n ".format( "|cdesc:|n {} \n|cprototype:|n ".format(
metaproto.key, ", ".join(metaproto.tags), prototype['prototype_key'],
metaproto.locks, metaproto.desc)) ", ".join(prototype['prototype_tags']),
prototype = ("{{\n {} \n}}".format("\n ".join("{!r}: {!r},".format(key, value) prototype['prototype_locks'],
for key, value in prototype['prototype_desc']))
sorted(metaproto.prototype.items())).rstrip(","))) proto = ("{{\n {} \n}}".format(
return header + prototype "\n ".join(
"{!r}: {!r},".format(key, value) for key, value in
sorted(prototype.items()) if key not in _PROTOTYPE_META_NAMES)).rstrip(","))
return header + proto
# Spawner mechanism # Spawner mechanism
@ -487,10 +489,12 @@ def validate_prototype(prototype, protkey=None, protparents=None, _visited=None)
""" """
if not protparents: if not protparents:
protparents = get_protparents() protparents = get_protparent_dict()
if _visited is None: if _visited is None:
_visited = [] _visited = []
protkey = protkey.lower() if protkey is not None else None protkey = protkey or prototype.get('prototype_key', None)
protkey = protkey.lower() or prototype.get('prototype_key', None)
assert isinstance(prototype, dict) assert isinstance(prototype, dict)
@ -537,8 +541,8 @@ def _batch_create_object(*objparams):
so make sure the spawned Typeclass works before using this! so make sure the spawned Typeclass works before using this!
Args: Args:
objsparams (tuple): Parameters for the respective creation/add objsparams (tuple): Each paremter tuple will create one object instance using the parameters within.
handlers in the following order: The parameters should be given in the following order:
- `create_kwargs` (dict): For use as new_obj = `ObjectDB(**create_kwargs)`. - `create_kwargs` (dict): For use as new_obj = `ObjectDB(**create_kwargs)`.
- `permissions` (str): Permission string used with `new_obj.batch_add(permission)`. - `permissions` (str): Permission string used with `new_obj.batch_add(permission)`.
- `lockstring` (str): Lockstring used with `new_obj.locks.add(lockstring)`. - `lockstring` (str): Lockstring used with `new_obj.locks.add(lockstring)`.
@ -555,9 +559,6 @@ def _batch_create_object(*objparams):
(the newly created object) available in the namespace. Execution (the newly created object) available in the namespace. Execution
will happend after all other properties have been assigned and will happend after all other properties have been assigned and
is intended for calling custom handlers etc. is intended for calling custom handlers etc.
for the respective creation/add handlers in the following
order: (create_kwargs, permissions, locks, aliases, nattributes,
attributes, tags, execs)
Returns: Returns:
objects (list): A list of created objects objects (list): A list of created objects
@ -664,6 +665,10 @@ def spawn(*prototypes, **kwargs):
alias_string = aliasval() if callable(aliasval) else aliasval alias_string = aliasval() if callable(aliasval) else aliasval
tagval = prot.pop("tags", []) tagval = prot.pop("tags", [])
tags = tagval() if callable(tagval) else tagval tags = tagval() if callable(tagval) else tagval
# we make sure to add a tag identifying which prototype created this object
# tags.append(())
attrval = prot.pop("attrs", []) attrval = prot.pop("attrs", [])
attributes = attrval() if callable(tagval) else attrval attributes = attrval() if callable(tagval) else attrval
@ -676,9 +681,9 @@ def spawn(*prototypes, **kwargs):
# the rest are attributes # the rest are attributes
simple_attributes = [(key, value()) if callable(value) else (key, value) simple_attributes = [(key, value()) if callable(value) else (key, value)
for key, value in prot.items() if not key.startswith("ndb_")] for key, value in prot.items() if not (key.startswith("ndb_"))]
attributes = attributes + simple_attributes attributes = attributes + simple_attributes
attributes = [tup for tup in attributes if not tup[0] in _CREATE_OBJECT_KWARGS] attributes = [tup for tup in attributes if not tup[0] in _NON_CREATE_KWARGS]
# pack for call into _batch_create_object # pack for call into _batch_create_object
objsparams.append((create_kwargs, permission_string, lock_string, objsparams.append((create_kwargs, permission_string, lock_string,
@ -695,34 +700,30 @@ def spawn(*prototypes, **kwargs):
# Helper functions # Helper functions
def _get_menu_metaprot(caller): def _get_menu_prototype(caller):
metaproto = None prototype = None
if hasattr(caller.ndb._menutree, "olc_metaprot"): if hasattr(caller.ndb._menutree, "olc_prototype"):
metaproto = caller.ndb._menutree.olc_metaprot prototype = caller.ndb._menutree.olc_prototype
if not metaproto: if not prototype:
metaproto = build_metaproto(None, '', [], [], None) caller.ndb._menutree.olc_prototype = {}
caller.ndb._menutree.olc_metaprot = metaproto
caller.ndb._menutree.olc_new = True caller.ndb._menutree.olc_new = True
return metaproto return prototype
def _is_new_prototype(caller): def _is_new_prototype(caller):
return hasattr(caller.ndb._menutree, "olc_new") return hasattr(caller.ndb._menutree, "olc_new")
def _set_menu_metaprot(caller, field, value): def _set_menu_prototype(caller, field, value):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
kwargs = dict(metaprot.__dict__) prototype[field] = value
kwargs[field] = value caller.ndb._menutree.olc_prototype = prototype
caller.ndb._menutree.olc_metaprot = build_metaproto(**kwargs)
def _format_property(key, required=False, metaprot=None, prototype=None, cropper=None): def _format_property(key, required=False, prototype=None, cropper=None):
key = key.lower() key = key.lower()
if metaprot is not None: if prototype is not None:
prop = getattr(metaprot, key) or ''
elif prototype is not None:
prop = prototype.get(key, '') prop = prototype.get(key, '')
out = prop out = prop
@ -753,14 +754,11 @@ def _set_property(caller, raw_string, **kwargs):
next_node (str): Next node to go to. next_node (str): Next node to go to.
""" """
prop = kwargs.get("prop", "meta_key") prop = kwargs.get("prop", "prototype_key")
processor = kwargs.get("processor", None) processor = kwargs.get("processor", None)
next_node = kwargs.get("next_node", "node_index") next_node = kwargs.get("next_node", "node_index")
propname_low = prop.strip().lower() propname_low = prop.strip().lower()
meta = propname_low.startswith("meta_")
if meta:
propname_low = propname_low[5:]
if callable(processor): if callable(processor):
try: try:
@ -776,23 +774,17 @@ def _set_property(caller, raw_string, **kwargs):
if not value: if not value:
return next_node return next_node
if meta: prototype = _get_menu_prototype(caller)
_set_menu_metaprot(caller, propname_low, value)
else:
metaprot = _get_menu_metaprot(caller)
prototype = metaprot.prototype
prototype[propname_low] = value
# typeclass and prototype can't co-exist # typeclass and prototype can't co-exist
if propname_low == "typeclass": if propname_low == "typeclass":
prototype.pop("prototype", None) prototype.pop("prototype", None)
if propname_low == "prototype": if propname_low == "prototype":
prototype.pop("typeclass", None) prototype.pop("typeclass", None)
_set_menu_metaprot(caller, "prototype", prototype) caller.ndb._menutree.olc_prototype = prototype
caller.msg("Set {prop} to '{value}'.".format( caller.msg("Set {prop} to '{value}'.".format(prop, value=str(value)))
prop=prop.replace("_", "-").capitalize(), value=str(value)))
return next_node return next_node
@ -829,8 +821,7 @@ def _path_cropper(pythonpath):
# Menu nodes # Menu nodes
def node_index(caller): def node_index(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
prototype = metaprot.prototype
text = ("|c --- Prototype wizard --- |n\n\n" text = ("|c --- Prototype wizard --- |n\n\n"
"Define the |yproperties|n of the prototype. All prototype values can be " "Define the |yproperties|n of the prototype. All prototype values can be "
@ -841,10 +832,9 @@ def node_index(caller):
"others later.\n\n(make choice; q to abort. If unsure, start from 1.)") "others later.\n\n(make choice; q to abort. If unsure, start from 1.)")
options = [] options = []
# The meta-key goes first
options.append( options.append(
{"desc": "|WMeta-Key|n|n{}".format(_format_property("Key", True, metaprot, None)), {"desc": "|WPrototype-Key|n|n{}".format(_format_property("Key", True, prototype, None)),
"goto": "node_meta_key"}) "goto": "node_prototype_key"})
for key in ('Prototype', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks', for key in ('Prototype', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
'Permissions', 'Location', 'Home', 'Destination'): 'Permissions', 'Location', 'Home', 'Destination'):
required = False required = False
@ -860,20 +850,20 @@ def node_index(caller):
required = False required = False
for key in ('Desc', 'Tags', 'Locks'): for key in ('Desc', 'Tags', 'Locks'):
options.append( options.append(
{"desc": "|WMeta-{}|n|n{}".format(key, _format_property(key, required, metaprot, None)), {"desc": "|WPrototype-{}|n|n{}".format(key, _format_property(key, required, prototype, None)),
"goto": "node_meta_{}".format(key.lower())}) "goto": "node_prototype_{}".format(key.lower())})
return text, options return text, options
def node_validate_prototype(caller, raw_string, **kwargs): def node_validate_prototype(caller, raw_string, **kwargs):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
txt = metaproto_to_str(metaprot) txt = prototype_to_str(prototype)
errors = "\n\n|g No validation errors found.|n (but errors could still happen at spawn-time)" errors = "\n\n|g No validation errors found.|n (but errors could still happen at spawn-time)"
try: try:
# validate, don't spawn # validate, don't spawn
spawn(metaprot.prototype, return_prototypes=True) spawn(prototype, return_prototypes=True)
except RuntimeError as err: except RuntimeError as err:
errors = "\n\n|rError: {}|n".format(err) errors = "\n\n|rError: {}|n".format(err)
text = (txt + errors) text = (txt + errors)
@ -883,42 +873,43 @@ def node_validate_prototype(caller, raw_string, **kwargs):
return text, options return text, options
def _check_meta_key(caller, key): def _check_prototype_key(caller, key):
old_metaprot = search_prototype(key) old_prototype = search_prototype(key)
olc_new = _is_new_prototype(caller) olc_new = _is_new_prototype(caller)
key = key.strip().lower() key = key.strip().lower()
if old_metaprot: if old_prototype:
# we are starting a new prototype that matches an existing # we are starting a new prototype that matches an existing
if not caller.locks.check_lockstring(caller, old_metaprot.locks, access_type='edit'): if not caller.locks.check_lockstring(
# return to the node_meta_key to try another key caller, old_prototype['prototype_locks'], access_type='edit'):
# return to the node_prototype_key to try another key
caller.msg("Prototype '{key}' already exists and you don't " caller.msg("Prototype '{key}' already exists and you don't "
"have permission to edit it.".format(key=key)) "have permission to edit it.".format(key=key))
return "node_meta_key" return "node_prototype_key"
elif olc_new: elif olc_new:
# we are selecting an existing prototype to edit. Reset to index. # we are selecting an existing prototype to edit. Reset to index.
del caller.ndb._menutree.olc_new del caller.ndb._menutree.olc_new
caller.ndb._menutree.olc_metaprot = old_metaprot caller.ndb._menutree.olc_prototype = old_prototype
caller.msg("Prototype already exists. Reloading.") caller.msg("Prototype already exists. Reloading.")
return "node_index" return "node_index"
return _set_property(caller, key, prop='meta_key', next_node="node_prototype") return _set_property(caller, key, prop='prototype_key', next_node="node_prototype")
def node_meta_key(caller): def node_prototype_key(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
text = ["The prototype name, or |wMeta-Key|n, uniquely identifies the prototype. " text = ["The prototype name, or |wMeta-Key|n, uniquely identifies the prototype. "
"It is used to find and use the prototype to spawn new entities. " "It is used to find and use the prototype to spawn new entities. "
"It is not case sensitive."] "It is not case sensitive."]
old_key = metaprot.key old_key = prototype['prototype_key']
if old_key: if old_key:
text.append("Current key is '|w{key}|n'".format(key=old_key)) text.append("Current key is '|w{key}|n'".format(key=old_key))
else: else:
text.append("The key is currently unset.") text.append("The key is currently unset.")
text.append("Enter text or make a choice (q for quit)") text.append("Enter text or make a choice (q for quit)")
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("meta_key", "index", "prototype") options = _wizard_options("prototype_key", "index", "prototype")
options.append({"key": "_default", options.append({"key": "_default",
"goto": _check_meta_key}) "goto": _check_prototype_key})
return text, options return text, options
@ -927,9 +918,9 @@ def _all_prototypes(caller):
def _prototype_examine(caller, prototype_name): def _prototype_examine(caller, prototype_name):
metaprot = search_prototype(key=prototype_name) prototypes = search_prototype(key=prototype_name)
if metaprot: if prototypes:
caller.msg(metaproto_to_str(metaprot[0])) caller.msg(prototype_to_str(prototypes[0]))
caller.msg("Prototype not registered.") caller.msg("Prototype not registered.")
return None return None
@ -942,17 +933,22 @@ def _prototype_select(caller, prototype):
@list_node(_all_prototypes, _prototype_select) @list_node(_all_prototypes, _prototype_select)
def node_prototype(caller): def node_prototype(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
prot = metaprot.prototype
prototype = prot.get("prototype")
text = ["Set the prototype's parent |yPrototype|n. If this is unset, Typeclass will be used."] prot_parent_key = prototype.get('prototype')
if prototype:
text.append("Current prototype is |y{prototype}|n.".format(prototype=prototype)) text = ["Set the prototype's |yParent Prototype|n. If this is unset, Typeclass will be used."]
if prot_parent_key:
prot_parent = search_prototype(prot_parent_key)
if prot_parent:
text.append("Current parent prototype is {}:\n{}".format(prototype_to_str(prot_parent)))
else:
text.append("Current parent prototype |r{prototype}|n "
"does not appear to exist.".format(prot_parent_key))
else: else:
text.append("Parent prototype is not set") text.append("Parent prototype is not set")
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("prototype", "meta_key", "typeclass", color="|W") options = _wizard_options("prototype", "prototype_key", "typeclass", color="|W")
options.append({"key": "_default", options.append({"key": "_default",
"goto": _prototype_examine}) "goto": _prototype_examine})
@ -961,7 +957,6 @@ def node_prototype(caller):
def _all_typeclasses(caller): def _all_typeclasses(caller):
return list(sorted(get_all_typeclasses().keys())) return list(sorted(get_all_typeclasses().keys()))
# return list(sorted(get_all_typeclasses(parent="evennia.objects.objects.DefaultObject").keys()))
def _typeclass_examine(caller, typeclass_path): def _typeclass_examine(caller, typeclass_path):
@ -994,9 +989,8 @@ def _typeclass_select(caller, typeclass):
@list_node(_all_typeclasses, _typeclass_select) @list_node(_all_typeclasses, _typeclass_select)
def node_typeclass(caller): def node_typeclass(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
prot = metaprot.prototype typeclass = prototype.get("typeclass")
typeclass = prot.get("typeclass")
text = ["Set the typeclass's parent |yTypeclass|n."] text = ["Set the typeclass's parent |yTypeclass|n."]
if typeclass: if typeclass:
@ -1012,9 +1006,8 @@ def node_typeclass(caller):
def node_key(caller): def node_key(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
prot = metaprot.prototype key = prototype.get("key")
key = prot.get("key")
text = ["Set the prototype's |yKey|n. This will retain case sensitivity."] text = ["Set the prototype's |yKey|n. This will retain case sensitivity."]
if key: if key:
@ -1032,9 +1025,8 @@ def node_key(caller):
def node_aliases(caller): def node_aliases(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
prot = metaprot.prototype aliases = prototype.get("aliases")
aliases = prot.get("aliases")
text = ["Set the prototype's |yAliases|n. Separate multiple aliases with commas. " text = ["Set the prototype's |yAliases|n. Separate multiple aliases with commas. "
"ill retain case sensitivity."] "ill retain case sensitivity."]
@ -1053,8 +1045,8 @@ def node_aliases(caller):
def _caller_attrs(caller): def _caller_attrs(caller):
metaprot = _get_menu_metaprot(caller) prototype = _get_menu_prototype(caller)
attrs = metaprot.prototype.get("attrs", []) attrs = prototype.get("attrs", [])
return attrs return attrs
@ -1078,10 +1070,9 @@ def _attrparse(caller, attr_string):
def _add_attr(caller, attr_string, **kwargs): def _add_attr(caller, attr_string, **kwargs):
attrname, value = _attrparse(caller, attr_string) attrname, value = _attrparse(caller, attr_string)
if attrname: if attrname:
metaprot = _get_menu_metaprot(caller) prot = _get_menu_prototype(caller)
prot = metaprot.prototype
prot['attrs'][attrname] = value prot['attrs'][attrname] = value
_set_menu_metaprot(caller, "prototype", prot) _set_menu_prototype(caller, "prototype", prot)
text = "Added" text = "Added"
else: else:
text = "Attribute must be given as 'attrname = <value>' where <value> uses valid Python." text = "Attribute must be given as 'attrname = <value>' where <value> uses valid Python."
@ -1093,8 +1084,7 @@ def _add_attr(caller, attr_string, **kwargs):
def _edit_attr(caller, attrname, new_value, **kwargs): def _edit_attr(caller, attrname, new_value, **kwargs):
attrname, value = _attrparse("{}={}".format(caller, attrname, new_value)) attrname, value = _attrparse("{}={}".format(caller, attrname, new_value))
if attrname: if attrname:
metaprot = _get_menu_metaprot(caller) prot = _get_menu_prototype(caller)
prot = metaprot.prototype
prot['attrs'][attrname] = value prot['attrs'][attrname] = value
text = "Edited Attribute {} = {}".format(attrname, value) text = "Edited Attribute {} = {}".format(attrname, value)
else: else:
@ -1105,16 +1095,14 @@ def _edit_attr(caller, attrname, new_value, **kwargs):
def _examine_attr(caller, selection): def _examine_attr(caller, selection):
metaprot = _get_menu_metaprot(caller) prot = _get_menu_prototype(caller)
prot = metaprot.prototype
value = prot['attrs'][selection] value = prot['attrs'][selection]
return "Attribute {} = {}".format(selection, value) return "Attribute {} = {}".format(selection, value)
@list_node(_caller_attrs) @list_node(_caller_attrs)
def node_attrs(caller): def node_attrs(caller):
metaprot = _get_menu_metaprot(caller) prot = _get_menu_prototype(caller)
prot = metaprot.prototype
attrs = prot.get("attrs") attrs = prot.get("attrs")
text = ["Set the prototype's |yAttributes|n. Separate multiple attrs with commas. " text = ["Set the prototype's |yAttributes|n. Separate multiple attrs with commas. "
@ -1134,7 +1122,7 @@ def node_attrs(caller):
def _caller_tags(caller): def _caller_tags(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
tags = prot.get("tags") tags = prot.get("tags")
return tags return tags
@ -1142,7 +1130,7 @@ def _caller_tags(caller):
def _add_tag(caller, tag, **kwargs): def _add_tag(caller, tag, **kwargs):
tag = tag.strip().lower() tag = tag.strip().lower()
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
tags = prot.get('tags', []) tags = prot.get('tags', [])
if tags: if tags:
@ -1151,7 +1139,7 @@ def _add_tag(caller, tag, **kwargs):
else: else:
tags = [tag] tags = [tag]
prot['tags'] = tags prot['tags'] = tags
_set_menu_metaprot(caller, "prototype", prot) _set_menu_prototype(caller, "prototype", prot)
text = kwargs.get("text") text = kwargs.get("text")
if not text: if not text:
text = "Added tag {}. (return to continue)".format(tag) text = "Added tag {}. (return to continue)".format(tag)
@ -1161,7 +1149,7 @@ def _add_tag(caller, tag, **kwargs):
def _edit_tag(caller, old_tag, new_tag, **kwargs): def _edit_tag(caller, old_tag, new_tag, **kwargs):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prototype = metaprot.prototype prototype = metaprot.prototype
tags = prototype.get('tags', []) tags = prototype.get('tags', [])
@ -1169,7 +1157,7 @@ def _edit_tag(caller, old_tag, new_tag, **kwargs):
new_tag = new_tag.strip().lower() new_tag = new_tag.strip().lower()
tags[tags.index(old_tag)] = new_tag tags[tags.index(old_tag)] = new_tag
prototype['tags'] = tags prototype['tags'] = tags
_set_menu_metaprot(caller, 'prototype', prototype) _set_menu_prototype(caller, 'prototype', prototype)
text = kwargs.get('text') text = kwargs.get('text')
if not text: if not text:
@ -1187,7 +1175,7 @@ def node_tags(caller):
def node_locks(caller): def node_locks(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
locks = prot.get("locks") locks = prot.get("locks")
@ -1208,7 +1196,7 @@ def node_locks(caller):
def node_permissions(caller): def node_permissions(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
permissions = prot.get("permissions") permissions = prot.get("permissions")
@ -1229,7 +1217,7 @@ def node_permissions(caller):
def node_location(caller): def node_location(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
location = prot.get("location") location = prot.get("location")
@ -1249,7 +1237,7 @@ def node_location(caller):
def node_home(caller): def node_home(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
home = prot.get("home") home = prot.get("home")
@ -1269,7 +1257,7 @@ def node_home(caller):
def node_destination(caller): def node_destination(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
prot = metaprot.prototype prot = metaprot.prototype
dest = prot.get("dest") dest = prot.get("dest")
@ -1279,18 +1267,18 @@ def node_destination(caller):
else: else:
text.append("No destination is set (default).") text.append("No destination is set (default).")
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("destination", "home", "meta_desc") options = _wizard_options("destination", "home", "prototype_desc")
options.append({"key": "_default", options.append({"key": "_default",
"goto": (_set_property, "goto": (_set_property,
dict(prop="dest", dict(prop="dest",
processor=lambda s: s.strip(), processor=lambda s: s.strip(),
next_node="node_meta_desc"))}) next_node="node_prototype_desc"))})
return text, options return text, options
def node_meta_desc(caller): def node_prototype_desc(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
text = ["The |wMeta-Description|n briefly describes the prototype for viewing in listings."] text = ["The |wMeta-Description|n briefly describes the prototype for viewing in listings."]
desc = metaprot.desc desc = metaprot.desc
@ -1299,18 +1287,18 @@ def node_meta_desc(caller):
else: else:
text.append("Description is currently unset.") text.append("Description is currently unset.")
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("meta_desc", "meta_key", "meta_tags") options = _wizard_options("prototype_desc", "prototype_key", "prototype_tags")
options.append({"key": "_default", options.append({"key": "_default",
"goto": (_set_property, "goto": (_set_property,
dict(prop='meta_desc', dict(prop='prototype_desc',
processor=lambda s: s.strip(), processor=lambda s: s.strip(),
next_node="node_meta_tags"))}) next_node="node_prototype_tags"))})
return text, options return text, options
def node_meta_tags(caller): def node_prototype_tags(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
text = ["|wMeta-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. " text = ["|wMeta-Tags|n can be used to classify and find prototypes. Tags are case-insensitive. "
"Separate multiple by tags by commas."] "Separate multiple by tags by commas."]
tags = metaprot.tags tags = metaprot.tags
@ -1320,18 +1308,18 @@ def node_meta_tags(caller):
else: else:
text.append("No tags are currently set.") text.append("No tags are currently set.")
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("meta_tags", "meta_desc", "meta_locks") options = _wizard_options("prototype_tags", "prototype_desc", "prototype_locks")
options.append({"key": "_default", options.append({"key": "_default",
"goto": (_set_property, "goto": (_set_property,
dict(prop="meta_tags", dict(prop="prototype_tags",
processor=lambda s: [ processor=lambda s: [
str(part.strip().lower()) for part in s.split(",")], str(part.strip().lower()) for part in s.split(",")],
next_node="node_meta_locks"))}) next_node="node_prototype_locks"))})
return text, options return text, options
def node_meta_locks(caller): def node_prototype_locks(caller):
metaprot = _get_menu_metaprot(caller) metaprot = _get_menu_prototype(caller)
text = ["Set |wMeta-Locks|n on the prototype. There are two valid lock types: " text = ["Set |wMeta-Locks|n on the prototype. There are two valid lock types: "
"'edit' (who can edit the prototype) and 'use' (who can apply the prototype)\n" "'edit' (who can edit the prototype) and 'use' (who can apply the prototype)\n"
"(If you are unsure, leave as default.)"] "(If you are unsure, leave as default.)"]
@ -1342,10 +1330,10 @@ def node_meta_locks(caller):
text.append("Lock unset - if not changed the default lockstring will be set as\n" text.append("Lock unset - if not changed the default lockstring will be set as\n"
" |w'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id)) " |w'use:all(); edit:id({dbref}) or perm(Admin)'|n".format(dbref=caller.id))
text = "\n\n".join(text) text = "\n\n".join(text)
options = _wizard_options("meta_locks", "meta_tags", "index") options = _wizard_options("prototype_locks", "prototype_tags", "index")
options.append({"key": "_default", options.append({"key": "_default",
"goto": (_set_property, "goto": (_set_property,
dict(prop="meta_locks", dict(prop="prototype_locks",
processor=lambda s: s.strip().lower(), processor=lambda s: s.strip().lower(),
next_node="node_index"))}) next_node="node_index"))})
return text, options return text, options
@ -1392,7 +1380,7 @@ def start_olc(caller, session=None, metaproto=None):
""" """
menudata = {"node_index": node_index, menudata = {"node_index": node_index,
"node_validate_prototype": node_validate_prototype, "node_validate_prototype": node_validate_prototype,
"node_meta_key": node_meta_key, "node_prototype_key": node_prototype_key,
"node_prototype": node_prototype, "node_prototype": node_prototype,
"node_typeclass": node_typeclass, "node_typeclass": node_typeclass,
"node_key": node_key, "node_key": node_key,
@ -1404,11 +1392,11 @@ def start_olc(caller, session=None, metaproto=None):
"node_location": node_location, "node_location": node_location,
"node_home": node_home, "node_home": node_home,
"node_destination": node_destination, "node_destination": node_destination,
"node_meta_desc": node_meta_desc, "node_prototype_desc": node_prototype_desc,
"node_meta_tags": node_meta_tags, "node_prototype_tags": node_prototype_tags,
"node_meta_locks": node_meta_locks, "node_prototype_locks": node_prototype_locks,
} }
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_metaprot=metaproto) OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=metaproto)
# Testing # Testing