|
1 | 1 | import configparser
|
2 | 2 | from functools import partial
|
3 |
| -from typing import Callable, Dict, List, Optional, Tuple |
| 3 | +from typing import Callable, Dict, List, NoReturn, Optional, Tuple, cast |
4 | 4 |
|
5 | 5 | from django.db.models.fields.related import RelatedField
|
6 |
| -from mypy.errors import Errors |
7 | 6 | from mypy.nodes import MypyFile, TypeInfo
|
8 | 7 | from mypy.options import Options
|
9 | 8 | from mypy.plugin import (
|
@@ -52,25 +51,40 @@ def add_new_manager_base(ctx: ClassDefContext) -> None:
|
52 | 51 |
|
53 | 52 |
|
54 | 53 | def extract_django_settings_module(config_file_path: Optional[str]) -> str:
|
55 |
| - errors = Errors() |
56 |
| - if config_file_path is None: |
57 |
| - errors.report(0, None, "'django_settings_module' is not set: no mypy config file specified") |
58 |
| - errors.raise_error() |
| 54 | + |
| 55 | + def exit(error_type: int) -> NoReturn: |
| 56 | + """Using mypy's argument parser, raise `SystemExit` to fail hard if validation fails. |
| 57 | +
|
| 58 | + Considering that the plugin's startup duration is around double as long as mypy's, this aims to |
| 59 | + import and construct objects only when that's required - which happens once and terminates the |
| 60 | + run. Considering that most of the runs are successful, there's no need for this to linger in the |
| 61 | + global scope. |
| 62 | + """ |
| 63 | + from mypy.main import CapturableArgumentParser |
| 64 | + |
| 65 | + usage = """(config) |
| 66 | + ... |
| 67 | + [mypy.plugins.django_stubs] |
| 68 | + django_settings_module: str (required) |
| 69 | + ... |
| 70 | + """.replace("\n" + 8 * " ", "\n") |
| 71 | + handler = CapturableArgumentParser(prog='(django-stubs) mypy', usage=usage) |
| 72 | + messages = {1: 'mypy config file is not specified or found', |
| 73 | + 2: 'no section [mypy.plugins.django-stubs]', |
| 74 | + 3: 'the setting is not provided'} |
| 75 | + handler.error("'django_settings_module' is not set: " + messages[error_type]) |
59 | 76 |
|
60 | 77 | parser = configparser.ConfigParser()
|
61 |
| - parser.read(config_file_path) # type: ignore |
62 |
| - |
63 |
| - if not parser.has_section('mypy.plugins.django-stubs'): |
64 |
| - errors.report(0, None, "'django_settings_module' is not set: no section [mypy.plugins.django-stubs]", |
65 |
| - file=config_file_path) |
66 |
| - errors.raise_error() |
67 |
| - if not parser.has_option('mypy.plugins.django-stubs', 'django_settings_module'): |
68 |
| - errors.report(0, None, "'django_settings_module' is not set: setting is not provided", |
69 |
| - file=config_file_path) |
70 |
| - errors.raise_error() |
71 |
| - |
72 |
| - django_settings_module = parser.get('mypy.plugins.django-stubs', 'django_settings_module').strip('\'"') |
73 |
| - return django_settings_module |
| 78 | + try: |
| 79 | + parser.read_file(open(cast(str, config_file_path), 'r'), source=config_file_path) |
| 80 | + except (IsADirectoryError, OSError): |
| 81 | + exit(1) |
| 82 | + |
| 83 | + section = 'mypy.plugins.django-stubs' |
| 84 | + if not parser.has_section(section): |
| 85 | + exit(2) |
| 86 | + settings = parser.get(section, 'django_settings_module', fallback=None) or exit(3) |
| 87 | + return cast(str, settings).strip('\'"') |
74 | 88 |
|
75 | 89 |
|
76 | 90 | class NewSemanalDjangoPlugin(Plugin):
|
|
0 commit comments