mirror of
https://github.com/evennia/evennia.git
synced 2026-03-26 01:36:32 +01:00
Validate prototype parent before chosing it
This commit is contained in:
parent
abed588f5e
commit
cc5d9ffd4d
3 changed files with 54 additions and 31 deletions
|
|
@ -42,14 +42,15 @@ def _get_menu_prototype(caller):
|
|||
return prototype
|
||||
|
||||
|
||||
def _get_flat_menu_prototype(caller, refresh=False):
|
||||
def _get_flat_menu_prototype(caller, refresh=False, validate=False):
|
||||
"""Return prototype where parent values are included"""
|
||||
flat_prototype = None
|
||||
if not refresh and hasattr(caller.ndb._menutree, "olc_flat_prototype"):
|
||||
flat_prototype = caller.ndb._menutree.olc_flat_prototype
|
||||
if not flat_prototype:
|
||||
prot = _get_menu_prototype(caller)
|
||||
caller.ndb._menutree.olc_flat_prototype = flat_prototype = spawner.flatten_prototype(prot)
|
||||
caller.ndb._menutree.olc_flat_prototype = \
|
||||
flat_prototype = spawner.flatten_prototype(prot, validate=validate)
|
||||
return flat_prototype
|
||||
|
||||
|
||||
|
|
@ -305,11 +306,11 @@ def node_index(caller):
|
|||
{"desc": "|WPrototype-Key|n|n{}".format(
|
||||
_format_option_value("Key", "prototype_key" not in prototype, prototype, None)),
|
||||
"goto": "node_prototype_key"})
|
||||
for key in ('Prototype-parent', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
|
||||
for key in ('Prototype_parent', 'Typeclass', 'Key', 'Aliases', 'Attrs', 'Tags', 'Locks',
|
||||
'Permissions', 'Location', 'Home', 'Destination'):
|
||||
required = False
|
||||
cropper = None
|
||||
if key in ("Prototype-parent", "Typeclass"):
|
||||
if key in ("Prototype_parent", "Typeclass"):
|
||||
required = ("prototype_parent" not in prototype) and ("typeclass" not in prototype)
|
||||
if key == 'Typeclass':
|
||||
cropper = _path_cropper
|
||||
|
|
@ -429,11 +430,24 @@ def _prototype_parent_examine(caller, prototype_name):
|
|||
caller.msg("Prototype not registered.")
|
||||
|
||||
|
||||
def _prototype_parent_select(caller, prototype):
|
||||
ret = _set_property(caller, "",
|
||||
prop="prototype_parent", processor=str, next_node="node_typeclass")
|
||||
caller.msg("Selected prototype |y{}|n.".format(prototype))
|
||||
def _prototype_parent_select(caller, new_parent):
|
||||
|
||||
ret = None
|
||||
prototype_parent = protlib.search_prototype(new_parent)
|
||||
try:
|
||||
if prototype_parent:
|
||||
spawner.flatten_prototype(prototype_parent[0], validate=True)
|
||||
else:
|
||||
raise RuntimeError("Not found.")
|
||||
except RuntimeError as err:
|
||||
caller.msg("Selected prototype parent {} "
|
||||
"caused Error(s):\n|r{}|n".format(new_parent, err))
|
||||
else:
|
||||
ret = _set_property(caller, new_parent,
|
||||
prop="prototype_parent",
|
||||
processor=str, next_node="node_prototype_parent")
|
||||
_get_flat_menu_prototype(caller, refresh=True)
|
||||
caller.msg("Selected prototype parent |c{}|n.".format(new_parent))
|
||||
return ret
|
||||
|
||||
|
||||
|
|
@ -441,12 +455,12 @@ def _prototype_parent_select(caller, prototype):
|
|||
def node_prototype_parent(caller):
|
||||
prototype = _get_menu_prototype(caller)
|
||||
|
||||
prot_parent_key = prototype.get('prototype')
|
||||
prot_parent_keys = prototype.get('prototype_parent')
|
||||
|
||||
text = """
|
||||
The |cPrototype Parent|n allows you to |winherit|n prototype values from another named
|
||||
prototype (given as that prototype's |wprototype_key|). If not changing these values in the
|
||||
current prototype, the parent's value will be used. Pick the available prototypes below.
|
||||
prototype (given as that prototype's |wprototype_key|n). If not changing these values in
|
||||
the current prototype, the parent's value will be used. Pick the available prototypes below.
|
||||
|
||||
Note that somewhere in the prototype's parentage, a |ctypeclass|n must be specified. If no
|
||||
parent is given, this prototype must define the typeclass (next menu node).
|
||||
|
|
@ -459,18 +473,23 @@ def node_prototype_parent(caller):
|
|||
prototype to be valid.
|
||||
"""
|
||||
|
||||
if prot_parent_key:
|
||||
prot_parent = protlib.search_prototype(prot_parent_key)
|
||||
if prot_parent:
|
||||
text = text.format(
|
||||
current="Current parent prototype is {}:\n{}".format(
|
||||
protlib.prototype_to_str(prot_parent)))
|
||||
else:
|
||||
text = text.format(
|
||||
current="Current parent prototype |r{prototype}|n "
|
||||
"does not appear to exist.".format(prot_parent_key))
|
||||
else:
|
||||
text = text.format(current="Parent prototype is not set")
|
||||
ptexts = []
|
||||
if prot_parent_keys:
|
||||
for pkey in utils.make_iter(prot_parent_keys):
|
||||
prot_parent = protlib.search_prototype(pkey)
|
||||
if prot_parent:
|
||||
prot_parent = prot_parent[0]
|
||||
ptexts.append("|c -- {pkey} -- |n\n{prot}".format(
|
||||
pkey=pkey,
|
||||
prot=protlib.prototype_to_str(prot_parent)))
|
||||
else:
|
||||
ptexts.append("Prototype parent |r{pkey} was not found.".format(pkey=pkey))
|
||||
|
||||
if not ptexts:
|
||||
ptexts.append("[No prototype_parent set]")
|
||||
|
||||
text = text.format(current="\n\n".join(ptexts))
|
||||
|
||||
text = (text, helptext)
|
||||
|
||||
options = _wizard_options("prototype_parent", "prototype_key", "typeclass", color="|W")
|
||||
|
|
@ -993,7 +1012,7 @@ def node_destination(caller):
|
|||
the exit 'leads to'. It's usually unset for all other types of objects.
|
||||
|
||||
{current}
|
||||
""".format(current=_get_current_node(caller, "destination"))
|
||||
""".format(current=_get_current_value(caller, "destination"))
|
||||
|
||||
helptext = """
|
||||
The destination can be given as a #dbref but can also be explicitly searched for using
|
||||
|
|
|
|||
|
|
@ -539,7 +539,7 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
|
|||
|
||||
|
||||
def validate_prototype(prototype, protkey=None, protparents=None,
|
||||
is_prototype_base=True, _flags=None):
|
||||
is_prototype_base=True, strict=True, _flags=None):
|
||||
"""
|
||||
Run validation on a prototype, checking for inifinite regress.
|
||||
|
||||
|
|
@ -552,6 +552,8 @@ def validate_prototype(prototype, protkey=None, protparents=None,
|
|||
is_prototype_base (bool, optional): We are trying to create a new object *based on this
|
||||
object*. This means we can't allow 'mixin'-style prototypes without typeclass/parent
|
||||
etc.
|
||||
strict (bool, optional): If unset, don't require needed keys, only check against infinite
|
||||
recursion etc.
|
||||
_flags (dict, optional): Internal work dict that should not be set externally.
|
||||
Raises:
|
||||
RuntimeError: If prototype has invalid structure.
|
||||
|
|
@ -570,14 +572,14 @@ def validate_prototype(prototype, protkey=None, protparents=None,
|
|||
|
||||
protkey = protkey and protkey.lower() or prototype.get('prototype_key', None)
|
||||
|
||||
if not bool(protkey):
|
||||
if strict and not bool(protkey):
|
||||
_flags['errors'].append("Prototype lacks a `prototype_key`.")
|
||||
protkey = "[UNSET]"
|
||||
|
||||
typeclass = prototype.get('typeclass')
|
||||
prototype_parent = prototype.get('prototype_parent', [])
|
||||
|
||||
if not (typeclass or prototype_parent):
|
||||
if strict and not (typeclass or prototype_parent):
|
||||
if is_prototype_base:
|
||||
_flags['errors'].append("Prototype {} requires `typeclass` "
|
||||
"or 'prototype_parent'.".format(protkey))
|
||||
|
|
@ -585,7 +587,7 @@ def validate_prototype(prototype, protkey=None, protparents=None,
|
|||
_flags['warnings'].append("Prototype {} can only be used as a mixin since it lacks "
|
||||
"a typeclass or a prototype_parent.".format(protkey))
|
||||
|
||||
if typeclass and typeclass not in get_all_typeclasses("evennia.objects.models.ObjectDB"):
|
||||
if strict and typeclass and typeclass not in get_all_typeclasses("evennia.objects.models.ObjectDB"):
|
||||
_flags['errors'].append(
|
||||
"Prototype {} is based on typeclass {}, which could not be imported!".format(
|
||||
protkey, typeclass))
|
||||
|
|
@ -615,7 +617,7 @@ def validate_prototype(prototype, protkey=None, protparents=None,
|
|||
_flags['typeclass'] = typeclass
|
||||
|
||||
# if we get back to the current level without a typeclass it's an error.
|
||||
if is_prototype_base and _flags['depth'] <= 0 and not _flags['typeclass']:
|
||||
if strict and is_prototype_base and _flags['depth'] <= 0 and not _flags['typeclass']:
|
||||
_flags['errors'].append("Prototype {} has no `typeclass` defined anywhere in its parent "
|
||||
"chain. Add `typeclass`, or a `prototype_parent` pointing to a "
|
||||
"prototype with a typeclass.".format(protkey))
|
||||
|
|
|
|||
|
|
@ -161,13 +161,14 @@ def _get_prototype(dic, prot, protparents):
|
|||
return prot
|
||||
|
||||
|
||||
def flatten_prototype(prototype):
|
||||
def flatten_prototype(prototype, validate=False):
|
||||
"""
|
||||
Produce a 'flattened' prototype, where all prototype parents in the inheritance tree have been
|
||||
merged into a final prototype.
|
||||
|
||||
Args:
|
||||
prototype (dict): Prototype to flatten. Its `prototype_parent` field will be parsed.
|
||||
validate (bool, optional): Validate for valid keys etc.
|
||||
|
||||
Returns:
|
||||
flattened (dict): The final, flattened prototype.
|
||||
|
|
@ -175,7 +176,8 @@ def flatten_prototype(prototype):
|
|||
"""
|
||||
if prototype:
|
||||
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
|
||||
protlib.validate_prototype(prototype, None, protparents, is_prototype_base=True)
|
||||
protlib.validate_prototype(prototype, None, protparents,
|
||||
is_prototype_base=validate, strict=validate)
|
||||
return _get_prototype(prototype, {}, protparents)
|
||||
return {}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue