2 # Copyright (C) 2010 W. Trevor King <wking@drexel.edu>
4 # This file is part of Dirtag.
6 # Dirtag is free software: you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
11 # Dirtag is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with Dirtag. If not, see <http://www.gnu.org/licenses/>.
26 >>> t.append(Tree('1'))
27 >>> t.append(Tree('2'))
28 >>> t[0].append(Tree('A'))
29 >>> print '\\n'.join(['|'.join(x) for x in t.traverse()])
34 >>> print '\\n'.join(['|'.join(x) for x in t.traverse(prefix=['z'])])
39 >>> print '\\n'.join(['|'.join(x) for x in t.traverse(depth=1)])
44 def __init__(self, data=None, *args, **kwargs):
46 list.__init__(self, *args, **kwargs)
48 def traverse(self, prefix=[], depth=0):
49 p = prefix + [self.data]
53 if hasattr(child, 'traverse'):
54 for grandchild in child.traverse(p, depth=depth):
57 yield (p + [child])[depth:]
60 def dir_tree(root_dir):
61 """Generate a directory tree.
63 >>> t = dir_tree('test/tag')
64 >>> print '\\n'.join(['|'.join(x) for x in t.traverse(depth=1)])
74 if not os.path.exists(root_dir):
75 raise ValueError(root_dir)
77 for dirpath,dirnames,filenames in os.walk(root_dir, followlinks=True):
80 Tree(os.path.basename(dirpath)))
81 if dirpath == root_dir:
83 for d in sorted(dirnames):
85 trees[os.path.join(dirpath, d)] = t2
87 t.extend(sorted(filenames))
91 class Dirtag (object):
93 >>> d = Dirtag('test/raw', 'test/tag')
94 >>> print '\\n'.join(['|'.join(x) for x in d.elements()])
103 >>> print '\\n'.join(['|'.join(x) for x in d.elements(['x'])])
108 >>> print '\\n'.join(['|'.join(x) for x in d.tags(['b', 'b2.dat'])])
110 >>> print '\\n'.join(['|'.join(x) for x in d.tags(['b', 'b1.dat'])])
114 >>> print d.tag_path(['a', 'a3.dat'], ['x', 'y'])
116 >>> os.listdir('test/tag/x/y')
118 >>> d.add_tag(['a', 'a3.dat'], ['x', 'y'])
119 >>> os.listdir('test/tag/x/y')
121 >>> print 'Z'+os.path.realpath('test/tag/x/y/a3.dat') # doctest: +ELLIPSIS
122 Z.../test/raw/a/a3.dat
123 >>> d.remove_tag(['a', 'a3.dat'], ['x', 'y'])
124 >>> os.listdir('test/tag/x/y')
127 def __init__(self, raw_dir, tag_dir):
128 self.raw_dir = raw_dir
129 self.tag_dir = tag_dir
131 def elements(self, tag=None):
134 nodes = dir_tree(self.raw_dir).traverse(depth=1)
136 p = [self.tag_dir]+tag
137 nodes = dir_tree(os.path.join(*p)).traverse(
138 prefix=p[:-1], depth=len(p))
142 def tags(self, target):
143 for node in dir_tree(self.tag_dir).traverse(depth=1):
144 p = os.path.join(*([self.tag_dir]+node))
145 t = os.path.abspath(os.path.join(*([self.raw_dir] + target)))
146 if (os.path.islink(p) and os.path.realpath(p) == t):
147 yield os.path.join(node[:-1])
149 def tag_path(self, target, tag):
150 # TODO: assumes unique basenames. Check?
151 return os.path.join(*([self.tag_dir] + tag + target[-1:]))
153 def add_tag(self, target, tag):
154 tag_path = self.tag_path(target, tag)
155 target_path = os.path.abspath(os.path.join(*([self.raw_dir]+target)))
158 os.chdir(os.path.dirname(tag_path))
159 os.symlink(os.path.relpath(target_path),
160 os.path.basename(tag_path))
164 def remove_tag(self, target, tag):
165 tag_path = self.tag_path(target, tag)
168 os.chdir(os.path.dirname(tag_path))
169 os.remove(os.path.basename(tag_path))
173 if __name__ == '__main__':