nutools/lib/nulib/python/deploydb/__main__.py

157 lines
5.9 KiB
Python
Raw Normal View History

2018-04-26 23:19:17 +04:00
#!/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()