Skip to content

Commit bc95027

Browse files
committed
DOC - mini-language doctest markup in action
1 parent 5422f87 commit bc95027

File tree

6 files changed

+97
-49
lines changed

6 files changed

+97
-49
lines changed

nibabel/analyze.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ def __setitem__(self, item, value):
655655
--------
656656
>>> hdr = AnalyzeHeader()
657657
>>> hdr['descrip'] = 'description'
658-
>>> np.asscalar(hdr['descrip']) #23: bytes
658+
>>> np.asscalar(hdr['descrip']) #2to3: next; replace("'d", "b'd")
659659
'description'
660660
'''
661661
self._header_data[item] = value
@@ -830,15 +830,15 @@ def set_data_dtype(self, datatype):
830830
>>> hdr.set_data_dtype(np.dtype(np.uint8))
831831
>>> hdr.get_data_dtype()
832832
dtype('uint8')
833-
>>> hdr.set_data_dtype('implausible')
833+
>>> hdr.set_data_dtype('implausible') #doctest: +IGNORE_EXCEPTION_DETAIL
834834
Traceback (most recent call last):
835835
...
836836
HeaderDataError: data dtype "implausible" not recognized
837-
>>> hdr.set_data_dtype('none')
837+
>>> hdr.set_data_dtype('none') #doctest: +IGNORE_EXCEPTION_DETAIL
838838
Traceback (most recent call last):
839839
...
840840
HeaderDataError: data dtype "none" known but not supported
841-
>>> hdr.set_data_dtype(np.void)
841+
>>> hdr.set_data_dtype(np.void) #doctest: +IGNORE_EXCEPTION_DETAIL
842842
Traceback (most recent call last):
843843
...
844844
HeaderDataError: data dtype "<type 'numpy.void'>" known but not supported

nibabel/nicom/structreader.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class Unpacker(object):
1414
1515
Examples
1616
--------
17-
>>> a = '1234567890'
17+
>>> a = '1234567890' #2to3: here; replace("'1", "b'1")
1818
>>> upk = Unpacker(a)
1919
>>> upk.unpack('2s')
2020
('12',)

nibabel/trackvis.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ def empty_header(endianness=None, version=2):
387387
>>> hdr = empty_header()
388388
>>> print hdr['version']
389389
2
390-
>>> np.asscalar(hdr['id_string']) #23: bytes
390+
>>> np.asscalar(hdr['id_string']) #2to3: next; line.replace("'T", "b'T")
391391
'TRACK'
392392
>>> endian_codes[hdr['version'].dtype.byteorder] == native_code
393393
True

nibabel/volumeutils.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -390,12 +390,12 @@ def array_from_file(shape, in_dtype, infile, offset=0, order='F'):
390390
>>> from StringIO import StringIO as BytesIO
391391
>>> bio = BytesIO()
392392
>>> arr = np.arange(6).reshape(1,2,3)
393-
>>> _ = bio.write(arr.tostring('F')) # outputs no in python3
393+
>>> _ = bio.write(arr.tostring('F')) # outputs int in python3
394394
>>> arr2 = array_from_file((1,2,3), arr.dtype, bio)
395395
>>> np.all(arr == arr2)
396396
True
397397
>>> bio = BytesIO()
398-
>>> _ = bio.write(' ' * 10) #2to3: line.replace(' ', b' ')
398+
>>> _ = bio.write(' ' * 10) #2to3: here; replace("' '", "b' '")
399399
>>> _ = bio.write(arr.tostring('F'))
400400
>>> arr2 = array_from_file((1,2,3), arr.dtype, bio, 10)
401401
>>> np.all(arr == arr2)
@@ -869,7 +869,7 @@ def rec2dict(rec):
869869
--------
870870
>>> r = np.zeros((), dtype = [('x', 'i4'), ('s', 'S10')])
871871
>>> d = rec2dict(r)
872-
>>> d == {'x': 0, 's': ''}
872+
>>> d == {'x': 0, 's': ''} #2to3: here; replace("''", "b''")
873873
True
874874
'''
875875
dct = {}

nisext/py3builder.py

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,11 @@ def doctest_markup_files(fnames):
6767
'from io import BytesIO'),
6868
)
6969

70-
MARK_COMMENT = re.compile('#23:\s*(.*?)\s*$')
71-
FIRST_NW = re.compile('(\s*)(\S.*)', re.DOTALL)
70+
MARK_COMMENT = re.compile('(.*?)(#2to3:\s*)(.*?\s*)$', re.DOTALL)
71+
PLACE_EXPR = re.compile('\s*([\w+\- ]+);\s*(.*)$')
7272

