10000 Added a coding_standards test module. Currently checks PEP8 complianc… · matplotlib/matplotlib@fd57377 · GitHub
[go: up one dir, main page]

Skip to content

Commit fd57377

Browse files
committed
Added a coding_standards test module. Currently checks PEP8 compliance on all files except those explicitly excluded.
1 parent 4d2699e commit fd57377

File tree

3 files changed

+235
-1
lines changed

3 files changed

+235
-1
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ python:
77
- 3.3
88

99
install:
10-
- pip -q install --use-mirrors nose python-dateutil numpy
10+
- pip install -q --use-mirrors nose python-dateutil numpy pep8
1111
# This is a workaround to install the latest versions of pyparsing,
1212
# which are not yet available on PyPI
1313
- 'if [ ${TRAVIS_PYTHON_VERSION:0:1} == "3" ]; then pip -q install http://sourceforge.net/projects/pyparsing/files/pyparsing/pyparsing-2.0.0/pyparsing-2.0.0.tar.gz; fi'

lib/matplotlib/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1177,6 +1177,7 @@ def tk_window_focus():
11771177
'matplotlib.tests.test_basic',
11781178
'matplotlib.tests.test_bbox_tight',
11791179
'matplotlib.tests.test_cbook',
1180+
'matplotlib.tests.test_coding_standards',
11801181
'matplotlib.tests.test_collections',
11811182
'matplotlib.tests.test_colorbar',
11821183
'matplotlib.tests.test_colors',
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
from fnmatch import fnmatch
2+
import os
3+
4+
from nose.tools import assert_equal
5+
import pep8
6+
7+
import matplotlib
8+
9+
10+
class StandardReportWithExclusions(pep8.StandardReport):
11+
#; A class attribute to store the exception exclusion file patterns.
12+
expected_bad_files = [
13+
'*/matplotlib/__init__.py',
14+
'*/matplotlib/_cm.py',
15+
'*/matplotlib/_mathtext_data.py',
16+
'*/matplotlib/_pylab_helpers.py',
17+
'*/matplotlib/afm.py',
18+
'*/matplotlib/animation.py',
19+
'*/matplotlib/artist.py',
20+
'*/matplotlib/axes.py',
21+
'*/matplotlib/axis.py',
22+
'*/matplotlib/backend_bases.py',
23+
'*/matplotlib/bezier.py',
24+
'*/matplotlib/cbook.py',
25+
'*/matplotlib/collections.py',
26+
'*/matplotlib/colorbar.py',
27+
'*/matplotlib/dates.py',
28+
'*/matplotlib/docstring.py',
29+
'*/matplotlib/dviread.py',
30+
'*/matplotlib/finance.py',
31+
'*/matplotlib/font_manager.py',
32+
'*/matplotlib/fontconfig_pattern.py',
33+
'*/matplotlib/gridspec.py',
34+
'*/matplotlib/hatch.py',
35+
'*/matplotlib/image.py',
36+
'*/matplotlib/legend.py',
37+
'*/matplotlib/legend_handler.py',
38+
'*/matplotlib/mathtext.py',
39+
'*/matplotlib/mlab.py',
40+
'*/matplotlib/nxutils.py',
41+
'*/matplotlib/offsetbox.py',
42+
'*/matplotlib/patches.py',
43+
'*/matplotlib/path.py',
44+
'*/matplotlib/patheffects.py',
45+
'*/matplotlib/pylab.py',
46+
'*/matplotlib/pyplot.py',
47+
'*/matplotlib/quiver.py',
48+
'*/matplotlib/rcsetup.py',
49+
'*/matplotlib/sankey.py',
50+
'*/matplotlib/scale.py',
51+
'*/matplotlib/stackplot.py',
52+
'*/matplotlib/streamplot.py',
53+
'*/matplotlib/table.py',
54+
'*/matplotlib/texmanager.py',
55+
'*/matplotlib/text.py',
56+
'*/matplotlib/textpath.py',
57+
'*/matplotlib/ticker.py',
58+
'*/matplotlib/tight_bbox.py',
59+
'*/matplotlib/tight_layout.py',
60+
'*/matplotlib/transforms.py',
61+
'*/matplotlib/type1font.py',
62+
'*/matplotlib/widgets.py',
63+
'*/matplotlib/testing/compare.py',
64+
'*/matplotlib/testing/decorators.py',
65+
'*/matplotlib/testing/image_util.py',
66+
'*/matplotlib/testing/noseclasses.py',
67+
'*/matplotlib/testing/jpl_units/Duration.py',
68+
'*/matplotlib/testing/jpl_units/Epoch.py',
69+
'*/matplotlib/testing/jpl_units/EpochConverter.py',
70+
'*/matplotlib/testing/jpl_units/StrConverter.py',
71+
'*/matplotlib/testing/jpl_units/UnitDbl.py',
72+
'*/matplotlib/testing/jpl_units/UnitDblConverter.py',
73+
'*/matplotlib/testing/jpl_units/UnitDblFormatter.py',
74+
'*/matplotlib/testing/jpl_units/__init__.py',
75+
'*/matplotlib/tri/triangulation.py',
76+
'*/matplotlib/tri/tricontour.py',
77+
'*/matplotlib/tri/triinterpolate.py',
78+
'*/matplotlib/tri/tripcolor.py',
79+
'*/matplotlib/tri/triplot.py',
80+
'*/matplotlib/tests/__init__.py',
81+
'*/matplotlib/tests/test_agg.py',
82+
'*/matplotlib/tests/test_animation.py',
83+
'*/matplotlib/tests/test_arrow_patches.py',
84+
'*/matplotlib/tests/test_artist.py',
85+
'*/matplotlib/tests/test_axes.py',
86+
'*/matplotlib/tests/test_backend_pdf.py',
87+
'*/matplotlib/tests/test_backend_pgf.py',
88+
'*/matplotlib/tests/test_backend_svg.py',
89+
'*/matplotlib/tests/test_basic.py',
90+
'*/matplotlib/tests/test_bbox_tight.py',
91+
'*/matplotlib/tests/test_cbook.py',
92+
'*/matplotlib/tests/test_colorbar.py',
93+
'*/matplotlib/tests/test_colors.py',
94+
'*/matplotlib/tests/test_compare_images.py',
95+
'*/matplotlib/tests/test_contour.py',
96+
'*/matplotlib/tests/test_dates.py',
97+
'*/matplotlib/tests/test_delaunay.py',
98+
'*/matplotlib/tests/test_dviread.py',
99+
'*/matplotlib/tests/test_image.py',
100+
'*/matplotlib/tests/test_legend.py',
101+
'*/matplotlib/tests/test_lines.py',
102+
'*/matplotlib/tests/test_mathtext.py',
103+
'*/matplotlib/tests/test_patches.py',
104+
'*/matplotlib/tests/test_pickle.py',
105+
'*/matplotlib/tests/test_png.py',
106+
'*/matplotlib/tests/test_rcparams.py',
107+
'*/matplotlib/tests/test_simplification.py',
108+
'*/matplotlib/tests/test_spines.py',
109+
'*/matplotlib/tests/test_streamplot.py',
110+
'*/matplotlib/tests/test_subplots.py',
111+
'*/matplotlib/tests/test_text.py',
112+
'*/matplotlib/tests/test_ticker.py',
113+
'*/matplotlib/tests/test_tightlayout.py',
114+
'*/matplotlib/tests/test_transforms.py',
115+
'*/matplotlib/tests/test_triangulation.py',
116+
'*/matplotlib/tests/test_ttconv.py',
117+
'*/matplotlib/compat/subprocess.py',
118+
'*/matplotlib/backends/__init__.py',
119+
'*/matplotlib/backends/backend_agg.py',
120+
'*/matplotlib/backends/backend_cairo.py',
121+
'*/matplotlib/backends/backend_cocoaagg.py',
122+
'*/matplotlib/backends/backend_emf.py',
123+
'*/matplotlib/backends/backend_fltkagg.py',
124+
'*/matplotlib/backends/backend_gdk.py',
125+
'*/matplotlib/backends/backend_gtk.py',
126+
'*/matplotlib/backends/backend_gtk3.py',
127+
'*/matplotlib/backends/backend_gtk3cairo.py',
128+
'*/matplotlib/backends/backend_gtkagg.py',
129+
'*/matplotlib/backends/backend_gtkcairo.py',
130+
'*/matplotlib/backends/backend_macosx.py',
131+
'*/matplotlib/backends/backend_mixed.py',
132+
'*/matplotlib/backends/backend_pdf.py',
133+
'*/matplotlib/backends/backend_pgf.py',
134+
'*/matplotlib/backends/backend_ps.py',
135+
'*/matplotlib/backends/backend_qt.py',
136+
'*/matplotlib/backends/backend_qt4.py',
137+
'*/matplotlib/backends/backend_qt4agg.py',
138+
'*/matplotlib/backends/backend_qtagg.py',
139+
'*/matplotlib/backends/backend_svg.py',
140+
'*/matplotlib/backends/backend_template.py',
141+
'*/matplotlib/backends/backend_tkagg.py',
142+
'*/matplotlib/backends/backend_webagg.py',
143+
'*/matplotlib/backends/backend_wx.py',
144+
'*/matplotlib/backends/backend_wxagg.py',
145+
'*/matplotlib/backends/qt4_compat.py',
146+
'*/matplotlib/backends/tkagg.py',
147+
'*/matplotlib/backends/windowing.py',
148+
'*/matplotlib/backends/qt4_editor/figureoptions.py',
149+
'*/matplotlib/backends/qt4_editor/formlayout.py',
150+
'*/matplotlib/delaunay/interpolate.py',
151+
'*/matplotlib/delaunay/testfuncs.py',
152+
'*/matplotlib/sphinxext/__init__.py',
153+
'*/matplotlib/sphinxext/ipython_console_highlighting.py',
154+
'*/matplotlib/sphinxext/ipython_directive.py',
155+
'*/matplotlib/sphinxext/mathmpl.py',
156+
'*/matplotlib/sphinxext/only_directives.py',
157+
'*/matplotlib/sphinxext/plot_directive.py',
158+
'*/matplotlib/projections/__init__.py',
159+
'*/matplotlib/projections/geo.py',
160+
'*/matplotlib/projections/polar.py']
161+
162+
#; A class attribute to store patterns which have seen exceptions.
163+
matched_exclusions = set()
164+
165+
def get_file_results(self):
166+
# If the file had no errors, return self.file_errors (which will be 0)
167+
if not self._deferred_print:
168+
return self.file_errors
169+
170+
# Iterate over all of the patterns, to find a possible exclusion. If we
171+
# the filename is to be excluded, go ahead and remove the counts that
172+
# self.error added.
173+
for pattern in self.expected_bad_files:
174+
if fnmatch(self.filename, pattern):
175+
self.matched_exclusions.add(pattern)
176+
# invert the error method's counters.
177+
for _, _, code, _, _ in self._deferred_print:
178+
self.counters[code] -= 1
179+
if self.counters[code] == 0:
180+
self.counters.pop(code)
181+
self.messages.pop(code)
182+
self.file_errors -= 1
183+
self.total_errors -= 1
184+
return self.file_errors
185+
186+
# Otherwise call the superclass' method to print the bad results.
187+
return super(StandardReportWithExclusions,
188+
self).get_file_results()
189+
190+
191+
def test_pep8_conformance():
192+
# Tests the matplotlib codebase against the "pep8" tool.
193+
#
194+
# Users can add their own excluded files (should files exist in the
195+
# local directory which is not in the repository) by adding a
196+
# ".pep8_test_exclude.txt" file in the same directory as this test.
197+
# The file should be a line separated list of filenames/directories
198+
# as can be passed to the "pep8" tool's exclude list.
199+
200+
# to get a list of bad files, rather than the specific errors, add
201+
# "reporter=pep8.FileReport" to the StyleGuide constructor.
202+
pep8style = pep8.StyleGuide(quiet=False,
203+
reporter=StandardReportWithExclusions)
204+
205+
# Allow users to add their own exclude list.
206+
extra_exclude_file = os.path.join(os.path.dirname(__file__),
207+
'.pep8_test_exclude.txt')
208+
if os.path.exists(extra_exclude_file):
209+
with open(extra_exclude_file, 'r') as fh:
210+
extra_exclude = [line.strip() for line in fh if line.strip()]
211+
pep8style.options.exclude.extend(extra_exclude)
212+
213+
result = pep8style.check_files([os.path.dirname(matplotlib.__file__)])
214+
assert_equal(result.total_errors, 0, "Found code syntax "
215+
"errors (and warnings).")
216+
217+
reporter = pep8style.options.reporter
218+
# If we've been using the exclusions reporter, check that we didn't
219+
# exclude files unnecessarily.
220+
if reporter is StandardReportWithExclusions:
221+
unexpectedly_good = sorted(set(reporter.expected_bad_files) -
222+
reporter.matched_exclusions)
223+
224+
if unexpectedly_good:
225+
raise ValueError('Some exclude patterns were unnecessary as the '
226+
'files they pointed to either passed the PEP8 '
227+
'tests or do not point to a file:\n '
228+
'{}'.format('\n '.join(unexpectedly_good)))
229+
230+
231+
if __name__ == '__main__':
232+
import nose
233+
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

0 commit comments

Comments
 (0)
0