diff --git a/evennia/__init__.py b/evennia/__init__.py index 6858744d42..e04ab84dba 100644 --- a/evennia/__init__.py +++ b/evennia/__init__.py @@ -350,12 +350,14 @@ def set_trace(debugger="auto", term_size=(140, 40)): """ import sys dbg = None + pudb_mode = False if debugger in ('auto', 'pudb'): try: from pudb import debugger dbg = debugger.Debugger(stdout=sys.__stdout__, term_size=term_size) + pudb_mode = True except ImportError: if debugger == 'pudb': raise @@ -364,7 +366,12 @@ def set_trace(debugger="auto", term_size=(140, 40)): if not dbg: import pdb dbg = pdb.Pdb(stdout=sys.__stdout__) + pudb_mode = False - # Start debugger, forcing it up one stack frame (otherwise `set_trace` will start # debugger at - # this point, not the actual code location) - dbg.set_trace(sys._getframe().f_back) + if pudb_mode: + # Stopped at breakpoint. Press 'n' to continue into the code. + dbg.set_trace() + else: + # Start debugger, forcing it up one stack frame (otherwise `set_trace` will start debugger + # this point, not the actual code location) + dbg.set_trace(sys._getframe().f_back) diff --git a/evennia/prototypes/menus.py b/evennia/prototypes/menus.py index 45f047b1e0..6e14993a1c 100644 --- a/evennia/prototypes/menus.py +++ b/evennia/prototypes/menus.py @@ -200,9 +200,8 @@ def _wizard_options(curr_node, prev_node, next_node, color="|W", search=False): color=color, node=next_node.replace("_", "-")), "goto": "node_{}".format(next_node)}) - if "index" not in (prev_node, next_node): - options.append({"key": ("|wI|Wndex", "i"), - "goto": "node_index"}) + options.append({"key": ("|wI|Wndex", "i"), + "goto": "node_index"}) if curr_node: options.append({"key": ("|wV|Walidate prototype", "validate", "v"), @@ -319,7 +318,7 @@ def _get_current_value(caller, keyname, comparer=None, formatter=str, only_inher if keyname in flat_prot: out = formatter(comparer(prot[keyname], flat_prot[keyname])) if only_inherit: - if out: + if str(out).strip(): return "|WCurrent|n {} |W(|binherited|W):|n {}".format(keyname, out) return "" else: @@ -1953,14 +1952,12 @@ def _keep_diff(caller, **kwargs): tmp[path[-1]] = "KEEP" -def _format_diff_text_and_options(diff, exclude=None): +def _format_diff_text_and_options(diff): """ Reformat the diff in a way suitable for the olc menu. Args: diff (dict): A diff as produced by `prototype_diff`. - exclude (list, optional): List of root keys to skip, regardless - of diff instruction. Returns: options (list): List of options dict. @@ -2051,8 +2048,7 @@ def node_apply_diff(caller, **kwargs): base_obj = choice(update_objects) # from evennia import set_trace - diff, obj_prototype = spawner.prototype_diff_from_object( - prototype, base_obj, exceptions={"location": "KEEP"}) + diff, obj_prototype = spawner.prototype_diff_from_object(prototype, base_obj) helptext = """ This will go through all existing objects and apply the changes you accept. @@ -2069,7 +2065,10 @@ def node_apply_diff(caller, **kwargs): Note that the `location` will never be auto-adjusted because it's so rare to want to homogenize the location of all object instances.""" - txt, options = _format_diff_text_and_options(diff, exclude=['location'] if custom_location else None) + if not custom_location: + diff.pop("location", None) + + txt, options = _format_diff_text_and_options(diff) if options: text = ["Suggested changes to {} objects. ".format(len(update_objects)), @@ -2261,7 +2260,7 @@ def node_prototype_spawn(caller, **kwargs): options.append( {"desc": "Spawn in prototype's defined location ({loc})".format(loc=location), "goto": (_spawn, - dict(prototype=prototype))}) + dict(prototype=prototype, location=location, custom_location=True))}) caller_loc = caller.location if location != caller_loc: options.append( diff --git a/evennia/prototypes/prototypes.py b/evennia/prototypes/prototypes.py index 7b60770ccb..67eccaafd0 100644 --- a/evennia/prototypes/prototypes.py +++ b/evennia/prototypes/prototypes.py @@ -16,7 +16,7 @@ from evennia.utils.utils import ( get_all_typeclasses, to_str, dbref, justify) from evennia.locks.lockhandler import validate_lockstring, check_lockstring from evennia.utils import logger -from evennia.utils import inlinefuncs +from evennia.utils import inlinefuncs, dbserialize from evennia.utils.evtable import EvTable @@ -102,9 +102,18 @@ class DbPrototype(DefaultScript): """ def at_script_creation(self): self.key = "empty prototype" # prototype_key - self.desc = "A prototype" # prototype_desc + 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', {})) + + @prototype.setter + def prototype(self, prototype): + self.attributes.add('prototype', prototype) + # Prototype manager functions @@ -152,7 +161,7 @@ def save_prototype(**kwargs): # make sure meta properties are included with defaults stored_prototype = DbPrototype.objects.filter(db_key=prototype_key) - prototype = dict(stored_prototype[0].db.prototype) if stored_prototype else {} + prototype = stored_prototype[0].prototype if stored_prototype else {} kwargs['prototype_desc'] = kwargs.get("prototype_desc", prototype.get("prototype_desc", "")) prototype_locks = kwargs.get( @@ -185,7 +194,7 @@ def save_prototype(**kwargs): DbPrototype, key=prototype_key, desc=prototype['prototype_desc'], persistent=True, locks=prototype_locks, tags=prototype['prototype_tags'], attributes=[("prototype", prototype)]) - return stored_prototype.db.prototype + return stored_prototype.prototype create_prototype = save_prototype # alias @@ -279,7 +288,7 @@ def search_prototype(key=None, tags=None): # exact or partial match on key db_matches = db_matches.filter(db_key=key) or db_matches.filter(db_key__icontains=key) # return prototype - db_prototypes = [dict(dbprot.attributes.get("prototype", {})) for dbprot in db_matches] + db_prototypes = [dbprot.prototype for dbprot in db_matches] matches = db_prototypes + module_prototypes nmatches = len(matches) diff --git a/evennia/utils/dbserialize.py b/evennia/utils/dbserialize.py index c08704be0e..0ea024274e 100644 --- a/evennia/utils/dbserialize.py +++ b/evennia/utils/dbserialize.py @@ -29,7 +29,7 @@ except ImportError: from pickle import dumps, loads from django.core.exceptions import ObjectDoesNotExist from django.contrib.contenttypes.models import ContentType -from evennia.utils.utils import to_str, uses_database +from evennia.utils.utils import to_str, uses_database, is_iter from evennia.utils import logger __all__ = ("to_pickle", "from_pickle", "do_pickle", "do_unpickle", @@ -364,6 +364,31 @@ class _SaverDeque(_SaverMutable): def rotate(self, *args): self._data.rotate(*args) + +_DESERIALIZE_MAPPING = {_SaverList.__name__: list, _SaverDict.__name__: dict, + _SaverSet.__name__: set, _SaverOrderedDict.__name__: OrderedDict, + _SaverDeque.__name__: deque} + + +def deserialize(obj): + """ + Make sure to *fully* decouple a structure from the database, by turning all _Saver*-mutables + inside it back into their normal Python forms. + + """ + def _iter(obj): + typ = type(obj) + tname = typ.__name__ + if tname in ('_SaverDict', 'dict'): + return {_iter(key): _iter(val) for key, val in obj.items()} + elif tname in _DESERIALIZE_MAPPING: + return _DESERIALIZE_MAPPING[tname](_iter(val) for val in obj) + elif is_iter(obj): + return typ(_iter(val) for val in obj) + return obj + return _iter(obj) + + # # serialization helpers