diff --git a/IPython/testing/globalipapp.py b/IPython/testing/globalipapp.py index d684ce3..1c3de6a 100644 --- a/IPython/testing/globalipapp.py +++ b/IPython/testing/globalipapp.py @@ -68,11 +68,19 @@ class ipnsdict(dict): This subclass adds a simple checkpointing capability so that when testing machinery clears it (we use it as the test execution context), it doesn't get completely destroyed. + + In addition, it can handle the presence of the '_' key in a special manner, + which is needed because of how Python's doctest machinery operates with + '_'. See constructor and :meth:`update` for details. """ def __init__(self,*a): dict.__init__(self,*a) self._savedict = {} + # If this flag is True, the .update() method will unconditionally + # remove a key named '_'. This is so that such a dict can be used as a + # namespace in doctests that call '_'. + self.protect_underscore = False def clear(self): dict.clear(self) @@ -86,12 +94,15 @@ class ipnsdict(dict): self._checkpoint() dict.update(self,other) - # If '_' is in the namespace, python won't set it when executing code - # *in doctests*, and we have multiple doctests that use '_'. So we - # ensure that the namespace is always 'clean' of it before it's used - # for test code execution. Note: this means that outside of doctests, - # our own testing - self.pop('_',None) + if self.protect_underscore: + # If '_' is in the namespace, python won't set it when executing + # code *in doctests*, and we have multiple doctests that use '_'. + # So we ensure that the namespace is always 'clean' of it before + # it's used for test code execution. + # This flag is only turned on by the doctest machinery, so that + # normal test code can assume the _ key is updated like any other + # key and can test for its presence after cell executions. + self.pop('_', None) # The builtins namespace must *always* be the real __builtin__ module, # else weird stuff happens. The main ipython code does have provisions diff --git a/IPython/testing/plugin/ipdoctest.py b/IPython/testing/plugin/ipdoctest.py index 2f2dc85..f2b2c75 100644 --- a/IPython/testing/plugin/ipdoctest.py +++ b/IPython/testing/plugin/ipdoctest.py @@ -273,6 +273,10 @@ class DocTestCase(doctests.DocTestCase): # fills with the necessary info from the module being tested). _ip.user_ns.update(self._dt_test.globs) self._dt_test.globs = _ip.user_ns + # IPython must protect the _ key in the namespace (it can't exist) + # so that Python's doctest code sets it naturally, so we enable + # this feature of our testing namespace. + _ip.user_ns.protect_underscore = True super(DocTestCase, self).setUp() @@ -282,6 +286,9 @@ class DocTestCase(doctests.DocTestCase): # teardown doesn't destroy the ipython namespace if isinstance(self._dt_test.examples[0],IPExample): self._dt_test.globs = self._dt_test_globs_ori + # Restore the behavior of the '_' key in the user namespace to + # normal after each doctest, so that unittests behave normally + _ip.user_ns.protect_underscore = False # XXX - fperez: I am not sure if this is truly a bug in nose 0.11, but # it does look like one to me: its tearDown method tries to run