diff --git a/IPython/core/fakemodule.py b/IPython/core/fakemodule.py deleted file mode 100644 index 41029f5..0000000 --- a/IPython/core/fakemodule.py +++ /dev/null @@ -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. -# -# 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) diff --git a/IPython/core/interactiveshell.py b/IPython/core/interactiveshell.py index 9aea028..5a9cc38 100644 --- a/IPython/core/interactiveshell.py +++ b/IPython/core/interactiveshell.py @@ -47,7 +47,6 @@ from IPython.core.displayhook import DisplayHook 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 @@ class InteractiveShell(SingletonConfigurable): # 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 @@ class InteractiveShell(SingletonConfigurable): 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 @@ class InteractiveShell(SingletonConfigurable): 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 diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index e01c978..28a6c69 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -550,6 +550,11 @@ python-profiler package from non-free.""") __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 @@ python-profiler package from non-free.""") # 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 diff --git a/IPython/core/tests/test_fakemodule.py b/IPython/core/tests/test_fakemodule.py deleted file mode 100644 index d12bd5d..0000000 --- a/IPython/core/tests/test_fakemodule.py +++ /dev/null @@ -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) diff --git a/IPython/core/tests/test_imports.py b/IPython/core/tests/test_imports.py index 04d83f4..88caef0 100644 --- a/IPython/core/tests/test_imports.py +++ b/IPython/core/tests/test_imports.py @@ -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 diff --git a/IPython/core/tests/test_run.py b/IPython/core/tests/test_run.py index 9ec10f2..9183d96 100644 --- a/IPython/core/tests/test_run.py +++ b/IPython/core/tests/test_run.py @@ -15,6 +15,7 @@ from __future__ import absolute_import import functools import os +from os.path import join as pjoin import random import sys import tempfile @@ -359,6 +360,17 @@ tclass.py: deleting object: C-third 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 @@ class TestMagicRunWithPackage(unittest.TestCase): 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 @@ class TestMagicRunWithPackage(unittest.TestCase): @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') diff --git a/IPython/extensions/storemagic.py b/IPython/extensions/storemagic.py index 4323b66..b4c698f 100644 --- a/IPython/extensions/storemagic.py +++ b/IPython/extensions/storemagic.py @@ -27,7 +27,6 @@ import inspect, os, sys, textwrap # 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 @@ class StoreMagics(Magics, Configurable): 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 diff --git a/docs/autogen_api.py b/docs/autogen_api.py index 74f841d..b0d831e 100755 --- a/docs/autogen_api.py +++ b/docs/autogen_api.py @@ -35,7 +35,7 @@ if __name__ == '__main__': 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', diff --git a/docs/source/whatsnew/pr/incompat-drop-fakemodule.rst b/docs/source/whatsnew/pr/incompat-drop-fakemodule.rst new file mode 100644 index 0000000..65bfa11 --- /dev/null +++ b/docs/source/whatsnew/pr/incompat-drop-fakemodule.rst @@ -0,0 +1 @@ +* The module ``IPython.core.fakemodule`` has been removed.