Show More
@@ -0,0 +1,34 b'' | |||||
|
1 | """Test code for https://bugs.launchpad.net/ipython/+bug/239054 | |||
|
2 | ||||
|
3 | WARNING: this script exits IPython! It MUST be run in a subprocess. | |||
|
4 | ||||
|
5 | When you run the following script from CPython it prints: | |||
|
6 | __init__ is here | |||
|
7 | __del__ is here | |||
|
8 | ||||
|
9 | and creates the __del__.txt file | |||
|
10 | ||||
|
11 | When you run it from IPython it prints: | |||
|
12 | __init__ is here | |||
|
13 | ||||
|
14 | When you exit() or Exit from IPython neothing is printed and no file is created | |||
|
15 | (the file thing is to make sure __del__ is really never called and not that | |||
|
16 | just the output is eaten). | |||
|
17 | ||||
|
18 | Note that if you call %reset in IPython then everything is Ok. | |||
|
19 | ||||
|
20 | IPython should do the equivalent of %reset and release all the references it | |||
|
21 | holds before exit. This behavior is important when working with binding objects | |||
|
22 | that rely on __del__. If the current behavior has some use case then I suggest | |||
|
23 | to add a configuration option to IPython to control it. | |||
|
24 | """ | |||
|
25 | import sys | |||
|
26 | ||||
|
27 | class A(object): | |||
|
28 | def __del__(self): | |||
|
29 | print 'object A deleted' | |||
|
30 | ||||
|
31 | a = A() | |||
|
32 | ||||
|
33 | # Now, we force an exit, the caller will check that the del printout was given | |||
|
34 | _ip.IP.ask_exit() |
@@ -0,0 +1,16 b'' | |||||
|
1 | """Tests for the key iplib module, where the main ipython class is defined. | |||
|
2 | """ | |||
|
3 | ||||
|
4 | import nose.tools as nt | |||
|
5 | ||||
|
6 | def test_reset(): | |||
|
7 | """reset must clear most namespaces.""" | |||
|
8 | ip = _ip.IP | |||
|
9 | ip.reset() # first, it should run without error | |||
|
10 | # Then, check that most namespaces end up empty | |||
|
11 | for ns in ip.ns_refs_table: | |||
|
12 | if ns is ip.user_ns: | |||
|
13 | # The user namespace is reset with some data, so we can't check for | |||
|
14 | # it being empty | |||
|
15 | continue | |||
|
16 | nt.assert_equals(len(ns),0) |
@@ -186,7 +186,6 b' class IPApi(object):' | |||||
186 | self.set_custom_exc = ip.set_custom_exc |
|
186 | self.set_custom_exc = ip.set_custom_exc | |
187 |
|
187 | |||
188 | self.user_ns = ip.user_ns |
|
188 | self.user_ns = ip.user_ns | |
189 | self.user_ns['_ip'] = self |
|
|||
190 |
|
189 | |||
191 | self.set_crash_handler = ip.set_crash_handler |
|
190 | self.set_crash_handler = ip.set_crash_handler | |
192 |
|
191 |
@@ -285,6 +285,13 b' class InteractiveShell(object,Magic):' | |||||
285 | # This is the namespace where all normal user variables live |
|
285 | # This is the namespace where all normal user variables live | |
286 | self.user_ns = user_ns |
|
286 | self.user_ns = user_ns | |
287 | self.user_global_ns = user_global_ns |
|
287 | self.user_global_ns = user_global_ns | |
|
288 | ||||
|
289 | # An auxiliary namespace that checks what parts of the user_ns were | |||
|
290 | # loaded at startup, so we can list later only variables defined in | |||
|
291 | # actual interactive use. Since it is always a subset of user_ns, it | |||
|
292 | # doesn't need to be seaparately tracked in the ns_table | |||
|
293 | self.user_config_ns = {} | |||
|
294 | ||||
288 | # A namespace to keep track of internal data structures to prevent |
|
295 | # A namespace to keep track of internal data structures to prevent | |
289 | # them from cluttering user-visible stuff. Will be updated later |
|
296 | # them from cluttering user-visible stuff. Will be updated later | |
290 | self.internal_ns = {} |
|
297 | self.internal_ns = {} | |
@@ -294,6 +301,24 b' class InteractiveShell(object,Magic):' | |||||
294 | # of positional arguments of the alias. |
|
301 | # of positional arguments of the alias. | |
295 | self.alias_table = {} |
|
302 | self.alias_table = {} | |
296 |
|
303 | |||
|
304 | # Now that FakeModule produces a real module, we've run into a nasty | |||
|
305 | # problem: after script execution (via %run), the module where the user | |||
|
306 | # code ran is deleted. Now that this object is a true module (needed | |||
|
307 | # so docetst and other tools work correctly), the Python module | |||
|
308 | # teardown mechanism runs over it, and sets to None every variable | |||
|
309 | # present in that module. Top-level references to objects from the | |||
|
310 | # script survive, because the user_ns is updated with them. However, | |||
|
311 | # calling functions defined in the script that use other things from | |||
|
312 | # the script will fail, because the function's closure had references | |||
|
313 | # to the original objects, which are now all None. So we must protect | |||
|
314 | # these modules from deletion by keeping a cache. To avoid keeping | |||
|
315 | # stale modules around (we only need the one from the last run), we use | |||
|
316 | # a dict keyed with the full path to the script, so only the last | |||
|
317 | # version of the module is held in the cache. The %reset command will | |||
|
318 | # flush this cache. See the cache_main_mod() and clear_main_mod_cache() | |||
|
319 | # methods for details on use. | |||
|
320 | self._user_main_modules = {} | |||
|
321 | ||||
297 | # A table holding all the namespaces IPython deals with, so that |
|
322 | # A table holding all the namespaces IPython deals with, so that | |
298 | # introspection facilities can search easily. |
|
323 | # introspection facilities can search easily. | |
299 | self.ns_table = {'user':user_ns, |
|
324 | self.ns_table = {'user':user_ns, | |
@@ -302,9 +327,14 b' class InteractiveShell(object,Magic):' | |||||
302 | 'internal':self.internal_ns, |
|
327 | 'internal':self.internal_ns, | |
303 | 'builtin':__builtin__.__dict__ |
|
328 | 'builtin':__builtin__.__dict__ | |
304 | } |
|
329 | } | |
305 | # The user namespace MUST have a pointer to the shell itself. |
|
|||
306 | self.user_ns[name] = self |
|
|||
307 |
|
330 | |||
|
331 | # Similarly, track all namespaces where references can be held and that | |||
|
332 | # we can safely clear (so it can NOT include builtin). This one can be | |||
|
333 | # a simple list. | |||
|
334 | self.ns_refs_table = [ user_ns, user_global_ns, self.user_config_ns, | |||
|
335 | self.alias_table, self.internal_ns, | |||
|
336 | self._user_main_modules ] | |||
|
337 | ||||
308 | # We need to insert into sys.modules something that looks like a |
|
338 | # We need to insert into sys.modules something that looks like a | |
309 | # module but which accesses the IPython namespace, for shelve and |
|
339 | # module but which accesses the IPython namespace, for shelve and | |
310 | # pickle to work interactively. Normally they rely on getting |
|
340 | # pickle to work interactively. Normally they rely on getting | |
@@ -329,32 +359,13 b' class InteractiveShell(object,Magic):' | |||||
329 | #print "pickle hack in place" # dbg |
|
359 | #print "pickle hack in place" # dbg | |
330 | #print 'main_name:',main_name # dbg |
|
360 | #print 'main_name:',main_name # dbg | |
331 | sys.modules[main_name] = FakeModule(self.user_ns) |
|
361 | sys.modules[main_name] = FakeModule(self.user_ns) | |
332 |
|
||||
333 | # Now that FakeModule produces a real module, we've run into a nasty |
|
|||
334 | # problem: after script execution (via %run), the module where the user |
|
|||
335 | # code ran is deleted. Now that this object is a true module (needed |
|
|||
336 | # so docetst and other tools work correctly), the Python module |
|
|||
337 | # teardown mechanism runs over it, and sets to None every variable |
|
|||
338 | # present in that module. Top-level references to objects from the |
|
|||
339 | # script survive, because the user_ns is updated with them. However, |
|
|||
340 | # calling functions defined in the script that use other things from |
|
|||
341 | # the script will fail, because the function's closure had references |
|
|||
342 | # to the original objects, which are now all None. So we must protect |
|
|||
343 | # these modules from deletion by keeping a cache. To avoid keeping |
|
|||
344 | # stale modules around (we only need the one from the last run), we use |
|
|||
345 | # a dict keyed with the full path to the script, so only the last |
|
|||
346 | # version of the module is held in the cache. The %reset command will |
|
|||
347 | # flush this cache. See the cache_main_mod() and clear_main_mod_cache() |
|
|||
348 | # methods for details on use. |
|
|||
349 | self._user_main_modules = {} |
|
|||
350 |
|
362 | |||
351 | # List of input with multi-line handling. |
|
363 | # List of input with multi-line handling. | |
352 | # Fill its zero entry, user counter starts at 1 |
|
364 | self.input_hist = InputList() | |
353 | self.input_hist = InputList(['\n']) |
|
|||
354 | # This one will hold the 'raw' input history, without any |
|
365 | # This one will hold the 'raw' input history, without any | |
355 | # pre-processing. This will allow users to retrieve the input just as |
|
366 | # pre-processing. This will allow users to retrieve the input just as | |
356 | # it was exactly typed in by the user, with %hist -r. |
|
367 | # it was exactly typed in by the user, with %hist -r. | |
357 |
self.input_hist_raw = InputList( |
|
368 | self.input_hist_raw = InputList() | |
358 |
|
369 | |||
359 | # list of visited directories |
|
370 | # list of visited directories | |
360 | try: |
|
371 | try: | |
@@ -380,17 +391,7 b' class InteractiveShell(object,Magic):' | |||||
380 | no_alias[key] = 1 |
|
391 | no_alias[key] = 1 | |
381 | no_alias.update(__builtin__.__dict__) |
|
392 | no_alias.update(__builtin__.__dict__) | |
382 | self.no_alias = no_alias |
|
393 | self.no_alias = no_alias | |
383 |
|
||||
384 | # make global variables for user access to these |
|
|||
385 | self.user_ns['_ih'] = self.input_hist |
|
|||
386 | self.user_ns['_oh'] = self.output_hist |
|
|||
387 | self.user_ns['_dh'] = self.dir_hist |
|
|||
388 |
|
||||
389 | # user aliases to input and output histories |
|
|||
390 | self.user_ns['In'] = self.input_hist |
|
|||
391 | self.user_ns['Out'] = self.output_hist |
|
|||
392 |
|
394 | |||
393 | self.user_ns['_sh'] = IPython.shadowns |
|
|||
394 | # Object variable to store code object waiting execution. This is |
|
395 | # Object variable to store code object waiting execution. This is | |
395 | # used mainly by the multithreaded shells, but it can come in handy in |
|
396 | # used mainly by the multithreaded shells, but it can come in handy in | |
396 | # other situations. No need to use a Queue here, since it's a single |
|
397 | # other situations. No need to use a Queue here, since it's a single | |
@@ -583,11 +584,13 b' class InteractiveShell(object,Magic):' | |||||
583 | else: |
|
584 | else: | |
584 | auto_alias = () |
|
585 | auto_alias = () | |
585 | self.auto_alias = [s.split(None,1) for s in auto_alias] |
|
586 | self.auto_alias = [s.split(None,1) for s in auto_alias] | |
586 |
|
||||
587 |
|
587 | |||
588 | # Produce a public API instance |
|
588 | # Produce a public API instance | |
589 | self.api = IPython.ipapi.IPApi(self) |
|
589 | self.api = IPython.ipapi.IPApi(self) | |
590 |
|
590 | |||
|
591 | # Initialize all user-visible namespaces | |||
|
592 | self.init_namespaces() | |||
|
593 | ||||
591 | # Call the actual (public) initializer |
|
594 | # Call the actual (public) initializer | |
592 | self.init_auto_alias() |
|
595 | self.init_auto_alias() | |
593 |
|
596 | |||
@@ -654,7 +657,6 b' class InteractiveShell(object,Magic):' | |||||
654 | # Load readline proper |
|
657 | # Load readline proper | |
655 | if rc.readline: |
|
658 | if rc.readline: | |
656 | self.init_readline() |
|
659 | self.init_readline() | |
657 |
|
||||
658 |
|
660 | |||
659 | # local shortcut, this is used a LOT |
|
661 | # local shortcut, this is used a LOT | |
660 | self.log = self.logger.log |
|
662 | self.log = self.logger.log | |
@@ -721,6 +723,39 b' class InteractiveShell(object,Magic):' | |||||
721 | if batchrun and not self.rc.interact: |
|
723 | if batchrun and not self.rc.interact: | |
722 | self.ask_exit() |
|
724 | self.ask_exit() | |
723 |
|
725 | |||
|
726 | def init_namespaces(self): | |||
|
727 | """Initialize all user-visible namespaces to their minimum defaults. | |||
|
728 | ||||
|
729 | Certain history lists are also initialized here, as they effectively | |||
|
730 | act as user namespaces. | |||
|
731 | ||||
|
732 | Note | |||
|
733 | ---- | |||
|
734 | All data structures here are only filled in, they are NOT reset by this | |||
|
735 | method. If they were not empty before, data will simply be added to | |||
|
736 | therm. | |||
|
737 | """ | |||
|
738 | # The user namespace MUST have a pointer to the shell itself. | |||
|
739 | self.user_ns[self.name] = self | |||
|
740 | ||||
|
741 | # Store the public api instance | |||
|
742 | self.user_ns['_ip'] = self.api | |||
|
743 | ||||
|
744 | # make global variables for user access to the histories | |||
|
745 | self.user_ns['_ih'] = self.input_hist | |||
|
746 | self.user_ns['_oh'] = self.output_hist | |||
|
747 | self.user_ns['_dh'] = self.dir_hist | |||
|
748 | ||||
|
749 | # user aliases to input and output histories | |||
|
750 | self.user_ns['In'] = self.input_hist | |||
|
751 | self.user_ns['Out'] = self.output_hist | |||
|
752 | ||||
|
753 | self.user_ns['_sh'] = IPython.shadowns | |||
|
754 | ||||
|
755 | # Fill the history zero entry, user counter starts at 1 | |||
|
756 | self.input_hist.append('\n') | |||
|
757 | self.input_hist_raw.append('\n') | |||
|
758 | ||||
724 | def add_builtins(self): |
|
759 | def add_builtins(self): | |
725 | """Store ipython references into the builtin namespace. |
|
760 | """Store ipython references into the builtin namespace. | |
726 |
|
761 | |||
@@ -1249,7 +1284,27 b' want to merge them back into the new files.""" % locals()' | |||||
1249 | except OSError: |
|
1284 | except OSError: | |
1250 | pass |
|
1285 | pass | |
1251 |
|
1286 | |||
|
1287 | # Clear all user namespaces to release all references cleanly. | |||
|
1288 | self.reset() | |||
|
1289 | ||||
|
1290 | # Run user hooks | |||
1252 | self.hooks.shutdown_hook() |
|
1291 | self.hooks.shutdown_hook() | |
|
1292 | ||||
|
1293 | def reset(self): | |||
|
1294 | """Clear all internal namespaces. | |||
|
1295 | ||||
|
1296 | Note that this is much more aggressive than %reset, since it clears | |||
|
1297 | fully all namespaces, as well as all input/output lists. | |||
|
1298 | """ | |||
|
1299 | for ns in self.ns_refs_table: | |||
|
1300 | ns.clear() | |||
|
1301 | ||||
|
1302 | # Clear input and output histories | |||
|
1303 | self.input_hist[:] = [] | |||
|
1304 | self.input_hist_raw[:] = [] | |||
|
1305 | self.output_hist.clear() | |||
|
1306 | # Restore the user namespaces to minimal usability | |||
|
1307 | self.init_namespaces() | |||
1253 |
|
1308 | |||
1254 | def savehist(self): |
|
1309 | def savehist(self): | |
1255 | """Save input history to a file (via readline library).""" |
|
1310 | """Save input history to a file (via readline library).""" | |
@@ -2012,7 +2067,6 b' want to merge them back into the new files.""" % locals()' | |||||
2012 | # NOT skip even a blank line if we are in a code block (more is |
|
2067 | # NOT skip even a blank line if we are in a code block (more is | |
2013 | # true) |
|
2068 | # true) | |
2014 |
|
2069 | |||
2015 |
|
||||
2016 | if line or more: |
|
2070 | if line or more: | |
2017 | # push to raw history, so hist line numbers stay in sync |
|
2071 | # push to raw history, so hist line numbers stay in sync | |
2018 | self.input_hist_raw.append("# " + line + "\n") |
|
2072 | self.input_hist_raw.append("# " + line + "\n") |
@@ -107,8 +107,6 b' def make_IPython(argv=None,user_ns=None,user_global_ns=None,debug=1,' | |||||
107 | IP.user_ns['help'] = _Helper() |
|
107 | IP.user_ns['help'] = _Helper() | |
108 | except ImportError: |
|
108 | except ImportError: | |
109 | warn('help() not available - check site.py') |
|
109 | warn('help() not available - check site.py') | |
110 | IP.user_config_ns = {} |
|
|||
111 |
|
||||
112 |
|
110 | |||
113 | if DEVDEBUG: |
|
111 | if DEVDEBUG: | |
114 | # For developer debugging only (global flag) |
|
112 | # For developer debugging only (global flag) |
@@ -72,7 +72,7 b' class py_file_finder(object):' | |||||
72 | def __call__(self,name): |
|
72 | def __call__(self,name): | |
73 | from IPython.genutils import get_py_filename |
|
73 | from IPython.genutils import get_py_filename | |
74 | try: |
|
74 | try: | |
75 | get_py_filename(name) |
|
75 | return get_py_filename(name) | |
76 | except IOError: |
|
76 | except IOError: | |
77 | test_dir = os.path.dirname(self.test_filename) |
|
77 | test_dir = os.path.dirname(self.test_filename) | |
78 | new_path = os.path.join(test_dir,name) |
|
78 | new_path = os.path.join(test_dir,name) |
@@ -80,6 +80,14 b' def doctest_hist_r():' | |||||
80 | """ |
|
80 | """ | |
81 |
|
81 | |||
82 |
|
82 | |||
|
83 | def test_obj_del(): | |||
|
84 | """Test that object's __del__ methods are called on exit.""" | |||
|
85 | test_dir = os.path.dirname(__file__) | |||
|
86 | del_file = os.path.join(test_dir,'obj_del.py') | |||
|
87 | out = _ip.IP.getoutput('ipython %s' % del_file) | |||
|
88 | nt.assert_equals(out,'object A deleted') | |||
|
89 | ||||
|
90 | ||||
83 | def test_shist(): |
|
91 | def test_shist(): | |
84 | # Simple tests of ShadowHist class - test generator. |
|
92 | # Simple tests of ShadowHist class - test generator. | |
85 | import os, shutil, tempfile |
|
93 | import os, shutil, tempfile |
General Comments 0
You need to be logged in to leave comments.
Login now