[IPython-dev] ipython1 and synchronous printing of stdout
Thu Jul 24 00:22:58 CDT 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
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.
More information about the IPython-dev