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 | |
---|