[3] | 1 | # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) |
---|
| 2 | # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php |
---|
| 3 | import os |
---|
| 4 | import py_compile |
---|
| 5 | import marshal |
---|
| 6 | import inspect |
---|
| 7 | import re |
---|
| 8 | from command import Command |
---|
| 9 | import pluginlib |
---|
| 10 | |
---|
| 11 | class GrepCommand(Command): |
---|
| 12 | |
---|
| 13 | summary = 'Search project for symbol' |
---|
| 14 | usage = 'SYMBOL' |
---|
| 15 | |
---|
| 16 | max_args = 1 |
---|
| 17 | min_args = 1 |
---|
| 18 | |
---|
| 19 | bad_names = ['.svn', 'CVS', '_darcs'] |
---|
| 20 | |
---|
| 21 | parser = Command.standard_parser() |
---|
| 22 | |
---|
| 23 | parser.add_option( |
---|
| 24 | '-x', '--exclude-module', |
---|
| 25 | metavar="module.name", |
---|
| 26 | dest="exclude_modules", |
---|
| 27 | action="append", |
---|
| 28 | help="Don't search the given module") |
---|
| 29 | |
---|
| 30 | parser.add_option( |
---|
| 31 | '-t', '--add-type', |
---|
| 32 | metavar=".ext", |
---|
| 33 | dest="add_types", |
---|
| 34 | action="append", |
---|
| 35 | help="Search the given type of files") |
---|
| 36 | |
---|
| 37 | def command(self): |
---|
| 38 | self.exclude_modules = self.options.exclude_modules or [] |
---|
| 39 | self.add_types = self.options.add_types or [] |
---|
| 40 | self.symbol = self.args[0] |
---|
| 41 | self.basedir = os.path.dirname( |
---|
| 42 | pluginlib.find_egg_info_dir(os.getcwd())) |
---|
| 43 | if self.verbose: |
---|
| 44 | print "Searching in %s" % self.basedir |
---|
| 45 | self.total_files = 0 |
---|
| 46 | self.search_dir(self.basedir) |
---|
| 47 | if self.verbose > 1: |
---|
| 48 | print "Searched %i files" % self.total_files |
---|
| 49 | |
---|
| 50 | def search_dir(self, dir): |
---|
| 51 | names = os.listdir(dir) |
---|
| 52 | names.sort() |
---|
| 53 | dirs = [] |
---|
| 54 | for name in names: |
---|
| 55 | full = os.path.join(dir, name) |
---|
| 56 | if name in self.bad_names: |
---|
| 57 | continue |
---|
| 58 | if os.path.isdir(full): |
---|
| 59 | # Breadth-first; we'll do this later... |
---|
| 60 | dirs.append(full) |
---|
| 61 | continue |
---|
| 62 | for t in self.add_types: |
---|
| 63 | if name.lower().endswith(t.lower()): |
---|
| 64 | self.search_text(full) |
---|
| 65 | if not name.endswith('.py'): |
---|
| 66 | continue |
---|
| 67 | self.search_file(full) |
---|
| 68 | for dir in dirs: |
---|
| 69 | self.search_dir(dir) |
---|
| 70 | |
---|
| 71 | def search_file(self, filename): |
---|
| 72 | self.total_files += 1 |
---|
| 73 | if not filename.endswith('.py'): |
---|
| 74 | self.search_text(filename) |
---|
| 75 | return |
---|
| 76 | pyc = filename[:-2]+'pyc' |
---|
| 77 | if not os.path.exists(pyc): |
---|
| 78 | py_compile.compile(filename) |
---|
| 79 | if not os.path.exists(pyc): |
---|
| 80 | # Invalid syntax... |
---|
| 81 | self.search_text(filename, as_module=True) |
---|
| 82 | return |
---|
| 83 | f = open(pyc, 'rb') |
---|
| 84 | # .pyc Header: |
---|
| 85 | f.read(8) |
---|
| 86 | code = marshal.load(f) |
---|
| 87 | f.close() |
---|
| 88 | self.search_code(code, filename, []) |
---|
| 89 | |
---|
| 90 | def search_code(self, code, filename, path): |
---|
| 91 | if code.co_name != "?": |
---|
| 92 | path = path + [code.co_name] |
---|
| 93 | else: |
---|
| 94 | path = path |
---|
| 95 | sym = self.symbol |
---|
| 96 | if sym in code.co_varnames: |
---|
| 97 | self.found(code, filename, path) |
---|
| 98 | elif sym in code.co_names: |
---|
| 99 | self.found(code, filename, path) |
---|
| 100 | for const in code.co_consts: |
---|
| 101 | if const == sym: |
---|
| 102 | self.found(code, filename, path) |
---|
| 103 | if inspect.iscode(const): |
---|
| 104 | if not const.co_filename == filename: |
---|
| 105 | continue |
---|
| 106 | self.search_code(const, filename, path) |
---|
| 107 | |
---|
| 108 | def search_text(self, filename, as_module=False): |
---|
| 109 | f = open(filename, 'rb') |
---|
| 110 | lineno = 0 |
---|
| 111 | any = False |
---|
| 112 | for line in f: |
---|
| 113 | lineno += 1 |
---|
| 114 | if line.find(self.symbol) != -1: |
---|
| 115 | if not any: |
---|
| 116 | any = True |
---|
| 117 | if as_module: |
---|
| 118 | print '%s (unloadable)' % self.module_name(filename) |
---|
| 119 | else: |
---|
| 120 | print self.relative_name(filename) |
---|
| 121 | print ' %3i %s' % (lineno, line) |
---|
| 122 | if not self.verbose: |
---|
| 123 | break |
---|
| 124 | f.close() |
---|
| 125 | |
---|
| 126 | def found(self, code, filename, path): |
---|
| 127 | print self.display(filename, path) |
---|
| 128 | self.find_occurance(code) |
---|
| 129 | |
---|
| 130 | def find_occurance(self, code): |
---|
| 131 | f = open(code.co_filename, 'rb') |
---|
| 132 | lineno = 0 |
---|
| 133 | for index, line in zip(xrange(code.co_firstlineno), f): |
---|
| 134 | lineno += 1 |
---|
| 135 | pass |
---|
| 136 | lines = [] |
---|
| 137 | first_indent = None |
---|
| 138 | for line in f: |
---|
| 139 | lineno += 1 |
---|
| 140 | if line.find(self.symbol) != -1: |
---|
| 141 | this_indent = len(re.match(r'^[ \t]*', line).group(0)) |
---|
| 142 | if first_indent is None: |
---|
| 143 | first_indent = this_indent |
---|
| 144 | else: |
---|
| 145 | if this_indent < first_indent: |
---|
| 146 | break |
---|
| 147 | print ' %3i %s' % (lineno, line[first_indent:].rstrip()) |
---|
| 148 | if not self.verbose: |
---|
| 149 | break |
---|
| 150 | |
---|
| 151 | def module_name(self, filename): |
---|
| 152 | assert filename, startswith(self.basedir) |
---|
| 153 | mod = filename[len(self.basedir):].strip('/').strip(os.path.sep) |
---|
| 154 | mod = os.path.splitext(mod)[0] |
---|
| 155 | mod = mod.replace(os.path.sep, '.').replace('/', '.') |
---|
| 156 | return mod |
---|
| 157 | |
---|
| 158 | def relative_name(self, filename): |
---|
| 159 | assert filename, startswith(self.basedir) |
---|
| 160 | name = filename[len(self.basedir):].strip('/').strip(os.path.sep) |
---|
| 161 | return name |
---|
| 162 | |
---|
| 163 | def display(self, filename, path): |
---|
| 164 | parts = '.'.join(path) |
---|
| 165 | if parts: |
---|
| 166 | parts = ':' + parts |
---|
| 167 | return self.module_name(filename) + parts |
---|
| 168 | |
---|