Hi,<br><br>I have been banging my head on the IPython refactoring this week.  Ouch.  I am specifically thinking about the config system, IP.rc and ipmaker stuff.<br><br>Yesterday, Fernando and I talked about some of my ideas and we tentatively settled on some abstractions that I think will greatly help in the refactoring.  I wanted to sketch these out to get feedback from folks.<br>
<br>Here are the main abstractions:<br><br>1.  Config objects.  Bags of configuration variables that can be read from file or the command line.<br>2.  Component.  An object that be configured and has a runtime api.  Just about everything in IPython will be a component.<br>
1.  App - a top-level IPython program like ipython, ipythonx, ipcluster, ipengine, ipcontroller.<br><br>The job of an App will be to assemble and merge all relevant config objects and then instantiate and configure components.  How do components get created and configured?<br>
<br>c = MyComponent()  # this build it with class defaults<br>c.configure(config) # The App or parent component passes the config to the object to the component to override the defaults<br>c.freeze_config() # This tells the component that config is done and that it should complete any remaining init related things.<br>
<br>From then on, the component ignore the config info and instead relies on its runtime data.  Each component will have a Struct-like object that contains the runtime data named rc = &#39;runtime configuration&#39;.  Notice, this is extremely similar to the existing design on IP.  BUT, it is very different because each component will hold its own runtime attributes (IP won&#39;t hold many at all).  The golden rule of the runtime attributes is this: if you can change them, IPython&#39;s behavior *must* change appropriately.<br>
<br>Components will have some sort of registry and querying system that will allow different parts of IPython to get the runtime api for other components.  This will also be completely independent of IP itself.  It will look something like this:<br>
<br>rc = Component.find_component_runtime(&#39;prompt1&#39;, Prompt) # get the runtime api for component named &#39;prompt1&#39; that is an instance of Prompt<br><br>Once you have one of these runtime objects you can do a number of things:<br>
<br>* read its attributes (rc.prompt_string)<br>* write its attributes<br>* setup notifications rc.add_notification(&#39;prompt_string&#39;, callable) # callable is called if prompt_string changes.<br><br>If you are thinking these runtime objects look a lot like enthought.traits, you are right.  While we can&#39;t use traits, the design of these objects will be very much traits inspired.<br>
<br>It will be up to each component to pass the config object it gets onto its children (by calling their configure methods).  Thus the config objects will propagate down the component graph as it is created.<br><br>There are still many things to work out.  After sleeping on it, I have some questions...<br>
<br>In many cases, components will have not only attributes that can be set/get by others, but also methods.  Where to we put these methods?  It makes the most sense to put them on the Components themselves.  This makes me think that the component/runtime-api relationship should be a &quot;is-a&quot; not a &quot;has-a&quot;.  The &quot;is-a&quot; approach is more of what Traits uses.  Initially, our instinct was to use &quot;has-a&quot;, to keep the component namespace clean.  But, now I am almost thinking that when other objects get a &quot;runtime api&quot; for a component, what they want is the component itself.<br>
<br>Here is what the &quot;is-a&quot; looks like:<br><br>c = Component.find_component(&#39;shell&#39;, InteractiveShell)<br>c.autocall = False<br>c.runlines(...)<br><br>And here is the &quot;has-a&quot;:<br><br>c = Component.find_component(&#39;shell&#39;, InteractiveShell)<br>

c.rc.autocall = False<br>
c.runlines(...)<br>
<br>Or we could put the runtime api entirely on the rc attribute:<br><br>rc = Component.find_runtime_api(&#39;shell&#39;,InteractiveShell)<br>rc.autocall = False<br>c.runlines(...)<br><br>Here is a short argument against having a separate runtime (I am arguing for the &quot;is-a&quot; approach).  This (the &#39;has-a&#39;) is what we use with InteractiveShell and ipapi.  ipapi is a thin wrapper around InteractiveShell.  The problem with this is that you can end up going in circles.  When do you call ipapi and when do you just call InteractiveShell methods?  It gets darned confusing.  This confusion shows up in silly things like methods of InteractiveShell using ipapi rather than just calling its own methods!  I think this is an abuse of OO design.  Objects themselves have public APIs.  You don&#39;t need the extra indirection of a public API that wraps that public API.  I think the original reason we came up with ipapi is that InteractiveShell didn&#39;t have a clean public api.  But, once that is fixed, I think ipapi should go away.<br>
<br>Thoughts?  Feeback?<br><br>Cheers,<br><br>Brian<br><br><br><br><br>