Hi all,

this is an important discussion also for us (ipython), so I'll go in
some detail into things.  It would be great if out of this we got
something that both ipython and matplotlib could reuse for the long
haul, though I'm not sure (in a sense, ipython has some nastier
requirements that mpl may not need to worry about).

On 7/17/07, John Hunter <[EMAIL PROTECTED]> wrote:
> On 7/17/07, Darren Dale <[EMAIL PROTECTED]> wrote:
>
> > I'm really impressed with how readable and well organized the code is in
> > ipython1. It looks like their approach to configuration has been carefully
> > considered. Any chance we can follow their lead? It looks like it would be a
>
> I haven't looked at it closely, but I am willing to trust Fernando on
> this for the most part.  I know he has put a lot of thought into it,
> and it's something we've talked about for years.

Thanks for the compliments.  We have put much thought into it, and
Brian deserves all the implementation credit.  But as you'll see in a
moment, all is not quite well; hopefully after this discussion and
feedback from you guys we'll find a good solution so we can (finally!)
consider this problem fixed and move on to other things.

> I am not too fond of the dictionary usage here:
>
> controllerConfig.listenForEnginesOn['ip'] = '127.0.0.1'
> controllerConfig.listenForEnginesOn['port'] = 20000
>
> I prefer
>
> controllerConfig.listenForEnginesOn.ip = '127.0.0.1'
> controllerConfig.listenForEnginesOn.port = 20000
>
> Since dicts and attrs are intimately linked, eg with something like
> Bunch, this should be a minor tweak.  Fernando, why did you prefer
> dict semantics.  And are you happy with the state of your config
> system in ipython1

Let's not worry about this for now: I agree with you, but it's an
implementation detail that is trivial to fix with a light wrapper,
which already basically exists in IPython.ipstruct.  The real problems
lie elsewhere.

Brian and I spent a lot of time discussing this today, and here's the
summary of the whole thing.  Again, some of these issues may not
affect mpl nearly as seriously as they do ipython.

Having config files be pure python has some nice benefits:

- familiar syntax for users

- the ability for users to put in complex logic when declaring their
configuration (like make decisions based on OS, hostname, etc).

- If developers want, wrapping the underlying objects in something
like Traits provides automatically validation mechanisms for
configuration, which is important.

The security issue is for *me* moot: ipython is a full-access Python
system where people are *expected* to run code.  So I don't care about
letting them run real code at config time.  But for other projects,
the story is completely different, obviously.  I suspect MPL falls
into ipython's category here though.

So what is the problem?  The main one is that we have a real difficult
time with circular import dependencies and init-time logic.
Basically, you need these configuration objects to exist at startup
for other objects to be built, but the configuration *itself* may need
to import various things if it is to work as real Python code.  In
IPython (the dev branches), for example, the user-visible system is
assembled out of multiple objects, which themselves need to build
their own internal sub-objects.  Some of these simply require value
parameters at init time, but in some cases even which *class* to use
is tweakable at init time (for example, so users can choose amongst
various classes that implement different network transport protocols
but with identical API as far as IPython goes).

While in any one case one can probably disentangle the various
dependency chains that arise, it's a major pain to have to deal with
this constantly.  The fact that the problem is a recurring one is to
us an indicator of a design problem that we should address
differently.

There are other issues as well, besides circular imports:

A. Initialization time modification of live objects: while config
objects are obviously meant to be built *before* the 'real' objects
(who in term use the config ones to assemble themselves), there are
cases where one would like to deal with the 'real thing'.  For
example, in today's IPython (not the dev branch, but the one y'all
run) you can add things like magics at init time.  This is only
possible because when the rc file is parsed, the real object is
already up and alive as __IPYTHON__ (also visible via ipapi.get() ).

Obviously this is a hack, and a cleaner design would probably separate
the declaration of code objects (say magics) that are meant to be fed
into the live instance as part of the construction of the config
object, and then would have the real ipython load up its tweaks 100%
from this config object.  But since ipython is also meant to be
*runtime* modifiable, it feels natural to let users access such an API
for their own tweaks, yet there is a conflict with doing this purely
on a config object that is basically just a fancy 'data bag'.

