# -*- mode: python; coding: utf-8 -*- import sys, os, stat, glob import logging logger = logging.getLogger("factory") import gtk import widgets import config import util import exc import domain import inspector_base as ibase from inspector_base import Inspector from skin_base import Skin import gobject import sqlobject import model import db class IssueInspector(Inspector): name = 'Issue' def __init__(self, issue): Inspector.__init__(self, None) self.issue = issue # FIXME: Cambiar estos métodos por set_value() y get_value() def set_issue(self, issue): self.issue = issue # FIXME, usa el TOPIC por defecto, el issue es el "estado" del inspector self.notify(self.issue, Inspector.TOPIC_ISSUE) # def get_level(self): # return self.issue.level # Si no tiene get_uri, debe tener un subject con # uri (para el get_uri del padre) def get_uri(self): return self.issue.uri # def get_value(self): # return self.issue.title # # def get_details(self): # return self.issue.details class IssueSkin(Skin, util.GladeWrapper): name = "issue" can_render_names = ['Issue'] def __init__(self, inspector=None, issue=None): if not inspector: inspector = IssueInspector(issue) Skin.__init__(self, inspector, Skin.HORIZONTAL) util.GladeWrapper.__init__(self, config.path("skin_message.glade"), 'gui') # Add the entry self.wg_entry_uri = widgets.Entry(copy=True) self.wg_entry_uri.set_editable(False) self.wg_entry_uri.show_all() self.wg_hbox_uri.pack_start(self.wg_entry_uri) self.gui = self.get_widget('gui') issue = inspector.issue if issue.level < util.Issue.ERROR: bg_color = gtk.gdk.Color(0xf500, 0xf500, 0xb500) elif issue.level == util.Issue.ERROR: bg_color = gtk.gdk.Color(0xff00, 0xc500, 0xa500) else: bg_color = gtk.gdk.color_parse('red') self.gui.modify_bg(gtk.STATE_NORMAL, bg_color) uri = str(issue.uri) if uri: self.wg_entry_uri.set_text(uri) self.wg_hbox_uri.show() else: self.wg_hbox_uri.hide() try: self.wg_label_error.set_markup("%s" % issue.title.encode("utf-8")) if issue.details and isinstance(issue.details, str): nlines = issue.details.count('\n') first_line = issue.details.split('\n')[0][:50] if first_line == issue.details: self.wg_label_details.set_markup(first_line) self.wg_hbox_details_mini.show() else: self.wg_details_label.set_text('Details: ' + first_line) self.wg_issue_details.get_buffer().insert_at_cursor(str(issue.details)) self.wg_expander_details.show() if issue.debug: self.wg_expander_debug.show() self.wg_issue_debug.get_buffer().insert_at_cursor(str(issue.debug)) except Exception, e: logger.error(util.format_ex()) self.wg_label_error.set_markup("There were an error decoding the string\n"+\ "exception: %s" % unicode(e)) #print msg # exceptions ##- DOMAINS --------------------------------------------- #domain_dict = util.ListDict() # method:str -> domain[]:class #skin_dict = util.ListDict() # inspector:name|iface -> skin[]:class trackers = [] # tracker:class #skin_cache = {} # inspector:dict -> class:(skin_type:class) method_info = {} # method:str -> info:str crashed_modules = set() # Dado un dominio, listar los métodos que proporciona # Dado un método, listar los dominios que lo proporcionan: domain_dict # Dado un dominio, listar los inspectores que contiene domain.inspectors class DefaultDomain(domain.Domain): name = 'Default' def __get_plugin_files(filter=''): " FILENAMES for all plugins found (e: ./plugins/method_dvd.py)" retval = [] for dname in config.PLUGDIRS: #print glob.glob(dname+'/'+filter) retval += [x for x in glob.glob(dname+'/'+filter) if os.path.splitext(x)[1] in ['.py','']] return retval def __get_plugin_modules(filter=''): " MODULE names (e: medhod_dvd)" files = __get_plugin_files(filter) files = [os.path.splitext(os.path.basename(x))[0] for x in files] return list(set(files)) def __get_plugin_names(filter=''): " NAMES for all plugins modules found (e: dvd)" files = __get_plugin_modules(filter) return [x[len(filter)-1:] for x in files] def get_domain_files(): return __get_plugin_files('domain_*') def get_domain_modules(): return __get_plugin_modules('domain_*') def get_domain_names(): return __get_plugin_names('domain_*') def get_method_plugins(): return __get_plugin_names('method_*') def get_method_modules(): return __get_plugin_modules('method_*') def get_method_files(): return __get_plugin_files('method_*') def get_skin_plugins(): return __get_plugin_names('skin_*') def get_skin_files(): return __get_plugin_files('skin_*') # FIXME: Añadir un observer para recoger información sobre problemas en la carga def load_module(name, where): module = __import__(name) try: module.load(where) except AttributeError, e: logging.error("Incompatible module '%s'" % name) def __load_module(name, where): try: load_module(name, where) except Exception, e: crashed_modules.add(name) logger.error("Could not load inspector '%s'" % name) return if logging.getLogger().level == logging.DEBUG: util.print_tb() logger.error(util.format_ex()) # raise def load_modules(names, domain): "Loads an inspector in the given domain" for m in names: try: __load_module(m, domain) except Exception, e: pass ##- INSPECTORS ------------------------------------------- def get_inspector_class(method): domain_mgr = get_domain(method) inspectors = domain_mgr.inspectors #print 'KEYS:', inspectors.keys() return inspectors.get(method, None) # 'uri' es un objeto util.URI def __create_inspector(uri, inspector_name='', flags=Inspector.AUTO): assert(uri, util.URI) domain_mgr = db.get_domain(uri.method) subject = domain_mgr.to_object(uri) if not inspector_name: ins = available_inspectors(subject, domain_mgr, flags) if ins: inspector_cls = ins[0] else: raise exc.InspectorNotFound("There is not plugin to manage this object") else: inspector_cls = db.get_inspector_by_name(inspector_name) logger.info("Creating an inspector '%s'." % inspector_cls.name) try: return inspector_cls(subject) except exc.InternalError, e: raise exc.UnavailableInspector("There were an error on '%s'\n%s" % (inspector_cls.name, e)) def create_inspector(uri, manager=None, inspector_name='', flags=Inspector.AUTO): logger.info("Inspect '%s' with '%s'" % (uri, inspector_name)) try: retval = __create_inspector(uri, inspector_name, flags) retval.manager = manager return retval except exc.IncompleteSubject, e: insp = create_inspector(util.parse_uri('about://info/%s' % uri.method), manager) # FIXME: los observadores de este inspector se añaden después # de su creación, pero el evento solo se puede programar en ese # momento. Si no retrasa, nunca llegaría. gobject.idle_add(insp.notify, util.Issue.from_exc(e, uri=uri), ibase.Inspector.TOPIC_ISSUE) return insp except (exc.TPException, AssertionError, Exception), e: util.print_tb() logger.error(util.format_ex()) if flags & Inspector.STRICT: return None return IssueInspector(util.Issue.from_exc(e, uri)) def __available_inspectors(subject, domain_mgr=None): 'returns all compatible inspectors' if hasattr(subject, 'inspectors'): return if not domain_mgr: domain_mgr = db.get_domain(subject.uri.method) domain_mgr.preload(subject) inspectors = db.get_inspectors_by_domain(domain_mgr) try: all = [x for x in inspectors if x.can_manage(subject)] except exc.IncompleteSubject, e: logger.warning("Incomplete subject or wrong inspector domain: '%s' missing" % e) return [],[] subject.inspectors = {Inspector.MANUAL: [], Inspector.AUTO: [], Inspector.MINI: []} for i in all: if i.flags & Inspector.AUTO: subject.inspectors[Inspector.AUTO].append(i) else: subject.inspectors[Inspector.MANUAL].append(i) if i.flags & Inspector.MINI: subject.inspectors[Inspector.MINI].append(i) logger.debug("available_inspectors(%s) auto:%s, non-auto:%s, mini:%s" % \ (subject.uri, [x.name for x in subject.inspectors[Inspector.AUTO]], [x.name for x in subject.inspectors[Inspector.MANUAL]], [x.name for x in subject.inspectors[Inspector.MINI]])) def available_inspectors(subject, domain_mgr=None, query=Inspector.AUTO): if not subject: return [] __available_inspectors(subject, domain_mgr) retval = [] if query & Inspector.AUTO: retval = subject.inspectors[Inspector.AUTO] if query & Inspector.MINI: for i in subject.inspectors[Inspector.MINI]: if i not in retval: retval.append(i) if not (retval or query & Inspector.STRICT): return subject.inspectors[Inspector.MANUAL][:] return retval def available_manual_inspectors(subject, domain_mgr=None): if not subject: return [] __available_inspectors(subject, domain_mgr) return subject.inspectors[Inspector.MANUAL] def available_auto_inspectors(subject, domain_mgr=None): if not subject: return [] __available_inspectors(subject, domain_mgr) return subject.inspectors[Inspector.AUTO] def available_mini_inspectors(subject, domain_mgr=None): if not subject: return [] __available_inspectors(subject, domain_mgr) return subject.inspectors[Inspector.MINI] def create_skin(inspector, skin_name=None, flags=Skin.AUTO): skin = None auto, non_auto, mini = available_skins(inspector.__class__) #TODO: si nos pasan nombre hay que buscarlo if flags & Skin.MINI and mini: skin = mini[0] else: if not skin_name: if auto: skin = auto[0] elif non_auto: skin = non_auto[0] else: inspector.delete() issue = IssueInspector(util.Issue.from_message( "There is no skin for inspector '%s'" % inspector.name)) return IssueSkin(issue) else: for s in auto+non_auto: if s.name == skin_name: skin = s break if not skin: logger.warning("there is no such skin (%s), selecting default" % skin_name) skin = auto[0] return skin(inspector) def available_skins(inspector_cls): ''' lista de skins (clases) compatibles con el inspector dado, ya sea por el nombre o por las interfaces implementadas por el inspector''' return db.available_skins(inspector_cls) ### TRACKERS def register_tracker(tracker_cls): trackers.append(tracker_cls) def available_trackers(inspector): return [x for x in trackers if x.can_manage(inspector)] def create_tracker(inspector, tracker_name): trackers = available_trackers(inspector) return trackers[0](inspector) ### INFO # FIXME: pasar a la base de datos def method_register_info(method, info): if not method_info.has_key(method): method_info[method] = '' method_info[method] += info + '\n -- \n' def method_get_info(method): try: return method_info[method] except KeyError: return None # FIXME: Se puede implementar una mejora sensible recargando # únicamente los plugins que hayan cambiado. Para ello, load() debería # comprobar la fecha de modificación de cada fichero con respecto a la # base de datos. def get_newer_plugins(): map_exists = os.path.exists(config.DBFILE) plugins = set() map_time = os.stat(config.DBFILE)[stat.ST_MTIME] if map_exists else None # incluir los enlaces a directorios que haya en PLUGDDIRS (solo hijos) plug_dirs = config.PLUGDIRS for base in plug_dirs: for d in os.listdir(base): full = os.path.join(base, d) if os.path.islink(full): plug_dirs.append(full) for d in plug_dirs: for root, dirs, files in os.walk(d): for f in files: fpath = os.path.join(root,f) name, ext = os.path.splitext(f) if ext != '.py': continue elif name == '__init__': name = os.path.basename(os.path.dirname(fpath)) try: if not map_exists or \ os.stat(fpath)[stat.ST_MTIME] > map_time: logger.info("Plugin '%s' is newer than cache" % fpath) plugins.add(name) except OSError, e: logger.error(e) plugins = list(plugins) plugins.sort() return map_time, plugins def clear_cache(): if os.path.exists(config.DBFILE): os.unlink(config.DBFILE) def load(observer): logger.info('Loading concrete modules...') db.dbTopic.attach(observer.setup, db.TOPIC_SETUP) db.dbTopic.attach(observer.update) map_exists, new_plugins = get_newer_plugins() db.connect() if not map_exists: logging.info("(Re)building Factory Map") db.register_domain(DefaultDomain, []) db.register_inspector('Default', IssueInspector) db.register_skin(IssueSkin) try: register_plugins(new_plugins) except sqlobject.dberrors.OperationalError: logging.critical("factory cache mismatch. Please rerun") clear_cache() sys.exit(1) register_tracker(ibase.StateTracker) #register_tracker(ibase.DataSetTracker) logger.info('Factory load complete!') def shutdown(): for d in [x for x in db.get_all_domains() if x.loaded()]: logger.debug("Deleting '%s'" % d) d().delete() # FIXME: bad name, change it # a 'func' symbol is imported by sqlobject class Func: def __init__(self, module=None): self.module = module def register_skin(self, skin_cls): db.register_skin(skin_cls, self.module) def register_domain(self, domain_cls, methods): db.register_domain(domain_cls, methods, self.module) def register_inspector(self, inspector_cls, methods): default = model.DomainDef.get('Default') for m in methods: try: md = model.MethodDef.get(m) except sqlobject.SQLObjectNotFound: md = model.MethodDef(id=m) default.addMethodDef(md) db.register_inspector('Default', inspector_cls, self.module) def method_register_info(*args): logger.warning('method_register_info not implemented') def register_plugins(plugins): logger.info('-- Registering changed or modified plugins ------') for p in plugins: kind = p.split('_')[0] if kind not in ['domain', 'method', 'skin']: continue logging.info('- %s --' % p) try: model.PluginDef(id=p, kind=kind) except sqlobject.dberrors.DuplicateEntryError: pass __load_module(p, Func(p)) logger.info('-- Registering private domain inspectors --------') for d in [x for x in model.DomainDef.select() if x.module in plugins]: #for d in model.DomainDef.select(): #print '::::-', d.module logger.info('- %s --' % d.cls.name) d.cls.load()