Format code with black. Add makefile to run fmt/tests

This commit is contained in:
Griatch 2019-09-28 18:18:11 +02:00
parent d00bce9288
commit c2c7fa311a
299 changed files with 19037 additions and 11611 deletions

File diff suppressed because it is too large Load diff

View file

@ -49,6 +49,7 @@ _RE_DBREF = re.compile(r"\#[0-9]+")
# default protfuncs
def random(*args, **kwargs):
"""
Usage: $random()
@ -77,7 +78,7 @@ def left_justify(*args, **kwargs):
"""
if args:
return base_justify(args[0], align='l')
return base_justify(args[0], align="l")
return ""
@ -88,7 +89,7 @@ def right_justify(*args, **kwargs):
"""
if args:
return base_justify(args[0], align='r')
return base_justify(args[0], align="r")
return ""
@ -100,7 +101,7 @@ def center_justify(*args, **kwargs):
"""
if args:
return base_justify(args[0], align='c')
return base_justify(args[0], align="c")
return ""
@ -122,7 +123,7 @@ def full_justify(*args, **kwargs):
"""
if args:
return base_justify(args[0], align='f')
return base_justify(args[0], align="f")
return ""
@ -134,7 +135,7 @@ def protkey(*args, **kwargs):
"""
if args:
prototype = kwargs['prototype']
prototype = kwargs["prototype"]
return prototype[args[0].strip()]
@ -287,7 +288,7 @@ def _obj_search(*args, **kwargs):
retlist = []
if account:
for target in targets:
if target.access(account, target, 'control'):
if target.access(account, target, "control"):
retlist.append(target)
else:
retlist = targets
@ -297,15 +298,19 @@ def _obj_search(*args, **kwargs):
if not targets:
raise ValueError("$obj: Query '{}' gave no matches.".format(query))
if len(targets) > 1:
raise ValueError("$obj: Query '{query}' gave {nmatches} matches. Limit your "
"query or use $objlist instead.".format(
query=query, nmatches=len(targets)))
raise ValueError(
"$obj: Query '{query}' gave {nmatches} matches. Limit your "
"query or use $objlist instead.".format(query=query, nmatches=len(targets))
)
target = targets[0]
if account:
if not target.access(account, target, 'control'):
raise ValueError("$obj: Obj {target}(#{dbref} cannot be added - "
"Account {account} does not have 'control' access.".format(
target=target.key, dbref=target.id, account=account))
if not target.access(account, target, "control"):
raise ValueError(
"$obj: Obj {target}(#{dbref} cannot be added - "
"Account {account} does not have 'control' access.".format(
target=target.key, dbref=target.id, account=account
)
)
return target
@ -336,6 +341,6 @@ def dbref(*args, **kwargs):
Validate that a #dbref input is valid.
"""
if not args or len(args) < 1 or _RE_DBREF.match(args[0]) is None:
raise ValueError('$dbref requires a valid #dbref argument.')
raise ValueError("$dbref requires a valid #dbref argument.")
return obj(args[0])

View file

@ -13,8 +13,17 @@ from evennia.scripts.scripts import DefaultScript
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, class_from_module)
all_from_module,
make_iter,
is_iter,
dbid_to_obj,
callables_from_module,
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
@ -24,10 +33,25 @@ from evennia.utils.evtable import EvTable
_MODULE_PROTOTYPE_MODULES = {}
_MODULE_PROTOTYPES = {}
_PROTOTYPE_META_NAMES = (
"prototype_key", "prototype_desc", "prototype_tags", "prototype_locks", "prototype_parent")
"prototype_key",
"prototype_desc",
"prototype_tags",
"prototype_locks",
"prototype_parent",
)
_PROTOTYPE_RESERVED_KEYS = _PROTOTYPE_META_NAMES + (
"key", "aliases", "typeclass", "location", "home", "destination",
"permissions", "locks", "exec", "tags", "attrs")
"key",
"aliases",
"typeclass",
"location",
"home",
"destination",
"permissions",
"locks",
"exec",
"tags",
"attrs",
)
_PROTOTYPE_TAG_CATEGORY = "from_prototype"
_PROTOTYPE_TAG_META_CATEGORY = "db_prototype"
PROT_FUNCS = {}
@ -41,6 +65,7 @@ class ValidationError(RuntimeError):
"""
Raised on prototype validation errors
"""
pass
@ -61,14 +86,14 @@ def homogenize_prototype(prototype, custom_keys=None):
"""
reserved = _PROTOTYPE_RESERVED_KEYS + (custom_keys or ())
attrs = list(prototype.get('attrs', [])) # break reference
tags = make_iter(prototype.get('tags', []))
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:
if key == 'tags':
if key == "tags":
for tag in tags:
if not is_iter(tag):
homogenized_tags.append((tag, None, None))
@ -78,18 +103,19 @@ def homogenize_prototype(prototype, custom_keys=None):
homogenized[key] = val
else:
# unassigned keys -> attrs
attrs.append((key, val, None, ''))
attrs.append((key, val, None, ""))
if attrs:
homogenized['attrs'] = attrs
homogenized["attrs"] = attrs
if homogenized_tags:
homogenized['tags'] = 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(bytes(str(time.time()), 'utf-8')).hexdigest()[:7])
hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:7]
)
if "typeclass" not in prototype and "prototype_parent" not in prototype:
homogenized["typeclass"] = settings.BASE_OBJECT_TYPECLASS
@ -106,19 +132,25 @@ for mod in settings.PROTOTYPE_MODULES:
for variable_name, prot in all_from_module(mod).items():
if isinstance(prot, dict):
if "prototype_key" not in prot:
prot['prototype_key'] = variable_name.lower()
prots.append((prot['prototype_key'], homogenize_prototype(prot)))
prot["prototype_key"] = variable_name.lower()
prots.append((prot["prototype_key"], homogenize_prototype(prot)))
# assign module path to each prototype_key for easy reference
_MODULE_PROTOTYPE_MODULES.update({prototype_key.lower(): mod for prototype_key, _ in prots})
# make sure the prototype contains all meta info
for prototype_key, prot in prots:
actual_prot_key = prot.get('prototype_key', prototype_key).lower()
prot.update({
"prototype_key": actual_prot_key,
"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();edit:false()"),
"prototype_tags": list(set(make_iter(prot.get('prototype_tags', [])) + ["module"]))})
actual_prot_key = prot.get("prototype_key", prototype_key).lower()
prot.update(
{
"prototype_key": actual_prot_key,
"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();edit:false()"
),
"prototype_tags": list(set(make_iter(prot.get("prototype_tags", [])) + ["module"])),
}
)
_MODULE_PROTOTYPES[actual_prot_key] = prot
@ -129,19 +161,20 @@ class DbPrototype(DefaultScript):
"""
This stores a single prototype, in an Attribute `prototype`.
"""
def at_script_creation(self):
self.key = "empty prototype" # prototype_key
self.desc = "A prototype" # prototype_desc (.tags are used for prototype_tags)
self.db.prototype = {} # actual prototype
self.desc = "A prototype" # prototype_desc (.tags are used for prototype_tags)
self.db.prototype = {} # actual prototype
@property
def prototype(self):
"Make sure to decouple from db!"
return dbserialize.deserialize(self.attributes.get('prototype', {}))
return dbserialize.deserialize(self.attributes.get("prototype", {}))
@prototype.setter
def prototype(self, prototype):
self.attributes.add('prototype', prototype)
self.attributes.add("prototype", prototype)
# Prototype manager functions
@ -174,7 +207,7 @@ def save_prototype(prototype):
if is_iter(inp):
# already a tuple/list, use as-is
return inp
return (inp, ) + args
return (inp,) + args
prototype_key = in_prototype.get("prototype_key")
if not prototype_key:
@ -185,25 +218,31 @@ def save_prototype(prototype):
# we can't edit a prototype defined in a module
if prototype_key in _MODULE_PROTOTYPES:
mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key, "N/A")
raise PermissionError("{} is a read-only prototype "
"(defined as code in {}).".format(prototype_key, mod))
raise PermissionError(
"{} is a read-only prototype " "(defined as code in {}).".format(prototype_key, mod)
)
# make sure meta properties are included with defaults
stored_prototype = DbPrototype.objects.filter(db_key=prototype_key)
prototype = stored_prototype[0].prototype if stored_prototype else {}
in_prototype['prototype_desc'] = in_prototype.get("prototype_desc", prototype.get("prototype_desc", ""))
in_prototype["prototype_desc"] = in_prototype.get(
"prototype_desc", prototype.get("prototype_desc", "")
)
prototype_locks = in_prototype.get(
"prototype_locks", prototype.get('prototype_locks', "spawn:all();edit:perm(Admin)"))
"prototype_locks", prototype.get("prototype_locks", "spawn:all();edit:perm(Admin)")
)
is_valid, err = validate_lockstring(prototype_locks)
if not is_valid:
raise ValidationError("Lock error: {}".format(err))
in_prototype['prototype_locks'] = prototype_locks
in_prototype["prototype_locks"] = prototype_locks
prototype_tags = [
_to_batchtuple(tag, _PROTOTYPE_TAG_META_CATEGORY)
for tag in make_iter(in_prototype.get("prototype_tags",
prototype.get('prototype_tags', [])))]
for tag in make_iter(
in_prototype.get("prototype_tags", prototype.get("prototype_tags", []))
)
]
in_prototype["prototype_tags"] = prototype_tags
prototype.update(in_prototype)
@ -211,21 +250,27 @@ def save_prototype(prototype):
if stored_prototype:
# edit existing prototype
stored_prototype = stored_prototype[0]
stored_prototype.desc = prototype['prototype_desc']
stored_prototype.desc = prototype["prototype_desc"]
if prototype_tags:
stored_prototype.tags.clear(category=_PROTOTYPE_TAG_CATEGORY)
stored_prototype.tags.batch_add(*prototype['prototype_tags'])
stored_prototype.locks.add(prototype['prototype_locks'])
stored_prototype.attributes.add('prototype', prototype)
stored_prototype.tags.batch_add(*prototype["prototype_tags"])
stored_prototype.locks.add(prototype["prototype_locks"])
stored_prototype.attributes.add("prototype", prototype)
else:
# create a new prototype
stored_prototype = create_script(
DbPrototype, key=prototype_key, desc=prototype['prototype_desc'], persistent=True,
locks=prototype_locks, tags=prototype['prototype_tags'],
attributes=[("prototype", prototype)])
DbPrototype,
key=prototype_key,
desc=prototype["prototype_desc"],
persistent=True,
locks=prototype_locks,
tags=prototype["prototype_tags"],
attributes=[("prototype", prototype)],
)
return stored_prototype.prototype
create_prototype = save_prototype # alias
create_prototype = save_prototype # alias
def delete_prototype(prototype_key, caller=None):
@ -244,8 +289,9 @@ def delete_prototype(prototype_key, caller=None):
"""
if prototype_key in _MODULE_PROTOTYPES:
mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key.lower(), "N/A")
raise PermissionError("{} is a read-only prototype "
"(defined as code in {}).".format(prototype_key, mod))
raise PermissionError(
"{} is a read-only prototype " "(defined as code in {}).".format(prototype_key, mod)
)
stored_prototype = DbPrototype.objects.filter(db_key__iexact=prototype_key)
@ -254,9 +300,11 @@ def delete_prototype(prototype_key, caller=None):
stored_prototype = stored_prototype[0]
if caller:
if not stored_prototype.access(caller, 'edit'):
raise PermissionError("{} needs explicit 'edit' permissions to "
"delete prototype {}.".format(caller, prototype_key))
if not stored_prototype.access(caller, "edit"):
raise PermissionError(
"{} needs explicit 'edit' permissions to "
"delete prototype {}.".format(caller, prototype_key)
)
stored_prototype.delete()
return True
@ -294,9 +342,11 @@ def search_prototype(key=None, tags=None, require_single=False):
if tags:
# use tags to limit selection
tagset = set(tags)
mod_matches = {prototype_key: prototype
for prototype_key, prototype in _MODULE_PROTOTYPES.items()
if tagset.intersection(prototype.get("prototype_tags", []))}
mod_matches = {
prototype_key: prototype
for prototype_key, prototype in _MODULE_PROTOTYPES.items()
if tagset.intersection(prototype.get("prototype_tags", []))
}
else:
mod_matches = _MODULE_PROTOTYPES
@ -306,8 +356,11 @@ def search_prototype(key=None, tags=None, require_single=False):
module_prototypes = [mod_matches[key]]
else:
# fuzzy matching
module_prototypes = [prototype for prototype_key, prototype in mod_matches.items()
if key in prototype_key]
module_prototypes = [
prototype
for prototype_key, prototype in mod_matches.items()
if key in prototype_key
]
else:
module_prototypes = [match for match in mod_matches.values()]
@ -322,8 +375,9 @@ def search_prototype(key=None, tags=None, require_single=False):
db_matches = DbPrototype.objects.all().order_by("id")
if key:
# exact or partial match on key
db_matches = (db_matches.filter(db_key=key) or
db_matches.filter(db_key__icontains=key)).order_by("id")
db_matches = (
db_matches.filter(db_key=key) or db_matches.filter(db_key__icontains=key)
).order_by("id")
# return prototype
db_prototypes = [dbprot.prototype for dbprot in db_matches]
@ -332,8 +386,9 @@ def search_prototype(key=None, tags=None, require_single=False):
if nmatches > 1 and key:
key = key.lower()
# avoid duplicates if an exact match exist between the two types
filter_matches = [mta for mta in matches
if mta.get('prototype_key') and mta['prototype_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) < nmatches:
matches = filter_matches
@ -381,20 +436,22 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
# get use-permissions of readonly attributes (edit is always False)
display_tuples = []
for prototype in sorted(prototypes, key=lambda d: d.get('prototype_key', '')):
for prototype in sorted(prototypes, key=lambda d: d.get("prototype_key", "")):
lock_use = caller.locks.check_lockstring(
caller, prototype.get('prototype_locks', ''), access_type='spawn', default=True)
caller, prototype.get("prototype_locks", ""), access_type="spawn", default=True
)
if not show_non_use and not lock_use:
continue
if prototype.get('prototype_key', '') in _MODULE_PROTOTYPES:
if prototype.get("prototype_key", "") in _MODULE_PROTOTYPES:
lock_edit = False
else:
lock_edit = caller.locks.check_lockstring(
caller, prototype.get('prototype_locks', ''), access_type='edit', default=True)
caller, prototype.get("prototype_locks", ""), access_type="edit", default=True
)
if not show_non_edit and not lock_edit:
continue
ptags = []
for ptag in prototype.get('prototype_tags', []):
for ptag in prototype.get("prototype_tags", []):
if is_iter(ptag):
if len(ptag) > 1:
ptags.append("{} (category: {}".format(ptag[0], ptag[1]))
@ -404,10 +461,13 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
ptags.append(str(ptag))
display_tuples.append(
(prototype.get('prototype_key', '<unset>'),
prototype.get('prototype_desc', '<unset>'),
"{}/{}".format('Y' if lock_use else 'N', 'Y' if lock_edit else 'N'),
",".join(ptags)))
(
prototype.get("prototype_key", "<unset>"),
prototype.get("prototype_desc", "<unset>"),
"{}/{}".format("Y" if lock_use else "N", "Y" if lock_edit else "N"),
",".join(ptags),
)
)
if not display_tuples:
return ""
@ -419,13 +479,14 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
table = EvTable("Key", "Desc", "Spawn/Edit", "Tags", table=table, crop=True, width=width)
table.reformat_column(0, width=22)
table.reformat_column(1, width=29)
table.reformat_column(2, width=11, align='c')
table.reformat_column(2, width=11, align="c")
table.reformat_column(3, width=16)
return table
def validate_prototype(prototype, protkey=None, protparents=None,
is_prototype_base=True, strict=True, _flags=None):
def validate_prototype(
prototype, protkey=None, protparents=None, is_prototype_base=True, strict=True, _flags=None
):
"""
Run validation on a prototype, checking for inifinite regress.
@ -453,83 +514,97 @@ def validate_prototype(prototype, protkey=None, protparents=None,
_flags = {"visited": [], "depth": 0, "typeclass": False, "errors": [], "warnings": []}
if not protparents:
protparents = {prototype.get('prototype_key', "").lower(): prototype
for prototype in search_prototype()}
protparents = {
prototype.get("prototype_key", "").lower(): prototype
for prototype in search_prototype()
}
protkey = protkey and protkey.lower() or prototype.get('prototype_key', None)
protkey = protkey and protkey.lower() or prototype.get("prototype_key", None)
if strict and not bool(protkey):
_flags['errors'].append("Prototype lacks a `prototype_key`.")
_flags["errors"].append("Prototype lacks a `prototype_key`.")
protkey = "[UNSET]"
typeclass = prototype.get('typeclass')
prototype_parent = prototype.get('prototype_parent', [])
typeclass = prototype.get("typeclass")
prototype_parent = prototype.get("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))
_flags["errors"].append(
"Prototype {} requires `typeclass` " "or 'prototype_parent'.".format(protkey)
)
else:
_flags['warnings'].append("Prototype {} can only be used as a mixin since it lacks "
"a typeclass or a prototype_parent.".format(protkey))
_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:
try:
class_from_module(typeclass)
except ImportError as err:
_flags['errors'].append(
_flags["errors"].append(
"{}: Prototype {} is based on typeclass {}, which could not be imported!".format(
err, protkey, typeclass))
err, protkey, typeclass
)
)
# recursively traverese prototype_parent chain
for protstring in make_iter(prototype_parent):
protstring = protstring.lower()
if protkey is not None and protstring == protkey:
_flags['errors'].append("Prototype {} tries to parent itself.".format(protkey))
_flags["errors"].append("Prototype {} tries to parent itself.".format(protkey))
protparent = protparents.get(protstring)
if not protparent:
_flags['errors'].append("Prototype {}'s prototype_parent '{}' was not found.".format(
(protkey, protstring)))
if id(prototype) in _flags['visited']:
_flags['errors'].append(
"{} has infinite nesting of prototypes.".format(protkey or prototype))
_flags["errors"].append(
"Prototype {}'s prototype_parent '{}' was not found.".format((protkey, protstring))
)
if id(prototype) in _flags["visited"]:
_flags["errors"].append(
"{} has infinite nesting of prototypes.".format(protkey or prototype)
)
if _flags['errors']:
raise RuntimeError("Error: " + "\nError: ".join(_flags['errors']))
_flags['visited'].append(id(prototype))
_flags['depth'] += 1
validate_prototype(protparent, protstring, protparents,
is_prototype_base=is_prototype_base, _flags=_flags)
_flags['visited'].pop()
_flags['depth'] -= 1
if _flags["errors"]:
raise RuntimeError("Error: " + "\nError: ".join(_flags["errors"]))
_flags["visited"].append(id(prototype))
_flags["depth"] += 1
validate_prototype(
protparent, protstring, protparents, is_prototype_base=is_prototype_base, _flags=_flags
)
_flags["visited"].pop()
_flags["depth"] -= 1
if typeclass and not _flags['typeclass']:
_flags['typeclass'] = typeclass
if typeclass and not _flags["typeclass"]:
_flags["typeclass"] = typeclass
# if we get back to the current level without a typeclass it's an error.
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\n "
"chain. Add `typeclass`, or a `prototype_parent` pointing to a "
"prototype with a typeclass.".format(protkey))
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\n "
"chain. Add `typeclass`, or a `prototype_parent` pointing to a "
"prototype with a typeclass.".format(protkey)
)
if _flags['depth'] <= 0:
if _flags['errors']:
raise RuntimeError("Error: " + "\nError: ".join(_flags['errors']))
if _flags['warnings']:
raise RuntimeWarning("Warning: " + "\nWarning: ".join(_flags['warnings']))
if _flags["depth"] <= 0:
if _flags["errors"]:
raise RuntimeError("Error: " + "\nError: ".join(_flags["errors"]))
if _flags["warnings"]:
raise RuntimeWarning("Warning: " + "\nWarning: ".join(_flags["warnings"]))
# make sure prototype_locks are set to defaults
prototype_locks = [lstring.split(":", 1)
for lstring in prototype.get("prototype_locks", "").split(';')
if ":" in lstring]
prototype_locks = [
lstring.split(":", 1)
for lstring in prototype.get("prototype_locks", "").split(";")
if ":" in lstring
]
locktypes = [tup[0].strip() for tup in prototype_locks]
if "spawn" not in locktypes:
prototype_locks.append(("spawn", "all()"))
if "edit" not in locktypes:
prototype_locks.append(("edit", "all()"))
prototype_locks = ";".join(":".join(tup) for tup in prototype_locks)
prototype['prototype_locks'] = prototype_locks
prototype["prototype_locks"] = prototype_locks
# Protfunc parsing (in-prototype functions)
@ -582,8 +657,8 @@ def protfunc_parser(value, available_functions=None, testing=False, stacktrace=F
available_functions = PROT_FUNCS if available_functions is None else available_functions
result = inlinefuncs.parse_inlinefunc(
value, available_funcs=available_functions,
stacktrace=stacktrace, testing=testing, **kwargs)
value, available_funcs=available_functions, stacktrace=stacktrace, testing=testing, **kwargs
)
err = None
try:
@ -599,6 +674,7 @@ def protfunc_parser(value, available_functions=None, testing=False, stacktrace=F
# Various prototype utilities
def format_available_protfuncs():
"""
Get all protfuncs in a pretty-formatted form.
@ -608,8 +684,11 @@ def format_available_protfuncs():
"""
out = []
for protfunc_name, protfunc in PROT_FUNCS.items():
out.append("- |c${name}|n - |W{docs}".format(
name=protfunc_name, docs=protfunc.__doc__.strip().replace("\n", "")))
out.append(
"- |c${name}|n - |W{docs}".format(
name=protfunc_name, docs=protfunc.__doc__.strip().replace("\n", "")
)
)
return justify("\n".join(out), indent=8)
@ -628,61 +707,70 @@ def prototype_to_str(prototype):
|c-desc|n: {prototype_desc}
|cprototype-parent:|n {prototype_parent}
\n""".format(
prototype_key=prototype.get('prototype_key', '|r[UNSET](required)|n'),
prototype_tags=prototype.get('prototype_tags', '|wNone|n'),
prototype_locks=prototype.get('prototype_locks', '|wNone|n'),
prototype_desc=prototype.get('prototype_desc', '|wNone|n'),
prototype_parent=prototype.get('prototype_parent', '|wNone|n'))
prototype_key=prototype.get("prototype_key", "|r[UNSET](required)|n"),
prototype_tags=prototype.get("prototype_tags", "|wNone|n"),
prototype_locks=prototype.get("prototype_locks", "|wNone|n"),
prototype_desc=prototype.get("prototype_desc", "|wNone|n"),
prototype_parent=prototype.get("prototype_parent", "|wNone|n"),
)
key = prototype.get('key', '')
key = prototype.get("key", "")
if key:
key = "|ckey:|n {key}".format(key=key)
aliases = prototype.get("aliases", '')
aliases = prototype.get("aliases", "")
if aliases:
aliases = "|caliases:|n {aliases}".format(
aliases=", ".join(aliases))
attrs = prototype.get("attrs", '')
aliases = "|caliases:|n {aliases}".format(aliases=", ".join(aliases))
attrs = prototype.get("attrs", "")
if attrs:
out = []
for (attrkey, value, category, locks) in attrs:
locks = ", ".join(lock for lock in locks if lock)
category = "|ccategory:|n {}".format(category) if category else ''
category = "|ccategory:|n {}".format(category) if category else ""
cat_locks = ""
if category or locks:
cat_locks = " (|ccategory:|n {category}, ".format(
category=category if category else "|wNone|n")
category=category if category else "|wNone|n"
)
out.append(
"{attrkey}{cat_locks} |c=|n {value}".format(
attrkey=attrkey,
cat_locks=cat_locks,
locks=locks if locks else "|wNone|n",
value=value))
attrkey=attrkey,
cat_locks=cat_locks,
locks=locks if locks else "|wNone|n",
value=value,
)
)
attrs = "|cattrs:|n\n {attrs}".format(attrs="\n ".join(out))
tags = prototype.get('tags', '')
tags = prototype.get("tags", "")
if tags:
out = []
for (tagkey, category, data) in tags:
out.append("{tagkey} (category: {category}{dat})".format(
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""))
out.append(
"{tagkey} (category: {category}{dat})".format(
tagkey=tagkey, category=category, dat=", data: {}".format(data) if data else ""
)
)
tags = "|ctags:|n\n {tags}".format(tags=", ".join(out))
locks = prototype.get('locks', '')
locks = prototype.get("locks", "")
if locks:
locks = "|clocks:|n\n {locks}".format(locks=locks)
permissions = prototype.get("permissions", '')
permissions = prototype.get("permissions", "")
if permissions:
permissions = "|cpermissions:|n {perms}".format(perms=", ".join(permissions))
location = prototype.get("location", '')
location = prototype.get("location", "")
if location:
location = "|clocation:|n {location}".format(location=location)
home = prototype.get("home", '')
home = prototype.get("home", "")
if home:
home = "|chome:|n {home}".format(home=home)
destination = prototype.get("destination", '')
destination = prototype.get("destination", "")
if destination:
destination = "|cdestination:|n {destination}".format(destination=destination)
body = "\n".join(part for part in (key, aliases, attrs, tags, locks, permissions,
location, home, destination) if part)
body = "\n".join(
part
for part in (key, aliases, attrs, tags, locks, permissions, location, home, destination)
if part
)
return header.lstrip() + body.strip()
@ -700,11 +788,12 @@ def check_permission(prototype_key, action, default=True):
passes (bool): If permission for action is granted or not.
"""
if action == 'edit':
if action == "edit":
if prototype_key in _MODULE_PROTOTYPES:
mod = _MODULE_PROTOTYPE_MODULES.get(prototype_key, "N/A")
logger.log_err("{} is a read-only prototype "
"(defined as code in {}).".format(prototype_key, mod))
logger.log_err(
"{} is a read-only prototype " "(defined as code in {}).".format(prototype_key, mod)
)
return False
prototype = search_prototype(key=prototype_key)
@ -715,8 +804,7 @@ def check_permission(prototype_key, action, default=True):
lockstring = prototype.get("prototype_locks")
if lockstring:
return check_lockstring(None, lockstring,
default=default, access_type=action)
return check_lockstring(None, lockstring, default=default, access_type=action)
return default
@ -753,8 +841,9 @@ def value_to_obj_or_any(value):
stype = type(value)
if is_iter(value):
if stype == dict:
return {value_to_obj_or_any(key):
value_to_obj_or_any(val) for key, val in value.items()}
return {
value_to_obj_or_any(key): value_to_obj_or_any(val) for key, val in value.items()
}
else:
return stype([value_to_obj_or_any(val) for val in value])
obj = dbid_to_obj(value, ObjectDB)

View file

@ -141,18 +141,33 @@ from evennia.objects.models import ObjectDB
from evennia.utils.utils import make_iter, is_iter
from evennia.prototypes import prototypes as protlib
from evennia.prototypes.prototypes import (
value_to_obj, value_to_obj_or_any, init_spawn_value, _PROTOTYPE_TAG_CATEGORY)
value_to_obj,
value_to_obj_or_any,
init_spawn_value,
_PROTOTYPE_TAG_CATEGORY,
)
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
_PROTOTYPE_ROOT_NAMES = ('typeclass', 'key', 'aliases', 'attrs', 'tags', 'locks', 'permissions',
'location', 'home', 'destination')
_PROTOTYPE_ROOT_NAMES = (
"typeclass",
"key",
"aliases",
"attrs",
"tags",
"locks",
"permissions",
"location",
"home",
"destination",
)
_NON_CREATE_KWARGS = _CREATE_OBJECT_KWARGS + _PROTOTYPE_META_NAMES
# Helper
def _get_prototype(inprot, protparents, uninherited=None, _workprot=None):
"""
Recursively traverse a prototype dictionary, including multiple
@ -170,6 +185,7 @@ def _get_prototype(inprot, protparents, uninherited=None, _workprot=None):
`prototype_parent` key is removed).
"""
def _inherit_tags(old_tags, new_tags):
old = {(tup[0], tup[1]): tup for tup in old_tags}
new = {(tup[0], tup[1]): tup for tup in new_tags}
@ -187,22 +203,21 @@ def _get_prototype(inprot, protparents, uninherited=None, _workprot=None):
# move backwards through the inheritance
for prototype in make_iter(inprot["prototype_parent"]):
# Build the prot dictionary in reverse order, overloading
new_prot = _get_prototype(protparents.get(prototype.lower(), {}),
protparents, _workprot=_workprot)
new_prot = _get_prototype(
protparents.get(prototype.lower(), {}), protparents, _workprot=_workprot
)
# attrs, tags have internal structure that should be inherited separately
new_prot['attrs'] = _inherit_attrs(
_workprot.get("attrs", {}), new_prot.get("attrs", {}))
new_prot['tags'] = _inherit_tags(
_workprot.get("tags", {}), new_prot.get("tags", {}))
new_prot["attrs"] = _inherit_attrs(
_workprot.get("attrs", {}), new_prot.get("attrs", {})
)
new_prot["tags"] = _inherit_tags(_workprot.get("tags", {}), new_prot.get("tags", {}))
_workprot.update(new_prot)
# the inprot represents a higher level (a child prot), which should override parents
inprot['attrs'] = _inherit_attrs(
_workprot.get("attrs", {}), inprot.get("attrs", {}))
inprot['tags'] = _inherit_tags(
_workprot.get("tags", {}), inprot.get("tags", {}))
inprot["attrs"] = _inherit_attrs(_workprot.get("attrs", {}), inprot.get("attrs", {}))
inprot["tags"] = _inherit_tags(_workprot.get("tags", {}), inprot.get("tags", {}))
_workprot.update(inprot)
if uninherited:
# put back the parts that should not be inherited
@ -227,16 +242,19 @@ def flatten_prototype(prototype, validate=False):
if prototype:
prototype = protlib.homogenize_prototype(prototype)
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
protlib.validate_prototype(prototype, None, protparents,
is_prototype_base=validate, strict=validate)
return _get_prototype(prototype, protparents,
uninherited={"prototype_key": prototype.get("prototype_key")})
protparents = {prot["prototype_key"].lower(): prot for prot in protlib.search_prototype()}
protlib.validate_prototype(
prototype, None, protparents, is_prototype_base=validate, strict=validate
)
return _get_prototype(
prototype, protparents, uninherited={"prototype_key": prototype.get("prototype_key")}
)
return {}
# obj-related prototype functions
def prototype_from_object(obj):
"""
Guess a minimal prototype from an existing object.
@ -257,43 +275,49 @@ def prototype_from_object(obj):
if not prot or len(prot) > 1:
# no unambiguous prototype found - build new prototype
prot = {}
prot['prototype_key'] = "From-Object-{}-{}".format(
obj.key, hashlib.md5(bytes(str(time.time()), 'utf-8')).hexdigest()[:7])
prot['prototype_desc'] = "Built from {}".format(str(obj))
prot['prototype_locks'] = "spawn:all();edit:all()"
prot['prototype_tags'] = []
prot["prototype_key"] = "From-Object-{}-{}".format(
obj.key, hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:7]
)
prot["prototype_desc"] = "Built from {}".format(str(obj))
prot["prototype_locks"] = "spawn:all();edit:all()"
prot["prototype_tags"] = []
else:
prot = prot[0]
prot['key'] = obj.db_key or hashlib.md5(bytes(str(time.time()), 'utf-8')).hexdigest()[:6]
prot['typeclass'] = obj.db_typeclass_path
prot["key"] = obj.db_key or hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:6]
prot["typeclass"] = obj.db_typeclass_path
location = obj.db_location
if location:
prot['location'] = location.dbref
prot["location"] = location.dbref
home = obj.db_home
if home:
prot['home'] = home.dbref
prot["home"] = home.dbref
destination = obj.db_destination
if destination:
prot['destination'] = destination.dbref
prot["destination"] = destination.dbref
locks = obj.locks.all()
if locks:
prot['locks'] = ";".join(locks)
prot["locks"] = ";".join(locks)
perms = obj.permissions.get(return_list=True)
if perms:
prot['permissions'] = make_iter(perms)
prot["permissions"] = make_iter(perms)
aliases = obj.aliases.get(return_list=True)
if aliases:
prot['aliases'] = aliases
tags = sorted([(tag.db_key, tag.db_category, tag.db_data)
for tag in obj.tags.all(return_objs=True)])
prot["aliases"] = aliases
tags = sorted(
[(tag.db_key, tag.db_category, tag.db_data) for tag in obj.tags.all(return_objs=True)]
)
if tags:
prot['tags'] = tags
attrs = sorted([(attr.key, attr.value, attr.category, ';'.join(attr.locks.all()))
for attr in obj.attributes.all()])
prot["tags"] = tags
attrs = sorted(
[
(attr.key, attr.value, attr.category, ";".join(attr.locks.all()))
for attr in obj.attributes.all()
]
)
if attrs:
prot['attrs'] = attrs
prot["attrs"] = attrs
return prot
@ -320,6 +344,7 @@ def prototype_diff(prototype1, prototype2, maxdepth=2):
instruction can be one of "REMOVE", "ADD", "UPDATE" or "KEEP".
"""
def _recursive_diff(old, new, depth=0):
old_type = type(old)
@ -330,8 +355,9 @@ def prototype_diff(prototype1, prototype2, maxdepth=2):
if depth < maxdepth and old_type == dict:
return {key: (part, None, "REMOVE") for key, part in old.items()}
elif depth < maxdepth and is_iter(old):
return {part[0] if is_iter(part) else part:
(part, None, "REMOVE") for part in old}
return {
part[0] if is_iter(part) else part: (part, None, "REMOVE") for part in old
}
return (old, new, "REMOVE")
elif not old and new:
if depth < maxdepth and new_type == dict:
@ -344,14 +370,18 @@ def prototype_diff(prototype1, prototype2, maxdepth=2):
return (old, new, "UPDATE")
elif depth < maxdepth and new_type == dict:
all_keys = set(list(old.keys()) + list(new.keys()))
return {key: _recursive_diff(old.get(key), new.get(key), depth=depth + 1)
for key in all_keys}
return {
key: _recursive_diff(old.get(key), new.get(key), depth=depth + 1)
for key in all_keys
}
elif depth < maxdepth and is_iter(new):
old_map = {part[0] if is_iter(part) else part: part for part in old}
new_map = {part[0] if is_iter(part) else part: part for part in new}
all_keys = set(list(old_map.keys()) + list(new_map.keys()))
return {key: _recursive_diff(old_map.get(key), new_map.get(key), depth=depth + 1)
for key in all_keys}
return {
key: _recursive_diff(old_map.get(key), new_map.get(key), depth=depth + 1)
for key in all_keys
}
elif old != new:
return (old, new, "UPDATE")
else:
@ -390,7 +420,7 @@ def flatten_diff(diff):
- Mix REMOVE, KEEP, UPDATE, ADD -> REPLACE
"""
valid_instructions = ('KEEP', 'REMOVE', 'ADD', 'UPDATE')
valid_instructions = ("KEEP", "REMOVE", "ADD", "UPDATE")
def _get_all_nested_diff_instructions(diffpart):
"Started for each root key, returns all instructions nested under it"
@ -403,8 +433,10 @@ def flatten_diff(diff):
for val in diffpart.values():
out.extend(_get_all_nested_diff_instructions(val))
else:
raise RuntimeError("Diff contains non-dicts that are not on the "
"form (old, new, inst): {}".format(diffpart))
raise RuntimeError(
"Diff contains non-dicts that are not on the "
"form (old, new, inst): {}".format(diffpart)
)
return out
flat_diff = {}
@ -480,7 +512,7 @@ def batch_update_objects_with_prototype(prototype, diff=None, objects=None):
else:
new_prototype = prototype
prototype_key = new_prototype['prototype_key']
prototype_key = new_prototype["prototype_key"]
if not objects:
objects = ObjectDB.objects.get_by_tag(prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
@ -504,7 +536,7 @@ def batch_update_objects_with_prototype(prototype, diff=None, objects=None):
obj.tags.add(prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
for key, directive in diff.items():
if directive in ('UPDATE', 'REPLACE'):
if directive in ("UPDATE", "REPLACE"):
if key in _PROTOTYPE_META_NAMES:
# prototype meta keys are not stored on-object
@ -513,72 +545,80 @@ def batch_update_objects_with_prototype(prototype, diff=None, objects=None):
val = new_prototype[key]
do_save = True
if key == 'key':
if key == "key":
obj.db_key = init_spawn_value(val, str)
elif key == 'typeclass':
elif key == "typeclass":
obj.db_typeclass_path = init_spawn_value(val, str)
elif key == 'location':
elif key == "location":
obj.db_location = init_spawn_value(val, value_to_obj)
elif key == 'home':
elif key == "home":
obj.db_home = init_spawn_value(val, value_to_obj)
elif key == 'destination':
elif key == "destination":
obj.db_destination = init_spawn_value(val, value_to_obj)
elif key == 'locks':
if directive == 'REPLACE':
elif key == "locks":
if directive == "REPLACE":
obj.locks.clear()
obj.locks.add(init_spawn_value(val, str))
elif key == 'permissions':
if directive == 'REPLACE':
elif key == "permissions":
if directive == "REPLACE":
obj.permissions.clear()
obj.permissions.batch_add(*(init_spawn_value(perm, str) for perm in val))
elif key == 'aliases':
if directive == 'REPLACE':
elif key == "aliases":
if directive == "REPLACE":
obj.aliases.clear()
obj.aliases.batch_add(*(init_spawn_value(alias, str) for alias in val))
elif key == 'tags':
if directive == 'REPLACE':
elif key == "tags":
if directive == "REPLACE":
obj.tags.clear()
obj.tags.batch_add(*(
(init_spawn_value(ttag, str), tcategory, tdata)
for ttag, tcategory, tdata in val))
elif key == 'attrs':
if directive == 'REPLACE':
obj.tags.batch_add(
*(
(init_spawn_value(ttag, str), tcategory, tdata)
for ttag, tcategory, tdata in val
)
)
elif key == "attrs":
if directive == "REPLACE":
obj.attributes.clear()
obj.attributes.batch_add(*(
(init_spawn_value(akey, str),
init_spawn_value(aval, value_to_obj),
acategory,
alocks)
for akey, aval, acategory, alocks in val))
elif key == 'exec':
obj.attributes.batch_add(
*(
(
init_spawn_value(akey, str),
init_spawn_value(aval, value_to_obj),
acategory,
alocks,
)
for akey, aval, acategory, alocks in val
)
)
elif key == "exec":
# we don't auto-rerun exec statements, it would be huge security risk!
pass
else:
obj.attributes.add(key, init_spawn_value(val, value_to_obj))
elif directive == 'REMOVE':
elif directive == "REMOVE":
do_save = True
if key == 'key':
obj.db_key = ''
elif key == 'typeclass':
if key == "key":
obj.db_key = ""
elif key == "typeclass":
# fall back to default
obj.db_typeclass_path = settings.BASE_OBJECT_TYPECLASS
elif key == 'location':
elif key == "location":
obj.db_location = None
elif key == 'home':
elif key == "home":
obj.db_home = None
elif key == 'destination':
elif key == "destination":
obj.db_destination = None
elif key == 'locks':
elif key == "locks":
obj.locks.clear()
elif key == 'permissions':
elif key == "permissions":
obj.permissions.clear()
elif key == 'aliases':
elif key == "aliases":
obj.aliases.clear()
elif key == 'tags':
elif key == "tags":
obj.tags.clear()
elif key == 'attrs':
elif key == "attrs":
obj.attributes.clear()
elif key == 'exec':
elif key == "exec":
# we don't auto-rerun exec statements, it would be huge security risk!
pass
else:
@ -639,12 +679,14 @@ def batch_create_object(*objparams):
obj = ObjectDB(**objparam[0])
# setup
obj._createdict = {"permissions": make_iter(objparam[1]),
"locks": objparam[2],
"aliases": make_iter(objparam[3]),
"nattributes": objparam[4],
"attributes": objparam[5],
"tags": make_iter(objparam[6])}
obj._createdict = {
"permissions": make_iter(objparam[1]),
"locks": objparam[2],
"aliases": make_iter(objparam[3]),
"nattributes": objparam[4],
"attributes": objparam[5],
"tags": make_iter(objparam[6]),
}
# this triggers all hooks
obj.save()
# run eventual extra code
@ -657,6 +699,7 @@ def batch_create_object(*objparams):
# Spawner mechanism
def spawn(*prototypes, **kwargs):
"""
Spawn a number of prototyped objects.
@ -688,12 +731,13 @@ def spawn(*prototypes, **kwargs):
"""
# search string (=prototype_key) from input
prototypes = [protlib.search_prototype(prot, require_single=True)[0]
if isinstance(prot, str) else prot
for prot in prototypes]
prototypes = [
protlib.search_prototype(prot, require_single=True)[0] if isinstance(prot, str) else prot
for prot in prototypes
]
# get available protparents
protparents = {prot['prototype_key'].lower(): prot for prot in protlib.search_prototype()}
protparents = {prot["prototype_key"].lower(): prot for prot in protlib.search_prototype()}
if not kwargs.get("only_validate"):
# homogenization to be more lenient about prototype format when entering the prototype manually
@ -704,7 +748,7 @@ def spawn(*prototypes, **kwargs):
# prototype imports. We need to insert prototype_key in this case
for key, protparent in kwargs.get("prototype_parents", {}).items():
key = str(key).lower()
protparent['prototype_key'] = str(protparent.get("prototype_key", key)).lower()
protparent["prototype_key"] = str(protparent.get("prototype_key", key)).lower()
protparents[key] = protparent
if "return_parents" in kwargs:
@ -715,8 +759,9 @@ def spawn(*prototypes, **kwargs):
for prototype in prototypes:
protlib.validate_prototype(prototype, None, protparents, is_prototype_base=True)
prot = _get_prototype(prototype, protparents,
uninherited={"prototype_key": prototype.get("prototype_key")})
prot = _get_prototype(
prototype, protparents, uninherited={"prototype_key": prototype.get("prototype_key")}
)
if not prot:
continue
@ -725,8 +770,10 @@ def spawn(*prototypes, **kwargs):
create_kwargs = {}
# we must always add a key, so if not given we use a shortened md5 hash. There is a (small)
# chance this is not unique but it should usually not be a problem.
val = prot.pop("key", "Spawned-{}".format(
hashlib.md5(bytes(str(time.time()), 'utf-8')).hexdigest()[:6]))
val = prot.pop(
"key",
"Spawned-{}".format(hashlib.md5(bytes(str(time.time()), "utf-8")).hexdigest()[:6]),
)
create_kwargs["db_key"] = init_spawn_value(val, str)
val = prot.pop("location", None)
@ -754,7 +801,7 @@ def spawn(*prototypes, **kwargs):
for (tag, category, data) in val:
tags.append((init_spawn_value(tag, str), category, data))
prototype_key = prototype.get('prototype_key', None)
prototype_key = prototype.get("prototype_key", None)
if prototype_key:
# we make sure to add a tag identifying which prototype created this object
tags.append((prototype_key, _PROTOTYPE_TAG_CATEGORY))
@ -763,8 +810,11 @@ def spawn(*prototypes, **kwargs):
execs = init_spawn_value(val, make_iter)
# extract ndb assignments
nattributes = dict((key.split("_", 1)[1], init_spawn_value(val, value_to_obj))
for key, val in prot.items() if key.startswith("ndb_"))
nattributes = dict(
(key.split("_", 1)[1], init_spawn_value(val, value_to_obj))
for key, val in prot.items()
if key.startswith("ndb_")
)
# the rest are attribute tuples (attrname, value, category, locks)
val = make_iter(prot.pop("attrs", []))
@ -773,21 +823,33 @@ def spawn(*prototypes, **kwargs):
attributes.append((attrname, init_spawn_value(value), category, locks))
simple_attributes = []
for key, value in ((key, value) for key, value in prot.items()
if not (key.startswith("ndb_"))):
for key, value in (
(key, value) for key, value in prot.items() if not (key.startswith("ndb_"))
):
# we don't support categories, nor locks for simple attributes
if key in _PROTOTYPE_META_NAMES:
continue
else:
simple_attributes.append(
(key, init_spawn_value(value, value_to_obj_or_any), None, None))
(key, init_spawn_value(value, value_to_obj_or_any), None, None)
)
attributes = attributes + simple_attributes
attributes = [tup for tup in attributes if not tup[0] in _NON_CREATE_KWARGS]
# pack for call into _batch_create_object
objsparams.append((create_kwargs, permission_string, lock_string,
alias_string, nattributes, attributes, tags, execs))
objsparams.append(
(
create_kwargs,
permission_string,
lock_string,
alias_string,
nattributes,
attributes,
tags,
execs,
)
)
if kwargs.get("only_validate"):
return objsparams

File diff suppressed because it is too large Load diff