1 | 1 patch for repository http://tahoe-lafs.org/source/tahoe/trunk: |
---|
2 | |
---|
3 | Fri Oct 7 03:15:29 BST 2011 david-sarah@jacaranda.org |
---|
4 | * Add misc/coding_tools/check-miscaptures.py to detect incorrect captures of variables declared in a for loop, and a 'make check-miscaptures' Makefile target to run it. (It is also run by 'make code-checks'.) refs #1555 |
---|
5 | |
---|
6 | New patches: |
---|
7 | |
---|
8 | [Add misc/coding_tools/check-miscaptures.py to detect incorrect captures of variables declared in a for loop, and a 'make check-miscaptures' Makefile target to run it. (It is also run by 'make code-checks'.) refs #1555 |
---|
9 | david-sarah@jacaranda.org**20111007021529 |
---|
10 | Ignore-this: 212f4f6039af508716e09b8952ae7ab |
---|
11 | ] { |
---|
12 | hunk ./Makefile 124 |
---|
13 | false |
---|
14 | endif |
---|
15 | |
---|
16 | -code-checks: build version-and-path check-interfaces -find-trailing-spaces -check-umids pyflakes |
---|
17 | +code-checks: build version-and-path check-interfaces check-miscaptures -find-trailing-spaces -check-umids pyflakes |
---|
18 | |
---|
19 | version-and-path: |
---|
20 | $(TAHOE) --version-and-path |
---|
21 | hunk ./Makefile 133 |
---|
22 | $(TAHOE) @misc/coding_tools/check-interfaces.py 2>&1 |tee violations.txt |
---|
23 | @echo |
---|
24 | |
---|
25 | +check-miscaptures: |
---|
26 | + $(PYTHON) misc/coding_tools/check-miscaptures.py $(SOURCES) 2>&1 |tee miscaptures.txt |
---|
27 | + @echo |
---|
28 | + |
---|
29 | pyflakes: |
---|
30 | $(PYTHON) -OOu `which pyflakes` $(SOURCES) |sort |uniq |
---|
31 | @echo |
---|
32 | addfile ./misc/coding_tools/check-miscaptures.py |
---|
33 | hunk ./misc/coding_tools/check-miscaptures.py 1 |
---|
34 | +#! /usr/bin/python |
---|
35 | + |
---|
36 | +import os, sys, compiler |
---|
37 | +from compiler.ast import Node, For, AssName, Name, Lambda, Function |
---|
38 | + |
---|
39 | +def check_file(filename): |
---|
40 | + results = [] |
---|
41 | + check_ast(compiler.parseFile(filename), results) |
---|
42 | + return results |
---|
43 | + |
---|
44 | +def check_source(source): |
---|
45 | + results = [] |
---|
46 | + check_ast(compiler.parse(source), results) |
---|
47 | + return results |
---|
48 | + |
---|
49 | +def check_ast(ast, results): |
---|
50 | + if isinstance(ast, For): |
---|
51 | + check_for(ast, results) |
---|
52 | + else: |
---|
53 | + for child in ast.getChildNodes(): |
---|
54 | + if isinstance(ast, Node): |
---|
55 | + check_ast(child, results) |
---|
56 | + |
---|
57 | +def check_for(ast, results): |
---|
58 | + declared = set() |
---|
59 | + captured = set() |
---|
60 | + collect_declared(ast, declared) |
---|
61 | + collect_captured(ast, captured, set(), False) |
---|
62 | + for name in captured & declared: |
---|
63 | + results.append((name, ast.lineno)) |
---|
64 | + |
---|
65 | +def collect_declared(ast, declared): |
---|
66 | + if isinstance(ast, AssName): |
---|
67 | + declared.add(ast.name) |
---|
68 | + else: |
---|
69 | + for child in ast.getChildNodes(): |
---|
70 | + if isinstance(ast, Node): |
---|
71 | + collect_declared(child, declared) |
---|
72 | + |
---|
73 | +def collect_captured(ast, captured, excluded, in_func): |
---|
74 | + if isinstance(ast, Name): |
---|
75 | + if in_func and ast.name not in excluded: |
---|
76 | + captured.add(ast.name) |
---|
77 | + elif isinstance(ast, (Lambda, Function)): |
---|
78 | + # Formal parameters of the function are not captures |
---|
79 | + newly_excluded = excluded.copy() |
---|
80 | + for argname in ast.argnames: |
---|
81 | + newly_excluded.add(argname) |
---|
82 | + |
---|
83 | + childnodes = ast.getChildNodes() |
---|
84 | + # ... and neither are variable uses in default argument |
---|
85 | + # expressions, provided we weren't already in a function. |
---|
86 | + if not in_func: |
---|
87 | + childnodes = childnodes[len(ast.defaults):] |
---|
88 | + |
---|
89 | + for child in childnodes: |
---|
90 | + if isinstance(ast, Node): |
---|
91 | + collect_captured(child, captured, newly_excluded, True) |
---|
92 | + else: |
---|
93 | + for child in ast.getChildNodes(): |
---|
94 | + if isinstance(ast, Node): |
---|
95 | + collect_captured(child, captured, excluded, in_func) |
---|
96 | + |
---|
97 | +def report(path, results, out): |
---|
98 | + for (name, lineno) in results: |
---|
99 | + print >>out, "%s:%d %r" % (path, lineno, name) |
---|
100 | + |
---|
101 | + |
---|
102 | +def check(sources, out): |
---|
103 | + class Counts: |
---|
104 | + n = 0 |
---|
105 | + processed_files = 0 |
---|
106 | + suspect_files = 0 |
---|
107 | + counts = Counts() |
---|
108 | + |
---|
109 | + def _process(path): |
---|
110 | + results = check_file(path) |
---|
111 | + report(path, results, out) |
---|
112 | + counts.n += len(results) |
---|
113 | + counts.processed_files += 1 |
---|
114 | + if len(results) > 0: |
---|
115 | + counts.suspect_files += 1 |
---|
116 | + |
---|
117 | + for source in sources: |
---|
118 | + print >>out, "Checking %s..." % (source,) |
---|
119 | + if os.path.isfile(source): |
---|
120 | + _process(source) |
---|
121 | + else: |
---|
122 | + for (dirpath, dirnames, filenames) in os.walk(source): |
---|
123 | + for fn in filenames: |
---|
124 | + (basename, ext) = os.path.splitext(fn) |
---|
125 | + if ext == '.py': |
---|
126 | + _process(os.path.join(dirpath, fn)) |
---|
127 | + |
---|
128 | + print >>out, ("%d suspiciously captured variables in %d out of %d files" |
---|
129 | + % (counts.n, counts.suspect_files, counts.processed_files)) |
---|
130 | + |
---|
131 | + |
---|
132 | +sources = ['src'] |
---|
133 | +if len(sys.argv) > 1: |
---|
134 | + sources = sys.argv[1:] |
---|
135 | +check(sources, sys.stderr) |
---|
136 | + |
---|
137 | + |
---|
138 | +# TODO: self-tests |
---|
139 | } |
---|
140 | |
---|
141 | Context: |
---|
142 | |
---|
143 | [no_network.py: Clean up whitespace around code changed by previous patch. |
---|
144 | david-sarah@jacaranda.org**20111004010407 |
---|
145 | Ignore-this: 647ec8a9346dca1a41212ab250619b72 |
---|
146 | ] |
---|
147 | [no_network.py: Fix potential bugs in some tests due to capture of slots in for loops. |
---|
148 | david-sarah@jacaranda.org**20111004010231 |
---|
149 | Ignore-this: 9c496877613a3befd54979e5de6e63d2 |
---|
150 | ] |
---|
151 | [docs: fix the rst formatting of COPYING.TGPPL.rst |
---|
152 | zooko@zooko.com**20111003043333 |
---|
153 | Ignore-this: c5fbc83f4a3db81a0c95b27053c463c5 |
---|
154 | Now it renders correctly both on trac and with rst2html --verbose from docutils v0.8.1. |
---|
155 | ] |
---|
156 | [MDMF: remove extension fields from caps, tolerate arbitrary ones. Fixes #1526 |
---|
157 | Brian Warner <warner@lothar.com>**20111001233553 |
---|
158 | Ignore-this: 335e1690aef1146a2c0b8d8c18c1cb21 |
---|
159 | |
---|
160 | The filecaps used to be produced with hints for 'k' and segsize, but they |
---|
161 | weren't actually used, and doing so had the potential to limit how we change |
---|
162 | those filecaps in the future. Also the parsing code had some problems dealing |
---|
163 | with other numbers of extensions. Removing the existing fields and making the |
---|
164 | parser tolerate (and ignore) extra ones makes MDMF more future-proof. |
---|
165 | ] |
---|
166 | [test/test_runner.py: BinTahoe.test_path has rare nondeterministic failures; this patch probably fixes a problem where the actual cause of failure is masked by a string conversion error. |
---|
167 | david-sarah@jacaranda.org**20110927225336 |
---|
168 | Ignore-this: 6f1ad68004194cc9cea55ace3745e4af |
---|
169 | ] |
---|
170 | [docs/configuration.rst: add section about the types of node, and clarify when setting web.port enables web-API service. fixes #1444 |
---|
171 | zooko@zooko.com**20110926203801 |
---|
172 | Ignore-this: ab94d470c68e720101a7ff3c207a719e |
---|
173 | ] |
---|
174 | [TAG allmydata-tahoe-1.9.0a2 |
---|
175 | warner@lothar.com**20110925234811 |
---|
176 | Ignore-this: e9649c58f9c9017a7d55008938dba64f |
---|
177 | ] |
---|
178 | Patch bundle hash: |
---|
179 | 83fda893e4e4585dd65a793d1d45225f7b8177ee |
---|