7878# machinery into a fit. This code should be considered a gross hack, but it
7979# gets the job done.
8080
81- class ncdict (dict ):
82- """Non-copying dict class.
83-
84- This is a special-purpose dict subclass that overrides the .copy() method
85- to return the original object itself. We need it to ensure that doctests
86- happen in the IPython namespace, but doctest always makes a shallow copy of
87- the given globals for execution. Since we actually *want* this namespace
88- to be persistent (this is how the user's session maintains state), we
89- simply fool doctest by returning the original object upoon copy.
90- """
91-
92- def copy (self ):
93- return self
94-
9581
9682# XXX - Hack to modify the %run command so we can sync the user's namespace
9783# with the test globals. Once we move over to a clean magic system, this will
@@ -130,11 +116,8 @@ def xsys(cmd):
130116 _excepthook = sys .excepthook
131117 _main = sys .modules .get ('__main__' )
132118
133- # Start IPython instance. We customize it to start with minimal frills and
134- # with our own namespace.
135- argv = ['--classic' ,'--noterm_title' ]
136- user_ns = ncdict ()
137- IPython .Shell .IPShell (argv ,user_ns )
119+ # Start IPython instance. We customize it to start with minimal frills.
120+ IPython .Shell .IPShell (['--classic' ,'--noterm_title' ])
138121
139122 # Deactivate the various python system hooks added by ipython for
140123 # interactive convenience so we don't confuse the doctest system
@@ -152,6 +135,7 @@ def xsys(cmd):
152135 # doctest machinery would miss them.
153136 _ip .system = xsys
154137
138+ # Also patch our %run function in.
155139 im = new .instancemethod (_run_ns_sync ,_ip .IP , _ip .IP .__class__ )
156140 _ip .IP .magic_run_ori = _ip .IP .magic_run
157141 _ip .IP .magic_run = im
@@ -406,6 +390,12 @@ class IPDocTestParser(doctest.DocTestParser):
406390 _EXAMPLE_RE_IP = re .compile ( _RE_TPL % (_PS1_IP ,_PS2_IP ,_PS1_IP ,_PS2_IP ),
407391 re .MULTILINE | re .VERBOSE )
408392
393+ # Mark a test as being fully random. In this case, we simply append the
394+ # random marker ('#random') to each individual example's output. This way
395+ # we don't need to modify any other code.
396+ _RANDOM_TEST = re .compile (r'#\s*all-random' )
397+
398+ # Mark tests to be executed in an external process - currently unsupported.
409399 _EXTERNAL_IP = re .compile (r'#\s*ipdoctest:\s*EXTERNAL' )
410400
411401 def ip2py (self ,source ):
@@ -438,6 +428,11 @@ def parse(self, string, name='<string>'):
438428 output = []
439429 charno , lineno = 0 , 0
440430
431+ if self ._RANDOM_TEST .search (string ):
432+ random_marker = '\n # random'
433+ else :
434+ random_marker = ''
435+
441436 # Whether to convert the input from ipython to python syntax
442437 ip2py = False
443438 # Find all doctest examples in the string. First, try them as Python
@@ -475,9 +470,15 @@ def parse(self, string, name='<string>'):
475470 # Extract info from the regexp match.
476471 (source , options , want , exc_msg ) = \
477472 self ._parse_example (m , name , lineno ,ip2py )
473+
474+ # Append the random-output marker (it defaults to empty in most
475+ # cases, it's only non-empty for 'all-random' tests):
476+ want += random_marker
477+
478478 if Example is IPExternalExample :
479479 options [doctest .NORMALIZE_WHITESPACE ] = True
480480 want += '\n '
481+
481482 # Create an Example, and add it to the list.
482483 if not self ._IS_BLANK_OR_COMMENT (source ):
483484 #print 'Example source:', source # dbg
0 commit comments