Permalink
Browse files

Merge pull request #3622 from takluyver/drop-fakemodule

Drop fakemodule

After PR #3555, I don't think we have any need for our FakeModule class, so this removes it in favour of just using ModuleType.

There shouldn't be any user-visible benefit, so we could leave this until after release if we're going into stability mode. On the other hand, it changes an (undocumented) API, so we might prefer to do this before 1.0.
  • Loading branch information...
2 parents fbb0174 + 3af175d commit 2e41dabab9160627c04b7fcf10ce4232842654ff @minrk minrk committed Sep 18, 2013
View
@@ -1,66 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Class which mimics a module.
-
-Needed to allow pickle to correctly resolve namespaces during IPython
-sessions.
-"""
-
-#*****************************************************************************
-# Copyright (C) 2002-2004 Fernando Perez. <fperez@colorado.edu>
-#
-# Distributed under the terms of the BSD License. The full license is in
-# the file COPYING, distributed as part of this software.
-#*****************************************************************************
-
-import types
-
-def init_fakemod_dict(fm,adict=None):
- """Initialize a FakeModule instance __dict__.
-
- Kept as a standalone function and not a method so the FakeModule API can
- remain basically empty.
-
- This should be considered for private IPython use, used in managing
- namespaces for %run.
-
- Parameters
- ----------
-
- fm : FakeModule instance
-
- adict : dict, optional
- """
-
- dct = {}
- # It seems pydoc (and perhaps others) needs any module instance to
- # implement a __nonzero__ method, so we add it if missing:
- dct.setdefault('__nonzero__',lambda : True)
- dct.setdefault('__file__',__file__)
-
- if adict is not None:
- dct.update(adict)
-
- # Hard assignment of the object's __dict__. This is nasty but deliberate.
- fm.__dict__.clear()
- fm.__dict__.update(dct)
-
-
-class FakeModule(types.ModuleType):
- """Simple class with attribute access to fake a module.
-
- This is not meant to replace a module, but to allow inserting a fake
- module in sys.modules so that systems which rely on run-time module
- importing (like shelve and pickle) work correctly in interactive IPython
- sessions.
-
- Do NOT use this code for anything other than this IPython private hack."""
-
- def __init__(self,adict=None):
-
- # tmp to force __dict__ instance creation, else self.__dict__ fails
- self.__iptmp = None
- # cleanup our temp trick
- del self.__iptmp
- # Now, initialize the actual data in the instance dict.
- init_fakemod_dict(self,adict)
@@ -47,7 +47,6 @@
from IPython.core.displaypub import DisplayPublisher
from IPython.core.error import UsageError
from IPython.core.extensions import ExtensionManager
-from IPython.core.fakemodule import FakeModule, init_fakemod_dict
from IPython.core.formatters import DisplayFormatter
from IPython.core.history import HistoryManager
from IPython.core.inputsplitter import IPythonInputSplitter, ESC_MAGIC, ESC_MAGIC2
@@ -826,15 +825,18 @@ def register_post_execute(self, func):
# Things related to the "main" module
#-------------------------------------------------------------------------
- def new_main_mod(self, filename):
+ def new_main_mod(self, filename, modname):
"""Return a new 'main' module object for user code execution.
``filename`` should be the path of the script which will be run in the
module. Requests with the same filename will get the same module, with
its namespace cleared.
+ ``modname`` should be the module name - normally either '__main__' or
+ the basename of the file without the extension.
+
When scripts are executed via %run, we must keep a reference to their
- __main__ module (a FakeModule instance) around so that Python doesn't
+ __main__ module around so that Python doesn't
clear it, rendering references to module globals useless.
This method keeps said reference in a private dict, keyed by the
@@ -847,9 +849,16 @@ def new_main_mod(self, filename):
try:
main_mod = self._main_mod_cache[filename]
except KeyError:
- main_mod = self._main_mod_cache[filename] = FakeModule()
+ main_mod = self._main_mod_cache[filename] = types.ModuleType(modname,
+ doc="Module created for script run in IPython")
else:
- init_fakemod_dict(main_mod)
+ main_mod.__dict__.clear()
+ main_mod.__name__ = modname
+
+ main_mod.__file__ = filename
+ # It seems pydoc (and perhaps others) needs any module instance to
+ # implement a __nonzero__ method
+ main_mod.__nonzero__ = lambda : True
return main_mod
@@ -863,7 +872,7 @@ def clear_main_mod_cache(self):
In [15]: import IPython
- In [16]: m = _ip.new_main_mod(IPython.__file__)
+ In [16]: m = _ip.new_main_mod(IPython.__file__, 'IPython')
In [17]: len(_ip._main_mod_cache) > 0
Out[17]: True
@@ -550,6 +550,11 @@ def run(self, parameter_s='', runner=None,
__name__save = self.shell.user_ns['__name__']
prog_ns['__name__'] = '__main__'
main_mod = self.shell.user_module
+
+ # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
+ # set the __file__ global in the script's namespace
+ # TK: Is this necessary in interactive mode?
+ prog_ns['__file__'] = filename
else:
# Run in a fresh, empty namespace
if 'n' in opts:
@@ -560,13 +565,8 @@ def run(self, parameter_s='', runner=None,
# 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)
+ main_mod = self.shell.new_main_mod(filename, name)
prog_ns = main_mod.__dict__
- prog_ns['__name__'] = name
-
- # Since '%run foo' emulates 'python foo.py' at the cmd line, we must
- # set the __file__ global in the script's namespace
- prog_ns['__file__'] = filename
# pickle fix. See interactiveshell for an explanation. But we need to
# make sure that, if we overwrite __main__, we replace it at the end
@@ -1,17 +0,0 @@
-"""Tests for the FakeModule objects.
-"""
-
-import nose.tools as nt
-
-from IPython.core.fakemodule import FakeModule
-
-# Make a fakemod and check a few properties
-def test_mk_fakemod():
- fm = FakeModule()
- yield nt.assert_true,fm
- yield nt.assert_true,lambda : hasattr(fm,'__file__')
-
-def test_mk_fakemod_fromdict():
- """Test making a FakeModule object with initial data"""
- fm = FakeModule(dict(hello=True))
- nt.assert_true(fm.hello)
@@ -9,9 +9,6 @@ def test_import_crashhandler():
def test_import_debugger():
from IPython.core import debugger
-def test_import_fakemodule():
- from IPython.core import fakemodule
-
def test_import_excolors():
from IPython.core import excolors
@@ -15,6 +15,7 @@
import functools
import os
+from os.path import join as pjoin
import random
import sys
import tempfile
@@ -359,6 +360,17 @@ def test_run_formatting(self):
self.mktmp(src)
_ip.magic('run -t -N 1 %s' % self.fname)
_ip.magic('run -t -N 10 %s' % self.fname)
+
+ def test_ignore_sys_exit(self):
+ """Test the -e option to ignore sys.exit()"""
+ src = "import sys; sys.exit(1)"
+ self.mktmp(src)
+ with tt.AssertPrints('SystemExit'):
+ _ip.magic('run %s' % self.fname)
+
+ with tt.AssertNotPrints('SystemExit'):
+ _ip.magic('run -e %s' % self.fname)
+
class TestMagicRunWithPackage(unittest.TestCase):
@@ -398,6 +410,7 @@ def tearDown(self):
self.tempdir.cleanup()
def check_run_submodule(self, submodule, opts=''):
+ _ip.user_ns.pop('x', None)
_ip.magic('run {2} -m {0}.{1}'.format(self.package, submodule, opts))
self.assertEqual(_ip.user_ns['x'], self.value,
'Variable `x` is not loaded from module `{0}`.'
@@ -430,3 +443,16 @@ def test_debug_run_submodule_with_absolute_import(self):
@with_fake_debugger
def test_debug_run_submodule_with_relative_import(self):
self.check_run_submodule('relative', '-d')
+
+def test_run__name__():
+ with TemporaryDirectory() as td:
+ path = pjoin(td, 'foo.py')
+ with open(path, 'w') as f:
+ f.write("q = __name__")
+
+ _ip.user_ns.pop('q', None)
+ _ip.magic('run {}'.format(path))
+ nt.assert_equal(_ip.user_ns.pop('q'), '__main__')
+
+ _ip.magic('run -n {}'.format(path))
+ nt.assert_equal(_ip.user_ns.pop('q'), 'foo')
@@ -27,7 +27,6 @@
# Our own
from IPython.config.configurable import Configurable
from IPython.core.error import UsageError
-from IPython.core.fakemodule import FakeModule
from IPython.core.magic import Magics, magics_class, line_magic
from IPython.testing.skipdoctest import skip_doctest
from IPython.utils.traitlets import Bool
@@ -224,7 +223,8 @@ def store(self, parameter_s=''):
raise UsageError("Unknown variable '%s'" % args[0])
else:
- if isinstance(inspect.getmodule(obj), FakeModule):
+ modname = getattr(inspect.getmodule(obj), '__name__', '')
+ if modname == '__main__':
print textwrap.dedent("""\
Warning:%s is %s
Proper storage of interactively declared classes (or instances
View
@@ -35,7 +35,7 @@
r'\.zmq',
]
- docwriter.module_skip_patterns += [ r'\.core\.fakemodule',
+ docwriter.module_skip_patterns += [
r'\.testing\.iptest',
# Keeping these disabled is OK
r'\.parallel\.controller\.mongodb',
@@ -0,0 +1 @@
+* The module ``IPython.core.fakemodule`` has been removed.

0 comments on commit 2e41dab

Please sign in to comment.