diff --git a/CHANGES.txt b/CHANGES.txt index 5d2f32a..e738a63 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,7 @@ +## Version 3.4.0 du 09/02/2016-11:25 + +788e1ff cgilsxml.py: ajout de l'option -E. bug avec le tri sur des valeurs inexistantes + ## Version 3.3.0 du 08/02/2016-09:41 da3ef60 cgilsxml.py: ajouter de squery_string permettant d'ajouter un préfixe diff --git a/VERSION.txt b/VERSION.txt index 15a2799..1809198 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -1 +1 @@ -3.3.0 +3.4.0 diff --git a/lib/ulib/.ulibver b/lib/ulib/.ulibver index 482734c..60e4ab9 100644 --- a/lib/ulib/.ulibver +++ b/lib/ulib/.ulibver @@ -1 +1 @@ -011001000 +011002000 diff --git a/lib/ulib/support/cgilsxml.py b/lib/ulib/support/cgilsxml.py index cbe343e..87b61bb 100755 --- a/lib/ulib/support/cgilsxml.py +++ b/lib/ulib/support/cgilsxml.py @@ -34,14 +34,19 @@ RE_SORT_FIELD_TYPE = re.compile(r'([_a-zA-Z][-_a-zA-Z0-9]*)(?::([aAdDcCnN]+))?') class Filter: re_spec = None + re_allows = None ex_vars = None ex_group = None def __init__(self, re_spec): self.re_spec = re.compile(re_spec) + self.re_allows = [] self.ex_vars = [] self.ex_group = None + def allow_spec(self, re_allow): + self.re_allows.append(re.compile(re_allow)) + def add_var(self, ex_var): mo = RE_VAR_NAME_EXPR.match(ex_var) if mo is None: raise ValueError('Invalid VAR_EXPR: %s' % ex_var) @@ -52,7 +57,8 @@ class Filter: self.ex_group = ex_group def __repr__(self): - return 'Filter<%s,%s,%s>' % (self.re_spec, repr(self.ex_vars), self.ex_group) + return 'Filter<%s,%s,%s,%s>' % (self.re_spec, repr(self.re_allows), + repr(self.ex_vars), self.ex_group) def match_fill(self, file): mo = self.re_spec.match(file.name) @@ -67,17 +73,25 @@ class Filter: file.group = group return True + def match_allow(self, file): + for re_allow in self.re_allows: + if re_allow.match(file.name) is not None: + return True + return False + class File: pf = None group = None vars = None + dontlist = None - def __init__(self, dir, name): + def __init__(self, dir, name, dontlist=False): dir = path.abspath(dir) pf = path.join(dir, name) self.__dict__['pf'] = pf self.__dict__['group'] = None self.__dict__['vars'] = {} + self.__dict__['dontlist'] = dontlist stat = os.stat(pf) mtime = int(stat.st_mtime) @@ -97,12 +111,12 @@ class File: def __getitem__(self, name): return self.vars[name] def __setitem__(self, name, value): self.vars[name] = value def has_key(self, name): return self.vars.has_key(name) - def __getattr__(self, name): return self.vars[name] + def __getattr__(self, name): + try: return self.vars[name] + except KeyError: raise AttributeError(name) def __setattr__(self, name, value): - if self.__dict__.has_key(name): - self.__dict__[name] = value - else: - self.vars[name] = value + if self.__dict__.has_key(name): self.__dict__[name] = value + else: self.vars[name] = value def __repr__(self): return 'File<%s>' % (self.pf) def find_files(basedir, filters): @@ -111,9 +125,13 @@ def find_files(basedir, filters): file = File(basedir, name) if not file.isfile(): continue matched = False + allowed = False for filter in filters: matched = filter.match_fill(file) or matched - if not matched: continue + allowed = filter.match_allow(file) or allowed + if matched: pass + elif allowed: file.dontlist = True + else: continue files.append(file) return files @@ -134,17 +152,23 @@ def build_sortfunc(sortby): SORTS.append((field, method, order)) def sortfunc(a, b): for field, method, order in SORTS: - av = getattr(a, field) - bv = getattr(b, field) - if method == 'C': - av = str(av) - bv = str(bv) - elif method == 'N': - av = int(av) - bv = int(bv) - if av < bv: outcome = -1 - elif av > bv: outcome = 1 - else: outcome = 0 + av = getattr(a, field, None) + bv = getattr(b, field, None) + if av is None: + if bv is None: outcome = 0 + else: outcome = 1 + elif bv is None: + outcome = -1 + else: + if method == 'C': + av = str(av) + bv = str(bv) + elif method == 'N': + av = int(av) + bv = int(bv) + if av < bv: outcome = -1 + elif av > bv: outcome = 1 + else: outcome = 0 if order == 'A': pass elif order == 'D': outcome = -outcome if outcome != 0: return outcome @@ -165,8 +189,9 @@ def build_fgroups(files): return fgroups def filter_files(files, group): - if group: return filter(lambda file: file.group == group, files) - else: return filter(lambda file: not file.group, files) + if group: func = lambda file: file.dontlist or file.group == group + else: func = lambda file: file.dontlist or not file.group + return filter(func, files) def select_file(files, name): matches = filter(lambda file: file.name == name, files) @@ -241,6 +266,7 @@ def print_files(files, fgroups=None, select_group=None, script_name=None, xslt=N xfgroup.set('selected', 'selected') xfiles = ET.SubElement(xresult, "files") for file in files: + if file.dontlist: continue xfile = ET.SubElement(xfiles, "file") if file.group is not None: xfile.set('group', file.group) for name, value in file.vars.items(): @@ -256,6 +282,9 @@ def run_cgilsxml(): def add_spec(option, opt, value, parser, *args, **kw): if env['filter'] is not None: env['filters'].append(env['filter']) env['filter'] = Filter(value) + def allow_spec(option, opt, value, parser, *args, **kw): + if env['filter'] is None: env['filter'] = default_filter + env['filter'].allow_spec(value) def add_var(option, opt_str, value, parser, *args, **kw): if env['filter'] is None: env['filter'] = default_filter env['filter'].add_var(value) @@ -265,14 +294,16 @@ def run_cgilsxml(): from optparse import OptionParser OP = OptionParser(usage=u"\n\t%prog [options] /path/to/dir", description=__doc__) - OP.add_option('-e', '--spec', dest='spec', action='callback', callback=add_spec, type="string", + OP.add_option('-e', '--spec', dest='spec', action='callback', callback=add_spec, type='string', help=u"Spécifier l'expression régulière permettant de sélectionner les fichiers à lister. L'expression régulière peut définir des groupes qui sont utilisées pour l'extraction des variables." + u"\n Il est possible de spécifier cette option plusieurs fois." + u"\n Note: TOUTES les expressions régulières sont testées par rapport au nom du fichier, et pour celles qui correspondent, les variables correspondantes sont définies. Il faut donc ordonner les expressions régulières de la plus générale à la plus spécifique, contrairement à ce qui se fait d'habitude.") - OP.add_option('-v', '--var', dest='var', action='callback', callback=add_var, type="string", + OP.add_option('-E', '--allow-spec', dest='spec', action='callback', callback=allow_spec, type='string', + help=u"Ajouter une spécification de fichier qui peut être demandé avec --cgi-path-info. Ces fichiers ne sont pas inclus dans la liste.") + OP.add_option('-v', '--var', dest='var', action='callback', callback=add_var, type='string', help=u"Définir la variable NAME à la valeur de l'expression VAR_EXPR. Dans cette expression, il est possible d'utiliser des expressions de la forme %%(var)s pour inclure des variables déjà définies, ou \\N et \\g pour inclure respective le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec." + u"\n Cette option peut être spécifiée plusieurs fois. Elle s'applique à l'expression régulière définie par la dernière option --spec") - OP.add_option('-g', '--group', dest='group', action='callback', callback=set_group, type="string", + OP.add_option('-g', '--group', dest='group', action='callback', callback=set_group, type='string', help=u"Spécifier l'expression qui permet de construire des ensembles de fichiers sur la base des groupes définis dans l'expression régulière de l'option --spec. Dans cette expression, il est possible d'utiliser des expressions de la forme %%(var)s pour inclure des variables déjà définies, ou \\N ou \\g pour inclure respective le groupe numéro N et le groupe nommé NAME de l'expression régulière --spec." + u"\n Cette option ne peut être spécifiée qu'une seule fois par option --spec") OP.add_option('-s', '--sort', dest='sortby',