diff --git a/IPython/core/magics/execution.py b/IPython/core/magics/execution.py index 572b223..c6b314e 100644 --- a/IPython/core/magics/execution.py +++ b/IPython/core/magics/execution.py @@ -41,6 +41,7 @@ from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic, line_cell_magic, on_off, needs_local_scope) from IPython.testing.skipdoctest import skip_doctest from IPython.utils import py3compat +from IPython.utils.contexts import preserve_keys from IPython.utils.io import capture_output from IPython.utils.ipstruct import Struct from IPython.utils.module_paths import find_mod @@ -466,7 +467,9 @@ python-profiler package from non-free.""") return if filename.lower().endswith('.ipy'): - self.shell.safe_execfile_ipy(filename) + with preserve_keys(self.shell.user_ns, '__file__'): + self.shell.user_ns['__file__'] = filename + self.shell.safe_execfile_ipy(filename) return # Control the response to exit() calls made by the script being run @@ -624,7 +627,8 @@ python-profiler package from non-free.""") # worry about a possible KeyError. prog_ns.pop('__name__', None) - self.shell.user_ns.update(prog_ns) + with preserve_keys(self.shell.user_ns, '__file__'): + self.shell.user_ns.update(prog_ns) finally: # It's a bit of a mystery why, but __builtins__ can change from # being a module to becoming a dict missing some key data after diff --git a/IPython/core/shellapp.py b/IPython/core/shellapp.py index 5c57228..d451d7c 100644 --- a/IPython/core/shellapp.py +++ b/IPython/core/shellapp.py @@ -31,6 +31,7 @@ from IPython.config.configurable import Configurable from IPython.config.loader import Config from IPython.core import pylabtools from IPython.utils import py3compat +from IPython.utils.contexts import preserve_keys from IPython.utils.path import filefind from IPython.utils.traitlets import ( Unicode, Instance, List, Bool, CaselessStrEnum @@ -277,20 +278,18 @@ class InteractiveShellApp(Configurable): sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ] try: if os.path.isfile(full_filename): - if full_filename.endswith('.ipy'): - self.log.info("Running file in user namespace: %s" % - full_filename) - self.shell.safe_execfile_ipy(full_filename) - else: - # default to python, even without extension - self.log.info("Running file in user namespace: %s" % - full_filename) - # Ensure that __file__ is always defined to match Python behavior + self.log.info("Running file in user namespace: %s" % + full_filename) + # Ensure that __file__ is always defined to match Python + # behavior. + with preserve_keys(self.shell.user_ns, '__file__'): self.shell.user_ns['__file__'] = fname - try: - self.shell.safe_execfile(full_filename, self.shell.user_ns) - finally: - del self.shell.user_ns['__file__'] + if full_filename.endswith('.ipy'): + self.shell.safe_execfile_ipy(full_filename) + else: + # default to python, even without extension + self.shell.safe_execfile(full_filename, + self.shell.user_ns) finally: sys.argv = save_argv diff --git a/IPython/utils/contexts.py b/IPython/utils/contexts.py new file mode 100644 index 0000000..b51fbfc --- /dev/null +++ b/IPython/utils/contexts.py @@ -0,0 +1,71 @@ +# encoding: utf-8 +""" +Context managers for temporarily updating dictionaries. + +Authors: + +* Bradley Froehle +""" + +#----------------------------------------------------------------------------- +# Copyright (C) 2012 The IPython Development Team +# +# Distributed under the terms of the BSD License. The full license is in +# the file COPYING, distributed as part of this software. +#----------------------------------------------------------------------------- + +#----------------------------------------------------------------------------- +# Code +#----------------------------------------------------------------------------- + +class preserve_keys(object): + """Preserve a set of keys in a dictionary. + + Upon entering the context manager the current values of the keys + will be saved. Upon exiting, the dictionary will be updated to + restore the original value of the preserved keys. Preserved keys + which did not exist when entering the context manager will be + deleted. + + Example + ------- + + >>> d = {'a': 1, 'b': 2, 'c': 3} + >>> with preserve_keys(d, 'b', 'c', 'd'): + ... del d['a'] + ... del d['b'] # will be reset to 2 + ... d['c'] = None # will be reset to 3 + ... d['d'] = 4 # will be deleted + ... d['e'] = 5 + ... print(sorted(d.items())) + ... + [('c', None), ('d', 4), ('e', 5)] + >>> print(sorted(d.items())) + [('b', 2), ('c', 3), ('e', 5)] + """ + + def __init__(self, dictionary, *keys): + self.dictionary = dictionary + self.keys = keys + + def __enter__(self): + # Actions to perform upon exiting. + to_delete = [] + to_update = {} + + d = self.dictionary + for k in self.keys: + if k in d: + to_update[k] = d[k] + else: + to_delete.append(k) + + self.to_delete = to_delete + self.to_update = to_update + + def __exit__(self, *exc_info): + d = self.dictionary + + for k in self.to_delete: + d.pop(k, None) + d.update(self.to_update)