0001
0002
0003import os
0004import py_compile
0005import marshal
0006import inspect
0007import re
0008from command import Command
0009import pluginlib
0010
0011class GrepCommand(Command):
0012
0013 summary = 'Search project for symbol'
0014 usage = 'SYMBOL'
0015
0016 max_args = 1
0017 min_args = 1
0018
0019 bad_names = ['.svn', 'CVS', '_darcs']
0020
0021 parser = Command.standard_parser()
0022
0023 parser.add_option(
0024 '-x', '--exclude-module',
0025 metavar="module.name",
0026 dest="exclude_modules",
0027 action="append",
0028 help="Don't search the given module")
0029
0030 parser.add_option(
0031 '-t', '--add-type',
0032 metavar=".ext",
0033 dest="add_types",
0034 action="append",
0035 help="Search the given type of files")
0036
0037 def command(self):
0038 self.exclude_modules = self.options.exclude_modules or []
0039 self.add_types = self.options.add_types or []
0040 self.symbol = self.args[0]
0041 self.basedir = os.path.dirname(
0042 pluginlib.find_egg_info_dir(os.getcwd()))
0043 if self.verbose:
0044 print "Searching in %s" % self.basedir
0045 self.total_files = 0
0046 self.search_dir(self.basedir)
0047 if self.verbose > 1:
0048 print "Searched %i files" % self.total_files
0049
0050 def search_dir(self, dir):
0051 names = os.listdir(dir)
0052 names.sort()
0053 dirs = []
0054 for name in names:
0055 full = os.path.join(dir, name)
0056 if name in self.bad_names:
0057 continue
0058 if os.path.isdir(full):
0059
0060 dirs.append(full)
0061 continue
0062 for t in self.add_types:
0063 if name.lower().endswith(t.lower()):
0064 self.search_text(full)
0065 if not name.endswith('.py'):
0066 continue
0067 self.search_file(full)
0068 for dir in dirs:
0069 self.search_dir(dir)
0070
0071 def search_file(self, filename):
0072 self.total_files += 1
0073 if not filename.endswith('.py'):
0074 self.search_text(filename)
0075 return
0076 pyc = filename[:-2]+'pyc'
0077 if not os.path.exists(pyc):
0078 py_compile.compile(filename)
0079 if not os.path.exists(pyc):
0080
0081 self.search_text(filename, as_module=True)
0082 return
0083 f = open(pyc, 'rb')
0084
0085 f.read(8)
0086 code = marshal.load(f)
0087 f.close()
0088 self.search_code(code, filename, [])
0089
0090 def search_code(self, code, filename, path):
0091 if code.co_name != "?":
0092 path = path + [code.co_name]
0093 else:
0094 path = path
0095 sym = self.symbol
0096 if sym in code.co_varnames:
0097 self.found(code, filename, path)
0098 elif sym in code.co_names:
0099 self.found(code, filename, path)
0100 for const in code.co_consts:
0101 if const == sym:
0102 self.found(code, filename, path)
0103 if inspect.iscode(const):
0104 if not const.co_filename == filename:
0105 continue
0106 self.search_code(const, filename, path)
0107
0108 def search_text(self, filename, as_module=False):
0109 f = open(filename, 'rb')
0110 lineno = 0
0111 any = False
0112 for line in f:
0113 lineno += 1
0114 if line.find(self.symbol) != -1:
0115 if not any:
0116 any = True
0117 if as_module:
0118 print '%s (unloadable)' % self.module_name(filename)
0119 else:
0120 print self.relative_name(filename)
0121 print ' %3i %s' % (lineno, line)
0122 if not self.verbose:
0123 break
0124 f.close()
0125
0126 def found(self, code, filename, path):
0127 print self.display(filename, path)
0128 self.find_occurance(code)
0129
0130 def find_occurance(self, code):
0131 f = open(code.co_filename, 'rb')
0132 lineno = 0
0133 for index, line in zip(xrange(code.co_firstlineno), f):
0134 lineno += 1
0135 pass
0136 lines = []
0137 first_indent = None
0138 for line in f:
0139 lineno += 1
0140 if line.find(self.symbol) != -1:
0141 this_indent = len(re.match(r'^[ \t]*', line).group(0))
0142 if first_indent is None:
0143 first_indent = this_indent
0144 else:
0145 if this_indent < first_indent:
0146 break
0147 print ' %3i %s' % (lineno, line[first_indent:].rstrip())
0148 if not self.verbose:
0149 break
0150
0151 def module_name(self, filename):
0152 assert filename, startswith(self.basedir)
0153 mod = filename[len(self.basedir):].strip('/').strip(os.path.sep)
0154 mod = os.path.splitext(mod)[0]
0155 mod = mod.replace(os.path.sep, '.').replace('/', '.')
0156 return mod
0157
0158 def relative_name(self, filename):
0159 assert filename, startswith(self.basedir)
0160 name = filename[len(self.basedir):].strip('/').strip(os.path.sep)
0161 return name
0162
0163 def display(self, filename, path):
0164 parts = '.'.join(path)
0165 if parts:
0166 parts = ':' + parts
0167 return self.module_name(filename) + parts