possibilité de matcher des sous-répertoires dans l'expression --spec. Support du match de type --glob
This commit is contained in:
parent
a49d6e2a24
commit
b7600aa43b
|
@ -6,7 +6,7 @@ u"""Ce script est prévu pour être utilisé dans un script CGI.
|
|||
Il permet de lister le contenu d'un répertoire au format XML, et de télécharger les fichiers trouvés.
|
||||
"""
|
||||
|
||||
import os, sys, re, shutil, mimetypes, urlparse, urllib
|
||||
import os, sys, re, shutil, mimetypes, urlparse, urllib, fnmatch, glob
|
||||
from os import path
|
||||
from time import time, localtime
|
||||
from types import UnicodeType, StringTypes
|
||||
|
@ -35,13 +35,23 @@ RE_SORT_FIELD_TYPE = re.compile(r'([_a-zA-Z][-_a-zA-Z0-9]*)(?::([aAdDcCnN]+))?')
|
|||
|
||||
class Filter:
|
||||
re_spec = None
|
||||
glob = None
|
||||
caret_anchor = None
|
||||
deep_scan = None
|
||||
re_allows = None
|
||||
ex_vars = None
|
||||
ex_group = None
|
||||
ex_break_on = None
|
||||
|
||||
def __init__(self, re_spec):
|
||||
self.re_spec = re.compile(re_spec)
|
||||
def __init__(self, spec=None, glob=None):
|
||||
if spec is not None:
|
||||
self.re_spec = re.compile(spec)
|
||||
self.caret_anchor = spec.startswith('^')
|
||||
self.deep_scan = '/' in spec
|
||||
elif glob is not None:
|
||||
self.glob = glob
|
||||
self.deep_scan = '/' in glob
|
||||
else: raise ValueError("either spec or glob must be specified")
|
||||
self.re_allows = []
|
||||
self.ex_vars = []
|
||||
self.ex_group = None
|
||||
|
@ -63,14 +73,34 @@ class Filter:
|
|||
self.ex_break_on = ex_break_on
|
||||
|
||||
def __repr__(self):
|
||||
return 'Filter<%s,%s,%s,%s,%s>' % \
|
||||
(self.re_spec, repr(self.re_allows),
|
||||
return 'Filter<%s||%s,%s,%s,%s,%s>' % \
|
||||
(self.re_spec, self.glob, repr(self.re_allows),
|
||||
repr(self.ex_vars),
|
||||
self.ex_group, self.ex_break_on,
|
||||
)
|
||||
|
||||
def match_fill(self, file):
|
||||
mo = self.re_spec.match(file.name)
|
||||
def match_fill(self, file, basedir=None):
|
||||
if self.glob:
|
||||
if self.deep_scan:
|
||||
# la correspondance a déjà été faite avant avec glob.glob(). on
|
||||
# se contente d'initialiser l'objet
|
||||
pass
|
||||
elif not fnmatch.fnmatch(file.name, self.glob):
|
||||
return None
|
||||
file.group = None
|
||||
file.break_on = None
|
||||
return True
|
||||
|
||||
if self.deep_scan:
|
||||
pf = file.pf
|
||||
if not self.caret_anchor:
|
||||
blen = len(basedir)
|
||||
if pf[:blen] != basedir: return None
|
||||
pf = pf[blen:]
|
||||
if not pf.startswith('/'): return None
|
||||
pf = pf[1:]
|
||||
mo = self.re_spec.match(pf)
|
||||
else: mo = self.re_spec.match(file.name)
|
||||
if mo is None: return None
|
||||
for name, expr in self.ex_vars:
|
||||
value = mo.expand(expr)
|
||||
|
@ -102,7 +132,11 @@ class File:
|
|||
vars = None
|
||||
dontlist = None
|
||||
|
||||
def __init__(self, dir, name, dontlist=False):
|
||||
def __init__(self, dir=None, name=None, pf=None, dontlist=False):
|
||||
if pf is None:
|
||||
if dir is None or name is None: raise ValueError("either dir/name or pf must be specified")
|
||||
pf = path.join(dir, name)
|
||||
dir, name = path.split(pf) # il est possible que name soit de la forme subdir/name
|
||||
dir = path.abspath(dir)
|
||||
pf = path.join(dir, name)
|
||||
self.__dict__['pf'] = pf
|
||||
|
@ -137,19 +171,49 @@ class File:
|
|||
def __repr__(self): return 'File<%s>' % (self.pf)
|
||||
|
||||
def find_files(basedir, filters):
|
||||
basedir = path.abspath(basedir)
|
||||
files = []
|
||||
deep_filters = [filter for filter in filters if filter.re_spec is not None and filter.deep_scan]
|
||||
if deep_filters:
|
||||
for dirpath, dirnames, filenames in os.walk(basedir):
|
||||
for filename in filenames:
|
||||
file = File(dirpath, filename)
|
||||
matched = False
|
||||
allowed = False
|
||||
for filter in deep_filters:
|
||||
matched = filter.match_fill(file, basedir) or matched
|
||||
allowed = filter.match_allow(file) or allowed
|
||||
if matched: pass
|
||||
elif allowed: file.dontlist = True
|
||||
else: continue
|
||||
files.append(file)
|
||||
simple_filters = [filter for filter in filters if filter.re_spec is not None and not filter.deep_scan]
|
||||
if simple_filters:
|
||||
for name in os.listdir(basedir):
|
||||
file = File(basedir, name)
|
||||
if not file.isfile(): continue
|
||||
matched = False
|
||||
allowed = False
|
||||
for filter in filters:
|
||||
for filter in simple_filters:
|
||||
matched = filter.match_fill(file) or matched
|
||||
allowed = filter.match_allow(file) or allowed
|
||||
if matched: pass
|
||||
elif allowed: file.dontlist = True
|
||||
else: continue
|
||||
files.append(file)
|
||||
glob_filters = [filter for filter in filters if filter.glob is not None]
|
||||
if glob_filters:
|
||||
for filter in glob_filters:
|
||||
if filter.deep_scan:
|
||||
candidates = [File(pf=pf) for pf in glob.glob(path.join(basedir, filter.glob))]
|
||||
else:
|
||||
candidates = [File(basedir, name) for name in os.listdir(basedir)]
|
||||
for file in candidates:
|
||||
if not file.isfile(): continue
|
||||
elif filter.match_fill(file): pass
|
||||
elif filter.match_allow(file): file.dontlist = True
|
||||
else: continue
|
||||
files.append(file)
|
||||
return files
|
||||
|
||||
def build_sortfunc(sortby):
|
||||
|
@ -318,7 +382,10 @@ def run_cgilsxml():
|
|||
default_filter = Filter(r'(.*)')
|
||||
def add_spec(option, opt, value, parser, *args, **kw):
|
||||
if env['filter'] is not None: env['filters'].append(env['filter'])
|
||||
env['filter'] = Filter(value)
|
||||
env['filter'] = Filter(spec=value)
|
||||
def add_glob(option, opt, value, parser, *args, **kw):
|
||||
if env['filter'] is not None: env['filters'].append(env['filter'])
|
||||
env['filter'] = Filter(glob=value)
|
||||
def allow_spec(option, opt, value, parser, *args, **kw):
|
||||
if env['filter'] is None: env['filter'] = default_filter
|
||||
env['filter'].allow_spec(value)
|
||||
|
@ -335,9 +402,21 @@ 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',
|
||||
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."
|
||||
help=u"Spécifier l'expression régulière permettant de sélectionner les fichiers à lister. "
|
||||
+ u"La correspondance est tentée sur le nom du fichier avec la fonction match(). "
|
||||
+ u"Penser à inclure l'ancre $ pour matcher le nom entier du fichier. "
|
||||
+ u"Si l'expression régulière contient un caractère '/', la correspondance peut se faire sur le chemin complet du fichier. "
|
||||
+ u"Pour simplifier, par défaut seule la partie située après le chemin de base est matchée, sauf si l'expression commence par '^', auquel cas la correspondance se fait sur le chemin complet du fichier. "
|
||||
+ u"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.")
|
||||
+ 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. "
|
||||
+ u"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('--glob', dest='pattern', action='callback', callback=add_glob, type='string',
|
||||
help=u"Comme --spec, mais en utilisant les patterns du shell *, ?, [seq], [!seq]. "
|
||||
+ u"Si l'expression contient un caractère '/', la correspondance se fait sur le chemin complet du fichier avec le module glob. "
|
||||
+ u"Cette précision est importante notamment pour la recherche de fichiers dont le nom commence par un point. "
|
||||
+ u"Si l'expression ne contient pas le caractère '/', la correspondance est faite uniquement sur le nom de fichier avec le module fnmatch, et le point n'a plus de signification particulière. "
|
||||
+ u"Cette méthode de correspondance ne permet pas de définir des groupes et ne permet donc pas d'extraire des variables.")
|
||||
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',
|
||||
|
|
Loading…
Reference in New Issue