Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/tutorius/addon.py
diff options
context:
space:
mode:
Diffstat (limited to 'tutorius/addon.py')
-rw-r--r--tutorius/addon.py120
1 files changed, 88 insertions, 32 deletions
diff --git a/tutorius/addon.py b/tutorius/addon.py
index 7ac68f7..8aa7b58 100644
--- a/tutorius/addon.py
+++ b/tutorius/addon.py
@@ -32,6 +32,7 @@ __action__ = {
"""
import os
+import sys
import re
import logging
@@ -41,55 +42,110 @@ PATH = re.sub("addon\\.py[c]$", "", __file__)+"addons"
TYPE_ACTION = 'action'
TYPE_EVENT = 'event'
-_cache = None
+STAT_MODIFY = 8
+
+# whether to auto reload modules on modifications
+AUTO_RELOAD = True
+
+__cache__ = {}
def _reload_addons():
- global _cache
- _cache = {}
- for addon in filter(lambda x: x.endswith("py"), os.listdir(PATH)):
- mod = __import__(PREFIX+'.'+re.sub("\\.py$", "", addon), {}, {}, [""])
- if hasattr(mod, "__action__"):
- _cache[mod.__action__['name']] = mod.__action__
- mod.__action__['type'] = TYPE_ACTION
- continue
- if hasattr(mod, "__event__"):
- _cache[mod.__event__['name']] = mod.__event__
- mod.__event__['type'] = TYPE_EVENT
+ """
+ Scan for all add-ons under the tutorius 'addons' folder and (re)cache
+ all modules found.
+ """
+ __cache__.clear()
+ for addon in (x for x in os.listdir(PATH) if x.endswith("py")):
+ mod_name = PREFIX+'.'+re.sub("\\.py$", "", addon)
+ _reload_module(mod_name)
+
+def _reload_module(mod_name):
+ """
+ Reload a specific module and update the cache. If the module changed
+ the module will be reloaded and future calls to create() will use
+ the new module. Existing instances won't be changed.
+
+ @param mod_name: the python name of the module.
+ Ex: 'sugar.tutorius.addons.foo'
+ """
+ # ensure module reloaded, by removing sys reference
+ sys.modules.pop(mod_name, False)
+
+ mod = __import__(name=mod_name, fromlist=[""])
+ if hasattr(mod, "__action__"):
+ __cache__[mod.__action__['name']] = mod.__action__
+ mod.__action__['type'] = TYPE_ACTION
+ mod.__action__['ts'] = os.stat(mod.__file__)[STAT_MODIFY]
+ elif hasattr(mod, "__event__"):
+ __cache__[mod.__event__['name']] = mod.__event__
+ mod.__event__['type'] = TYPE_EVENT
+ mod.__event__['ts'] = os.stat(mod.__file__)[STAT_MODIFY]
def create(name, *args, **kwargs):
- global _cache
- if not _cache:
+ """
+ Create an instance of an addon from its name.
+ If it cannot be instanciated for whatever reason, a message will be logged
+ to the console.
+
+ Note: properties can usually be initialized kwargs having the same name.
+ However, args and kwargs are always optionals.
+
+ @param name: the short name of the addon, as found in the 'name' key
+ of the __action__ or __event__ dict.
+ @returns: an instance of the add-on or None.
+ """
+ if (not __cache__) or (name not in __cache__):
_reload_addons()
try:
- comp_metadata = _cache[name]
+ metadata = __cache__[name]
+ module_name = metadata['class'].__module__
+ module_file = sys.modules[module_name].__file__
+ if AUTO_RELOAD and metadata['ts'] != os.stat(module_file)[STAT_MODIFY]:
+ _reload_module(module_name)
+ metadata = __cache__[name]
+
try:
- return comp_metadata['class'](*args, **kwargs)
- except:
- logging.error("Could not instantiate %s with parameters %s, %s"%(comp_metadata['name'],str(args), str(kwargs)))
- return None
+ return metadata['class'](*args, **kwargs)
+ except TypeError:
+ logging.error("Could not instantiate %s with parameters %s, %s" \
+ %(metadata['name'],str(args), str(kwargs)))
+ return None
except KeyError:
logging.error("Addon not found for class '%s'", name)
return None
def list_addons():
- global _cache
- if not _cache:
+ """
+ Returns a list of all loaded add-ons, filling the cache if necesary.
+
+ @returns: a list of addon names.
+ """
+ if not __cache__:
_reload_addons()
- return _cache.keys()
+ return __cache__.keys()
def get_addon_meta(name):
- global _cache
- if not _cache:
+ """
+ Returns an add-on's meta data from its name.
+ @param name: the short name of the add-on, as found in the 'name' metadata.
+ @returns: a reference to the dictionary of meta data.
+ """
+ if not __cache__ or (name not in __cache__):
_reload_addons()
- return _cache[name]
+ return __cache__[name]
def get_name_from_type(typ):
- global _cache
- if not _cache:
- _reload_addons()
- for addon in _cache.keys():
- if typ == _cache[addon]['class']:
- return addon
- return None
+ """
+ Lookup an addon's name from the type of an object returned by create().
+ Note: After refreshing the cache, this function should still return the
+ correct name of old instances but calls using that name will refer to
+ the new code.
+
+ @param typ: the type (class) of an add-on.
+ @returns: the short name of the add-on, as used in this module's functions.
+ """
+ mod = sys.modules[typ.__module__]
+ meta = getattr(mod, '__action__', None) or getattr(mod, '__event__')
+ return meta['name']
# vim:set ts=4 sts=4 sw=4 et: