##// END OF EJS Templates
Better handling of `__file__` when running scripts....
Bradley M. Froehle -
Show More
@@ -0,0 +1,71 b''
1 # encoding: utf-8
2 """
3 Context managers for temporarily updating dictionaries.
4
5 Authors:
6
7 * Bradley Froehle
8 """
9
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2012 The IPython Development Team
12 #
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
16
17 #-----------------------------------------------------------------------------
18 # Code
19 #-----------------------------------------------------------------------------
20
21 class preserve_keys(object):
22 """Preserve a set of keys in a dictionary.
23
24 Upon entering the context manager the current values of the keys
25 will be saved. Upon exiting, the dictionary will be updated to
26 restore the original value of the preserved keys. Preserved keys
27 which did not exist when entering the context manager will be
28 deleted.
29
30 Example
31 -------
32
33 >>> d = {'a': 1, 'b': 2, 'c': 3}
34 >>> with preserve_keys(d, 'b', 'c', 'd'):
35 ... del d['a']
36 ... del d['b'] # will be reset to 2
37 ... d['c'] = None # will be reset to 3
38 ... d['d'] = 4 # will be deleted
39 ... d['e'] = 5
40 ... print(sorted(d.items()))
41 ...
42 [('c', None), ('d', 4), ('e', 5)]
43 >>> print(sorted(d.items()))
44 [('b', 2), ('c', 3), ('e', 5)]
45 """
46
47 def __init__(self, dictionary, *keys):
48 self.dictionary = dictionary
49 self.keys = keys
50
51 def __enter__(self):
52 # Actions to perform upon exiting.
53 to_delete = []
54 to_update = {}
55
56 d = self.dictionary
57 for k in self.keys:
58 if k in d:
59 to_update[k] = d[k]
60 else:
61 to_delete.append(k)
62
63 self.to_delete = to_delete
64 self.to_update = to_update
65
66 def __exit__(self, *exc_info):
67 d = self.dictionary
68
69 for k in self.to_delete:
70 d.pop(k, None)
71 d.update(self.to_update)
@@ -41,6 +41,7 b' from IPython.core.magic import (Magics, magics_class, line_magic, cell_magic,'
41 line_cell_magic, on_off, needs_local_scope)
41 line_cell_magic, on_off, needs_local_scope)
42 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
43 from IPython.utils import py3compat
43 from IPython.utils import py3compat
44 from IPython.utils.contexts import preserve_keys
44 from IPython.utils.io import capture_output
45 from IPython.utils.io import capture_output
45 from IPython.utils.ipstruct import Struct
46 from IPython.utils.ipstruct import Struct
46 from IPython.utils.module_paths import find_mod
47 from IPython.utils.module_paths import find_mod
@@ -466,7 +467,9 b' python-profiler package from non-free.""")'
466 return
467 return
467
468
468 if filename.lower().endswith('.ipy'):
469 if filename.lower().endswith('.ipy'):
469 self.shell.safe_execfile_ipy(filename)
470 with preserve_keys(self.shell.user_ns, '__file__'):
471 self.shell.user_ns['__file__'] = filename
472 self.shell.safe_execfile_ipy(filename)
470 return
473 return
471
474
472 # Control the response to exit() calls made by the script being run
475 # Control the response to exit() calls made by the script being run
@@ -624,7 +627,8 b' python-profiler package from non-free.""")'
624 # worry about a possible KeyError.
627 # worry about a possible KeyError.
625 prog_ns.pop('__name__', None)
628 prog_ns.pop('__name__', None)
626
629
627 self.shell.user_ns.update(prog_ns)
630 with preserve_keys(self.shell.user_ns, '__file__'):
631 self.shell.user_ns.update(prog_ns)
628 finally:
632 finally:
629 # It's a bit of a mystery why, but __builtins__ can change from
633 # It's a bit of a mystery why, but __builtins__ can change from
630 # being a module to becoming a dict missing some key data after
634 # being a module to becoming a dict missing some key data after
@@ -31,6 +31,7 b' from IPython.config.configurable import Configurable'
31 from IPython.config.loader import Config
31 from IPython.config.loader import Config
32 from IPython.core import pylabtools
32 from IPython.core import pylabtools
33 from IPython.utils import py3compat
33 from IPython.utils import py3compat
34 from IPython.utils.contexts import preserve_keys
34 from IPython.utils.path import filefind
35 from IPython.utils.path import filefind
35 from IPython.utils.traitlets import (
36 from IPython.utils.traitlets import (
36 Unicode, Instance, List, Bool, CaselessStrEnum
37 Unicode, Instance, List, Bool, CaselessStrEnum
@@ -277,20 +278,18 b' class InteractiveShellApp(Configurable):'
277 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
278 sys.argv = [ py3compat.cast_bytes(a) for a in sys.argv ]
278 try:
279 try:
279 if os.path.isfile(full_filename):
280 if os.path.isfile(full_filename):
280 if full_filename.endswith('.ipy'):
281 self.log.info("Running file in user namespace: %s" %
281 self.log.info("Running file in user namespace: %s" %
282 full_filename)
282 full_filename)
283 # Ensure that __file__ is always defined to match Python
283 self.shell.safe_execfile_ipy(full_filename)
284 # behavior.
284 else:
285 with preserve_keys(self.shell.user_ns, '__file__'):
285 # default to python, even without extension
286 self.log.info("Running file in user namespace: %s" %
287 full_filename)
288 # Ensure that __file__ is always defined to match Python behavior
289 self.shell.user_ns['__file__'] = fname
286 self.shell.user_ns['__file__'] = fname
290 try:
287 if full_filename.endswith('.ipy'):
291 self.shell.safe_execfile(full_filename, self.shell.user_ns)
288 self.shell.safe_execfile_ipy(full_filename)
292 finally:
289 else:
293 del self.shell.user_ns['__file__']
290 # default to python, even without extension
291 self.shell.safe_execfile(full_filename,
292 self.shell.user_ns)
294 finally:
293 finally:
295 sys.argv = save_argv
294 sys.argv = save_argv
296
295
General Comments 0
You need to be logged in to leave comments. Login now