Remove all code related to "hot-reloading"

Support is being dropped for now and until further notice.
This commit is contained in:
Ben 2019-08-23 21:46:31 +01:00
parent 9098ae50a1
commit 8a9f2e8b99
8 changed files with 27 additions and 311 deletions

View File

@ -5,7 +5,8 @@ from abc import abstractmethod
import asyncio
import enum
import logging
import copy
import importlib
import pkgutil
class StatusField(enum.IntEnum):
@ -19,7 +20,7 @@ class StatusField(enum.IntEnum):
HasWalkedPuffleFirstTime = 65536
HasWalkedPuffleSecondTime = 131072
package_modules = package_modules + sub_package_modules
class ConflictResolution(enum.Enum):
Silent = 0
Append = 1
@ -45,24 +46,15 @@ class _AbstractManager(dict):
self.server = server
self.logger = logging.getLogger('houdini')
self.__backup = None
super().__init__()
@abstractmethod
def load(self, module):
"""Loads entries from module"""
async def setup(self, module):
"""Setup manager class"""
@abstractmethod
def remove(self, module):
"""Removes all entries by module"""
def backup(self):
self.__backup = copy.copy(self)
def restore(self):
if self.__backup is not None:
self.update(self.__backup)
self.__backup = None
async def load(self, module):
"""Loads entries from module"""
class PenguinStringCompiler(OrderedDict):

View File

@ -64,6 +64,7 @@ player_in_room = handlers.player_in_room
class CommandManager(_AbstractManager):
def load(self, module):
async def load(self, module):
command_objects = inspect.getmembers(module, is_command)
if not isinstance(module, plugins.IPlugin):
raise TypeError('Commands can only be loaded from plugins')
@ -94,12 +95,6 @@ class CommandManager(_AbstractManager):
else:
parent_commands[name] = [command_object]
def remove(self, module):
for command_name, command_handlers in self.items():
for command_handler in command_handlers:
if module.__name__ == command_handler.callback.__module__:
command_handlers.remove(command_handler)
def is_command(command_object):
return issubclass(type(command_object), _Command)

View File

@ -1,41 +0,0 @@
import os
def evaluate_listener_file_event(listener_file_event):
# Ignore all directory events
if listener_file_event.is_directory:
return False
listener_module_path = listener_file_event.src_path[2:]
# Ignore non-Python files
if listener_module_path[-3:] != ".py":
return False
# Ignore package index files
if '__init__.py' in listener_module_path:
return False
listener_module = listener_module_path.replace(os.path.sep, ".")[:-3]
return listener_module_path, listener_module
def evaluate_plugin_file_event(plugin_file_event):
# Ignore all directory events
if plugin_file_event.is_directory:
return False
plugin_module_path = plugin_file_event.src_path[2:]
# Ignore non-Python files
if plugin_module_path[-3:] != ".py":
return False
# Remove file extension and replace path separator with dots. Then make like a banana.. and split.
plugin_module_tokens = plugin_module_path.replace(os.path.sep, ".")[:-3].split(".")
if plugin_module_tokens.pop() == "__init__":
return plugin_module_path, ".".join(plugin_module_tokens)
return False

View File

@ -1,88 +0,0 @@
import sys
import importlib
import logging
from watchdog.events import FileSystemEventHandler
from houdini.events import evaluate_listener_file_event
class ListenerFileEventHandler(FileSystemEventHandler):
def __init__(self, server):
self.server = server
self.logger = logging.getLogger('houdini')
def on_created(self, event):
listener_module_details = evaluate_listener_file_event(event)
if not listener_module_details:
return
listener_module_path, listener_module = listener_module_details
self.logger.debug('New listener module detected {}'.format(listener_module))
try:
module = importlib.import_module(listener_module)
self.server.xt_listeners.load(module)
self.server.xml_listeners.load(module)
self.logger.info('New listener module loaded {}'.format(listener_module))
except Exception as import_error:
self.logger.error('{} detected in {}, not importing'.format(
import_error.__class__.__name__, listener_module))
def on_deleted(self, event):
listener_module_details = evaluate_listener_file_event(event)
if not listener_module_details:
return
listener_module_path, listener_module = listener_module_details
if listener_module not in sys.modules:
return
listener_module_object = sys.modules[listener_module]
self.logger.debug('Deleting listener module {}'.format(listener_module))
self.server.xt_listeners.remove(listener_module_object)
self.server.xml_listeners.remove(listener_module_object)
del sys.modules[listener_module]
self.logger.info('Deleted listener module {}'.format(listener_module))
def on_modified(self, event):
listener_module_details = evaluate_listener_file_event(event)
if not listener_module_details:
return
listener_module_path, listener_module = listener_module_details
if listener_module not in sys.modules:
return False
self.logger.info('Reloading listener module {}'.format(listener_module))
self.server.xt_listeners.backup()
self.server.xml_listeners.backup()
listener_module_object = sys.modules[listener_module]
self.server.xt_listeners.remove(listener_module_object)
self.server.xml_listeners.remove(listener_module_object)
try:
module = importlib.reload(listener_module_object)
self.server.xt_listeners.load(module)
self.server.xml_listeners.load(module)
self.logger.info('Successfully reloaded listener module {}!'.format(listener_module))
except Exception as rebuild_error:
self.logger.error('{} detected in {}, not reloading.'.format(
rebuild_error.__class__.__name__, listener_module))
self.server.xt_listeners.restore()
self.server.xml_listeners.restore()

