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() |