# coding: utf-8
import atexit
import glob
import importlib
import inspect
import os.path
import queue
import threading
import time

import modder


class ModManager(object):

    def __init__(self, mod_directory=None):
        if not mod_directory:
            mod_directory = os.path.join(os.path.dirname(__file__), 'mods')
        self.mod_directory = mod_directory
        self.mods = []
        self.mod_definitions = []

        self.check_mod_directory()
        self.load_mods()
        self.init_mods()

    def check_mod_directory(self):
        '''Make sure `mod_directory` is a valid Python package'''
        if not os.path.isdir(self.mod_directory):
            os.makedirs(self.mod_directory)

        package_init_file = os.path.join(self.mod_directory, '__init__.py')
        if not os.path.isfile(package_init_file):
            with open(package_init_file, 'w') as wf:
                wf.write('# coding: utf-8')

    def load_mods(self):
        '''
        Import all modules in `mod_directory`,
        and load all ModBase subclasses into memory.
        '''
        base_package = os.path.basename(self.mod_directory)
        for pyfile in glob.glob('{}/*.py'.format(base_package)):
            # Get module name out of file path
            pymodule_name = os.path.splitext(os.path.basename(pyfile))[0]
            # Import target module
            pymodule = importlib.import_module('{}.{}'.format(base_package, pymodule_name))

            # Store all ModBase subclass definitions
            for name in dir(pymodule):
                reference = getattr(pymodule, name)
                if not name.startswith('_'):
                    if inspect.isclass(reference) and issubclass(reference, modder.ModBase):
                        self.mod_definitions.append(reference)

    def init_mods(self):
        '''Instanciate all ModBase subclasses'''
        for cls in self.mod_definitions:
            if issubclass(cls, modder.ModBase):
                self.mods.append(cls())

    def execute(self, mod_func, event):
        '''Trigger a registered mod method'''
        # TODO Use a threadpool or something to async execute funtions
        if callable(mod_func):
            mod_func(event)

    def trigger(self, name):
        '''Trigger an event and broadcast it to all subscribed mods'''
        if name in modder.EVENTS:
            if name in modder.MOD_REGISTRY:
                event = self.__generate_event(name)
                [self.execute(func, event) for func in modder.MOD_REGISTRY[name]]

    def __generate_event(self, name):
        '''Generate Event object with data'''
        if name.startswith('Modder.'):
            return modder.Event(name)
        elif name.startswith('Timer.'):
            return modder.Event(name, data={'timestamp': time.time()})
        else:
            return modder.Event(name)


def test():
    event_queue = queue.Queue()
    mod_manager = ModManager()
    event_queue.put('Modder.Started')

    timer_stop = threading.Event()
    timer_thread = modder.timer.TimerThread(event_queue, timer_stop)
    timer_thread.daemon = True
    timer_thread.start()

    def before_quit():
        timer_stop.set()
        mod_manager.trigger('Modder.BeforeQuit')

    atexit.register(before_quit)

    while 1:
        try:
            event_name = event_queue.get(timeout=1)
        except queue.Empty:
            pass
        else:
            if event_name in modder.EVENTS:
                mod_manager.trigger(event_name)


if __name__ == '__main__':
    test()

@@ -56,23 +56,8 @@
 lib%0A
-import inspect%0A
 impo
@@ -363,51 +363,21 @@
 elf.
-mods = %5B%5D%0A        self.mod_definitions = %5B%5D
+__pool = None
 %0A%0A  
@@ -452,21 +452,314 @@
 lf.init_
-mods(
+pool()%0A%0A    def init_pool(self):%0A        '''Init pool container for mod executors'''%0A        if self.__pool is None:%0A            pool_size = 0%0A            for event, mods in modder.MOD_REGISTRY.items():%0A                pool_size += len(mods)%0A%0A            self.__pool = modder.ExecutorPool(pool_size
 )%0A%0A    d
@@ -1209,25 +1209,16 @@
      '''
-%0A        
 Import a
@@ -1250,71 +1250,8 @@
 ory%60
-,%0A        and load all ModBase subclasses into memory.%0A        
 '''%0A
@@ -1546,19 +1546,8 @@
     
- pymodule =
  imp
@@ -1621,285 +1621,306 @@
     
-        # Store all ModBase subclass definitions%0A            for name in dir(pymodule):%0A                reference = getattr(pymodule, name)%0A                if not name.startswith('_'):%0A                    if inspect.isclass(reference) and issubclass(reference, modder.ModBase):
+def execute(self, mod_func, event):%0A        '''Trigger a registered mod callable'''%0A        if callable(mod_func):%0A            mod_worker = threading.Thread(%0A                target=mod_func,%0A                name='%7B%7D on %7B%7D event'.format(mod_func.__name__, event.name),%0A                args=(event, )
 %0A   
@@ -1920,32 +1920,34 @@
 , )%0A            
+)%0A
             self
@@ -1946,440 +1946,306 @@
     
-self.mod_definitions.append(reference)%0A%0A    def init_mods(self):%0A        '''Instanciate all ModBase subclasses'''%0A        for cls in self.mod_definitions:%0A            if issubclass(cls, modder.ModBase):%0A                self.mods.append(cls())%0A%0A    def execute(self, mod_func, event):%0A        '''Trigger a registered mod method'''%0A        # TODO Use a threadpool or something to async execute funtions%0A        if callable(mod_func):%0A
+mod_worker.daemon = True%0A%0A            try:%0A                self.__pool.add(mod_worker)%0A            except modder.exceptions.Full:%0A                raise UserWarning(%0A                    'ExecutorPool currently full (maxsize %7B:d%7D)'.format(self.__pool.maxsize)%0A                )%0A            else:%0A    
     
@@ -2260,18 +2260,21 @@
 mod_
-func(event
+worker.start(
 )%0A%0A 
@@ -2368,24 +2368,56 @@
 bed mods'''%0A
+        self.__pool.clean_up()%0A%0A
         if n

Use thread in pool to execute mod functions; Remove class-based mods