This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Suspicious operation in `doctest.py`: `None - 1`
Type: behavior Stage: resolved
Components: Library (Lib) Versions: Python 3.11, Python 3.10, Python 3.9
process
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: Nosy List: eric.smith, sobolevn
Priority: normal Keywords: patch

Created on 2022-01-08 10:39 by sobolevn, last changed 2022-04-11 14:59 by admin. This issue is now closed.

Pull Requests
URL Status Linked Edit
PR 30481 merged sobolevn, 2022-01-08 10:55
Messages (2)
msg410092 - (view) Author: Nikita Sobolev (sobolevn) * (Python triager) Date: 2022-01-08 10:39
This line `lineno = getattr(obj, 'co_firstlineno', None)-1` does not look good. Link: https://github.com/python/cpython/blame/45d44b950f1dab0ef90d0a8f4fa75ffaae71500b/Lib/doctest.py#L1116

Why? 
1. `CodeType` is guaranteed to have `co_firstlineno`. Docs: https://github.com/python/cpython/blob/45d44b950f1dab0ef90d0a8f4fa75ffaae71500b/Lib/inspect.py#L487
2. Even if it does not have it for some reason, this `getattr` does not help. It raises unhandled `TypeError`. We can simulate it by replacing `lineno = getattr(obj, 'co_firstlineno', None)-1` with `lineno = None-1`

Here's what happens in this case:

```
» ./python.exe -m test -v test_doctest
== CPython 3.11.0a3+ (heads/issue-26767:45d44b950f, Jan 8 2022, 12:23:45) [Clang 11.0.0 (clang-1100.0.33.16)]
== Darwin-18.7.0-x86_64-i386-64bit little-endian
== cwd: /Users/sobolev/Desktop/cpython/build/test_python_78065æ
== CPU count: 4
== encodings: locale=UTF-8, FS=utf-8
0:00:00 load avg: 3.51 Run tests sequentially
0:00:00 load avg: 3.51 [1/1] test_doctest
Failed to call load_tests:
Traceback (most recent call last):
  File "/Users/sobolev/Desktop/cpython/Lib/unittest/loader.py", line 108, in loadTestsFromModule
    return load_tests(self, tests, pattern)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/test/test_doctest.py", line 3134, in load_tests
    tests.addTest(doctest.DocTestSuite(doctest))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 2391, in DocTestSuite
    tests = test_finder.find(module, globs=globs, extraglobs=extraglobs)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 940, in find
    self._find(tests, obj, name, module, source_lines, globs, {})
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 1013, in _find
    self._find(tests, val, valname, module, source_lines,
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 1001, in _find
    test = self._get_test(obj, name, module, globs, source_lines)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 1069, in _get_test
    lineno = self._find_lineno(obj, source_lines)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/doctest.py", line 1117, in _find_lineno
    None - 1
    ~~~~~^~~
TypeError: unsupported operand type(s) for -: 'NoneType' and 'int'

test test_doctest crashed -- Traceback (most recent call last):
  File "/Users/sobolev/Desktop/cpython/Lib/test/libregrtest/runtest.py", line 352, in _runtest_inner
    refleak = _runtest_inner2(ns, test_name)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/test/libregrtest/runtest.py", line 309, in _runtest_inner2
    test_runner()
    ^^^^^^^^^^^^^
  File "/Users/sobolev/Desktop/cpython/Lib/test/libregrtest/runtest.py", line 272, in _test_module
    raise Exception("errors while loading tests")
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Exception: errors while loading tests

test_doctest failed (uncaught exception)

== Tests result: FAILURE ==

1 test failed:
    test_doctest

Total duration: 1.0 sec
Tests result: FAILURE
```

So, we have two options:
1. Think of a case when `CodeType` does not have `co_firstlineno` and handle it properly, like using `0` or `None` as `lineno`
2. Simplify this line to remove potential `TypeError`

I think that this line should be rewritten as `lineno = obj.co_firstlineno - 1`

I will send a PR for others to judge.
msg410113 - (view) Author: Eric V. Smith (eric.smith) * (Python committer) Date: 2022-01-08 20:13
New changeset 0fc58c1e051026baff4919d8519ce2aabe3b2ba1 by Nikita Sobolev in branch 'main':
bpo-46306: simplify `CodeType` attribute access in `doctest.py` (GH-30481)
https://github.com/python/cpython/commit/0fc58c1e051026baff4919d8519ce2aabe3b2ba1
History
Date User Action Args
2022-04-11 14:59:54adminsetgithub: 90464
2022-01-08 20:14:43eric.smithsetstatus: open -> closed
resolution: fixed
stage: patch review -> resolved
2022-01-08 20:13:49eric.smithsetnosy: + eric.smith
messages: + msg410113
2022-01-08 11:36:47AlexWaygoodsettitle: Suspicios operation in `doctest.py`: `None - 1` -> Suspicious operation in `doctest.py`: `None - 1`
2022-01-08 10:55:37sobolevnsetkeywords: + patch
stage: patch review
pull_requests: + pull_request28685
2022-01-08 10:39:04sobolevncreate