Reworked spawner with new batch_add functionality for both tags and attributes.

This commit is contained in:
Griatch 2017-04-06 19:53:57 +02:00
parent e34d32bd60
commit 58589126b8
3 changed files with 60 additions and 50 deletions

View file

@ -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)

View file

@ -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())

View file

@ -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_<name> - 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_<name> - 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,