Repository: PyCQA/pyflakes

Issue:
2.1.0: PYFLAKES_DOCTEST=1 causes stacktrace: AttributeError: 'NoneType' object has no attribute 'parent'
From https://gitlab.com/pycqa/flake8/issues/492

```console
$ pyflakes --version
2.1.0 Python 3.6.7 on Linux
```

```python
import foo as _

def x():
    """doc

    >>> x()
    >>> x()
    """
```

```console
$ PYFLAKES_DOCTEST=1 pyflakes t.py 
Traceback (most recent call last):
  File "/tmp/t/venv/bin/pyflakes", line 10, in <module>
    sys.exit(main())
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/api.py", line 207, in main
    warnings = checkRecursive(args, reporter)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/api.py", line 156, in checkRecursive
    warnings += checkPath(sourcePath, reporter)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/api.py", line 99, in checkPath
    return check(codestr, filename, reporter)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/api.py", line 74, in check
    w = checker.Checker(tree, file_tokens=file_tokens, filename=filename)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 665, in __init__
    self.runDeferred(self._deferredFunctions)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 702, in runDeferred
    handler()
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 1410, in <lambda>
    self.deferFunction(lambda: self.handleDoctests(node))
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 1148, in handleDoctests
    self.addBinding(None, Builtin('_'))
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 870, in addBinding
    parent_stmt = self.getParent(value.source)
  File "/tmp/t/venv/lib/python3.6/site-packages/pyflakes/checker.py", line 808, in getParent
    node = node.parent
AttributeError: 'NoneType' object has no attribute 'parent'

```

Discussion:
mgedmin: It might be a good idea to backport this fix to the 2.1.x branch and release a 2.1.2, to fix the regression in 2.1.0.

(AFAIU the current git master is aiming to be 2.2.0 due to #426.)
sigmavirus24: There is no `2.1.x` branch anywhere. 
mgedmin: > There is no 2.1.x branch anywhere

I meant the 2.1.x branch of version numbers rather than a git branch.

(There's a 2.1.1 tag that with one commit that branches off `master` but is not contained in any other git branch.  I imagine it was created for the same reason: to have a fix in the 2.1.x release series without introducing backwards-incompatible output changes of #426.)

Patch:
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index 83f92aa..86cfd11 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -1162,7 +1162,8 @@ class Checker(object):
         self.scopeStack = [self.scopeStack[0]]
         node_offset = self.offset or (0, 0)
         self.pushScope(DoctestScope)
-        self.addBinding(None, Builtin('_'))
+        if '_' not in self.scopeStack[0]:
+            self.addBinding(None, Builtin('_'))
         for example in examples:
             try:
                 tree = ast.parse(example.source, "<doctest>")


Test patch:
diff --git a/pyflakes/test/test_doctests.py b/pyflakes/test/test_doctests.py
index 0825ffe..5565226 100644
--- a/pyflakes/test/test_doctests.py
+++ b/pyflakes/test/test_doctests.py
@@ -433,6 +433,16 @@ class Test(TestCase):
             return 1
         ''')
 
+    def test_globalUnderscoreInDoctest(self):
+        self.flakes("""
+        from gettext import ugettext as _
+
+        def doctest_stuff():
+            '''
+                >>> pass
+            '''
+        """, m.UnusedImport)
+
 
 class TestOther(_DoctestMixin, TestOther):
     """Run TestOther with each test wrapped in a doctest."""