Jonathan Fine wrote:
<snip>
I'll post some usage examples later today, I hope.
Well, here are some examples. A day later, I'm afraid.
** Pipelines and other composites
This is arising for me at work.
I produce Postscript by running TeX on a document.
And then running dvips on the output of TeX.
TeX as a program has parameters (command line options).
As does dvips.
For various reasons I wish to wrap TeX and dvips.
def tex_fn(filename, input_path=None, eps_path=None):
'''Run tex on filename.
Search for input files on input_path.
Search for EPS files on eps_path. '''
pass
def dvips_fn(filename, page_size=None, color_profile=None):
'''Run dvips on filename. etc.'''
pass
In reality, these tex and dvips have many options.
More parameters will be added later.
So now I wish to produce a composite function, that runs
both tex and dvips. And does other glue things.
def tex_and_dvips_fn(filename,
tex:input_path=xxx,
dvips:color_profile=yyy):
# glueing stuff
tex_fn(filename, **tex)
# more glueing stuff
dvips_fn(filename, **dvips)
To avoid a name clash, we use 'tex' for the parameter
space, and 'tex_fn' for the function that takes 'tex'
as parameter.
The point is that parameters can be added to tex_fn and
dvips_fn without our having to change tex_and_dvips_fn
** Wrapping functions
This is the problem that originally motivated my
suggestion.
We have coded a new function.
def adder(i, j): return i + j
We wish to test 'adder'.
But unittest is too verbose for us.
We wish to define a decorator (?) test_wrap to
work as follows.
orig_adder = adder
adder = test_wrap(adder)
new_adder = adder
orig_adder(i, j) and new_adder(i, j) to be
effectively identical - same return, same side
effects, raise same exceptions.
So far,
def test_wrap(fn): return fn
does the job.
But now we want
new_adder(2, 2, returns=4)
new_adder(2, '', raises=TypeError)
to be same as
orig_adder(2, 2)
orig_adder(2, '')
(which can be achieved by ignoring 'returns' and 'raises'.
The idea here is that we can call
adder = new(adder)
early on, and not break any working code.
And we also want
new_adder(2, 2, 5)
new_adder('', '', raises=TypeError)
to raise something like an AssertionError.
OK - so I have an informal specification of test_wrap.
Its clear, I think, that test_wrap must be something like
def test_wrap(fn):
def wrapped_fn(*args, **kwargs):
test_args = {}
# transfer entries from one dict to another
for key in ('returns', 'raises'):
if kwargs.has_key(key):
test_args[key] = kwargs[key]
del kwargs[key]
result = fn(args, kwargs)
if test_args.has_key(result):
assert test_args['result'] = result
(I've not coded testing for 'raises'.)
Now, the more parameters added by the test_wrap function,
the more the chance of a name clash.
So why not reduce the chances by using name spaces.
One possible namespace syntax is:
new_adder(2, 3, test=dict(returns=5))
Another such syntax is:
new_adder(2, 3, test:returns=5)
Each has its merits.
The first works with Python 2.4.
The second is, in my opinion, easier on the eye.
Anyway, that's my suggestion.
Jonathan
--
http://mail.python.org/mailman/listinfo/python-list