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

View File

@ -64,6 +64,7 @@ player_in_room = handlers.player_in_room
class CommandManager(_AbstractManager): class CommandManager(_AbstractManager):
def load(self, module): def load(self, module):
async def load(self, module):
command_objects = inspect.getmembers(module, is_command) command_objects = inspect.getmembers(module, is_command)
if not isinstance(module, plugins.IPlugin): if not isinstance(module, plugins.IPlugin):
raise TypeError('Commands can only be loaded from plugins') raise TypeError('Commands can only be loaded from plugins')
@ -94,12 +95,6 @@ class CommandManager(_AbstractManager):
else: else:
parent_commands[name] = [command_object] 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): def is_command(command_object):
return issubclass(type(command_object), _Command) 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) 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 self.strict_load, self.exclude_load = strict_load, exclude_load
for handler_module in self.server.get_package_modules(module): for handler_module in get_package_modules(module):
module = sys.modules[handler_module] if handler_module in sys.modules.keys() \ await self.load(handler_module)
else importlib.import_module(handler_module)
self.load(module)
self.logger.debug('Loaded {} listeners'.format(len(self))) async def load(self, module):
def load(self, module):
module_name = module.__module__ if isinstance(module, plugins.IPlugin) else module.__name__ 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 if not (self.strict_load and module_name not in self.strict_load or
self.exclude_load and module_name in self.exclude_load): self.exclude_load and module_name in self.exclude_load):
@ -159,12 +155,6 @@ class _ListenerManager(_AbstractManager):
for override in listener_object.overrides: for override in listener_object.overrides:
self[override.packet].remove(override) 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 @classmethod
def is_listener(cls, listener): def is_listener(cls, listener):
return issubclass(type(listener), _Listener) return issubclass(type(listener), _Listener)

View File

@ -1,8 +1,6 @@
import asyncio import asyncio
import os import os
import sys import sys
import pkgutil
import importlib
import copy import copy
from houdini.spheniscidae import Spheniscidae from houdini.spheniscidae import Spheniscidae
@ -15,7 +13,6 @@ from logging.handlers import RotatingFileHandler
import aioredis import aioredis
from aiocache import SimpleMemoryCache, caches from aiocache import SimpleMemoryCache, caches
from watchdog.observers import Observer
from houdini.data import db from houdini.data import db
from houdini.data.item import ItemCrumbsCollection from houdini.data.item import ItemCrumbsCollection
@ -37,8 +34,6 @@ except ImportError:
import houdini.handlers import houdini.handlers
import houdini.plugins 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.handlers import XTListenerManager, XMLListenerManager
from houdini.plugins import PluginManager from houdini.plugins import PluginManager
@ -170,11 +165,12 @@ class Houdini:
PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler) PenguinStringCompiler.setup_default_builder(self.penguin_string_compiler)
PenguinStringCompiler.setup_anonymous_default_builder(self.anonymous_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') await self.xml_listeners.setup(houdini.handlers, exclude_load='houdini.handlers.login.login')
self.xt_listeners.setup(houdini.handlers) await self.xt_listeners.setup(houdini.handlers)
await self.dummy_event_listeners.setup(houdini.handlers)
self.logger.info('World server started') self.logger.info('World server started')
else: 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.logger.info('Login server started')
self.items = await ItemCrumbsCollection.get_collection() self.items = await ItemCrumbsCollection.get_collection()
@ -218,11 +214,6 @@ class Houdini:
self.permissions = await PermissionCrumbsCollection.get_collection() 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( self.logger.info('Multi-client support is {}'.format(
'enabled' if self.config.client['MultiClientSupport'] else 'disabled')) 'enabled' if self.config.client['MultiClientSupport'] else 'disabled'))
self.logger.info('Listening on {}:{}'.format(self.server_config['Address'], self.server_config['Port'])) 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, ' self.logger.warning('The static key has been changed from the default, '
'this may cause authentication issues!') 'this may cause authentication issues!')
self.plugins.setup(houdini.plugins) await self.plugins.setup(houdini.plugins)
async with self.server: async with self.server:
await self.server.serve_forever() await self.server.serve_forever()
@ -239,31 +230,3 @@ class Houdini:
async def client_connected(self, reader, writer): async def client_connected(self, reader, writer):
client_object = self.client_class(self, reader, writer) client_object = self.client_class(self, reader, writer)
await client_object.run() 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 from abc import abstractmethod
import inspect import inspect
import asyncio
from houdini import _AbstractManager from houdini import _AbstractManager, get_package_modules
class IPlugin(ABC): class IPlugin(ABC):
@ -37,11 +36,11 @@ class IPlugin(ABC):
class PluginManager(_AbstractManager): class PluginManager(_AbstractManager):
def setup(self, module): async def setup(self, module):
for plugin_package in self.server.get_package_modules(module): for plugin_package in get_package_modules(module):
self.load(plugin_package) await self.load(plugin_package)
def load(self, module): async def load(self, module):
plugin_class, plugin_type = inspect.getmembers(module, is_plugin).pop() plugin_class, plugin_type = inspect.getmembers(module, is_plugin).pop()
if self.server.server_config['Plugins'] is not True and \ if self.server.server_config['Plugins'] is not True and \
@ -51,19 +50,12 @@ class PluginManager(_AbstractManager):
plugin_object = plugin_type(self.server) plugin_object = plugin_type(self.server)
self[module.__name__] = plugin_object self[module.__name__] = plugin_object
self.server.commands.load(plugin_object) await self.server.commands.load(plugin_object)
self.server.xt_listeners.load(plugin_object) await self.server.xt_listeners.load(plugin_object)
self.server.xml_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()) await plugin_object.ready()
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)
def is_plugin(plugin_class): def is_plugin(plugin_class):