Skip to content

Commit d1ca12b

Browse files
authored
Merge pull request #199 from asottile/typeddict
add workaround for AnnAssign in class bodies
2 parents cf792cd + 7d85bf1 commit d1ca12b

File tree

2 files changed

+43
-0
lines changed

2 files changed

+43
-0
lines changed

dead.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,20 @@ def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
9999
def visit_ClassDef(self, node: ast.ClassDef) -> None:
100100
self.define(node.name, node)
101101
self.generic_visit(node)
102+
# this is a bad middle ground to handle TypedDict and related classes
103+
# simply mark any annotated assignment directly in a class body as
104+
# "used"
105+
# *ideally* with full semantic analysis you'd be able to know when
106+
# a dictionary literal uses the same key as a specific TypedDict
107+
# and know if it is used. unfortunately it's difficult to know
108+
# statically in isolation whether a class definition is a TypedDict
109+
# due to inheritance (and multiple inheritance (and module boundaries))
110+
for stmt in node.body:
111+
if (
112+
isinstance(stmt, ast.AnnAssign) and
113+
isinstance(stmt.target, ast.Name)
114+
):
115+
self.read(stmt.target.id, stmt)
102116

103117
def _is_stub_function(self, node: FunctionDef) -> bool:
104118
for stmt in node.body:

tests/dead_test.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,35 @@ def test_unused_annotated_assignment(git_dir, capsys):
276276
assert out == 'SOME_CONSTANT is never read, defined in f.py:2\n'
277277

278278

279+
def test_typedict_keys_used(git_dir, capsys):
280+
src = '''\
281+
from typing import TypedDict
282+
283+
class T(TypedDict):
284+
foo: str
285+
bar: str
286+
287+
288+
def f() -> T:
289+
return {
290+
"foo": "str1",
291+
"bar": "str2",
292+
}
293+
294+
295+
def g() -> None:
296+
f = f()
297+
print(f["foo"])
298+
print(f["bar"])
299+
300+
301+
g()
302+
'''
303+
git_dir.join('f.py').write(src)
304+
subprocess.check_call(('git', 'add', '.'))
305+
assert not dead.main(())
306+
307+
279308
def test_allowed_symbols_file(git_dir, tmp_path, capsys):
280309
allowed = tmp_path.joinpath('allowed')
281310
allowed.write_text('unused_function\n')

0 commit comments

Comments
 (0)