View File

@ -1,87 +0,0 @@
import sys
import importlib
import os.path
import logging
from watchdog.events import FileSystemEventHandler
from houdini.events import evaluate_plugin_file_event
class PluginFileEventHandler(FileSystemEventHandler):
def __init__(self, server):
self.server = server
self.logger = logging.getLogger('houdini')
def on_created(self, event):
plugin_module_details = evaluate_plugin_file_event(event)
if not plugin_module_details:
return
plugin_module_path, plugin_module = plugin_module_details
self.logger.debug('New plugin detected {}'.format(plugin_module))
try:
plugin_module_object = importlib.import_module(plugin_module)
self.server.plugins.load(plugin_module_object)
self.logger.info('New plugin {} has been loaded'.format(plugin_module))
except Exception as import_error:
self.logger.error('{} detected in {}, not importing'.format(
import_error.__class__.__name__, plugin_module))
def on_deleted(self, event):
plugin_module_path = event.src_path[2:]
plugin_module = plugin_module_path.replace(os.path.sep, ".")
if plugin_module not in sys.modules:
return
self.logger.debug('Deleting plugin {}'.format(plugin_module))
plugin_module_object = sys.modules[plugin_module]
self.server.plugins.remove(plugin_module_object)
del sys.modules[plugin_module]
self.logger.info('Plugin {} has been removed'.format(plugin_module))
def on_modified(self, event):
plugin_module_details = evaluate_plugin_file_event(event)
if not plugin_module_details:
return
plugin_module_path, plugin_module = plugin_module_details
if plugin_module not in sys.modules:
return
self.logger.info('Reloading plugin {}'.format(plugin_module))
plugin_module_object = sys.modules[plugin_module]
self.server.xt_listeners.backup()
self.server.xml_listeners.backup()
self.server.commands.backup()
self.server.plugins.remove(plugin_module_object)
try:
new_plugin_module = importlib.reload(plugin_module_object)
self.server.plugins.load(new_plugin_module)
self.logger.info('Successfully reloaded plugin {}'.format(plugin_module))
except LookupError as lookup_error:
self.logger.warning('Did not reload plugin \'{}\': {}'.format(plugin_module, lookup_error))
except Exception as rebuild_error:
self.logger.error('{} detected in {}, not reloading'.format(
rebuild_error.__class__.__name__, plugin_module))
self.server.xt_listeners.restore()
self.server.xml_listeners.restore()
self.server.commands.restore()

View File

@ -118,16 +118,12 @@ class _ListenerManager(_AbstractManager):
super().__init__(server)
def setup(self, module, strict_load=None, exclude_load=None):
async def setup(self, module, strict_load=None, exclude_load=None):
self.strict_load, self.exclude_load = strict_load, exclude_load
for handler_module in self.server.get_package_modules(module):
module = sys.modules[handler_module] if handler_module in sys.modules.keys() \
else importlib.import_module(handler_module)
self.load(module)
for handler_module in get_package_modules(module):
await self.load(handler_module)
self.logger.debug('Loaded {} listeners'.format(len(self)))
def load(self, module):
async def load(self, module):
module_name = module.__module__ if isinstance(module, plugins.IPlugin) else module.__name__
if not (self.strict_load and module_name not in self.strict_load or
self.exclude_load and module_name in self.exclude_load):
@ -159,12 +155,6 @@ class _ListenerManager(_AbstractManager):
for override in listener_object.overrides:
self[override.packet].remove(override)
def remove(self, module):
for handler_id, handler_listeners in self.items():
for handler_listener in handler_listeners:
if module.__name__ == handler_listener.callback.__module__:
handler_listeners.remove(handler_listener)
@classmethod
def is_listener(cls, listener):
return issubclass(type(listener), _Listener)

View File

