'''Support for importing files with list of tags.''' import os import re import traceback def nicesplit(string): '''Split string at commas or spaces, do not mind multiple delimiters.''' space_sep = string.replace(',', ' ') all_parts = space_sep.split(' ') # Return non-empty parts return [p.strip() for p in all_parts if p.strip()] NAME_REGEXP = re.compile('^[a-zA-Z0-9_]+(/[a-zA-Z0-9_]+)*$') def validname(string): '''Check if a string is a valid tag name.''' return bool(NAME_REGEXP.match(string)) class TagCollection: '''A collection of tags and associated files.''' def __init__(self): # Mapping of tags to set of paths self.tags = {} # List of import messages self.msgs = [] def import_tagfile(self, infile, relpath): '''Import a file with tags. Relpath is the path to the directory.''' for line in infile: if ':' in line: # Individual files listed after : parts = line.split(':', 1) tags = nicesplit(parts[0]) files = nicesplit(parts[1]) # Add directory to the file names targets = [os.path.join(relpath, f) for f in files] else: # Tags listed for the whole folder tags = nicesplit(line) targets = [relpath] # Strip any .. or other evil things from the path targets = [os.path.normpath(p.replace('..', '')) for p in targets] for tag in tags: if not self.tags.has_key(tag): if not validname(tag): self.msgs.append(infile.name + ': Invalid tag ' + tag) continue self.tags[tag] = set(targets) else: already_exists = self.tags[tag].intersection(targets) if already_exists: self.msgs.append(infile.name + ': Following targets already exist for tag ' + tag + ': ' + ', '.join(already_exists) ) self.tags[tag].update(targets) def import_recursively(self, basepath, tagfilename): '''Import all tag files recursively from basepath.''' def callback(arg, dirname, fnames): '''os.walk callback function to read tagfiles.''' if tagfilename in fnames: assert dirname.startswith(basepath) relpath = dirname[len(basepath):].lstrip('/') tagfilepath = os.path.join(dirname, tagfilename) try: tagfile = open(tagfilepath, 'rU') self.import_tagfile(tagfile, relpath) except: self.msgs.append('Importing tagfile ' + tagfilepath + ' failed with exception:\n' + traceback.format_exc()) os.path.walk(basepath, callback, None)