diff --git a/ctag_python.py b/ctag_python.py deleted file mode 100755 index 6c46667..0000000 --- a/ctag_python.py +++ /dev/null @@ -1,222 +0,0 @@ -#!/usr/bin/python -# -# by Erik Osheim - -import os, re, sets, sys - -# regular expressions -class_re = re.compile('^(.*?)\t(.*?)\t(.*?)\tc$') -function_re = re.compile('^(.*?)\t(.*?)\t(.*?)\tf$') -method_re = re.compile('^([^\t]+)\t([^\t]+)\t([^\t]+)\tm\tclass:([^\t]+)(?:\t.*)?$') - -class_supers_re = re.compile('^\/\^ *class +[_A-Za-z][_A-Za-z0-9]* *\((.*?)\) *: *\$\/;\"$') -def_args_re = re.compile('^\/\^ *def +[_A-Za-z][_A-Za-z0-9]* *\((.*?)\) *: *\$\/;\"$') -find_args_re = re.compile('[\*_a-zA-Z][\*_a-zA-Z0-9]*(?:=(?:[^,\'" ]+|"(?:\\.|[^\\"])*"|\'(?:\\.|[^\\"])*\'))?') - -# set of base python objects which can be assumed to exist -base_objects = sets.Set(['object', 'list', 'dict']) - -def is_fully_qualified(s): - return s in base_objects or '.' in s - -def parse_entry(line): - m = class_re.match(line) - if m: - return ClassEntry(m.group(1), m.group(2), m.group(3)) - m = function_re.match(line) - if m: - return FunctionEntry(m.group(1), m.group(2), m.group(3)) - m = method_re.match(line) - if m: - return MethodEntry(m.group(1), m.group(2), m.group(3), m.group(4)) - raise Exception, "Oh no: %s" % line - -class PythonCTagger: - def __init__(self): - self.entries = {} - self.packages = {} - self.classes = {} - self.class_methods = {} - - def process_tagfile(self, path): - f = open(path, 'r') - data = f.read() - f.close() - self.process_data(data) - - def process_paths(self, paths=['.']): - (stdin, stdout, stderr) = os.popen3("exuberant-ctags -L - -f -") - for base in paths: - for root, dirs, files in os.walk(base): - if 'CVS' in dirs: - dirs.remove('CVS') - for name in files: - if name.endswith('.py'): - if base != '.': - path = os.path.join(root, name) - else: - path = name - stdin.write('%s\n' % path) - stdin.flush() - stdin.close() - data = stdout.read() - stdout.close() - stderr.close() - self.process_data(data) - - def process_data(self, data): - # process the ctags output data - for l in data.split('\n'): - if not l: - continue - elif l.startswith('!'): - continue - else: - e = parse_entry(l) - self.entries[e.fullname()] = e - - package = e.package() - if e.type == 'method': - p = e.parent - if not is_fully_qualified(p): - p = '%s.%s' % (package, p) - self.classes.setdefault(p, {}) - self.classes[p][e.symbol] = e - else: - self.packages.setdefault(package, {}) - self.packages[package][e.symbol] = e - - # this returns the methods available in the class - def get_methods_for_class(self, c): - cn = c.fullname() - - # if we haven't determined this classes methods yet, then let's do it - if cn not in self.class_methods: - classes_seen = sets.Set() - methods_seen = sets.Set() - self.class_methods[cn] = [] - - # create a queue of classes to process... this solves the ordering - # problem for class inheritance...i.e.: - # class Shape - # class Rectangle(Shape) - # class Rhombus(Shape) - # class Square(Rectangle, Rhombus) - # 1. [Square] --> process Square --> [Rectangle, Rhombus] - # 2. [Rectangle, Rhombus] --> process Rectangle --> [Rhombus, Shape] - # 3. [Rhombus, Shape] --> process Rhombus --> [Shape, Shape] - # 4. [Shape, Shape] --> process Shape --> [Shape] - # 5. [Shape] --> already processed Shape, skipping - to_process = [c] - while to_process: - e = to_process.pop(0) - fn = e.fullname() - - # if we've seen this class already, then skip it - if fn in classes_seen: - continue - - # mark that we've seen this class; if we don't know about it's - # methods, then let's just skip it. - classes_seen.add(fn) - if fn not in self.classes: - continue - - # for each method in the class, add it to our list if it's new - for msymbol in self.classes[fn]: - if msymbol not in methods_seen: - self.class_methods[cn].append(self.classes[fn][msymbol]) - methods_seen.add(msymbol) - - # for each parent of this class, append it to the end of the queue - # if we know about it - for sfn in e.supers: - if sfn in self.entries: - to_process.append(self.entries[sfn]) - - return self.class_methods[cn] - - def display(self): - # for each package, print out the classes and functions in that package - for p in ct.packages: - print 'package %s' % p - for es in ct.packages[p]: - e = ct.packages[p][es] - print ' %s %s' % (e.type, e.prototype()) - fn = e.fullname() - # for each class, print out the methods that class provides (either - # implemented directly or inherited from a super class) - if e.type == 'class': - for m in ct.get_methods_for_class(e): - # status determines whether the class is being inherited, - # or implemented directly - if fn != m.parent: - status = '*' - else: - status = ' ' - print ' %s %s' % (status, m.dump()) - print '' - -class Entry: - type = 'generic' - def __init__(self, symbol, path): - self.symbol = symbol - self.path = path - def __repr__(self): - return '<%s %s.%s>' % (self.type.title(), self.package(), self.symbol) - def package(self): - return self.path[:-3].replace('/', '.') - def fullname(self): - return '%s.%s' % (self.package(), self.symbol) - def prototype(self): - return self.fullname() - def dump(self): - return '%s %s' % (self.type, self.prototype()) -class ClassEntry(Entry): - type = 'class' - def __init__(self, symbol, path, match): - Entry.__init__(self, symbol, path) - m = class_supers_re.match(match) - self.match = match - self.supers = [] - if m: - self.supers = [x.strip() for x in m.group(1).split(',')] - for i in range(0, len(self.supers)): - if not is_fully_qualified(self.supers[i]): - self.supers[i] = '%s.%s' % (self.package(), self.supers[i]) - def prototype(self): - return '%s(%s)' % (self.fullname(), ', '.join(self.supers)) -class FunctionEntry(Entry): - type = 'function' - def __init__(self, symbol, path, match): - Entry.__init__(self, symbol, path) - m = def_args_re.match(match) - self.match = match - self.args = [] - if m: - self.args = re.findall(find_args_re, m.group(1)) - def prototype(self): - return '%s(%s)' % (self.fullname(), ', '.join(self.args)) -class MethodEntry(FunctionEntry): - type = 'method' - def __init__(self, symbol, path, match, parent): - FunctionEntry.__init__(self, symbol, path, match) - self.parent = parent - if is_fully_qualified(parent): - self.parent = parent - else: - self.parent = '%s.%s' % (self.package(), parent) - def fullname(self): - return '%s.%s' % (self.parent, self.symbol) - -if __name__ == "__main__": - ct = PythonCTagger() - if len(sys.argv[1:]) == 0: - ct.process_paths() - else: - ct.process_tagfile(sys.argv[1]) - - if False: - sys.exit(0) - else: - ct.display() diff --git a/ctags.py b/ctags.py deleted file mode 100755 index 4ba2e9a..0000000 --- a/ctags.py +++ /dev/null @@ -1,218 +0,0 @@ -#!/usr/bin/python -# -# by Erik Osheim - -import os, re, sets, sys - -entries = {} -packages = {} -classes = {} -class_methods = {} - -class_re = re.compile('^(.*?)\t(.*?)\t(.*?)\tc$') -function_re = re.compile('^(.*?)\t(.*?)\t(.*?)\tf$') -method_re = re.compile('^([^\t]+)\t([^\t]+)\t([^\t]+)\tm\tclass:([^\t]+)(?:\t.*)?$') - -class_supers_re = re.compile('^\/\^ *class +[_A-Za-z][_A-Za-z0-9]* *\((.*?)\) *: *\$\/;\"$') -def_args_re = re.compile('^\/\^ *def +[_A-Za-z][_A-Za-z0-9]* *\((.*?)\) *: *\$\/;\"$') -find_args_re = re.compile('[\*_a-zA-Z][\*_a-zA-Z0-9]*(?:=(?:[^,\'" ]+|"(?:\\.|[^\\"])*"|\'(?:\\.|[^\\"])*\'))?') - -base_objects = sets.Set(['object', 'list', 'dict']) - -def is_fully_qualified(s): - return s in base_objects or '.' in s - -def parse_entry(line): - m = class_re.match(line) - if m: - return ClassEntry(m.group(1), m.group(2), m.group(3)) - m = function_re.match(line) - if m: - return FunctionEntry(m.group(1), m.group(2), m.group(3)) - m = method_re.match(line) - if m: - return MethodEntry(m.group(1), m.group(2), m.group(3), m.group(4)) - raise Exception, "Oh no: %s" % line - -class Entry: - type = 'generic' - def __init__(self, symbol, path): - self.symbol = symbol - self.path = path - def __repr__(self): - return '<%s %s.%s>' % (self.type.title(), self.package(), self.symbol) - def package(self): - return self.path[:-3].replace('/', '.') - def fullname(self): - return '%s.%s' % (self.package(), self.symbol) - def prototype(self): - return self.fullname() - def dump(self): - return '%s %s' % (self.type, self.prototype()) -class ClassEntry(Entry): - type = 'class' - def __init__(self, symbol, path, match): - Entry.__init__(self, symbol, path) - m = class_supers_re.match(match) - self.match = match - self.supers = [] - if m: - self.supers = [x.strip() for x in m.group(1).split(',')] - for i in range(0, len(self.supers)): - if not is_fully_qualified(self.supers[i]): - self.supers[i] = '%s.%s' % (self.package(), self.supers[i]) - def prototype(self): - return '%s(%s)' % (self.fullname(), ', '.join(self.supers)) -class FunctionEntry(Entry): - type = 'function' - def __init__(self, symbol, path, match): - Entry.__init__(self, symbol, path) - m = def_args_re.match(match) - self.match = match - self.args = [] - if m: - self.args = re.findall(find_args_re, m.group(1)) - def prototype(self): - return '%s(%s)' % (self.fullname(), ', '.join(self.args)) -class MethodEntry(FunctionEntry): - type = 'method' - def __init__(self, symbol, path, match, parent): - FunctionEntry.__init__(self, symbol, path, match) - self.parent = parent - if is_fully_qualified(parent): - self.parent = parent - else: - self.parent = '%s.%s' % (self.package(), parent) - def fullname(self): - return '%s.%s' % (self.parent, self.symbol) - -def process_tagfile(path): - global entries, classes, packages - f = open(path, 'r') - data = f.read() - f.close() - process_data(data) - -def process_direct(): - (stdin, stdout, stderr) = os.popen3("exuberant-ctags -L - -f -") - for base in ('.'): - for root, dirs, files in os.walk(base): - if 'CVS' in dirs: - dirs.remove('CVS') - for name in files: - if name.endswith('.py'): - if base != '.': - path = os.path.join(root, name) - else: - path = name - stdin.write('%s\n' % path) - stdin.flush() - stdin.close() - data = stdout.read() - stdout.close() - stderr.close() - process_data(data) - -def process_data(data): - global entries, classes, packages - # process the ctags output data - for l in data.split('\n'): - if not l: - continue - elif l.startswith('!'): - continue - else: - e = parse_entry(l) - entries[e.fullname()] = e - - package = e.package() - if e.type == 'method': - p = e.parent - if not is_fully_qualified(p): - p = '%s.%s' % (package, p) - classes.setdefault(p, {}) - classes[p][e.symbol] = e - else: - packages.setdefault(package, {}) - packages[package][e.symbol] = e - -# this returns the methods available in the class -def get_methods_for_class(c): - global class_methods - - cn = c.fullname() - - # if we haven't determined this classes methods yet, then let's do it - if cn not in class_methods: - classes_seen = sets.Set() - methods_seen = sets.Set() - class_methods[cn] = [] - - # create a queue of classes to process... this solves the ordering - # problem for class inheritance...i.e.: - # class Shape - # class Rectangle(Shape) - # class Rhombus(Shape) - # class Square(Rectangle, Rhombus) - # 1. [Square] --> process Square --> [Rectangle, Rhombus] - # 2. [Rectangle, Rhombus] --> process Rectangle --> [Rhombus, Shape] - # 3. [Rhombus, Shape] --> process Rhombus --> [Shape, Shape] - # 4. [Shape, Shape] --> process Shape --> [Shape] - # 5. [Shape] --> already processed Shape, skipping - to_process = [c] - while to_process: - e = to_process.pop(0) - fn = e.fullname() - - # if we've seen this class already, then skip it - if fn in classes_seen: - continue - - # mark that we've seen this class; if we don't know about it's - # methods, then let's just skip it. - classes_seen.add(fn) - if fn not in classes: - continue - - # for each method in the class, add it to our list if it's new - for msymbol in classes[fn]: - if msymbol not in methods_seen: - class_methods[cn].append(classes[fn][msymbol]) - methods_seen.add(msymbol) - - # for each parent of this class, append it to the end of the queue - # if we know about it - for sfn in e.supers: - if sfn in entries: - to_process.append(entries[sfn]) - - return class_methods[cn] - -if __name__ == "__main__": - if len(sys.argv[1:]) == 0: - process_direct() - else: - process_tagfile(sys.argv[1]) - - # exit here to get good timing data - #sys.exit(0) - - # for each package, print out the classes and functions in that package - for p in packages: - print 'package %s' % p - for es in packages[p]: - e = packages[p][es] - print ' %s %s' % (e.type, e.prototype()) - fn = e.fullname() - # for each class, print out the methods that class provides (either - # implemented directly or inherited from a super class) - if e.type == 'class': - for m in get_methods_for_class(e): - # status determines whether the class is being inherited, - # or implemented directly - if fn != m.parent: - status = '*' - else: - status = ' ' - print ' %s %s' % (status, m.dump()) - print ''