@ -1,8 +1,6 @@
import asyncio
import os
import sys
import pkgutil
import importlib
import copy
from houdini.spheniscidae import Spheniscidae
@ -15,7 +13,6 @@ from logging.handlers import RotatingFileHandler
import aioredis
from aiocache import SimpleMemoryCache, caches
from watchdog.observers import Observer
from houdini.data import db
from houdini.data.item import ItemCrumbsCollection
@ -37,8 +34,6 @@ except ImportError:
import houdini.handlers
import houdini.plugins
from houdini.events.listener_file_event import ListenerFileEventHandler
from houdini.events.plugin_file_event import PluginFileEventHandler
from houdini.handlers import XTListenerManager, XMLListenerManager
from houdini.plugins import PluginManager
@ -170,11 +165,12 @@ class Houdini:
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_penguin_string_compiler)
self.xml_listeners.setup(houdini.handlers, exclude_load='houdini.handlers.login.login')
self.xt_listeners.setup(houdini.handlers)
await self.xml_listeners.setup(houdini.handlers, exclude_load='houdini.handlers.login.login')
await self.xt_listeners.setup(houdini.handlers)
await self.dummy_event_listeners.setup(houdini.handlers)
self.logger.info('World server started')
else:
self.xml_listeners.setup(houdini.handlers, 'houdini.handlers.login.login')
await self.xml_listeners.setup(houdini.handlers, 'houdini.handlers.login.login')
self.logger.info('Login server started')
self.items = await ItemCrumbsCollection.get_collection()
@ -218,11 +214,6 @@ class Houdini:
self.permissions = await PermissionCrumbsCollection.get_collection()
handlers_path = os.path.join(os.path.dirname(__file__), 'handlers')
plugins_path = os.path.join(os.path.dirname(__file__), 'plugins')
self.configure_observers([handlers_path, ListenerFileEventHandler],
[plugins_path, PluginFileEventHandler])
self.logger.info('Multi-client support is {}'.format(
'enabled' if self.config.client['MultiClientSupport'] else 'disabled'))
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port']))
@ -231,7 +222,7 @@ class Houdini:
self.logger.warning('The static key has been changed from the default, '
'this may cause authentication issues!')
self.plugins.setup(houdini.plugins)
await self.plugins.setup(houdini.plugins)
async with self.server:
await self.server.serve_forever()
@ -239,31 +230,3 @@ class Houdini:
async def client_connected(self, reader, writer):
client_object = self.client_class(self, reader, writer)
await client_object.run()
def get_package_modules(self, package):
package_modules = []
for importer, module_name, is_package in pkgutil.iter_modules(package.__path__):
full_module_name = '{0}.{1}'.format(package.__name__, module_name)
if is_package:
subpackage_object = importlib.import_module(full_module_name, package=package.__path__)
subpackage_object_directory = dir(subpackage_object)
if houdini.plugins.IPlugin.__name__ in subpackage_object_directory:
package_modules.append(subpackage_object)
continue
sub_package_modules = self.get_package_modules(subpackage_object)
package_modules = package_modules + sub_package_modules
else:
package_modules.append(full_module_name)
return package_modules
def configure_observers(self, *observer_settings):
for observer_path, observer_class in observer_settings:
event_observer = Observer()
event_observer.schedule(observer_class(self), observer_path, recursive=True)
event_observer.start()

View File

@ -2,9 +2,8 @@ from abc import ABC
from abc import abstractmethod
import inspect
import asyncio
from houdini import _AbstractManager
from houdini import _AbstractManager, get_package_modules
class IPlugin(ABC):
@ -37,11 +36,11 @@ class IPlugin(ABC):
class PluginManager(_AbstractManager):
def setup(self, module):
for plugin_package in self.server.get_package_modules(module):
self.load(plugin_package)
async def setup(self, module):
for plugin_package in get_package_modules(module):
await self.load(plugin_package)
def load(self, module):
async def load(self, module):
plugin_class, plugin_type = inspect.getmembers(module, is_plugin).pop()
if self.server.server_config['Plugins'] is not True and \
@ -51,19 +50,12 @@ class PluginManager(_AbstractManager):
plugin_object = plugin_type(self.server)
self[module.__name__] = plugin_object
self.server.commands.load(plugin_object)
self.server.xt_listeners.load(plugin_object)
self.server.xml_listeners.load(plugin_object)
await self.server.commands.load(plugin_object)
await self.server.xt_listeners.load(plugin_object)
await self.server.xml_listeners.load(plugin_object)
await self.server.dummy_event_listeners.load(plugin_object)
asyncio.run_coroutine_threadsafe(plugin_object.ready(), self.server.server.get_loop())
def remove(self, module):
if module.__name__ in self:
del self[module.__name__]
self.server.commands.remove(module)
self.server.xt_listeners.remove(module)
self.server.xml_listeners.remove(module)
await plugin_object.ready()
def is_plugin(plugin_class):