-
-
Notifications
You must be signed in to change notification settings - Fork 8.3k
Expand file tree
/
Copy pathCODING_GUIDE
More file actions
231 lines (175 loc) · 8.5 KB
/
CODING_GUIDE
File metadata and controls
231 lines (175 loc) · 8.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
= The matplotlib developer's guide =
This is meant to be a guide to developers on the mpl coding practices
and standards. Please edit and extend this document.
== Committing changes ==
When committing changes to matplotlib, there are a few things to bear
in mind.
* if your changes are non-trivial, please make an entry in the
CHANGELOG
* if you change the API, please document it in API_CHANGES, and
consider posting to mpl-devel
* Are your changes python2.3 compatible? We are still trying to
support 2.3, so avoid 2.4 only features like decorators until we
remove 2.3 support
* Are your changes Numeric, numarray and numpy compatible? Try
running simple_plot.py or image_demo.py with --Numeric, --numarray
and --numpy (Note, someone should add examples to
backend_driver.py which explicitly require numpy, numarray and
Numeric so we can automatically catch these)
* Can you pass examples/backend_driver.py? This is our poor man's
unit test.
* If you have altered extension code, do you pass
unit/memleak_hawaii.py?
== Naming and spacing conventions ==
In general, we want to hew as closely as possible to the standard
coding guidelines for python written by Guido in
http://www.python.org/dev/peps/pep-0008, though we do not do this
throughout.
functions and class methods : lower or lower_underscore_separated
attributes and variables : lower or lowerUpper
classes : Upper or MixedCase
Personally, I prefer the shortest names that are still readable.
Also, use an editor that does not put tabs in files. Four spaces
should be used for indentation everywhere and if there is a file with
tabs or more or less spaces it is a bug -- please fix it.
== Licenses ==
matplotlib only uses BSD compatible code. If you bring in code from
another project make sure it has a PSF, BSD, MIT or compatible
license. If not, you may consider contacting the author and asking
them to relicense it. GPL and LGPL code are not acceptible in the
main code base, though we are considering an alternative way of
distributing L/GPL code through an separate channel, possibly a
toolkit. If you include code, make sure you include a copy of that
code's license in the license directory if the code's license requires
you to distribute the license with it.
== Keyword argument processing ==
Matplotlib makes extensive use of **kwargs for pass through
customizations from one function to another. A typical example is in
pylab.text, The definition of the pylab text function is a simple
pass-through to axes.Axes.text
# in pylab.py
def text(*args, **kwargs):
ret = gca().text(*args, **kwargs)
draw_if_interactive()
return ret
axes.Axes.text in simplified form looks like this, ie it just passes
them on to text.Text.__init__
# in axes.py
def text(self, x, y, s, fontdict=None, withdash=False, **kwargs):
t = Text(x=x, y=y, text=s, **kwargs)
and Text.__init__ (again with liberties for illustration) just passes
them on to the artist.Artist.update method
# in text.py
def __init__(self, x=0, y=0, text='', **kwargs):
Artist.__init__(self)
self.update(kwargs)
'update' does the work looking for methods named like 'set_property'
if 'property' is a keyword argument. Ie, noone looks at the keywords,
they just get passed through the API to the artist constructor which
looks for suitably named methods and calls them with the value.
As a general rule, the use of **kwargs should be reserved for
pass-through keyword arguments, as in the exmaple above. If I intend
for all the keyword args to be used in some function and not passed
on, I just use the key/value keyword args in the function definition
rather than the **kwargs idiom.
In some cases I want to consume some keys and pass through the others,
in which case I pop the ones I want to use locally and pass on the
rest, eg I pop scalex and scaley in Axes.plot and assume the rest are
Line2D keyword arguments. Whenever you mutate a kwargs dictionary (eg
by popping it), you must first copy it since the user may be explitly
passing in a dictionary which is used across many function calls. As
an example of a copy, pop, passthrough usage, see Axes.plot:
# in axes.py
def plot(self, *args, **kwargs):
kwargs = kwargs.copy()
scalex = popd(kwargs, 'scalex', True)
scaley = popd(kwargs, 'scaley', True)
if not self._hold: self.cla()
lines = []
for line in self._get_lines(*args, **kwargs):
self.add_line(line)
lines.append(line)
popd is a matplotlib.cbook function to pop an item from a dictionary
with a default value if the item doesn't exist
Note there is a use case when kwargs are meant to be used locally in
the function (not passed on), but you still need the **kwargs idiom.
That is when you want to use *args to allow variable numbers of
non-keyword args. In this case, python will not allow you to use
named keyword args after the *args usage, so you will be forced to use
**kwargs. An example is matplotlib.contour.ContourLabeler.clabel
# in contour.py
def clabel(self, *args, **kwargs):
fontsize = kwargs.get('fontsize', None)
inline = kwargs.get('inline', 1)
self.fmt = kwargs.get('fmt', '%1.3f')
colors = kwargs.get('colors', None)
if len(args) == 0:
levels = self.levels
indices = range(len(self.levels))
elif len(args) == 1:
...etc...
== Class documentation ==
matplotlib uses artist instrospection of docstrings to support
properties. All properties that you want to support through setp and
getp should have a set_property and get_property method in the Artist
class. Yes, this is not ideal given python properties or enthought
traits, but it is a historical legacy for now. The setter methods use
the docstring with the ACCEPTS token to indicate the type of argument
the method accepts. Eg in matplotlib.lines.Line2D
# in lines.py
def set_linestyle(self, linestyle):
"""
Set the linestyle of the line
ACCEPTS: [ '-' | '--' | '-.' | ':' | 'steps' | 'None' | ' ' | '' ]
"""
Since matplotlib uses a lot of pass through kwargs, eg in every
function that creates a line (plot, semilogx, semilogy, etc...), it
can be difficult for the new user to know which kwargs are supported.
I have developed a docstring interpolation scheme to support
documentation of every function that takes a **kwargs. The
requirements are:
1) single point of configuration so changes to the properties don't
require multiple docstring edits
2) as automated as possible so that as properties change the docs
are updated automagically.
I have added a matplotlib.artist.kwdocd and kwdoc() to faciliate this.
They combines python string interpolation in the docstring with the
matplotlib artist introspection facility that underlies setp and getp.
The kwdocd is a single dictionary that maps class name to a docstring
of kwargs. Here is an example from matplotlib.lines
# in lines.py
artist.kwdocd['Line2D'] = artist.kwdoc(Line2D)
Then in any function accepting Line2D passthrough kwargs, eg
matplotlib.axes.Axes.plot
# in axes.py
from cbook import dedent
...
def plot(self, *args, **kwargs):
"""
Some stuff omitted
The kwargs are Line2D properties:
%(Line2D)s
kwargs scalex and scaley, if defined, are passed on
to autoscale_view to determine whether the x and y axes are
autoscaled; default True. See Axes.autoscale_view for more
information
"""
pass
plot.__doc__ = dedent(plot.__doc__) % artist.kwdocd
Note there is a problem for Artist __init__ methods, eg Patch.__init__
which supports Patch kwargs, since the artist inspector cannot work
until the class is fully defined and we can't modify the
Patch.__init__.__doc__ docstring outside the class definition. I have
made some manual hacks in this case which violates the "single entry
point" requirement above; hopefully we'll find a more elegant solution
before too long
== Formatting (editor setup) ==
Indentation is in units of 4 spaces.
Please avoid hard tabs--use only spaces.
Please avoid spurious invisible spaces at the ends of lines.
(Tell your editor to strip whitespace from line ends when saving
a file.)
Keep docstrings uniformly indented as in the example above, with
nothing to the left of the triple quotes. The dedent() function
is needed to remove excess indentation only if something will be
interpolated into the docstring, again as in the example above.