73-
def doctest_markup(lines):
73+
74+
def doctest_markup(in_lines):
7475
""" Process doctest comment markup on sequence of strings
7576
7677
The markup is very crude. The algorithm looks for lines starting with
@@ -82,52 +83,80 @@ def doctest_markup(lines):
8283
BytesIO``.
8384
* ``from StringIO import StringIO`` replaced by ``from io import StringIO``.
8485
85-
Next it looks to see if the line ends with a comment starting with ``#23:``.
86+
Next it looks to see if the line ends with a comment starting with ``#2to3:``.
87+
88+
The stuff after the ``#2to3:`` marker is, of course, a little language, of
89+
form <place>; <expr>
90+
91+
* <place> is an expression giving a line number. In this expression, ``here`` is
92+
a variable referring to the current line number, and ``next`` is just
93+
``here+1``.
94+
* <expr> is a python3 expression returning a processed value, where
95+
``line`` contains the line number referred to by ``here``, and ``lines``
96+
is a list of all lines, such that ``lines[here]`` gives the value of
97+
``line``.
8698
87-
* ``bytes`` : prepend 'b' to next output line; for when you want to show
88-
output of bytes type in python 3
99+
An <expr> beginning with "replace(" we take to be short for "line.replace(".
89100
90101
Parameters
91102
----------
92-
lines : sequence of str
103+
in_lines : sequence of str
93104
94105
Returns
95106
-------
96-
newlines : sequence of str
107+
out_lines : sequence of str
97108
"""
98-
newlines = []
99-
lines = iter(lines)
100-
while(True):
101-
try:
102-
line = next(lines)
103-
except StopIteration:
104-
break
105-
if not line.lstrip().startswith('>>>'):
106-
newlines.append(line)
109+
pos = 0
110+
lines = list(in_lines)
111+
while pos < len(lines):
112+
this = lines[pos]
113+
here = pos
114+
pos += 1
115+
if not this.lstrip().startswith('>>>'):
107116
continue
108117
# Check simple regexps (no markup)
109118
for reg, substr in REGGIES:
110-
if reg.search(line):
111-
line = reg.sub(substr, line)
119+
if reg.search(this):
120+
lines[here] = reg.sub(substr, this)
112121
break
113122
# Check for specific markup
114-
mark_match = MARK_COMMENT.search(line)
123+
mark_match = MARK_COMMENT.search(this)
115124
if mark_match is None:
116-
newlines.append(line)
117125
continue
118-
markup = mark_match.groups()[0]
119-
# Actually we don't know what to do with markup yet
120-
if markup == 'bytes':
121-
newlines.append(line)
126+
start, marker, markup = mark_match.groups()
127+
pe_match = PLACE_EXPR.match(markup)
128+
if pe_match:
129+
place, expr = pe_match.groups()
122130
try:
123-
line = next(lines)
124-
except StopIteration:
125-
break
126-
match = FIRST_NW.match(line)
127-
if match:
128-
line = '%sb%s' % match.groups()
129-
newlines.append(line)
130-
else:
131-
newlines.append(line)
132-
return newlines
131+
place = eval(place, {'here': here, 'next': here+1})
132+
except:
133+
print('Error finding place with "%s", line "%s"; skipping' %
134+
(place, this))
135+
continue
136+
# Prevent processing operating on comment
137+
if place == here:
138+
line = start
139+
else:
140+
line = lines[place]
141+
expr = expr.strip()
142+
# Shorthand stuff
143+
if expr == 'bytes':
144+
# Any strings on the given line are byte strings
145+
raise NotImplementedError()
146+
# If expr starts with 'replace', implies "line.replace"
147+
if expr.startswith('replace('):
148+
expr = 'line.' + expr
149+
try:
150+
res = eval(expr, dict(line=line,
151+
lines=lines))
152+
except:
153+
print('Error working on "%s" at line %d with "%s"; skipping' %
154+
(line, place, expr))
155+
continue
156+
# Put back comment if removed
157+
if place == here:
158+
res += marker + markup
159+
if res != line:
160+
lines[place] = res
161+
return lines
133162

nisext/tests/test_doctest_markup.py

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,27 @@ def test_search_replace():
2121
line = line + '\n'
2222
assert_equal(['>>> from io import BytesIO\n'], doctest_markup([line]))
2323
# Bytes output
24-
assert_equal(['any', ' some'], doctest_markup(['any', ' some']))
25-
assert_equal(['any #23: bytes', ' some'],
26-
doctest_markup(['any #23: bytes', ' some']))
27-
assert_equal([' >>> any #23: bytes \n', ' bsome \n'],
28-
doctest_markup([' >>> any #23: bytes \n', ' some \n']))
24+
marked_lines = ['any', ' some']
25+
assert_equal(marked_lines, doctest_markup(marked_lines))
26+
marked_lines = ['any #2to3: here+1; line.replace("some", "boo") ',
27+
' some ']
28+
assert_equal(marked_lines, doctest_markup(marked_lines))
29+
marked_lines = ['>>> any #2to3: here+1; line.replace("some", "boo") ',
30+
' some ']
31+
exp_out = ['>>> any #2to3: here+1; line.replace("some", "boo") ',
32+
' boo ']
33+
assert_equal(exp_out, doctest_markup(marked_lines))
34+
marked_lines = ['>>> any #2to3: next; line.replace("some", "boo") ',
35+
' some ']
36+
exp_out = ['>>> any #2to3: next; line.replace("some", "boo") ',
37+
' boo ']
38+
assert_equal(exp_out, doctest_markup(marked_lines))
39+
assert_equal(['>>> woo #2to3: here ; line.replace("wow", "woo") '],
40+
doctest_markup(
41+
['>>> wow #2to3: here ; line.replace("wow", "woo") ']))
42+
assert_equal(['>>> woo #2to3: here ; line.replace("wow", "woo") \n'],
43+
doctest_markup(
44+
['>>> wow #2to3: here ; line.replace("wow", "woo") \n']))
45+
assert_equal(['>>> woo #2to3: here ; replace("wow", "woo") '],
46+
doctest_markup(
47+
['>>> wow #2to3: here ; replace("wow", "woo") ']))

0 commit comments

Comments
 (0)