# -*- 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()