From 2e41dabab9160627c04b7fcf10ce4232842654ff 2013-09-18 16:32:23 From: Min RK Date: 2013-09-18 16:32:23 Subject: [PATCH] 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. --- 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.