[IPython-dev] ipython1 and synchronous printing of stdout

Brian Granger ellisonbg.net at gmail.com
Thu Jul 24 01:22:58 EDT 2008


> I think there's a fourth, that Gael mentioned in passing and which
> we've been discussing here face to face for a while: using objects
> whose basic behavior defines the core API, but which can be given at
> construction time instead of being hardcoded in our various __init__
> methods.  This allows subclasses (or even non-subclassing application
> writers who use the code as a library to build their own tools) to
> cleanly provide only the level of behavior modification they need.

Yes, this is a design pattern that I very much like and that is well
suited for certain things in ipython.  We actually use this in a few
places in the ipython1 code:

1.  An IPython engine takes an interpreter class as a parameter in its
init method.  Thus a user can pass the engine a custom interpreter
subclass to get custom behavior.

2.  We don't do it yet, but the interpreter could take a Traited dict
like object to use as the users namespace.

We definitely should do more of this.  But, I think this design
pattern addresses a slightly different need than the
callback/observer/notification stuff.  Here is why:

The real benefit of the observer/notification model is that everything
can be completely transient.  For example, you might have a notifier
that observes the user's namespace.  But you might only want it to
operate when the user opens a specific window.  Another example is
when an observer is hooked up to an unreliable network connection.  So
I guess I would ad another design constraint that I have until now
kept in the back of my mind:  we need loose coupling that is also
dynamic and transient.  And for these situations, I still think the
observer pattern is the best solution.

> This seems to provide a simple solution to our design question: all we
> need to do is to  clarify which objects are our core publicly
> modifiable components in the API, and then users can tweak only the
> parts they need.  Said objects can themselves provide
> observer/delegation behavior if they so desire, but they can do it in
> the most appropriate way for a given problem.  For example, someone
> writing a Traits app (who's already committed to using Traits for
> their own reasons) can simply stick in there traited versions of the
> same things, and with essentially zero extra code they get the Traits
> event model everyhwere.

I worry that such interfaces _can_ at times be a bit too implicit and
thus hide subtle behaviors and introduce code coupling that isn't
obvious.  One example of a subtle behavior is the following.  Traits
is based on a model that says this "an interface consists of a set of
attributes that you simply get and set as atributes"  But, the second
you go to propagate an interface over a network connection, you
discover that it is really difficult.  Interfaces that "network
friendly" tend to be i) be methods/functions that ii) have arguments
with very basic types.

We do have some examples in IPython.kernel where we propagate
interfaces that are attribute based, but a good amount of extra work
and care is required.

Thus, while I think the idea of passing a Traited dict to the
interpreter to observe the user's namespace is a beautiful idea, the
reality is much more painful:  there is no straightforward way of
propagating that interface over a network connection.

With a standardize observer/notifier pattern that is based on methods,
it is very straightforward to:

* Propagate observation/notification events over a network
* Unregister such events when network connections fail
* Integrate such events with Twisted.
* Integrate with other event loops like wx, qt, cocoa

One thing that I hadn't realized is that the observer/notifier pattern
has come up for Min and I in IPython.kernel a number of times, but we
have never taken the time to really abstract it properly.

.....a few minutes later...

I just reread this email and I think I am talking in circles and a bit
tired.  But, I do think we need to have a solution that is "network
friendly".

Cheers,

Brian

PS - starting tomorrow, I will be offline for about a week.


> In a Cocoa environment, these objects could be lightly wrapped
> versions of the originals that register (via a modified __getattr__
> for example) the necessary observation/update calls into the
> NSDistributedNotificationCenter that Cocoa provides, if that's the
> best way to do it in such an environment.
>
> And someone who just needs a callback here and there can simply stick
> their callback-enhanced object where they want.  I could even see this
> being very handy for occasional debugging, by quickly activating
> tracing versions of certain objects as needed.
>
> One thing that I like about this approach is that it doesn't
> necessarily close the door on a *future* implementation of an
> ipython-wide notification center, if we end up finding in practice
> that it's really needed as the code grows.  But right now, it seems to
> me that this simpler approach solves all of our problems, with the
> advantage of introducing exactly *zero* (not small, but truly zero)
> overhead for the normal use that has no customizations.
>
> Does this sound reasonable to others, or did I totally miss this
> particular boat?  I really want us all to be happy with the solution
> we find to this so that we move forward reasonably convinced of it
> being viable.
>
> Cheers,
>
> f
>



More information about the IPython-dev mailing list