B. Automatic creation of default config files.  With pure python, this
is just hard to do, since you'd be in the business of auto-generating
valid code from the structure of an object.  For a pure 'class
Bunch:pass' type thing this may not be *too* hard, as long as all
values allowed are such that eval(repr(foo))==foo, which isn't really
always the case.

C. Complexity: even for Brian, who wrote the config code, it's hard to
figure out where all the details go, since declaring new flags
typically ends up requiring fishing inside the config classes
themselves.  A more plain-text, purely declarative file would
summarize these things in a way that would probably be easier for both
developers and end users.


Given all this, we are currently looking at going back to a mixture of
text-based config and real code, as a two-stage process.  The
ConfigObj library:

http://www.voidspace.org.uk/python/configobj.html

looks like a nice, self-contained step above hand-parsing rc files or
Python's rather primitive ConfigParser module.  It has a lot of good
features, is mature and is small enough that one can ship it inside
the project (license is BSD).

Our current plan is something along these lines:

1. Expose for all configuration objects the part that is really just a
key=value API over ConfigObj.  This would dispense with all the
circular import problems, and let users specify the most common things
they want to tweak with a really simple syntax (that of .ini files).
We would then load these and build all necessary objects.

2. Make sure that the part of any given object that we mean to expose
for *runtime* modification (say adding magics to a running ipython,
for example) is easily available.  This would let users modify or
extend those aspects of the system that can't just be easily declared
as simple key=value settings.  The mechanism for this would simply be
to have a filename in each object's ConfigObj session like:

[SomeObject]

post_init_file = "my_post_init.py"

This file would be run via execfile() *once SomeObject* were
guaranteed to be up and running.  This would then allow users to have
code that manipulates SomeObject programatically, via its runtime API.


All this conversation brings us to...

3. Traits.  We (Brian and I) have gone back and forth a lot on Traits,
and we've come very close to just making them a dependency.  The only
real issue holding us back is that ipython so far has exactly *zero*
extension code, which is a plus in terms of ease of
installation/deployment.  Having said that, as far as extension code
is concerned, Traits is light and clean, and nowhere near the kinds of
complexities that MPL deals with.  But since anything is more than
zero, it is a bit of an issue for us.  We may tip over though, I'm
just stating what our reasoning so far has been.

In terms of Traits, point (2) above makes them even more attractive.
The delegation aspect of Traits is a very appealing way of combining
validation with additional action for the runtime modification of an
object.  For example:

ipython.color_scheme = "foo"

If color_scheme were a Trait, the above could simply:

a) validate that "foo" was acceptable as a value
b) trigger the chain of other trait updates (dependent color schemes
for exceptions, prompts, etc).

All of this can obviously be done with python properties, but before I
write Traits again, poorly (I'm no David Morrill) I'd rather ride
Enthought's back.

This approach via Traits (or something like them) also ensures that
the work done in ensuring the consitency/robustness of an object's
*runtime* configuration API becomes automatically useful to users, and
it simplifies the init-time config API, since it gives us the option
to defer more complicated tasks to runtime.


So this is a summary of where we stand.  It's surprising that the
'simple question' of configuring an application can turn out to be
such a can of worms, but for us it has proven to be.  Input/ideas from
you all would be very welcome, as it's quite likely we've missed
possible solutions.

I'd love to get out of this discussion some ideas, clarity and
ultimately a plan for how to proceed in the long haul.  If in addition
this means that ipython, mpl and others end up uniformizing further
our approach to this problem, even better!  Having our end users find
familiar configuration mechanisms across their various libraries would
only be a good thing in the long run.

Cheers,

Brian and f.


ps - I debated on having this discussion on ipython-dev, but for now
I'm going to not cross-post.  The MPL team is attentive to the issue
now, so I'd rather collect your wisdom here, and I'll take it upon
myself to summarize our conclusions on ipython-dev later.  I just want
to avoid list cross-posting.

-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
Matplotlib-devel mailing list
Matplotlib-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/matplotlib-devel

Reply via email to