Use this script to generate a mypy.ini where you can enable strict mode based on errors from a current run.
Needs a few improvments:
- output pyproject.toml
- update source files by adding # type: ignore[xxx] instead of ignore_errors
Use this script to generate a mypy.ini where you can enable strict mode based on errors from a current run.
Needs a few improvments:
import collections | |
import enum | |
import os | |
import re | |
import subprocess | |
from dataclasses import dataclass | |
p = subprocess.run(["mypy", "--strict", "-p", "project"], stdout=subprocess.PIPE) | |
class Action(enum.Enum): | |
disallow_any_generics = enum.auto() | |
disallow_incomplete_defs = enum.auto() | |
disallow_subclassing_any = enum.auto() | |
disallow_untyped_calls = enum.auto() | |
disallow_untyped_defs = enum.auto() | |
allow_redefinition = enum.auto() | |
ignore_errors = enum.auto() | |
@dataclass | |
class Handler: | |
pattern: str | |
action: Action = Action.ignore_errors | |
regex: re.Pattern = None | |
def __post_init__(self): | |
self.regex = re.compile(self.pattern) | |
error_handles = [ | |
Handler(r'(Item ".*?" of |)".*?" has no attribute ".*?"'), | |
Handler(r'Argument .*? to ".*?" has incompatible type ".*?"; expected ".*?"'), | |
Handler(r'Call to untyped function ".*?" (of ".*?"|)in typed context', Action.disallow_untyped_calls), | |
Handler(r'Cannot instantiate abstract class ".*?" with abstract attributes .*', Action.disallow_untyped_calls), | |
Handler(r'Class cannot subclass ".*?" \(has type ".*?"\)', Action.disallow_subclassing_any), | |
Handler(r'Exception must be derived from BaseException'), | |
Handler(r'Function is missing a return type annotation', Action.disallow_untyped_defs), | |
Handler(r'Function is missing a type annotation', Action.disallow_untyped_defs), | |
Handler(r'Function is missing a type annotation for one or more arguments', Action.disallow_untyped_defs), | |
Handler(r'Incompatible types in assignment \(expression has type ".*?", variable has type ".*?"\)'), | |
Handler(r'Invalid index type ".*?" for ".*?"; expected type ".*?"'), | |
Handler(r'Missing type parameters for generic type ".*?"', Action.disallow_any_generics), | |
Handler(r'Module has no attribute ".*?"'), | |
Handler(r'Module ".*?" does not explicitly export attribute ".*?"; implicit reexport disabled'), | |
Handler(r'Need type annotation for ".*?"', Action.disallow_untyped_defs), | |
Handler(r'Name ".*?" already defined on line \d+', Action.allow_redefinition), | |
Handler(r'On Python 3 formatting "b\'abc\'" with "{}" produces "b\'abc\'", not "abc"; use "{!r}" if this is desired behavior'), | |
Handler(r'Returning Any from function declared to return ".*?"'), | |
Handler(r'Unsupported left operand type for .*? \(".*?"\)'), | |
Handler(r'Too many arguments for ".*?"'), | |
Handler(r'TypedDict ".*?" has no key ".*?"'), | |
Handler(r'Untyped decorator makes function ".*?" untyped'), | |
Handler(r'Value of type ".*?" is not indexable'), | |
] | |
file_actions = collections.defaultdict(set) | |
line_re = re.compile(r''' | |
(?P<filename>.*?) | |
: | |
(?P<lineno>\d+) | |
:\s | |
(?P<category>.*?) | |
:\s | |
(?P<message>.*) | |
''', re.VERBOSE) | |
finished_re = re.compile(r'Found \d+ errors in \d+ files \(checked \d+ source files\)') | |
for line in p.stdout.decode('utf-8').split("\n"): | |
match = line_re.match(line) | |
if not match: | |
if finished_re.match(line): | |
break | |
raise NotImplementedError(repr(line)) | |
filename = match.group("filename") | |
category = match.group("category") | |
message = match.group("message") | |
if category == "error": | |
for handler in error_handles: | |
if handler.regex.match(message): | |
file_actions[filename].add(handler.action) | |
break | |
else: | |
raise NotImplementedError(f"Unhandled line: {line}") | |
elif category == "note": | |
pass | |
else: | |
raise NotImplementedError(f"Unhandled category: {category}") | |
def filename_to_module(filename: str) -> str: | |
return filename[:-len('.py')].replace(os.sep, '.') | |
for filename, options in sorted(file_actions.items()): | |
print(f'[mypy-{filename_to_module(filename)}]') | |
for name in sorted([o.name for o in options]): | |
if name == 'ignore_errors': | |
value = True | |
else: | |
value = False | |
print(f'{name} = {value}') | |
print() |