From 7dcd8a59f035d8762dc028e7062de2883b1c89c2 Mon Sep 17 00:00:00 2001 From: Jeremy Stanley Date: Fri, 13 Jul 2018 16:56:17 +0000 Subject: [PATCH] Overhaul data reloading The data model change left the reload feature in a miserable state causing facets of mutable elements to be lost, changes introduced in read-only origins to be ignored, and so on. Simplify the implementation to just save, wipe and re-read all persistent data as if the engine were starting initially. Also add a basic check to the reload test to make sure a mutable element still has facets after reloading, so that we reduce the risk of future regression. Include a bit more verbose logging around when and what files are read at load/reload time. --- mudpy/data.py | 13 ++++++----- mudpy/misc.py | 61 +++++++++++++++++-------------------------------- mudpy/tests/selftest.py | 5 +++- 3 files changed, 32 insertions(+), 47 deletions(-) diff --git a/mudpy/data.py b/mudpy/data.py index d9eb8eb..6371045 100644 --- a/mudpy/data.py +++ b/mudpy/data.py @@ -1,6 +1,6 @@ """Data interface functions for the mudpy engine.""" -# Copyright (c) 2004-2017 Jeremy Stanley . Permission +# Copyright (c) 2004-2018 Jeremy Stanley . Permission # to use, copy, modify, and distribute this software is granted under # terms provided in the LICENSE file distributed with this software. @@ -58,15 +58,16 @@ class Data: self.source, relative=self.relative, universe=self.universe) try: self.data = yaml.safe_load(open(self.source)) + log_entry = ("Loaded file %s into memory." % self.source, 5) except FileNotFoundError: # it's normal if the file is one which doesn't exist yet self.data = {} log_entry = ("File %s is unavailable." % self.source, 6) - try: - mudpy.misc.log(*log_entry) - except NameError: - # happens when we're not far enough along in the init process - self.universe.setup_loglines.append(log_entry) + try: + mudpy.misc.log(*log_entry) + except NameError: + # happens when we're not far enough along in the init process + self.universe.setup_loglines.append(log_entry) if not hasattr(self.universe, "files"): self.universe.files = {} self.universe.files[self.source] = self diff --git a/mudpy/misc.py b/mudpy/misc.py index 3cd8e64..bae94b6 100644 --- a/mudpy/misc.py +++ b/mudpy/misc.py @@ -64,8 +64,9 @@ class Element: def reload(self): """Create a new element and replace this one.""" - Element(self.key, self.universe, self.origin) - del(self) + args = (self.key, self.universe, self.origin) + self.destroy() + Element(*args) def destroy(self): """Remove an element from the universe and destroy it.""" @@ -363,23 +364,9 @@ class Universe: # it's possible for this to enter before logging configuration is read pending_loglines = [] - # the files dict must exist and filename needs to be read-only - if not hasattr( - self, "files" - ) or not ( - self.filename in self.files and self.files[ - self.filename - ].is_writeable() - ): - - # clear out all read-only files - if hasattr(self, "files"): - for data_filename in list(self.files.keys()): - if not self.files[data_filename].is_writeable(): - del self.files[data_filename] - - # start loading from the initial file - mudpy.data.Data(self.filename, self) + # start populating the (re)files dict from the base config + self.files = {} + mudpy.data.Data(self.filename, self) # load default storage locations for groups if hasattr(self, "contents") and "mudpy.filing" in self.contents: @@ -404,17 +391,6 @@ class Universe: if user.avatar in inactive_avatars: inactive_avatars.remove(user.avatar) - # go through all elements to clear out inactive avatar locations - for element in self.contents.values(): - area = element.get("location") - if element in inactive_avatars and area: - if area in self.contents and element.key in self.contents[ - area - ].contents: - del self.contents[area].contents[element.key] - element.set("default_location", area) - element.remove_facet("location") - # another pass to straighten out all the element contents for element in self.contents.values(): element.update_location() @@ -584,26 +560,30 @@ class User: def reload(self): """Save, load a new user and relocate the connection.""" + # copy old attributes + attributes = self.__dict__ + # get out of the list self.remove() + # get rid of the old user object + del(self) + # create a new user object new_user = User() # set everything equivalent - for attribute in vars(self).keys(): - exec("new_user." + attribute + " = self." + attribute) + new_user.__dict__ = attributes # the avatar needs a new owner if new_user.avatar: + new_user.account = universe.contents[new_user.account.key] + new_user.avatar = universe.contents[new_user.avatar.key] new_user.avatar.owner = new_user # add it to the list universe.userlist.append(new_user) - # get rid of the old user object - del(self) - def replace_old_connections(self): """Disconnect active users with the same name.""" @@ -1414,12 +1394,13 @@ def on_pulse(): def reload_data(): """Reload all relevant objects.""" - for user in universe.userlist[:]: - user.reload() - for element in universe.contents.values(): - if element.origin.is_writeable(): - element.reload() + universe.save() + old_userlist = universe.userlist[:] + for element in list(universe.contents.values()): + element.destroy() universe.load() + for user in old_userlist: + user.reload() def check_for_connection(listening_socket): diff --git a/mudpy/tests/selftest.py b/mudpy/tests/selftest.py index 0b29386..f262063 100644 --- a/mudpy/tests/selftest.py +++ b/mudpy/tests/selftest.py @@ -183,7 +183,10 @@ test_admin_help = ( test_reload = ( (2, "> ", "reload"), (2, r"Reloading all code modules, configs and data\." - r".* User admin reloaded the world\.", ""), + r".* User admin reloaded the world\.", + "show element account.admin"), + (2, 'These are the properties of the "account.admin" element.*' + " \x1b\[32mpasshash:\r\n\x1b\[31m\$.*> ", ""), ) test_set_facet = ( -- 2.11.0