From 7133492630936604c53ed6089da4fdbdc9a4030b Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 6 Oct 2018 19:00:54 +0200 Subject: [PATCH 1/4] Be more lenient with spawning old, more free-form prototypes --- evennia/commands/default/building.py | 3 +- evennia/prototypes/prototypes.py | 51 ++++++++++++++++++++++------ evennia/prototypes/spawner.py | 3 ++ 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/evennia/commands/default/building.py b/evennia/commands/default/building.py index bd4fb5e188..f0ae108f00 100644 --- a/evennia/commands/default/building.py +++ b/evennia/commands/default/building.py @@ -2889,7 +2889,8 @@ class CmdSpawn(COMMAND_DEFAULT_CLASS): "use the 'exec' prototype key.") return None try: - protlib.validate_prototype(prototype) + # we homogenize first, to be more lenient + protlib.validate_prototype(protlib.homogenize_prototype(prototype)) except RuntimeError as err: self.caller.msg(str(err)) return diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index 67eccaafd0..a03cbc519f 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -6,6 +6,8 @@ Handling storage of prototypes, both database-based ones (DBPrototypes) and thos """ import re +import hashlib +import time from ast import literal_eval from django.conf import settings from evennia.scripts.scripts import DefaultScript @@ -13,7 +15,7 @@ from evennia.objects.models import ObjectDB from evennia.utils.create import create_script from evennia.utils.utils import ( all_from_module, make_iter, is_iter, dbid_to_obj, callables_from_module, - get_all_typeclasses, to_str, dbref, justify) + get_all_typeclasses, to_str, dbref, justify, class_from_module) from evennia.locks.lockhandler import validate_lockstring, check_lockstring from evennia.utils import logger from evennia.utils import inlinefuncs, dbserialize @@ -47,8 +49,8 @@ class ValidationError(RuntimeError): def homogenize_prototype(prototype, custom_keys=None): """ - Homogenize the more free-form prototype (where undefined keys are non-category attributes) - into the stricter form using `attrs` required by the system. + Homogenize the more free-form prototype supported pre Evennia 0.7 into the stricter form. + Args: prototype (dict): Prototype. @@ -56,18 +58,45 @@ def homogenize_prototype(prototype, custom_keys=None): the default reserved keys. Returns: - homogenized (dict): Prototype where all non-identified keys grouped as attributes. + homogenized (dict): Prototype where all non-identified keys grouped as attributes and other + homogenizations like adding missing prototype_keys and setting a default typeclass. + """ reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ()) + attrs = list(prototype.get('attrs', [])) # break reference + tags = make_iter(prototype.get('tags', [])) + homogenized_tags = [] + homogenized = {} for key, val in prototype.items(): if key in reserved: - homogenized[key] = val + if key == 'tags': + for tag in tags: + if not is_iter(tag): + homogenized_tags.append((tag, None, None)) + else: + homogenized_tags.append(tag) + else: + homogenized[key] = val else: + # unassigned keys -> attrs attrs.append((key, val, None, '')) if attrs: homogenized['attrs'] = attrs + if homogenized_tags: + homogenized['tags'] = homogenized_tags + + # add required missing parts that had defaults before + + if "prototype_key" not in prototype: + # assign a random hash as key + homogenized["prototype_key"] = "prototype-{}".format( + hashlib.md5(str(time.time())).hexdigest()[:7]) + + if "typeclass" not in prototype: + homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS + return homogenized @@ -432,11 +461,13 @@ 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 (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)) + if strict and typeclass: + try: + class_from_module(typeclass) + except ImportError as err: + _flags['errors'].append( + "{}: Prototype {} is based on typeclass {}, which could not be imported!".format( + err, protkey, typeclass)) # recursively traverese prototype_parent chain diff --git a/evennia/prototypes/spawner.py b/evennia/prototypes/spawner.py index 7d876cf580..1ca7229bea 100644 --- a/evennia/prototypes/spawner.py +++ b/evennia/prototypes/spawner.py @@ -659,6 +659,9 @@ def spawn(*prototypes, **kwargs): # get available protparents protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()} + if not kwargs.get("only_validate"): + prototypes = [protlib.homogenize_prototype(prot) for prot in prototypes] + # overload module's protparents with specifically given protparents # we allow prototype_key to be the key of the protparent dict, to allow for module-level # prototype imports. We need to insert prototype_key in this case From b5c6a483ac70ccb25d0d046fc1602f8f89eeb273 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 6 Oct 2018 19:05:57 +0200 Subject: [PATCH 2/4] Fix bug in spawning with attributes --- evennia/prototypes/spawner.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/evennia/prototypes/spawner.py b/evennia/prototypes/spawner.py index 1ca7229bea..b22770aa94 100644 --- a/evennia/prototypes/spawner.py +++ b/evennia/prototypes/spawner.py @@ -733,8 +733,9 @@ def spawn(*prototypes, **kwargs): val = make_iter(prot.pop("attrs", [])) attributes = [] for (attrname, value, category, locks) in val: - attributes.append((attrname, init_spawn_value(val), category, locks)) + attributes.append((attrname, init_spawn_value(value), category, locks)) + print("attributes to spawn: IN: {}, OUT: {}".format(val, attributes)) simple_attributes = [] for key, value in ((key, value) for key, value in prot.items() if not (key.startswith("ndb_"))): From 8b1ab0bc8594513732fa5be0bd0431f1ad43c1ad Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 6 Oct 2018 19:06:44 +0200 Subject: [PATCH 3/4] Remove debug info --- evennia/prototypes/spawner.py | 1 - 1 file changed, 1 deletion(-) diff --git a/evennia/prototypes/spawner.py b/evennia/prototypes/spawner.py index b22770aa94..ce08944139 100644 --- a/evennia/prototypes/spawner.py +++ b/evennia/prototypes/spawner.py @@ -735,7 +735,6 @@ def spawn(*prototypes, **kwargs): for (attrname, value, category, locks) in val: attributes.append((attrname, init_spawn_value(value), category, locks)) - print("attributes to spawn: IN: {}, OUT: {}".format(val, attributes)) simple_attributes = [] for key, value in ((key, value) for key, value in prot.items() if not (key.startswith("ndb_"))): From 550a25820d55095fa6e9179955fa53f2f28d2fb7 Mon Sep 17 00:00:00 2001 From: Griatch Date: Sat, 6 Oct 2018 21:00:31 +0200 Subject: [PATCH 4/4] Fix unittests. Implement #1675. --- evennia/prototypes/prototypes.py | 2 +- evennia/prototypes/tests.py | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index a03cbc519f..550f1b2e7b 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -94,7 +94,7 @@ def homogenize_prototype(prototype, custom_keys=None): homogenized["prototype_key"] = "prototype-{}".format( hashlib.md5(str(time.time())).hexdigest()[:7]) - if "typeclass" not in prototype: + if "typeclass" not in prototype and "prototype_parent" not in prototype: homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS return homogenized diff --git a/evennia/prototypes/tests.py b/evennia/prototypes/tests.py index 1ad1d9ac47..3cf2e38c7b 100644 --- a/evennia/prototypes/tests.py +++ b/evennia/prototypes/tests.py @@ -384,8 +384,9 @@ class TestPrototypeStorage(EvenniaTest): prot3 = protlib.create_prototype(**self.prot3) # partial match - self.assertEqual(list(protlib.search_prototype("prot")), [prot1b, prot2, prot3]) - self.assertEqual(list(protlib.search_prototype(tags="foo1")), [prot1b, prot2, prot3]) + with mock.patch("evennia.prototypes.prototypes._MODULE_PROTOTYPES", {}): + self.assertEqual(list(protlib.search_prototype("prot")), [prot1b, prot2, prot3]) + self.assertEqual(list(protlib.search_prototype(tags="foo1")), [prot1b, prot2, prot3]) self.assertTrue(str(unicode(protlib.list_prototypes(self.char1))))