Skip to content

Instantly share code, notes, and snippets.

@jdahlin
Last active May 25, 2022 12:01
Show Gist options
  • Save jdahlin/02e3b0b03cef87e423718797638b69f6 to your computer and use it in GitHub Desktop.
Save jdahlin/02e3b0b03cef87e423718797638b69f6 to your computer and use it in GitHub Desktop.
Use this script to generate a mypy.ini where you can enable strict mode based on errors from a current run.

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
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()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment