|
1 | 1 | from configparser import ConfigParser
|
2 | 2 | from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType, Union
|
3 | 3 |
|
4 |
| -from pydantic.utils import is_valid_field |
5 |
| - |
6 |
| -try: |
7 |
| - import toml |
8 |
| -except ImportError: # pragma: no cover |
9 |
| - # future-proofing for upcoming `mypy` releases which will switch dependencies |
10 |
| - try: |
11 |
| - import tomli as toml # type: ignore |
12 |
| - except ImportError: |
13 |
| - import warnings |
14 |
| - |
15 |
| - warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.') |
16 |
| - toml = None # type: ignore |
17 |
| - |
18 | 4 | from mypy.errorcodes import ErrorCode
|
19 | 5 | from mypy.nodes import (
|
20 | 6 | ARG_NAMED,
|
|
60 | 46 | Type,
|
61 | 47 | TypeOfAny,
|
62 | 48 | TypeType,
|
63 |
| - TypeVarLikeType, |
64 | 49 | TypeVarType,
|
65 | 50 | UnionType,
|
66 | 51 | get_proper_type,
|
67 | 52 | )
|
68 | 53 | from mypy.typevars import fill_typevars
|
69 | 54 | from mypy.util import get_unique_redefinition_name
|
| 55 | +from mypy.version import __version__ as mypy_version |
| 56 | + |
| 57 | +from pydantic.utils import is_valid_field |
| 58 | + |
| 59 | +try: |
| 60 | + from mypy.types import TypeVarDef # type: ignore[attr-defined] |
| 61 | +except ImportError: # pragma: no cover |
| 62 | + # Backward-compatible with TypeVarDef from Mypy 0.910. |
| 63 | + from mypy.types import TypeVarType as TypeVarDef |
70 | 64 |
|
71 | 65 | CONFIGFILE_KEY = 'pydantic-mypy'
|
72 | 66 | METADATA_KEY = 'pydantic-mypy-metadata'
|
73 | 67 | BASEMODEL_FULLNAME = 'pydantic.main.BaseModel'
|
74 | 68 | BASESETTINGS_FULLNAME = 'pydantic.env_settings.BaseSettings'
|
75 | 69 | FIELD_FULLNAME = 'pydantic.fields.Field'
|
76 | 70 | DATACLASS_FULLNAME = 'pydantic.dataclasses.dataclass'
|
| 71 | +BUILTINS_NAME = 'builtins' if float(mypy_version) >= 0.930 else '__builtins__' |
77 | 72 |
|
78 | 73 |
|
79 | 74 | def plugin(version: str) -> 'TypingType[Plugin]':
|
@@ -125,9 +120,9 @@ def __init__(self, options: Options) -> None:
|
125 | 120 | if options.config_file is None: # pragma: no cover
|
126 | 121 | return
|
127 | 122 |
|
128 |
| - if toml and options.config_file.endswith('.toml'): |
129 |
| - with open(options.config_file, 'r') as rf: |
130 |
| - config = toml.load(rf).get('tool', {}).get('pydantic-mypy', {}) |
| 123 | + toml_config = parse_toml(options.config_file) |
| 124 | + if toml_config is not None: |
| 125 | + config = toml_config.get('tool', {}).get('pydantic-mypy', {}) |
131 | 126 | for key in self.__slots__:
|
132 | 127 | setting = config.get(key, False)
|
133 | 128 | if not isinstance(setting, bool):
|
@@ -348,26 +343,32 @@ def add_construct_method(self, fields: List['PydanticModelField']) -> None:
|
348 | 343 | and does not treat settings fields as optional.
|
349 | 344 | """
|
350 | 345 | ctx = self._ctx
|
351 |
| - set_str = ctx.api.named_type('builtins.set', [ctx.api.named_type('builtins.str')]) |
| 346 | + set_str = ctx.api.named_type(f'{BUILTINS_NAME}.set', [ctx.api.named_type(f'{BUILTINS_NAME}.str')]) |
352 | 347 | optional_set_str = UnionType([set_str, NoneType()])
|
353 | 348 | fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT)
|
354 | 349 | construct_arguments = self.get_field_arguments(fields, typed=True, force_all_optional=False, use_alias=False)
|
355 | 350 | construct_arguments = [fields_set_argument] + construct_arguments
|
356 | 351 |
|
357 |
| - obj_type = ctx.api.named_type('builtins.object') |
| 352 | + obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object') |
358 | 353 | self_tvar_name = '_PydanticBaseModel' # Make sure it does not conflict with other names in the class
|
359 | 354 | tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name
|
360 |
| - self_type = TypeVarType(self_tvar_name, tvar_fullname, -1, [], obj_type) |
| 355 | + tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type) |
361 | 356 | self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type)
|
362 | 357 | ctx.cls.info.names[self_tvar_name] = SymbolTableNode(MDEF, self_tvar_expr)
|
363 | 358 |
|
| 359 | + # Backward-compatible with TypeVarDef from Mypy 0.910. |
| 360 | + if isinstance(tvd, TypeVarType): |
| 361 | + self_type = tvd |
| 362 | + else: |
| 363 | + self_type = TypeVarType(tvd) # type: ignore[call-arg] |
| 364 | + |
364 | 365 | add_method(
|
365 | 366 | ctx,
|
366 | 367 | 'construct',
|
367 | 368 | construct_arguments,
|
368 | 369 | return_type=self_type,
|
369 | 370 | self_type=self_type,
|
370 |
| - tvar_like_type=self_type, |
| 371 | + tvar_def=tvd, |
371 | 372 | is_classmethod=True,
|
372 | 373 | )
|
373 | 374 |
|
@@ -619,7 +620,7 @@ def add_method(
|
619 | 620 | args: List[Argument],
|
620 | 621 | return_type: Type,
|
621 | 622 | self_type: Optional[Type] = None,
|
622 |
| - tvar_like_type: Optional[TypeVarLikeType] = None, |
| 623 | + tvar_def: Optional[TypeVarDef] = None, |
623 | 624 | is_classmethod: bool = False,
|
624 | 625 | is_new: bool = False,
|
625 | 626 | # is_staticmethod: bool = False,
|
@@ -654,10 +655,10 @@ def add_method(
|
654 | 655 | arg_names.append(get_name(arg.variable))
|
655 | 656 | arg_kinds.append(arg.kind)
|
656 | 657 |
|
657 |
| - function_type = ctx.api.named_type('builtins.function') |
| 658 | + function_type = ctx.api.named_type(f'{BUILTINS_NAME}.function') |
658 | 659 | signature = CallableType(arg_types, arg_kinds, arg_names, return_type, function_type)
|
659 |
| - if tvar_like_type: |
660 |
| - signature.variables = [tvar_like_type] |
| 660 | + if tvar_def: |
| 661 | + signature.variables = [tvar_def] |
661 | 662 |
|
662 | 663 | func = FuncDef(name, args, Block([PassStmt()]))
|
663 | 664 | func.info = info
|
@@ -714,3 +715,25 @@ def get_name(x: Union[FuncBase, SymbolNode]) -> str:
|
714 | 715 | if callable(fn): # pragma: no cover
|
715 | 716 | return fn()
|
716 | 717 | return fn
|
| 718 | + |
| 719 | + |
| 720 | +def parse_toml(config_file: str) -> Optional[Dict[str, Any]]: |
| 721 | + if not config_file.endswith('.toml'): |
| 722 | + return None |
| 723 | + |
| 724 | + read_mode = 'rb' |
| 725 | + try: |
| 726 | + import tomli as toml_ |
| 727 | + except ImportError: |
| 728 | + # older versions of mypy have toml as a dependency, not tomli |
| 729 | + read_mode = 'r' |
| 730 | + try: |
| 731 | + import toml as toml_ # type: ignore[no-redef] |
| 732 | + except ImportError: # pragma: no cover |
| 733 | + import warnings |
| 734 | + |
| 735 | + warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.') |
| 736 | + return None |
| 737 | + |
| 738 | + with open(config_file, read_mode) as rf: |
| 739 | + return toml_.load(rf) # type: ignore[arg-type] |
0 commit comments