From b05386d19621369a50c780ebe47139833b58c06f 2008-07-31 01:37:36 From: Robert Kern Date: 2008-07-31 01:37:36 Subject: [PATCH] ENH: Allow non-dict namespaces. This involves a change in the ipapi for setting user namespaces. --- diff --git a/IPython/ipapi.py b/IPython/ipapi.py index de23da0..752720d 100644 --- a/IPython/ipapi.py +++ b/IPython/ipapi.py @@ -566,17 +566,7 @@ def make_user_ns(user_ns = None): classes in ipython. """ - if user_ns is None: - # Set __name__ to __main__ to better match the behavior of the - # normal interpreter. - user_ns = {'__name__' :'__main__', - '__builtins__' : __builtin__, - } - else: - user_ns.setdefault('__name__','__main__') - user_ns.setdefault('__builtins__',__builtin__) - - return user_ns + raise NotImplementedError def make_user_global_ns(ns = None): @@ -586,8 +576,56 @@ def make_user_global_ns(ns = None): embedded applications, where there is a distinction between the user's interactive namespace and the global one where ipython is running.""" - if ns is None: ns = {} - return ns + raise NotImplementedError + +# Record the true objects in order to be able to test if the user has overridden +# these API functions. +_make_user_ns = make_user_ns +_make_user_global_ns = make_user_global_ns + + +def make_user_namespaces(user_ns = None,user_global_ns = None): + """Return a valid local and global user interactive namespaces. + + This builds a dict with the minimal information needed to operate as a + valid IPython user namespace, which you can pass to the various embedding + classes in ipython. The default implementation returns the same dict for + both the locals and the globals to allow functions to refer to variables in + the namespace. Customized implementations can return different dicts. The + locals dictionary can actually be anything following the basic mapping + protocol of a dict, but the globals dict must be a true dict, not even + a subclass. It is recommended that any custom object for the locals + namespace synchronize with the globals dict somehow. + + Raises TypeError if the provided globals namespace is not a true dict. + """ + + if user_ns is None: + if make_user_ns is not _make_user_ns: + # Old API overridden. + # FIXME: Issue DeprecationWarning, or just let the old API live on? + user_ns = make_user_ns(user_ns) + else: + # Set __name__ to __main__ to better match the behavior of the + # normal interpreter. + user_ns = {'__name__' :'__main__', + '__builtins__' : __builtin__, + } + else: + user_ns.setdefault('__name__','__main__') + user_ns.setdefault('__builtins__',__builtin__) + + if user_global_ns is None: + if make_user_global_ns is not _make_user_global_ns: + # Old API overridden. + user_global_ns = make_user_global_ns(user_global_ns) + else: + user_global_ns = user_ns + if type(user_global_ns) is not dict: + raise TypeError("user_global_ns must be a true dict; got %r" + % type(user_global_ns)) + + return user_ns, user_global_ns def make_session(user_ns = None, shellclass = None): diff --git a/IPython/iplib.py b/IPython/iplib.py index 4355beb..d7c8440 100644 --- a/IPython/iplib.py +++ b/IPython/iplib.py @@ -207,7 +207,7 @@ class InteractiveShell(object,Magic): isthreaded = False def __init__(self,name,usage=None,rc=Struct(opts=None,args=None), - user_ns = None,user_global_ns=None,banner2='', + user_ns=None,user_global_ns=None,banner2='', custom_exceptions=((),None),embedded=False): # log system @@ -215,10 +215,11 @@ class InteractiveShell(object,Magic): # some minimal strict typechecks. For some core data structures, I # want actual basic python types, not just anything that looks like - # one. This is especially true for namespaces. - for ns in (user_ns,user_global_ns): - if ns is not None and type(ns) != types.DictType: - raise TypeError,'namespace must be a dictionary' + # one. This is especially true for the global namespace. + if user_global_ns is not None and type(user_global_ns) is not dict: + raise TypeError('global namespace must be a true dict; got %r' + % type(user_global_ns)) + # Job manager (for jobs run as background threads) self.jobs = BackgroundJobManager() @@ -260,7 +261,8 @@ class InteractiveShell(object,Magic): # the locals argument. But we do carry a user_global_ns namespace # given as the exec 'globals' argument, This is useful in embedding # situations where the ipython shell opens in a context where the - # distinction between locals and globals is meaningful. + # distinction between locals and globals is meaningful. For + # non-embedded contexts, it is just the same object as the user_ns dict. # FIXME. For some strange reason, __builtins__ is showing up at user # level as a dict instead of a module. This is a manual fix, but I @@ -290,14 +292,12 @@ class InteractiveShell(object,Magic): # These routines return properly built dicts as needed by the rest of # the code, and can also be used by extension writers to generate # properly initialized namespaces. - user_ns = IPython.ipapi.make_user_ns(user_ns) - user_global_ns = IPython.ipapi.make_user_global_ns(user_global_ns) - + user_ns, user_global_ns = IPython.ipapi.make_user_namespaces(user_ns, + user_global_ns) + # Assign namespaces # This is the namespace where all normal user variables live self.user_ns = user_ns - # Embedded instances require a separate namespace for globals. - # Normally this one is unused by non-embedded instances. self.user_global_ns = user_global_ns # A namespace to keep track of internal data structures to prevent # them from cluttering user-visible stuff. Will be updated later @@ -2066,16 +2066,7 @@ want to merge them back into the new files.""" % locals() try: try: self.hooks.pre_runcode_hook() - # Embedded instances require separate global/local namespaces - # so they can see both the surrounding (local) namespace and - # the module-level globals when called inside another function. - if self.embedded: - exec code_obj in self.user_global_ns, self.user_ns - # Normal (non-embedded) instances should only have a single - # namespace for user code execution, otherwise functions won't - # see interactive top-level globals. - else: - exec code_obj in self.user_ns + exec code_obj in self.user_global_ns, self.user_ns finally: # Reset our crash handler in place sys.excepthook = old_excepthook