Skip to content

Commit 9d85c4a

Browse files
committed
Improved boilerplate.py so that it generates the correct signatures for pyplot functions.
svn path=/trunk/matplotlib/; revision=7282
1 parent 4794d80 commit 9d85c4a

File tree

3 files changed

+4801
-652
lines changed

3 files changed

+4801
-652
lines changed

CHANGELOG

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2009-07-22 Improved boilerplate.py so that it generates the correct
2+
signatures for pyplot functions. - JKS
3+
14
2009-07-19 Fixed the docstring of Axes.step to reflect the correct
25
meaning of the kwargs "pre" and "post" - See SF bug
36
https://sourceforge.net/tracker/index.php?func=detail&aid=2823304&group_id=80706&atid=560720

boilerplate.py

+130-47
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
1-
# wrap the plot commands defined in axes. The code generated by this
1+
# Wrap the plot commands defined in axes. The code generated by this
22
# file is pasted into pylab.py. We did try to do this the smart way,
33
# with callable functions and new.function, but could never get the
44
# docstrings right for python2.2. See
55
# http://groups.google.com/group/comp.lang.python/browse_frm/thread/dcd63ec13096a0f6/1b14640f3a4ad3dc?#1b14640f3a4ad3dc
6+
# For some later history, see
7+
# http://thread.gmane.org/gmane.comp.python.matplotlib.devel/7068
68

9+
import inspect
10+
import random
11+
import re
12+
import sys
13+
import types
714

8-
# note we check for __doc__ is not None since py2exe optimize removes
9-
# the docstrings
15+
# import the local copy of matplotlib, not the installed one
16+
sys.path.insert(0, './lib')
17+
from matplotlib.axes import Axes
18+
from matplotlib.cbook import dedent
1019

1120
_fmtplot = """\
1221
# This function was autogenerated by boilerplate.py. Do not edit as
1322
# changes will be lost
14-
def %(func)s(*args, **kwargs):
23+
def %(func)s(%(argspec)s):
24+
%(docstring)s
25+
%(ax)s = gca()
1526
# allow callers to override the hold state by passing hold=True|False
16-
b = ishold()
17-
h = kwargs.pop('hold', None)
18-
if h is not None:
19-
hold(h)
27+
%(washold)s = %(ax)s.ishold()
28+
%(sethold)s
29+
if hold is not None:
30+
%(ax)s.hold(hold)
2031
try:
21-
ret = gca().%(func)s(*args, **kwargs)
32+
%(ret)s = %(ax)s.%(func)s(%(call)s)
2233
draw_if_interactive()
23-
except:
24-
hold(b)
25-
raise
34+
finally:
35+
%(ax)s.hold(%(washold)s)
2636
%(mappable)s
27-
hold(b)
28-
return ret
29-
if Axes.%(func)s.__doc__ is not None:
30-
%(func)s.__doc__ = dedent(Axes.%(func)s.__doc__) + \"\"\"
31-
32-
Additional kwargs: hold = [True|False] overrides default hold state\"\"\"
37+
return %(ret)s
3338
"""
3439

3540
_fmtmisc = """\
3641
# This function was autogenerated by boilerplate.py. Do not edit as
3742
# changes will be lost
38-
def %(func)s(*args, **kwargs):
39-
40-
ret = gca().%(func)s(*args, **kwargs)
43+
def %(func)s(%(argspec)s):
44+
%(docstring)s
45+
%(ret)s = gca().%(func)s(%(call)s)
4146
draw_if_interactive()
42-
return ret
43-
if Axes.%(func)s.__doc__ is not None:
44-
%(func)s.__doc__ = dedent(Axes.%(func)s.__doc__)
47+
return %(ret)s
4548
"""
4649

4750
# these methods are all simple wrappers of Axes methods by the same
@@ -101,32 +104,112 @@ def %(func)s(*args, **kwargs):
101104
)
102105

103106
cmappable = {
104-
'contour' : 'if ret._A is not None: gci._current = ret',
105-
'contourf': 'if ret._A is not None: gci._current = ret',
106-
'hexbin' : 'gci._current = ret[0]',
107-
'scatter' : 'gci._current = ret',
108-
'pcolor' : 'gci._current = ret',
109-
'pcolormesh' : 'gci._current = ret',
110-
'imshow' : 'gci._current = ret',
111-
'spy' : 'gci._current = ret',
112-
'quiver' : 'gci._current = ret',
113-
'specgram' : 'gci._current = ret[-1]',
107+
'contour' : 'if %(ret)s._A is not None: gci._current = %(ret)s',
108+
'contourf': 'if %(ret)s._A is not None: gci._current = %(ret)s',
109+
'hexbin' : 'gci._current = %(ret)s',
110+
'scatter' : 'gci._current = %(ret)s',
111+
'pcolor' : 'gci._current = %(ret)s',
112+
'pcolormesh' : 'gci._current = %(ret)s',
113+
'imshow' : 'gci._current = %(ret)s',
114+
'spy' : 'gci._current = %(ret)s',
115+
'quiver' : 'gci._current = %(ret)s',
116+
'specgram' : 'gci._current = %(ret)s[-1]',
114117

115118
}
116119

