|
17 | 17 | import warnings as _builtin_warnings
|
18 | 18 | import logging
|
19 | 19 | import numpy
|
| 20 | +import inspect |
20 | 21 | from nutils import warnings, numeric
|
21 | 22 |
|
22 | 23 |
|
@@ -316,4 +317,62 @@ def _parse_array_tokens(cls, tokens):
|
316 | 317 | else:
|
317 | 318 | return float(token)
|
318 | 319 |
|
| 320 | + |
| 321 | +class DocTestFinder(doctest.DocTestFinder): |
| 322 | + |
| 323 | + # This is a modified version of doctest.DocTestFinder to fix issue |
| 324 | + # https://github.com/python/cpython/issues/107715, which prevents doctest |
| 325 | + # operation for the SI module. The modification assumes that `find` |
| 326 | + # continues to rely on the internal `_find_lineno` method, which will |
| 327 | + # hopefully be the case until Python merges #107716. |
| 328 | + |
| 329 | + def _find_lineno(self, obj, source_lines): |
| 330 | + """ |
| 331 | + Return a line number of the given object's docstring. Note: |
| 332 | + this method assumes that the object has a docstring. |
| 333 | + """ |
| 334 | + lineno = None |
| 335 | + |
| 336 | + # Find the line number for modules. |
| 337 | + if inspect.ismodule(obj): |
| 338 | + lineno = 0 |
| 339 | + |
| 340 | + # Find the line number for classes. |
| 341 | + # Note: this could be fooled if a class is defined multiple |
| 342 | + # times in a single file. |
| 343 | + if inspect.isclass(obj): |
| 344 | + if source_lines is None: |
| 345 | + return None |
| 346 | + pat = re.compile(r'^\s*class\s*%s\b' % |
| 347 | + re.escape(getattr(obj, '__name__', '-'))) |
| 348 | + for i, line in enumerate(source_lines): |
| 349 | + if pat.match(line): |
| 350 | + lineno = i |
| 351 | + break |
| 352 | + |
| 353 | + # Find the line number for functions & methods. |
| 354 | + if inspect.ismethod(obj): obj = obj.__func__ |
| 355 | + if inspect.isfunction(obj): obj = obj.__code__ |
| 356 | + if inspect.istraceback(obj): obj = obj.tb_frame |
| 357 | + if inspect.isframe(obj): obj = obj.f_code |
| 358 | + if inspect.iscode(obj): |
| 359 | + lineno = getattr(obj, 'co_firstlineno', None)-1 |
| 360 | + |
| 361 | + # Find the line number where the docstring starts. Assume |
| 362 | + # that it's the first line that begins with a quote mark. |
| 363 | + # Note: this could be fooled by a multiline function |
| 364 | + # signature, where a continuation line begins with a quote |
| 365 | + # mark. |
| 366 | + if lineno is not None: |
| 367 | + if source_lines is None: |
| 368 | + return lineno+1 |
| 369 | + pat = re.compile(r'(^|.*:)\s*\w*("|\')') |
| 370 | + for lineno in range(lineno, len(source_lines)): |
| 371 | + if pat.match(source_lines[lineno]): |
| 372 | + return lineno |
| 373 | + |
| 374 | + # We couldn't find the line number. |
| 375 | + return None |
| 376 | + |
| 377 | + |
319 | 378 | # vim:sw=4:sts=4:et
|
0 commit comments