#!/usr/bin/env python # -*- coding: utf-8 mode: python -*- vim:sw=4:sts=4:et:ai:si:sta:fenc=utf-8 """Interroger la base deploydb """ import logging; log = logging.getLogger(__name__) import sys from os import path from argparse import ArgumentParser from .utils import * from .parser import Parser from .objects import catalog DEFAULT_CONFIG = 'deploydb.conf' USER_CONFDIR = path.expanduser('~/etc/deploy') SYSTEM_CONFDIR = '/var/local/deploy' DEFAULT_MODULES = ['base'] DEFAULT_FUNC = 'base.query' DEFAULT_ACTION = 'run' SCRIPTDIR = path.dirname(path.dirname(path.dirname(__file__))) ################################################################################ # Programme principal logging.basicConfig() from argparse import ArgumentParser, HelpFormatter if sys.argv[1:2] == ['--compat']: # Avec l'argument --compat, désactiver la classe FancyHelpFormatter qui # se base sur une API non documentée sys.argv = sys.argv[0:1] + sys.argv[2:] FancyHelpFormatter = HelpFormatter else: class FancyHelpFormatter(HelpFormatter): """Comme HelpFormatter, mais ne touche pas aux lignes qui commencent par les caractères '>>>'. Cela permet de mixer du texte formaté et du texte non formaté. """ def _fill_text(self, text, width, indent): return ''.join([indent + line for line in text.splitlines(True)]) def _split_lines(self, text, width): lines = [''] for line in text.splitlines(): if line.startswith('>>>'): lines.append(line) lines.append('') else: lines[-1] += '\n' + line lines = filter(None, lines) texts = [] for line in lines: if line.startswith('>>>'): line = line[3:] if line: texts.append(line) else: texts.extend(super(FancyHelpFormatter, self)._split_lines(line, width)) return texts AP = ArgumentParser( usage=u"deploydb args...", description=__doc__, formatter_class=FancyHelpFormatter, ) AP.set_defaults( missing_ok=False, modules=DEFAULT_MODULES, func=DEFAULT_FUNC, resolve=True, action=DEFAULT_ACTION, ) AP.add_argument('-c', '--config', dest='config', help=u"Spécifier le fichier de configuration à utiliser. S'il s'agit d'un nom sans chemin, il est recherché dans les répertoires par défaut.") AP.add_argument('--missing-ok', action='store_true', dest='missing_ok', help=u"Sortir sans erreur si le fichier de configuration n'est pas trouvé") AP.add_argument('-m', '--module', action='append', dest='modules', metavar='MODULE', help=u"Spécifier un module à charger. Cette option peut être spécifiée autant de fois que nécessaire. Par défaut, seul le module 'base' est chargé.") AP.add_argument('-r', '--func', dest='func', help=u"Spécifier la fonction à lancer après le chargement de la base de données. La valeur par défaut est %s" % DEFAULT_FUNC) AP.add_argument('--no-resolve', action='store_false', dest='resolve', help=u"Ne pas résoudre les objets avant de lancer la fonction. Cette option avancée ne devrait pas avoir besoin d'être utilisée.") AP.add_argument('--run', action='store_const', dest='action', const='run', help=u"Lancer la fonction spécifiée avec l'option --func") AP.add_argument('--dump', action='store_const', dest='action', const='dump', help=u"Afficher le contenu de la base de données") AP.add_argument('args', nargs='*') o = AP.parse_args() # charger les modules MODULES = {} for module in o.modules: MODULES[module] = __import__('%s_module' % module, globals()) # charger la configuration config = o.config if config is not None and ('/' in config or path.isfile(config)): deploydb_path = [path.abspath(path.dirname(config))] else: deploydb_path = [USER_CONFDIR, SYSTEM_CONFDIR, SCRIPTDIR] cname = config if config is not None else DEFAULT_CONFIG config = find_in_path(cname, deploydb_path) if config is None and not o.missing_ok: raise ValueError("Impossible de trouver le fichier de configuration %s" % cname) catalog.create_object('deploydb', 'path', dir=deploydb_path) Parser(config) dd_path = catalog.get('deploydb', 'path') dd_include = catalog.get('deploydb', 'include', None, create=False) if dd_include is not None: included = set([config]) while True: done = True for file in dd_include.get('file', ()): # cette valeur peut changer au fur et à mesure que les fichiers sont # inclus. la recharger systématiquement deploydb_path = dd_path.get('dir', ()) pf = find_in_path(file, deploydb_path) if pf in included: continue included.add(pf) if pf is not None: Parser(pf) # il y a peut-être de nouveaux fichiers à inclure. configurer # une nouvelle itération done = False else: log.warning("deploydb:include: %s: Fichier introuvable", file) if done: break deploydb_path = dd_path.get('dir', ()) dd_loadcsv = catalog.get('deploydb', 'loadcsv', None, create=False) if dd_loadcsv is not None: otype = dd_loadcsv.first('otype', None) otype_col = dd_loadcsv.first('otype_col', 'otype') oid_col = dd_loadcsv.first('oid_col', 'oid') for file in dd_loadcsv.get('file', ()): pf = find_in_path(file, deploydb_path) if pf is not None: catalog.load_csv(pf, otype, otype_col, oid_col) else: log.warning("deploydb:loadcsv: %s: Fichier introuvable", file) # actions if o.resolve: catalog.resolve() if o.action == 'run': names = o.func.split('.') func = MODULES[names[0]] for name in names[1:]: func = getattr(func, name) func(*o.args) elif o.action == 'dump': catalog.dump()