Repository: asottile/dead

Issue:
treat names in `__all__` as un-dead

Discussion:

Patch:
diff --git a/dead.py b/dead.py
index a8254d5..7879cc5 100644
--- a/dead.py
+++ b/dead.py
@@ -87,6 +87,17 @@ class Visitor(ast.NodeVisitor):
             for target in node.targets:
                 if isinstance(target, ast.Name):
                     self.defines[target.id].add(self.definition_str(node))
+
+        if (
+                len(node.targets) == 1 and
+                isinstance(node.targets[0], ast.Name) and
+                node.targets[0].id == '__all__' and
+                isinstance(node.value, (ast.Tuple, ast.List))
+        ):
+            for elt in node.value.elts:
+                if isinstance(elt, ast.Str):
+                    self.reads_target[elt.s].add(self.definition_str(elt))
+
         self.generic_visit(node)
 
     # TODO: AnnAssign


Test patch:
diff --git a/tests/dead_test.py b/tests/dead_test.py
index 875efb2..9ee3a09 100644
--- a/tests/dead_test.py
+++ b/tests/dead_test.py
@@ -28,7 +28,7 @@ def git_dir(tmpdir):
 @pytest.mark.parametrize(
     's',
     (
-        # asign
+        # assign
         'x = 1\nprint(x)\n',
         # function
         'def f(): ...\n'
@@ -61,9 +61,15 @@ def git_dir(tmpdir):
         'print(C())\n',
         # disabled by comment
         'def unused(): ... # dead: disable\n',
+        # exported in __all__
+        'def f(): ...\n'
+        '__all__ = ("f",)',
+        'def g(): ...\n'
+        'def f(): ...\n'
+        '__all__ = ["f", g.__name__]',
     ),
 )
-def test_is_mark_as_used(git_dir, capsys, s):
+def test_is_marked_as_used(git_dir, capsys, s):
     git_dir.join('f.py').write(s)
     subprocess.check_call(('git', 'add', '.'))
     assert not dead.main(())