Finish refactor prototypes/spawner/menus
This commit is contained in:
parent
1a8651f18b
commit
55f8e58c43
4 changed files with 399 additions and 389 deletions
|
|
@ -4,8 +4,13 @@ OLC Prototype menu nodes
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from ast import literal_eval
|
||||||
|
from django.conf import settings
|
||||||
from evennia.utils.evmenu import EvMenu, list_node
|
from evennia.utils.evmenu import EvMenu, list_node
|
||||||
from evennia.utils.ansi import strip_ansi
|
from evennia.utils.ansi import strip_ansi
|
||||||
|
from evennia.utils import utils
|
||||||
|
from evennia.utils.prototypes import prototypes as protlib
|
||||||
|
from evennia.utils.prototypes import spawner
|
||||||
|
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
#
|
#
|
||||||
|
|
@ -13,6 +18,13 @@ from evennia.utils.ansi import strip_ansi
|
||||||
#
|
#
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|
||||||
|
_MENU_CROP_WIDTH = 15
|
||||||
|
_MENU_ATTR_LITERAL_EVAL_ERROR = (
|
||||||
|
"|rCritical Python syntax error in your value. Only primitive Python structures are allowed.\n"
|
||||||
|
"You also need to use correct Python syntax. Remember especially to put quotes around all "
|
||||||
|
"strings inside lists and dicts.|n")
|
||||||
|
|
||||||
|
|
||||||
# Helper functions
|
# Helper functions
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -48,11 +60,11 @@ def _format_property(prop, required=False, prototype=None, cropper=None):
|
||||||
out = "<{}>".format(prop.__name__)
|
out = "<{}>".format(prop.__name__)
|
||||||
else:
|
else:
|
||||||
out = repr(prop)
|
out = repr(prop)
|
||||||
if is_iter(prop):
|
if utils.is_iter(prop):
|
||||||
out = ", ".join(str(pr) for pr in prop)
|
out = ", ".join(str(pr) for pr in prop)
|
||||||
if not out and required:
|
if not out and required:
|
||||||
out = "|rrequired"
|
out = "|rrequired"
|
||||||
return " ({}|n)".format(cropper(out) if cropper else crop(out, _MENU_CROP_WIDTH))
|
return " ({}|n)".format(cropper(out) if cropper else utils.crop(out, _MENU_CROP_WIDTH))
|
||||||
|
|
||||||
|
|
||||||
def _set_property(caller, raw_string, **kwargs):
|
def _set_property(caller, raw_string, **kwargs):
|
||||||
|
|
@ -166,7 +178,8 @@ def node_index(caller):
|
||||||
required = False
|
required = False
|
||||||
for key in ('Desc', 'Tags', 'Locks'):
|
for key in ('Desc', 'Tags', 'Locks'):
|
||||||
options.append(
|
options.append(
|
||||||
{"desc": "|WPrototype-{}|n|n{}".format(key, _format_property(key, required, prototype, None)),
|
{"desc": "|WPrototype-{}|n|n{}".format(
|
||||||
|
key, _format_property(key, required, prototype, None)),
|
||||||
"goto": "node_prototype_{}".format(key.lower())})
|
"goto": "node_prototype_{}".format(key.lower())})
|
||||||
|
|
||||||
return text, options
|
return text, options
|
||||||
|
|
@ -175,11 +188,11 @@ def node_index(caller):
|
||||||
def node_validate_prototype(caller, raw_string, **kwargs):
|
def node_validate_prototype(caller, raw_string, **kwargs):
|
||||||
prototype = _get_menu_prototype(caller)
|
prototype = _get_menu_prototype(caller)
|
||||||
|
|
||||||
txt = prototype_to_str(prototype)
|
txt = protlib.prototype_to_str(prototype)
|
||||||
errors = "\n\n|g No validation errors found.|n (but errors could still happen at spawn-time)"
|
errors = "\n\n|g No validation errors found.|n (but errors could still happen at spawn-time)"
|
||||||
try:
|
try:
|
||||||
# validate, don't spawn
|
# validate, don't spawn
|
||||||
spawn(prototype, return_prototypes=True)
|
spawner.spawn(prototype, return_prototypes=True)
|
||||||
except RuntimeError as err:
|
except RuntimeError as err:
|
||||||
errors = "\n\n|rError: {}|n".format(err)
|
errors = "\n\n|rError: {}|n".format(err)
|
||||||
text = (txt + errors)
|
text = (txt + errors)
|
||||||
|
|
@ -190,7 +203,7 @@ def node_validate_prototype(caller, raw_string, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def _check_prototype_key(caller, key):
|
def _check_prototype_key(caller, key):
|
||||||
old_prototype = search_prototype(key)
|
old_prototype = protlib.search_prototype(key)
|
||||||
olc_new = _is_new_prototype(caller)
|
olc_new = _is_new_prototype(caller)
|
||||||
key = key.strip().lower()
|
key = key.strip().lower()
|
||||||
if old_prototype:
|
if old_prototype:
|
||||||
|
|
@ -231,13 +244,13 @@ def node_prototype_key(caller):
|
||||||
|
|
||||||
def _all_prototypes(caller):
|
def _all_prototypes(caller):
|
||||||
return [prototype["prototype_key"]
|
return [prototype["prototype_key"]
|
||||||
for prototype in search_prototype() if "prototype_key" in prototype]
|
for prototype in protlib.search_prototype() if "prototype_key" in prototype]
|
||||||
|
|
||||||
|
|
||||||
def _prototype_examine(caller, prototype_name):
|
def _prototype_examine(caller, prototype_name):
|
||||||
prototypes = search_prototype(key=prototype_name)
|
prototypes = protlib.search_prototype(key=prototype_name)
|
||||||
if prototypes:
|
if prototypes:
|
||||||
caller.msg(prototype_to_str(prototypes[0]))
|
caller.msg(protlib.prototype_to_str(prototypes[0]))
|
||||||
caller.msg("Prototype not registered.")
|
caller.msg("Prototype not registered.")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
@ -256,9 +269,10 @@ def node_prototype(caller):
|
||||||
|
|
||||||
text = ["Set the prototype's |yParent Prototype|n. If this is unset, Typeclass will be used."]
|
text = ["Set the prototype's |yParent Prototype|n. If this is unset, Typeclass will be used."]
|
||||||
if prot_parent_key:
|
if prot_parent_key:
|
||||||
prot_parent = search_prototype(prot_parent_key)
|
prot_parent = protlib.search_prototype(prot_parent_key)
|
||||||
if prot_parent:
|
if prot_parent:
|
||||||
text.append("Current parent prototype is {}:\n{}".format(prototype_to_str(prot_parent)))
|
text.append(
|
||||||
|
"Current parent prototype is {}:\n{}".format(protlib.prototype_to_str(prot_parent)))
|
||||||
else:
|
else:
|
||||||
text.append("Current parent prototype |r{prototype}|n "
|
text.append("Current parent prototype |r{prototype}|n "
|
||||||
"does not appear to exist.".format(prot_parent_key))
|
"does not appear to exist.".format(prot_parent_key))
|
||||||
|
|
@ -273,7 +287,7 @@ def node_prototype(caller):
|
||||||
|
|
||||||
|
|
||||||
def _all_typeclasses(caller):
|
def _all_typeclasses(caller):
|
||||||
return list(sorted(get_all_typeclasses().keys()))
|
return list(sorted(utils.get_all_typeclasses().keys()))
|
||||||
|
|
||||||
|
|
||||||
def _typeclass_examine(caller, typeclass_path):
|
def _typeclass_examine(caller, typeclass_path):
|
||||||
|
|
@ -281,7 +295,7 @@ def _typeclass_examine(caller, typeclass_path):
|
||||||
# this means we are exiting the listing
|
# this means we are exiting the listing
|
||||||
return "node_key"
|
return "node_key"
|
||||||
|
|
||||||
typeclass = get_all_typeclasses().get(typeclass_path)
|
typeclass = utils.get_all_typeclasses().get(typeclass_path)
|
||||||
if typeclass:
|
if typeclass:
|
||||||
docstr = []
|
docstr = []
|
||||||
for line in typeclass.__doc__.split("\n"):
|
for line in typeclass.__doc__.split("\n"):
|
||||||
|
|
@ -453,8 +467,8 @@ def _add_tag(caller, tag, **kwargs):
|
||||||
tags.append(tag)
|
tags.append(tag)
|
||||||
else:
|
else:
|
||||||
tags = [tag]
|
tags = [tag]
|
||||||
prot['tags'] = tags
|
prototype['tags'] = tags
|
||||||
_set_menu_prototype(caller, "prototype", prot)
|
_set_menu_prototype(caller, "prototype", prototype)
|
||||||
text = kwargs.get("text")
|
text = kwargs.get("text")
|
||||||
if not text:
|
if not text:
|
||||||
text = "Added tag {}. (return to continue)".format(tag)
|
text = "Added tag {}. (return to continue)".format(tag)
|
||||||
|
|
@ -706,4 +720,3 @@ def start_olc(caller, session=None, prototype=None):
|
||||||
"node_prototype_locks": node_prototype_locks,
|
"node_prototype_locks": node_prototype_locks,
|
||||||
}
|
}
|
||||||
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype)
|
OLCMenu(caller, menudata, startnode='node_index', session=session, olc_prototype=prototype)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,26 @@ Handling storage of prototypes, both database-based ones (DBPrototypes) and thos
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
|
||||||
from evennia.scripts.scripts import DefaultScript
|
from evennia.scripts.scripts import DefaultScript
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.utils.create import create_script
|
from evennia.utils.create import create_script
|
||||||
from evennia.utils.utils import all_from_module, make_iter, callables_from_module, is_iter
|
from evennia.utils.utils import (
|
||||||
|
all_from_module, make_iter, is_iter, dbid_to_obj)
|
||||||
from evennia.locks.lockhandler import validate_lockstring, check_lockstring
|
from evennia.locks.lockhandler import validate_lockstring, check_lockstring
|
||||||
from evennia.utils import logger
|
from evennia.utils import logger
|
||||||
|
from evennia.utils.evtable import EvTable
|
||||||
|
from evennia.utils.prototypes.protfuncs import protfunc_parser
|
||||||
|
|
||||||
|
|
||||||
_MODULE_PROTOTYPE_MODULES = {}
|
_MODULE_PROTOTYPE_MODULES = {}
|
||||||
_MODULE_PROTOTYPES = {}
|
_MODULE_PROTOTYPES = {}
|
||||||
|
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
||||||
|
_PROTOTYPE_TAG_CATEGORY = "spawned_by_prototype"
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionError(RuntimeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class ValidationError(RuntimeError):
|
class ValidationError(RuntimeError):
|
||||||
|
|
@ -25,6 +35,99 @@ class ValidationError(RuntimeError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# helper functions
|
||||||
|
|
||||||
|
def value_to_obj(value, force=True):
|
||||||
|
return dbid_to_obj(value, ObjectDB)
|
||||||
|
|
||||||
|
|
||||||
|
def value_to_obj_or_any(value):
|
||||||
|
obj = dbid_to_obj(value, ObjectDB)
|
||||||
|
return obj if obj is not None else value
|
||||||
|
|
||||||
|
|
||||||
|
def prototype_to_str(prototype):
|
||||||
|
"""
|
||||||
|
Format a prototype to a nice string representation.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prototype (dict): The prototype.
|
||||||
|
"""
|
||||||
|
|
||||||
|
header = (
|
||||||
|
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
||||||
|
"|cdesc:|n {} \n|cprototype:|n ".format(
|
||||||
|
prototype['prototype_key'],
|
||||||
|
", ".join(prototype['prototype_tags']),
|
||||||
|
prototype['prototype_locks'],
|
||||||
|
prototype['prototype_desc']))
|
||||||
|
proto = ("{{\n {} \n}}".format(
|
||||||
|
"\n ".join(
|
||||||
|
"{!r}: {!r},".format(key, value) for key, value in
|
||||||
|
sorted(prototype.items()) if key not in _PROTOTYPE_META_NAMES)).rstrip(","))
|
||||||
|
return header + proto
|
||||||
|
|
||||||
|
|
||||||
|
def check_permission(prototype_key, action, default=True):
|
||||||
|
"""
|
||||||
|
Helper function to check access to actions on given prototype.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prototype_key (str): The prototype to affect.
|
||||||
|
action (str): One of "spawn" or "edit".
|
||||||
|
default (str): If action is unknown or prototype has no locks
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
passes (bool): If permission for action is granted or not.
|
||||||
|
|
||||||
|
"""
|
||||||
|
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))
|
||||||
|
return False
|
||||||
|
|
||||||
|
prototype = search_prototype(key=prototype_key)
|
||||||
|
if not prototype:
|
||||||
|
logger.log_err("Prototype {} not found.".format(prototype_key))
|
||||||
|
return False
|
||||||
|
|
||||||
|
lockstring = prototype.get("prototype_locks")
|
||||||
|
|
||||||
|
if lockstring:
|
||||||
|
return check_lockstring(None, lockstring, default=default, access_type=action)
|
||||||
|
return default
|
||||||
|
|
||||||
|
|
||||||
|
def init_spawn_value(value, validator=None):
|
||||||
|
"""
|
||||||
|
Analyze the prototype value and produce a value useful at the point of spawning.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value (any): This can be:
|
||||||
|
callable - will be called as callable()
|
||||||
|
(callable, (args,)) - will be called as callable(*args)
|
||||||
|
other - will be assigned depending on the variable type
|
||||||
|
validator (callable, optional): If given, this will be called with the value to
|
||||||
|
check and guarantee the outcome is of a given type.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
any (any): The (potentially pre-processed value to use for this prototype key)
|
||||||
|
|
||||||
|
"""
|
||||||
|
value = protfunc_parser(value)
|
||||||
|
validator = validator if validator else lambda o: o
|
||||||
|
if callable(value):
|
||||||
|
return validator(value())
|
||||||
|
elif value and is_iter(value) and callable(value[0]):
|
||||||
|
# a structure (callable, (args, ))
|
||||||
|
args = value[1:]
|
||||||
|
return validator(value[0](*make_iter(args)))
|
||||||
|
else:
|
||||||
|
return validator(value)
|
||||||
|
|
||||||
|
|
||||||
# module-based prototypes
|
# module-based prototypes
|
||||||
|
|
||||||
for mod in settings.PROTOTYPE_MODULES:
|
for mod in settings.PROTOTYPE_MODULES:
|
||||||
|
|
@ -59,39 +162,7 @@ class DbPrototype(DefaultScript):
|
||||||
self.db.prototype = {} # actual prototype
|
self.db.prototype = {} # actual prototype
|
||||||
|
|
||||||
|
|
||||||
# General prototype functions
|
# Prototype manager functions
|
||||||
|
|
||||||
|
|
||||||
def check_permission(prototype_key, action, default=True):
|
|
||||||
"""
|
|
||||||
Helper function to check access to actions on given prototype.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
prototype_key (str): The prototype to affect.
|
|
||||||
action (str): One of "spawn" or "edit".
|
|
||||||
default (str): If action is unknown or prototype has no locks
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
passes (bool): If permission for action is granted or not.
|
|
||||||
|
|
||||||
"""
|
|
||||||
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))
|
|
||||||
return False
|
|
||||||
|
|
||||||
prototype = search_prototype(key=prototype_key)
|
|
||||||
if not prototype:
|
|
||||||
logger.log_err("Prototype {} not found.".format(prototype_key))
|
|
||||||
return False
|
|
||||||
|
|
||||||
lockstring = prototype.get("prototype_locks")
|
|
||||||
|
|
||||||
if lockstring:
|
|
||||||
return check_lockstring(None, lockstring, default=default, access_type=action)
|
|
||||||
return default
|
|
||||||
|
|
||||||
|
|
||||||
def create_prototype(**kwargs):
|
def create_prototype(**kwargs):
|
||||||
|
|
@ -281,45 +352,6 @@ def search_objects_with_prototype(prototype_key):
|
||||||
return ObjectDB.objects.get_by_tag(key=prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
|
return ObjectDB.objects.get_by_tag(key=prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
|
||||||
|
|
||||||
|
|
||||||
def prototype_from_object(obj):
|
|
||||||
"""
|
|
||||||
Guess a minimal prototype from an existing object.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
obj (Object): An object to analyze.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
prototype (dict): A prototype estimating the current state of the object.
|
|
||||||
|
|
||||||
"""
|
|
||||||
# first, check if this object already has a prototype
|
|
||||||
|
|
||||||
prot = obj.tags.get(category=_PROTOTYPE_TAG_CATEGORY, return_list=True)
|
|
||||||
prot = search_prototype(prot)
|
|
||||||
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(str(time.time())).hexdigest()[:6])
|
|
||||||
prot['prototype_desc'] = "Built from {}".format(str(obj))
|
|
||||||
prot['prototype_locks'] = "spawn:all();edit:all()"
|
|
||||||
|
|
||||||
prot['key'] = obj.db_key or hashlib.md5(str(time.time())).hexdigest()[:6]
|
|
||||||
prot['location'] = obj.db_location
|
|
||||||
prot['home'] = obj.db_home
|
|
||||||
prot['destination'] = obj.db_destination
|
|
||||||
prot['typeclass'] = obj.db_typeclass_path
|
|
||||||
prot['locks'] = obj.locks.all()
|
|
||||||
prot['permissions'] = obj.permissions.get()
|
|
||||||
prot['aliases'] = obj.aliases.get()
|
|
||||||
prot['tags'] = [(tag.key, tag.category, tag.data)
|
|
||||||
for tag in obj.tags.get(return_tagobj=True, return_list=True)]
|
|
||||||
prot['attrs'] = [(attr.key, attr.value, attr.category, attr.locks)
|
|
||||||
for attr in obj.attributes.get(return_obj=True, return_list=True)]
|
|
||||||
|
|
||||||
return prot
|
|
||||||
|
|
||||||
|
|
||||||
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
|
def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_edit=True):
|
||||||
"""
|
"""
|
||||||
Collate a list of found prototypes based on search criteria and access.
|
Collate a list of found prototypes based on search criteria and access.
|
||||||
|
|
@ -384,171 +416,3 @@ def list_prototypes(caller, key=None, tags=None, show_non_use=False, show_non_ed
|
||||||
table.reformat_column(2, width=11, align='c')
|
table.reformat_column(2, width=11, align='c')
|
||||||
table.reformat_column(3, width=16)
|
table.reformat_column(3, width=16)
|
||||||
return table
|
return table
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def batch_update_objects_with_prototype(prototype, diff=None, objects=None):
|
|
||||||
"""
|
|
||||||
Update existing objects with the latest version of the prototype.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
prototype (str or dict): Either the `prototype_key` to use or the
|
|
||||||
prototype dict itself.
|
|
||||||
diff (dict, optional): This a diff structure that describes how to update the protototype.
|
|
||||||
If not given this will be constructed from the first object found.
|
|
||||||
objects (list, optional): List of objects to update. If not given, query for these
|
|
||||||
objects using the prototype's `prototype_key`.
|
|
||||||
Returns:
|
|
||||||
changed (int): The number of objects that had changes applied to them.
|
|
||||||
|
|
||||||
"""
|
|
||||||
prototype_key = prototype if isinstance(prototype, basestring) else prototype['prototype_key']
|
|
||||||
prototype_obj = search_db_prototype(prototype_key, return_queryset=True)
|
|
||||||
prototype_obj = prototype_obj[0] if prototype_obj else None
|
|
||||||
new_prototype = prototype_obj.db.prototype
|
|
||||||
objs = ObjectDB.objects.get_by_tag(prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
|
|
||||||
|
|
||||||
if not objs:
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if not diff:
|
|
||||||
diff = prototype_diff_from_object(new_prototype, objs[0])
|
|
||||||
|
|
||||||
changed = 0
|
|
||||||
for obj in objs:
|
|
||||||
do_save = False
|
|
||||||
for key, directive in diff.items():
|
|
||||||
val = new_prototype[key]
|
|
||||||
if directive in ('UPDATE', 'REPLACE'):
|
|
||||||
do_save = True
|
|
||||||
if key == 'key':
|
|
||||||
obj.db_key = validate_spawn_value(val, str)
|
|
||||||
elif key == 'typeclass':
|
|
||||||
obj.db_typeclass_path = validate_spawn_value(val, str)
|
|
||||||
elif key == 'location':
|
|
||||||
obj.db_location = validate_spawn_value(val, _to_obj)
|
|
||||||
elif key == 'home':
|
|
||||||
obj.db_home = validate_spawn_value(val, _to_obj)
|
|
||||||
elif key == 'destination':
|
|
||||||
obj.db_destination = validate_spawn_value(val, _to_obj)
|
|
||||||
elif key == 'locks':
|
|
||||||
if directive == 'REPLACE':
|
|
||||||
obj.locks.clear()
|
|
||||||
obj.locks.add(validate_spawn_value(val, str))
|
|
||||||
elif key == 'permissions':
|
|
||||||
if directive == 'REPLACE':
|
|
||||||
obj.permissions.clear()
|
|
||||||
obj.permissions.batch_add(validate_spawn_value(val, make_iter))
|
|
||||||
elif key == 'aliases':
|
|
||||||
if directive == 'REPLACE':
|
|
||||||
obj.aliases.clear()
|
|
||||||
obj.aliases.batch_add(validate_spawn_value(val, make_iter))
|
|
||||||
elif key == 'tags':
|
|
||||||
if directive == 'REPLACE':
|
|
||||||
obj.tags.clear()
|
|
||||||
obj.tags.batch_add(validate_spawn_value(val, make_iter))
|
|
||||||
elif key == 'attrs':
|
|
||||||
if directive == 'REPLACE':
|
|
||||||
obj.attributes.clear()
|
|
||||||
obj.attributes.batch_add(validate_spawn_value(val, make_iter))
|
|
||||||
elif key == 'exec':
|
|
||||||
# we don't auto-rerun exec statements, it would be huge security risk!
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
obj.attributes.add(key, validate_spawn_value(val, _to_obj))
|
|
||||||
elif directive == 'REMOVE':
|
|
||||||
do_save = True
|
|
||||||
if key == 'key':
|
|
||||||
obj.db_key = ''
|
|
||||||
elif key == 'typeclass':
|
|
||||||
# fall back to default
|
|
||||||
obj.db_typeclass_path = settings.BASE_OBJECT_TYPECLASS
|
|
||||||
elif key == 'location':
|
|
||||||
obj.db_location = None
|
|
||||||
elif key == 'home':
|
|
||||||
obj.db_home = None
|
|
||||||
elif key == 'destination':
|
|
||||||
obj.db_destination = None
|
|
||||||
elif key == 'locks':
|
|
||||||
obj.locks.clear()
|
|
||||||
elif key == 'permissions':
|
|
||||||
obj.permissions.clear()
|
|
||||||
elif key == 'aliases':
|
|
||||||
obj.aliases.clear()
|
|
||||||
elif key == 'tags':
|
|
||||||
obj.tags.clear()
|
|
||||||
elif key == 'attrs':
|
|
||||||
obj.attributes.clear()
|
|
||||||
elif key == 'exec':
|
|
||||||
# we don't auto-rerun exec statements, it would be huge security risk!
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
obj.attributes.remove(key)
|
|
||||||
if do_save:
|
|
||||||
changed += 1
|
|
||||||
obj.save()
|
|
||||||
|
|
||||||
return changed
|
|
||||||
|
|
||||||
def batch_create_object(*objparams):
|
|
||||||
"""
|
|
||||||
This is a cut-down version of the create_object() function,
|
|
||||||
optimized for speed. It does NOT check and convert various input
|
|
||||||
so make sure the spawned Typeclass works before using this!
|
|
||||||
|
|
||||||
Args:
|
|
||||||
objsparams (tuple): Each paremter tuple will create one object instance using the parameters within.
|
|
||||||
The parameters should be given in the following order:
|
|
||||||
- `create_kwargs` (dict): For use as new_obj = `ObjectDB(**create_kwargs)`.
|
|
||||||
- `permissions` (str): Permission string used with `new_obj.batch_add(permission)`.
|
|
||||||
- `lockstring` (str): Lockstring used with `new_obj.locks.add(lockstring)`.
|
|
||||||
- `aliases` (list): A list of alias strings for
|
|
||||||
adding with `new_object.aliases.batch_add(*aliases)`.
|
|
||||||
- `nattributes` (list): list of tuples `(key, value)` to be loop-added to
|
|
||||||
add with `new_obj.nattributes.add(*tuple)`.
|
|
||||||
- `attributes` (list): list of tuples `(key, value[,category[,lockstring]])` for
|
|
||||||
adding with `new_obj.attributes.batch_add(*attributes)`.
|
|
||||||
- `tags` (list): list of tuples `(key, category)` for adding
|
|
||||||
with `new_obj.tags.batch_add(*tags)`.
|
|
||||||
- `execs` (list): Code strings to execute together with the creation
|
|
||||||
of each object. They will be executed with `evennia` and `obj`
|
|
||||||
(the newly created object) available in the namespace. Execution
|
|
||||||
will happend after all other properties have been assigned and
|
|
||||||
is intended for calling custom handlers etc.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
objects (list): A list of created objects
|
|
||||||
|
|
||||||
Notes:
|
|
||||||
The `exec` list will execute arbitrary python code so don't allow this to be available to
|
|
||||||
unprivileged users!
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
# bulk create all objects in one go
|
|
||||||
|
|
||||||
# unfortunately this doesn't work since bulk_create doesn't creates pks;
|
|
||||||
# the result would be duplicate objects at the next stage, so we comment
|
|
||||||
# it out for now:
|
|
||||||
# dbobjs = _ObjectDB.objects.bulk_create(dbobjs)
|
|
||||||
|
|
||||||
dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams]
|
|
||||||
objs = []
|
|
||||||
for iobj, obj in enumerate(dbobjs):
|
|
||||||
# call all setup hooks on each object
|
|
||||||
objparam = objparams[iobj]
|
|
||||||
# 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])}
|
|
||||||
# this triggers all hooks
|
|
||||||
obj.save()
|
|
||||||
# run eventual extra code
|
|
||||||
for code in objparam[7]:
|
|
||||||
if code:
|
|
||||||
exec(code, {}, {"evennia": evennia, "obj": obj})
|
|
||||||
objs.append(obj)
|
|
||||||
return objs
|
|
||||||
|
|
|
||||||
|
|
@ -126,70 +126,25 @@ from __future__ import print_function
|
||||||
import copy
|
import copy
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
from ast import literal_eval
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from random import randint
|
|
||||||
import evennia
|
import evennia
|
||||||
|
from random import randint
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
from evennia.utils.utils import (
|
from evennia.utils.utils import (
|
||||||
make_iter, dbid_to_obj,
|
make_iter, dbid_to_obj,
|
||||||
is_iter, crop, get_all_typeclasses)
|
is_iter, get_all_typeclasses)
|
||||||
|
from evennia.prototypes import prototypes as protlib
|
||||||
from evennia.utils.evtable import EvTable
|
from evennia.prototypes.prototypes import value_to_obj, value_to_obj_or_any, init_spawn_value
|
||||||
|
|
||||||
|
|
||||||
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
_CREATE_OBJECT_KWARGS = ("key", "location", "home", "destination")
|
||||||
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
||||||
_NON_CREATE_KWARGS = _CREATE_OBJECT_KWARGS + _PROTOTYPE_META_NAMES
|
_NON_CREATE_KWARGS = _CREATE_OBJECT_KWARGS + _PROTOTYPE_META_NAMES
|
||||||
_MENU_CROP_WIDTH = 15
|
|
||||||
_PROTOTYPE_TAG_CATEGORY = "spawned_by_prototype"
|
_PROTOTYPE_TAG_CATEGORY = "spawned_by_prototype"
|
||||||
|
|
||||||
_MENU_ATTR_LITERAL_EVAL_ERROR = (
|
|
||||||
"|rCritical Python syntax error in your value. Only primitive Python structures are allowed.\n"
|
|
||||||
"You also need to use correct Python syntax. Remember especially to put quotes around all "
|
|
||||||
"strings inside lists and dicts.|n")
|
|
||||||
|
|
||||||
|
|
||||||
# Helper functions
|
|
||||||
|
|
||||||
def _to_obj(value, force=True):
|
|
||||||
return dbid_to_obj(value, ObjectDB)
|
|
||||||
|
|
||||||
|
|
||||||
def _to_obj_or_any(value):
|
|
||||||
obj = dbid_to_obj(value, ObjectDB)
|
|
||||||
return obj if obj is not None else value
|
|
||||||
|
|
||||||
|
|
||||||
def validate_spawn_value(value, validator=None):
|
|
||||||
"""
|
|
||||||
Analyze the value and produce a value for use at the point of spawning.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value (any): This can be:
|
|
||||||
callable - will be called as callable()
|
|
||||||
(callable, (args,)) - will be called as callable(*args)
|
|
||||||
other - will be assigned depending on the variable type
|
|
||||||
validator (callable, optional): If given, this will be called with the value to
|
|
||||||
check and guarantee the outcome is of a given type.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
any (any): The (potentially pre-processed value to use for this prototype key)
|
|
||||||
|
|
||||||
"""
|
|
||||||
value = protfunc_parser(value)
|
|
||||||
validator = validator if validator else lambda o: o
|
|
||||||
if callable(value):
|
|
||||||
return validator(value())
|
|
||||||
elif value and is_iter(value) and callable(value[0]):
|
|
||||||
# a structure (callable, (args, ))
|
|
||||||
args = value[1:]
|
|
||||||
return validator(value[0](*make_iter(args)))
|
|
||||||
else:
|
|
||||||
return validator(value)
|
|
||||||
|
|
||||||
# Spawner mechanism
|
|
||||||
|
|
||||||
|
# Helper
|
||||||
|
|
||||||
def _get_prototype(dic, prot, protparents):
|
def _get_prototype(dic, prot, protparents):
|
||||||
"""
|
"""
|
||||||
|
|
@ -209,6 +164,246 @@ def _get_prototype(dic, prot, protparents):
|
||||||
return prot
|
return prot
|
||||||
|
|
||||||
|
|
||||||
|
# obj-related prototype functions
|
||||||
|
|
||||||
|
def prototype_from_object(obj):
|
||||||
|
"""
|
||||||
|
Guess a minimal prototype from an existing object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obj (Object): An object to analyze.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
prototype (dict): A prototype estimating the current state of the object.
|
||||||
|
|
||||||
|
"""
|
||||||
|
# first, check if this object already has a prototype
|
||||||
|
|
||||||
|
prot = obj.tags.get(category=_PROTOTYPE_TAG_CATEGORY, return_list=True)
|
||||||
|
prot = protlib.search_prototype(prot)
|
||||||
|
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(str(time.time())).hexdigest()[:6])
|
||||||
|
prot['prototype_desc'] = "Built from {}".format(str(obj))
|
||||||
|
prot['prototype_locks'] = "spawn:all();edit:all()"
|
||||||
|
|
||||||
|
prot['key'] = obj.db_key or hashlib.md5(str(time.time())).hexdigest()[:6]
|
||||||
|
prot['location'] = obj.db_location
|
||||||
|
prot['home'] = obj.db_home
|
||||||
|
prot['destination'] = obj.db_destination
|
||||||
|
prot['typeclass'] = obj.db_typeclass_path
|
||||||
|
prot['locks'] = obj.locks.all()
|
||||||
|
prot['permissions'] = obj.permissions.get()
|
||||||
|
prot['aliases'] = obj.aliases.get()
|
||||||
|
prot['tags'] = [(tag.key, tag.category, tag.data)
|
||||||
|
for tag in obj.tags.get(return_tagobj=True, return_list=True)]
|
||||||
|
prot['attrs'] = [(attr.key, attr.value, attr.category, attr.locks)
|
||||||
|
for attr in obj.attributes.get(return_obj=True, return_list=True)]
|
||||||
|
|
||||||
|
return prot
|
||||||
|
|
||||||
|
|
||||||
|
def prototype_diff_from_object(prototype, obj):
|
||||||
|
"""
|
||||||
|
Get a simple diff for a prototype compared to an object which may or may not already have a
|
||||||
|
prototype (or has one but changed locally). For more complex migratations a manual diff may be
|
||||||
|
needed.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prototype (dict): Prototype.
|
||||||
|
obj (Object): Object to
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
diff (dict): Mapping for every prototype key: {"keyname": "REMOVE|UPDATE|KEEP", ...}
|
||||||
|
|
||||||
|
"""
|
||||||
|
prot1 = prototype
|
||||||
|
prot2 = prototype_from_object(obj)
|
||||||
|
|
||||||
|
diff = {}
|
||||||
|
for key, value in prot1.items():
|
||||||
|
diff[key] = "KEEP"
|
||||||
|
if key in prot2:
|
||||||
|
if callable(prot2[key]) or value != prot2[key]:
|
||||||
|
diff[key] = "UPDATE"
|
||||||
|
elif key not in prot2:
|
||||||
|
diff[key] = "REMOVE"
|
||||||
|
|
||||||
|
return diff
|
||||||
|
|
||||||
|
|
||||||
|
def batch_update_objects_with_prototype(prototype, diff=None, objects=None):
|
||||||
|
"""
|
||||||
|
Update existing objects with the latest version of the prototype.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
prototype (str or dict): Either the `prototype_key` to use or the
|
||||||
|
prototype dict itself.
|
||||||
|
diff (dict, optional): This a diff structure that describes how to update the protototype.
|
||||||
|
If not given this will be constructed from the first object found.
|
||||||
|
objects (list, optional): List of objects to update. If not given, query for these
|
||||||
|
objects using the prototype's `prototype_key`.
|
||||||
|
Returns:
|
||||||
|
changed (int): The number of objects that had changes applied to them.
|
||||||
|
|
||||||
|
"""
|
||||||
|
prototype_key = prototype if isinstance(prototype, basestring) else prototype['prototype_key']
|
||||||
|
prototype_obj = protlib.DbPrototype.objects.filter(db_key=prototype_key)
|
||||||
|
prototype_obj = prototype_obj[0] if prototype_obj else None
|
||||||
|
new_prototype = prototype_obj.db.prototype
|
||||||
|
objs = ObjectDB.objects.get_by_tag(prototype_key, category=_PROTOTYPE_TAG_CATEGORY)
|
||||||
|
|
||||||
|
if not objs:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if not diff:
|
||||||
|
diff = prototype_diff_from_object(new_prototype, objs[0])
|
||||||
|
|
||||||
|
changed = 0
|
||||||
|
for obj in objs:
|
||||||
|
do_save = False
|
||||||
|
for key, directive in diff.items():
|
||||||
|
val = new_prototype[key]
|
||||||
|
if directive in ('UPDATE', 'REPLACE'):
|
||||||
|
do_save = True
|
||||||
|
if key == 'key':
|
||||||
|
obj.db_key = init_spawn_value(val, str)
|
||||||
|
elif key == 'typeclass':
|
||||||
|
obj.db_typeclass_path = init_spawn_value(val, str)
|
||||||
|
elif key == 'location':
|
||||||
|
obj.db_location = init_spawn_value(val, value_to_obj)
|
||||||
|
elif key == 'home':
|
||||||
|
obj.db_home = init_spawn_value(val, value_to_obj)
|
||||||
|
elif key == 'destination':
|
||||||
|
obj.db_destination = init_spawn_value(val, value_to_obj)
|
||||||
|
elif key == 'locks':
|
||||||
|
if directive == 'REPLACE':
|
||||||
|
obj.locks.clear()
|
||||||
|
obj.locks.add(init_spawn_value(val, str))
|
||||||
|
elif key == 'permissions':
|
||||||
|
if directive == 'REPLACE':
|
||||||
|
obj.permissions.clear()
|
||||||
|
obj.permissions.batch_add(init_spawn_value(val, make_iter))
|
||||||
|
elif key == 'aliases':
|
||||||
|
if directive == 'REPLACE':
|
||||||
|
obj.aliases.clear()
|
||||||
|
obj.aliases.batch_add(init_spawn_value(val, make_iter))
|
||||||
|
elif key == 'tags':
|
||||||
|
if directive == 'REPLACE':
|
||||||
|
obj.tags.clear()
|
||||||
|
obj.tags.batch_add(init_spawn_value(val, make_iter))
|
||||||
|
elif key == 'attrs':
|
||||||
|
if directive == 'REPLACE':
|
||||||
|
obj.attributes.clear()
|
||||||
|
obj.attributes.batch_add(init_spawn_value(val, make_iter))
|
||||||
|
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':
|
||||||
|
do_save = True
|
||||||
|
if key == 'key':
|
||||||
|
obj.db_key = ''
|
||||||
|
elif key == 'typeclass':
|
||||||
|
# fall back to default
|
||||||
|
obj.db_typeclass_path = settings.BASE_OBJECT_TYPECLASS
|
||||||
|
elif key == 'location':
|
||||||
|
obj.db_location = None
|
||||||
|
elif key == 'home':
|
||||||
|
obj.db_home = None
|
||||||
|
elif key == 'destination':
|
||||||
|
obj.db_destination = None
|
||||||
|
elif key == 'locks':
|
||||||
|
obj.locks.clear()
|
||||||
|
elif key == 'permissions':
|
||||||
|
obj.permissions.clear()
|
||||||
|
elif key == 'aliases':
|
||||||
|
obj.aliases.clear()
|
||||||
|
elif key == 'tags':
|
||||||
|
obj.tags.clear()
|
||||||
|
elif key == 'attrs':
|
||||||
|
obj.attributes.clear()
|
||||||
|
elif key == 'exec':
|
||||||
|
# we don't auto-rerun exec statements, it would be huge security risk!
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
obj.attributes.remove(key)
|
||||||
|
if do_save:
|
||||||
|
changed += 1
|
||||||
|
obj.save()
|
||||||
|
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
def batch_create_object(*objparams):
|
||||||
|
"""
|
||||||
|
This is a cut-down version of the create_object() function,
|
||||||
|
optimized for speed. It does NOT check and convert various input
|
||||||
|
so make sure the spawned Typeclass works before using this!
|
||||||
|
|
||||||
|
Args:
|
||||||
|
objsparams (tuple): Each paremter tuple will create one object instance using the parameters
|
||||||
|
within.
|
||||||
|
The parameters should be given in the following order:
|
||||||
|
- `create_kwargs` (dict): For use as new_obj = `ObjectDB(**create_kwargs)`.
|
||||||
|
- `permissions` (str): Permission string used with `new_obj.batch_add(permission)`.
|
||||||
|
- `lockstring` (str): Lockstring used with `new_obj.locks.add(lockstring)`.
|
||||||
|
- `aliases` (list): A list of alias strings for
|
||||||
|
adding with `new_object.aliases.batch_add(*aliases)`.
|
||||||
|
- `nattributes` (list): list of tuples `(key, value)` to be loop-added to
|
||||||
|
add with `new_obj.nattributes.add(*tuple)`.
|
||||||
|
- `attributes` (list): list of tuples `(key, value[,category[,lockstring]])` for
|
||||||
|
adding with `new_obj.attributes.batch_add(*attributes)`.
|
||||||
|
- `tags` (list): list of tuples `(key, category)` for adding
|
||||||
|
with `new_obj.tags.batch_add(*tags)`.
|
||||||
|
- `execs` (list): Code strings to execute together with the creation
|
||||||
|
of each object. They will be executed with `evennia` and `obj`
|
||||||
|
(the newly created object) available in the namespace. Execution
|
||||||
|
will happend after all other properties have been assigned and
|
||||||
|
is intended for calling custom handlers etc.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
objects (list): A list of created objects
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
The `exec` list will execute arbitrary python code so don't allow this to be available to
|
||||||
|
unprivileged users!
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
# bulk create all objects in one go
|
||||||
|
|
||||||
|
# unfortunately this doesn't work since bulk_create doesn't creates pks;
|
||||||
|
# the result would be duplicate objects at the next stage, so we comment
|
||||||
|
# it out for now:
|
||||||
|
# dbobjs = _ObjectDB.objects.bulk_create(dbobjs)
|
||||||
|
|
||||||
|
dbobjs = [ObjectDB(**objparam[0]) for objparam in objparams]
|
||||||
|
objs = []
|
||||||
|
for iobj, obj in enumerate(dbobjs):
|
||||||
|
# call all setup hooks on each object
|
||||||
|
objparam = objparams[iobj]
|
||||||
|
# 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])}
|
||||||
|
# this triggers all hooks
|
||||||
|
obj.save()
|
||||||
|
# run eventual extra code
|
||||||
|
for code in objparam[7]:
|
||||||
|
if code:
|
||||||
|
exec(code, {}, {"evennia": evennia, "obj": obj})
|
||||||
|
objs.append(obj)
|
||||||
|
return objs
|
||||||
|
|
||||||
|
|
||||||
|
# Spawner mechanism
|
||||||
|
|
||||||
def spawn(*prototypes, **kwargs):
|
def spawn(*prototypes, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|
@ -234,12 +429,12 @@ def spawn(*prototypes, **kwargs):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
# get available protparents
|
# get available protparents
|
||||||
protparents = {prot['prototype_key']: prot for prot in search_prototype()}
|
protparents = {prot['prototype_key']: prot for prot in protlib.search_prototype()}
|
||||||
|
|
||||||
# overload module's protparents with specifically given protparents
|
# overload module's protparents with specifically given protparents
|
||||||
protparents.update(kwargs.get("prototype_parents", {}))
|
protparents.update(kwargs.get("prototype_parents", {}))
|
||||||
for key, prototype in protparents.items():
|
for key, prototype in protparents.items():
|
||||||
validate_prototype(prototype, key.lower(), protparents)
|
protlib.validate_prototype(prototype, key.lower(), protparents)
|
||||||
|
|
||||||
if "return_prototypes" in kwargs:
|
if "return_prototypes" in kwargs:
|
||||||
# only return the parents
|
# only return the parents
|
||||||
|
|
@ -248,7 +443,7 @@ def spawn(*prototypes, **kwargs):
|
||||||
objsparams = []
|
objsparams = []
|
||||||
for prototype in prototypes:
|
for prototype in prototypes:
|
||||||
|
|
||||||
validate_prototype(prototype, None, protparents)
|
protlib.validate_prototype(prototype, None, protparents)
|
||||||
prot = _get_prototype(prototype, {}, protparents)
|
prot = _get_prototype(prototype, {}, protparents)
|
||||||
if not prot:
|
if not prot:
|
||||||
continue
|
continue
|
||||||
|
|
@ -260,30 +455,30 @@ def spawn(*prototypes, **kwargs):
|
||||||
# chance this is not unique but it should usually not be a problem.
|
# chance this is not unique but it should usually not be a problem.
|
||||||
val = prot.pop("key", "Spawned-{}".format(
|
val = prot.pop("key", "Spawned-{}".format(
|
||||||
hashlib.md5(str(time.time())).hexdigest()[:6]))
|
hashlib.md5(str(time.time())).hexdigest()[:6]))
|
||||||
create_kwargs["db_key"] = validate_spawn_value(val, str)
|
create_kwargs["db_key"] = init_spawn_value(val, str)
|
||||||
|
|
||||||
val = prot.pop("location", None)
|
val = prot.pop("location", None)
|
||||||
create_kwargs["db_location"] = validate_spawn_value(val, _to_obj)
|
create_kwargs["db_location"] = init_spawn_value(val, value_to_obj)
|
||||||
|
|
||||||
val = prot.pop("home", settings.DEFAULT_HOME)
|
val = prot.pop("home", settings.DEFAULT_HOME)
|
||||||
create_kwargs["db_home"] = validate_spawn_value(val, _to_obj)
|
create_kwargs["db_home"] = init_spawn_value(val, value_to_obj)
|
||||||
|
|
||||||
val = prot.pop("destination", None)
|
val = prot.pop("destination", None)
|
||||||
create_kwargs["db_destination"] = validate_spawn_value(val, _to_obj)
|
create_kwargs["db_destination"] = init_spawn_value(val, value_to_obj)
|
||||||
|
|
||||||
val = prot.pop("typeclass", settings.BASE_OBJECT_TYPECLASS)
|
val = prot.pop("typeclass", settings.BASE_OBJECT_TYPECLASS)
|
||||||
create_kwargs["db_typeclass_path"] = validate_spawn_value(val, str)
|
create_kwargs["db_typeclass_path"] = init_spawn_value(val, str)
|
||||||
|
|
||||||
# extract calls to handlers
|
# extract calls to handlers
|
||||||
val = prot.pop("permissions", [])
|
val = prot.pop("permissions", [])
|
||||||
permission_string = validate_spawn_value(val, make_iter)
|
permission_string = init_spawn_value(val, make_iter)
|
||||||
val = prot.pop("locks", "")
|
val = prot.pop("locks", "")
|
||||||
lock_string = validate_spawn_value(val, str)
|
lock_string = init_spawn_value(val, str)
|
||||||
val = prot.pop("aliases", [])
|
val = prot.pop("aliases", [])
|
||||||
alias_string = validate_spawn_value(val, make_iter)
|
alias_string = init_spawn_value(val, make_iter)
|
||||||
|
|
||||||
val = prot.pop("tags", [])
|
val = prot.pop("tags", [])
|
||||||
tags = validate_spawn_value(val, make_iter)
|
tags = init_spawn_value(val, make_iter)
|
||||||
|
|
||||||
prototype_key = prototype.get('prototype_key', None)
|
prototype_key = prototype.get('prototype_key', None)
|
||||||
if prototype_key:
|
if prototype_key:
|
||||||
|
|
@ -291,15 +486,15 @@ def spawn(*prototypes, **kwargs):
|
||||||
tags.append((prototype_key, _PROTOTYPE_TAG_CATEGORY))
|
tags.append((prototype_key, _PROTOTYPE_TAG_CATEGORY))
|
||||||
|
|
||||||
val = prot.pop("exec", "")
|
val = prot.pop("exec", "")
|
||||||
execs = validate_spawn_value(val, make_iter)
|
execs = init_spawn_value(val, make_iter)
|
||||||
|
|
||||||
# extract ndb assignments
|
# extract ndb assignments
|
||||||
nattribute = dict((key.split("_", 1)[1], validate_spawn_value(val, _to_obj))
|
nattributes = dict((key.split("_", 1)[1], init_spawn_value(val, value_to_obj))
|
||||||
for key, val in prot.items() if key.startswith("ndb_"))
|
for key, val in prot.items() if key.startswith("ndb_"))
|
||||||
|
|
||||||
# the rest are attributes
|
# the rest are attributes
|
||||||
val = prot.pop("attrs", [])
|
val = prot.pop("attrs", [])
|
||||||
attributes = validate_spawn_value(val, list)
|
attributes = init_spawn_value(val, list)
|
||||||
|
|
||||||
simple_attributes = []
|
simple_attributes = []
|
||||||
for key, value in ((key, value) for key, value in prot.items()
|
for key, value in ((key, value) for key, value in prot.items()
|
||||||
|
|
@ -307,11 +502,11 @@ def spawn(*prototypes, **kwargs):
|
||||||
if is_iter(value) and len(value) > 1:
|
if is_iter(value) and len(value) > 1:
|
||||||
# (value, category)
|
# (value, category)
|
||||||
simple_attributes.append((key,
|
simple_attributes.append((key,
|
||||||
validate_spawn_value(value[0], _to_obj_or_any),
|
init_spawn_value(value[0], value_to_obj_or_any),
|
||||||
validate_spawn_value(value[1], str)))
|
init_spawn_value(value[1], str)))
|
||||||
else:
|
else:
|
||||||
simple_attributes.append((key,
|
simple_attributes.append((key,
|
||||||
validate_spawn_value(value, _to_obj_or_any)))
|
init_spawn_value(value, value_to_obj_or_any)))
|
||||||
|
|
||||||
attributes = attributes + simple_attributes
|
attributes = attributes + simple_attributes
|
||||||
attributes = [tup for tup in attributes if not tup[0] in _NON_CREATE_KWARGS]
|
attributes = [tup for tup in attributes if not tup[0] in _NON_CREATE_KWARGS]
|
||||||
|
|
@ -320,7 +515,7 @@ def spawn(*prototypes, **kwargs):
|
||||||
objsparams.append((create_kwargs, permission_string, lock_string,
|
objsparams.append((create_kwargs, permission_string, lock_string,
|
||||||
alias_string, nattributes, attributes, tags, execs))
|
alias_string, nattributes, attributes, tags, execs))
|
||||||
|
|
||||||
return _batch_create_object(*objsparams)
|
return batch_create_object(*objsparams)
|
||||||
|
|
||||||
|
|
||||||
# Testing
|
# Testing
|
||||||
|
|
|
||||||
|
|
@ -1,62 +0,0 @@
|
||||||
"""
|
|
||||||
|
|
||||||
Prototype utilities
|
|
||||||
|
|
||||||
"""
|
|
||||||
|
|
||||||
_PROTOTYPE_META_NAMES = ("prototype_key", "prototype_desc", "prototype_tags", "prototype_locks")
|
|
||||||
|
|
||||||
|
|
||||||
class PermissionError(RuntimeError):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def prototype_to_str(prototype):
|
|
||||||
"""
|
|
||||||
Format a prototype to a nice string representation.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
prototype (dict): The prototype.
|
|
||||||
"""
|
|
||||||
|
|
||||||
header = (
|
|
||||||
"|cprototype key:|n {}, |ctags:|n {}, |clocks:|n {} \n"
|
|
||||||
"|cdesc:|n {} \n|cprototype:|n ".format(
|
|
||||||
prototype['prototype_key'],
|
|
||||||
", ".join(prototype['prototype_tags']),
|
|
||||||
prototype['prototype_locks'],
|
|
||||||
prototype['prototype_desc']))
|
|
||||||
proto = ("{{\n {} \n}}".format(
|
|
||||||
"\n ".join(
|
|
||||||
"{!r}: {!r},".format(key, value) for key, value in
|
|
||||||
sorted(prototype.items()) if key not in _PROTOTYPE_META_NAMES)).rstrip(","))
|
|
||||||
return header + proto
|
|
||||||
|
|
||||||
|
|
||||||
def prototype_diff_from_object(prototype, obj):
|
|
||||||
"""
|
|
||||||
Get a simple diff for a prototype compared to an object which may or may not already have a
|
|
||||||
prototype (or has one but changed locally). For more complex migratations a manual diff may be
|
|
||||||
needed.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
prototype (dict): Prototype.
|
|
||||||
obj (Object): Object to
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
diff (dict): Mapping for every prototype key: {"keyname": "REMOVE|UPDATE|KEEP", ...}
|
|
||||||
|
|
||||||
"""
|
|
||||||
prot1 = prototype
|
|
||||||
prot2 = prototype_from_object(obj)
|
|
||||||
|
|
||||||
diff = {}
|
|
||||||
for key, value in prot1.items():
|
|
||||||
diff[key] = "KEEP"
|
|
||||||
if key in prot2:
|
|
||||||
if callable(prot2[key]) or value != prot2[key]:
|
|
||||||
diff[key] = "UPDATE"
|
|
||||||
elif key not in prot2:
|
|
||||||
diff[key] = "REMOVE"
|
|
||||||
|
|
||||||
return diff
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue