From 58589126b8c3de4068f105db33f74442cf17d00a Mon Sep 17 00:00:00 2001 From: Griatch Date: Thu, 6 Apr 2017 19:53:57 +0200 Subject: [PATCH] Reworked spawner with new batch_add functionality for both tags and attributes. --- evennia/typeclasses/attributes.py | 64 +++++++++++++++---------------- evennia/typeclasses/tags.py | 9 ++--- evennia/utils/spawner.py | 37 +++++++++++------- 3 files changed, 60 insertions(+), 50 deletions(-) diff --git a/evennia/typeclasses/attributes.py b/evennia/typeclasses/attributes.py index 58755b4955..fc0741b716 100644 --- a/evennia/typeclasses/attributes.py +++ b/evennia/typeclasses/attributes.py @@ -508,47 +508,47 @@ class AttributeHandler(object): # update cache self._setcache(keystr, category, new_attr) - def batch_add(self, key, value, category=None, lockstring="", - strattr=False, accessing_obj=None, default_access=True): + def batch_add(self, *args, **kwargs): """ Batch-version of `add()`. This is more efficient than repeat-calling add when having many Attributes to add. Args: - key (list): A list of Attribute names to add. - value (list): A list of values. It must match the `key` - list. If `strattr` keyword is set, all entries *must* be - strings. - category (str, optional): The category for the Attribute. - The default `None` is the normal category used. - lockstring (str, optional): A lock string limiting access - to the attribute. - strattr (bool, optional): Make this a string-only Attribute. - This is only ever useful for optimization purposes. - accessing_obj (object, optional): An entity to check for - the `attrcreate` access-type. If not passing, this method - will be exited. - default_access (bool, optional): What access to grant if - `accessing_obj` is given but no lock of the type - `attrcreate` is defined on the Attribute in question. + indata (tuple): Tuples of varying length representing the + Attribute to add to this object. + - `(key, value)` + - `(key, value, category)` + - `(key, value, category, lockstring)` + - `(key, value, category, lockstring, default_access)` + + Kwargs: + strattr (bool): If `True`, value must be a string. This + will save the value without pickling which is less + flexible but faster to search (not often used except + internally). Raises: - RuntimeError: If `key` and `value` lists are not of the - same lengths. + RuntimeError: If trying to pass a non-iterable as argument. + + Notes: + The indata tuple order matters, so if you want a lockstring + but no category, set the category to `None`. This method + does not have the ability to check editing permissions like + normal .add does, and is mainly used internally. It does not + use the normal self.add but apply the Attributes directly + to the database. + """ - if accessing_obj and not self.obj.access(accessing_obj, self._attrcreate, default=default_access): - # check create access - return - - keys, values = make_iter(key), make_iter(value) - - if len(keys) != len(values): - raise RuntimeError("AttributeHandler.add(): key and value lists of different length: %s vs %s" % key, value) - category = category.strip().lower() if category is not None else None new_attrobjs = [] - for ikey, keystr in enumerate(keys): - keystr = keystr.strip().lower() - new_value = values[ikey] + strattr = kwargs.get('strattr', False) + for tup in args: + if not is_iter(tup) or len(tup) < 2: + raise RuntimeError("batch_add requires iterables as arguments (got %r)." % tup) + ntup = len(tup) + keystr = str(tup[0]).strip().lower() + new_value = tup[1] + category = str(tup[2]).strip().lower() if tup > 2 else None + lockstring = tup[3] if tup > 3 else "" attr_objs = self._getcache(keystr, category) diff --git a/evennia/typeclasses/tags.py b/evennia/typeclasses/tags.py index 1f1896f8a9..67f0662f28 100644 --- a/evennia/typeclasses/tags.py +++ b/evennia/typeclasses/tags.py @@ -357,7 +357,7 @@ class TagHandler(object): else: return [to_str(tag.db_key) for tag in tags] - def batch_add(self, *tuples): + def batch_add(self, *args): """ Batch-add tags from a list of tuples. @@ -374,20 +374,19 @@ class TagHandler(object): """ keys = defaultdict(list) data = {} - for tup in tuples: + for tup in args: tup = make_iter(tup) nlen = len(tup) - if nlen == 1: # just a key + if nlen == 1: # just a key keys[None].append(tup[0]) elif nlen == 2: keys[tup[1]].append(tup[0]) else: keys[tup[1]].append(tup[0]) - data[tup[1]] = tup[2] # overwrite previous + data[tup[1]] = tup[2] # overwrite previous for category, key in keys.iteritems(): self.add(tag=key, category=category, data=data.get(category, None)) - def __str__(self): return ",".join(self.all()) diff --git a/evennia/utils/spawner.py b/evennia/utils/spawner.py index c6e5e0dea3..1d9b3b38c4 100644 --- a/evennia/utils/spawner.py +++ b/evennia/utils/spawner.py @@ -16,7 +16,8 @@ GOBLIN = { "resists": ["cold", "poison"], "attacks": ["fists"], "weaknesses": ["fire", "light"] - "tags:": ["mob", "evil"] + "tags": ["mob", "evil", ('greenskin','mob')] + "args": [("weapon", "sword")] } ``` @@ -31,21 +32,28 @@ Possible keywords are: permissions - string or list of permission strings locks - a lock-string aliases - string or list of strings - tags - string or list of strings - ndb_ - value of a nattribute (ndb_ is stripped) exec - this is a string of python code to execute or a list of such codes. This can be used e.g. to trigger custom handlers on the object. The - execution environment contains 'evennia' for the library and 'obj' - for accessing the just created object. - any other keywords are interpreted as Attributes and their values. + execution namespace contains 'evennia' for the library and 'obj' + tags - string or list of strings or tuples `(tagstr, category)`. Plain + strings will be result in tags with no category (default tags). + args - tuple or list of tuples of Attributes to add. This form allows + more complex Attributes to be set. Tuples at least specify `(key, value)` + but can also specify up to `(key, value, category, lockstring)`. If + you want to specify a lockstring but not a category, set the category + to `None`. + ndb_ - value of a nattribute (ndb_ is stripped) + other - any other name is interpreted as the key of an Attribute with + its value. Such Attributes have no categories. Each value can also be a callable that takes no arguments. It should return the value to enter into the field and will be called every time -the prototype is used to spawn an object. +the prototype is used to spawn an object. Note, if you want to store +a callable in an Attribute, embed it in a tuple to the `args` keyword. -By specifying a prototype, the child will inherit all prototype slots -it does not explicitly define itself, while overloading those that it -does specify. +By specifying the "prototype" key, the prototype becomes a child of +that prototype, inheritng all prototype slots it does not explicitly +define itself, while overloading those that it does specify. ```python GOBLIN_WIZARD = { @@ -252,6 +260,8 @@ def spawn(*prototypes, **kwargs): alias_string = aliasval() if callable(aliasval) else aliasval tagval = prot.pop("tags", "") tags = tagval() if callable(tagval) else tagval + attrval = prot.pop("args", "") + attributes = attrval() if callable(tagval) else attrval exval = prot.pop("exec", "") execs = make_iter(exval() if callable(exval) else exval) @@ -261,9 +271,10 @@ def spawn(*prototypes, **kwargs): for key, value in prot.items() if key.startswith("ndb_")) # the rest are attributes - attributes = dict((key, value() if callable(value) else value) - for key, value in prot.items() - if not (key in _CREATE_OBJECT_KWARGS or key.startswith("ndb_"))) + simple_attributes = [(key, value) if callable(value) else value + for key, value in prot.items() if not key.startswith("ndb_")] + attributes = attributes + simple_attributes + attributes = [tup for tup in attributes if not tup[0] in _CREATE_OBJECT_KWARGS] # pack for call into _batch_create_object objsparams.append((create_kwargs, permission_string, lock_string,