What could 'f(this:that=other):' mean?

Jonathan Fine jfine at pytex.org
Fri Jan 7 16:19:20 EST 2005


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





More information about the Python-list mailing list