117-
118-
for func in _plotcommands:
119-
if func in cmappable:
120-
mappable = cmappable[func]
121-
else:
122-
mappable = ''
123-
print _fmtplot%locals()
124-
125-
126-
for func in _misccommands:
127-
print _fmtmisc%locals()
128-
129-
120+
def format_value(value):
121+
"""
122+
Format function default values as needed for inspect.formatargspec.
123+
The interesting part is a hard-coded list of functions used
124+
as defaults in pyplot methods.
125+
"""
126+
if isinstance(value, types.FunctionType):
127+
if value.func_name in ('detrend_none', 'window_hanning'):
128+
return '=mlab.' + value.func_name
129+
if value.func_name == 'mean':
130+
return '=np.' + value.func_name
131+
raise ValueError, ('default value %s unknown to boilerplate.formatvalue'
132+
% value)
133+
return '='+repr(value)
134+
135+
def remove_final_whitespace(string):
136+
"""
137+
Return a copy of *string* with final whitespace removed from each line.
138+
"""
139+
return '\n'.join(x.rstrip() for x in string.split('\n'))
140+
141+
def make_docstring(cmd, mention_hold):
142+
func = getattr(Axes, cmd)
143+
docstring = inspect.getdoc(func)
144+
if docstring is None:
145+
return ""
146+
escaped = re.sub(r'\\', r'\\\\', docstring)
147+
if mention_hold:
148+
escaped += '''
149+
150+
Additional kwargs: hold = [True|False] overrides default hold state
151+
'''
152+
return '"""'+escaped+'"""'
153+
154+
for fmt,cmdlist in (_fmtplot,_plotcommands),(_fmtmisc,_misccommands):
155+
for func in cmdlist:
156+
# For some commands, an additional line is needed to set the
157+
# color map
158+
if func in cmappable:
159+
mappable = cmappable[func] % locals()
160+
else:
161+
mappable = ''
162+
163+
# Format docstring
164+
docstring = make_docstring(func, fmt is _fmtplot)
165+
166+
# Get argspec of wrapped function
167+
args, varargs, varkw, defaults = inspect.getargspec(getattr(Axes, func))
168+
args.pop(0) # remove 'self' argument
169+
if defaults is None:
170+
defaults = ()
171+
172+
# How to call the wrapped function
173+
call = map(str, args)
174+
if varargs is not None:
175+
call.append('*'+varargs)
176+
if varkw is not None:
177+
call.append('**'+varkw)
178+
call = ', '.join(call)
179+
180+
# Add a hold keyword argument if needed (fmt is _fmtplot) and
181+
# possible (if *args is used, we can't just add a hold
182+
# argument in front of it since it would gobble one of the
183+
# arguments the user means to pass via *args)
184+
if varargs:
185+
sethold = "hold = %(varkw)s.pop('hold', None)" % locals()
186+
elif fmt is _fmtplot:
187+
args.append('hold')
188+
defaults = defaults + (None,)
189+
sethold = ''
190+
191+
# Now we can build the argspec for defining the wrapper
192+
argspec = inspect.formatargspec(args, varargs, varkw, defaults,
193+
formatvalue=format_value)
194+
argspec = argspec[1:-1] # remove parens
195+
196+
# A gensym-like facility in case some function takes an
197+
# argument named washold, ax, or ret
198+
washold,ret,ax = 'washold', 'ret', 'ax'
199+
bad = set(args) | set((varargs, varkw))
200+
while washold in bad or ret in bad or ax in bad:
201+
washold = 'washold' + str(random.randrange(10**12))
202+
ret = 'ret' + str(random.randrange(10**12))
203+
ax = 'ax' + str(random.randrange(10**12))
204+
205+
# Since we can't avoid using some function names,
206+
# bail out if they are used as argument names
207+
for reserved in ('gca', 'gci', 'draw_if_interactive'):
208+
if reserved in bad:
209+
raise ValueError, \
210+
'Axes method %s has kwarg named %s' % (func, reserved)
211+
212+
print remove_final_whitespace(fmt%locals())
130213

131214
# define the colormap functions
132215
_fmtcmap = """\

0 commit comments

Comments
 (0)