Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Simplify caching of modules from %run-ing scripts.

Previously, we cleared and re-used a single FakeModule instance in which to
run cells, and cached copies of the modules' namespaces to prevent them from
being cleared. Now, we cache one FakeModule instance per script file, clearing
it and re-using it if the same script is re-run. This seems more robust.

Closes gh-3547
  • Loading branch information...
commit 279211b62dc7a4b01fc526cfe81a41a322ec6b16 1 parent 601235a
@takluyver authored
Showing with 23 additions and 53 deletions.
  1. +18 −47 IPython/core/interactiveshell.py
  2. +5 −6 IPython/core/magics/execution.py
View
65 IPython/core/interactiveshell.py
@@ -819,16 +819,9 @@ def register_post_execute(self, func):
# Things related to the "main" module
#-------------------------------------------------------------------------
- def new_main_mod(self,ns=None):
+ def new_main_mod(self,filename):
"""Return a new 'main' module object for user code execution.
- """
- main_mod = self._user_main_module
- init_fakemod_dict(main_mod,ns)
- return main_mod
-
- def cache_main_mod(self,ns,fname):
- """Cache a main module's namespace.
-
+
When scripts are executed via %run, we must keep a reference to the
namespace of their __main__ module (a FakeModule instance) around so
that Python doesn't clear it, rendering objects defined therein
@@ -840,32 +833,16 @@ def cache_main_mod(self,ns,fname):
keep one copy of the namespace (the last one), thus preventing memory
leaks from old references while allowing the objects from the last
execution to be accessible.
-
- Note: we can not allow the actual FakeModule instances to be deleted,
- because of how Python tears down modules (it hard-sets all their
- references to None without regard for reference counts). This method
- must therefore make a *copy* of the given namespace, to allow the
- original module's __dict__ to be cleared and reused.
-
-
- Parameters
- ----------
- ns : a namespace (a dict, typically)
-
- fname : str
- Filename associated with the namespace.
-
- Examples
- --------
-
- In [10]: import IPython
-
- In [11]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
-
- In [12]: IPython.__file__ in _ip._main_ns_cache
- Out[12]: True
"""
- self._main_ns_cache[os.path.abspath(fname)] = ns.copy()
+ filename = os.path.abspath(filename)
+ try:
+ main_mod = self._main_mod_cache[filename]
+ except KeyError:
+ main_mod = self._main_mod_cache[filename] = FakeModule()
+ else:
+ init_fakemod_dict(main_mod)
+
+ return main_mod
def clear_main_mod_cache(self):
"""Clear the cache of main modules.
@@ -877,17 +854,17 @@ def clear_main_mod_cache(self):
In [15]: import IPython
- In [16]: _ip.cache_main_mod(IPython.__dict__,IPython.__file__)
+ In [16]: m = _ip.new_main_mod(IPython.__file__)
- In [17]: len(_ip._main_ns_cache) > 0
+ In [17]: len(_ip._main_mod_cache) > 0
Out[17]: True
In [18]: _ip.clear_main_mod_cache()
- In [19]: len(_ip._main_ns_cache) == 0
+ In [19]: len(_ip._main_mod_cache) == 0
Out[19]: True
"""
- self._main_ns_cache.clear()
+ self._main_mod_cache.clear()
#-------------------------------------------------------------------------
# Things related to debugging
@@ -1017,10 +994,7 @@ def init_create_namespaces(self, user_module=None, user_ns=None):
# and clear_main_mod_cache() methods for details on use.
# This is the cache used for 'main' namespaces
- self._main_ns_cache = {}
- # And this is the single instance of FakeModule whose __dict__ we keep
- # copying and clearing for reuse on each %run
- self._user_main_module = FakeModule()
+ self._main_mod_cache = {}
# A table holding all the namespaces IPython deals with, so that
# introspection facilities can search easily.
@@ -1174,8 +1148,8 @@ def all_ns_refs(self):
Note that this does not include the displayhook, which also caches
objects from the output."""
- return [self.user_ns, self.user_global_ns,
- self._user_main_module.__dict__] + self._main_ns_cache.values()
+ return [self.user_ns, self.user_global_ns] + \
+ [m.__dict__ for m in self._main_mod_cache.values()]
def reset(self, new_session=True):
"""Clear all internal namespaces, and attempt to release references to
@@ -1219,9 +1193,6 @@ def reset(self, new_session=True):
# execution protection
self.clear_main_mod_cache()
- # Clear out the namespace from the last %run
- self.new_main_mod()
-
def del_var(self, varname, by_name=False):
"""Delete a variable from the various namespaces, so that, as
far as possible, we're not keeping any hidden references to it.
View
11 IPython/core/magics/execution.py
@@ -508,7 +508,7 @@ def run(self, parameter_s='', runner=None,
prog_ns = self.shell.user_ns
__name__save = self.shell.user_ns['__name__']
prog_ns['__name__'] = '__main__'
- main_mod = self.shell.new_main_mod(prog_ns)
+ main_mod = self.shell.user_module
else:
# Run in a fresh, empty namespace
if 'n' in opts:
@@ -516,7 +516,10 @@ def run(self, parameter_s='', runner=None,
else:
name = '__main__'
- main_mod = self.shell.new_main_mod()
+ # The shell MUST hold a reference to prog_ns so after %run
+ # exits, the python deletion mechanism doesn't zero it out
+ # (leaving dangling references). See interactiveshell for details
+ main_mod = self.shell.new_main_mod(filename)
prog_ns = main_mod.__dict__
prog_ns['__name__'] = name
@@ -593,10 +596,6 @@ def run():
if 'i' in opts:
self.shell.user_ns['__name__'] = __name__save
else:
- # The shell MUST hold a reference to prog_ns so after %run
- # exits, the python deletion mechanism doesn't zero it out
- # (leaving dangling references).
- self.shell.cache_main_mod(prog_ns, filename)
# update IPython interactive namespace
# Some forms of read errors on the file may mean the
Please sign in to comment.
Something went wrong with that request. Please try again.