[IPython-dev] IPython1 API design question

Brian Granger ellisonbg.net at gmail.com
Fri Feb 8 11:16:25 EST 2008


I have been working on IPython1 a lot this week and have run into an
API design dilemma.  Because the IPython1 API will be the core aPI
that all of us develop to in the near future, I wanted to get some
opinions about the issue.  Here we go...

The IPython1 API has methods:

push(self, **namespace)            # push namespace of key, values
pairs into the users namespace
pull(self, *keys)                         # get python objects from
the users namespace by keys

These are used in the following way:

push(a=10, b=30)
pull('a', 'b')

The key point is that the usage of *keys and **namespace makes the
methods very easy to use - you can basically get around creating
tuples and dicts by hand.

But, here is the problem we are running into.  In various parts of the
API, we need to add additional arguments to these methods.  The two
that have come up are:

block -> should the call block or not?
targets -> when you are working with multiple ipython core instances,
which should you push/pull to.

Our current API handles these in the following way:

push(self, block, targets, **ns)
pull(self, block, targets, *keys)

And would be used in this way:

push(True, [0,1,2,3], a=10, b=30)
pull(False, 'all', 'b', 'c')

Here are the problems I see with this:

1.  We can't introduce new keyword arguments that have defaults
2.  All new arguments have to be positional and thus completely change
the API in a backwards incompatible manner.
3.  The design violates my aesthetic sense which says the most
important arguments (keys, ns) should come first, less important ones
4.  We have to have additional API methods to handle default cases,
thus we have pushAll/pullAll where targets is set to 'all'

The alternate API would look some thing like this:

push(self, ns, block=True, targets='all')
pull(self, keys, block=True, targets='all')

And be used:

push(dict(a=10,b=30), block=False)      # targets = 'all'
pull(('a','b'))     # block=True, tagets='all'

the main disadvantage is that a user has to manually build the dict
and tuple.  For pull, we could do a test to see if keys is a string
and build the tuple for then, but for the dict, they really have to
build it using dict or {}

So, which of these approaches seems better, both from the development
and user's perspective?  Thoughts?


More information about the IPython-dev mailing list