FeatureNew: add mypy type annotations for subproject arg

Use a derived type when passing `subproject` around, so that mypy knows
it's actually a SubProject, not a str. This means that passing anything
other than a handle to the interpreter state's subproject attribute
becomes a type violation, specifically when the order of the *four*
different str arguments is typoed.
pull/9990/head
Eli Schwartz 3 years ago
parent bab651a7a0
commit c0b8e02d9f
No known key found for this signature in database
GPG Key ID: CEB167EFB5722BD6
  1. 5
      mesonbuild/build.py
  2. 3
      mesonbuild/interpreter/interpreterobjects.py
  3. 5
      mesonbuild/interpreter/primitives/range.py
  4. 4
      mesonbuild/interpreterbase/__init__.py
  5. 6
      mesonbuild/interpreterbase/baseobjects.py
  6. 7
      mesonbuild/interpreterbase/decorators.py
  7. 4
      mesonbuild/interpreterbase/interpreterbase.py
  8. 3
      mesonbuild/optinterpreter.py
  9. 3
      mesonbuild/wrap/wrap.py

@ -49,6 +49,7 @@ if T.TYPE_CHECKING:
from ._typing import ImmutableListProtocol, ImmutableSetProtocol from ._typing import ImmutableListProtocol, ImmutableSetProtocol
from .backend.backends import Backend, ExecutableSerialisation from .backend.backends import Backend, ExecutableSerialisation
from .interpreter.interpreter import Test, SourceOutputs, Interpreter from .interpreter.interpreter import Test, SourceOutputs, Interpreter
from .interpreterbase import SubProject
from .mesonlib import FileMode, FileOrString from .mesonlib import FileMode, FileOrString
from .modules import ModuleState from .modules import ModuleState
from .mparser import BaseNode from .mparser import BaseNode
@ -514,7 +515,7 @@ class Target(HoldableObject):
name: str name: str
subdir: str subdir: str
subproject: str subproject: 'SubProject'
build_by_default: bool build_by_default: bool
for_machine: MachineChoice for_machine: MachineChoice
@ -675,7 +676,7 @@ class BuildTarget(Target):
install_dir: T.List[T.Union[str, bool]] install_dir: T.List[T.Union[str, bool]]
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: 'SubProject', for_machine: MachineChoice,
sources: T.List['SourceOutputs'], objects, environment: environment.Environment, kwargs): sources: T.List['SourceOutputs'], objects, environment: environment.Environment, kwargs):
super().__init__(name, subdir, subproject, True, for_machine) super().__init__(name, subdir, subproject, True, for_machine)
unity_opt = environment.coredata.get_option(OptionKey('unity')) unity_opt = environment.coredata.get_option(OptionKey('unity'))

@ -31,6 +31,7 @@ if T.TYPE_CHECKING:
from . import kwargs from . import kwargs
from .interpreter import Interpreter from .interpreter import Interpreter
from ..envconfig import MachineInfo from ..envconfig import MachineInfo
from ..interpreterbase import SubProject
from typing_extensions import TypedDict from typing_extensions import TypedDict
@ -40,7 +41,7 @@ if T.TYPE_CHECKING:
def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired', def extract_required_kwarg(kwargs: 'kwargs.ExtractRequired',
subproject: str, subproject: 'SubProject',
feature_check: T.Optional[FeatureCheckBase] = None, feature_check: T.Optional[FeatureCheckBase] = None,
default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]: default: bool = True) -> T.Tuple[bool, bool, T.Optional[str]]:
val = kwargs.get('required', default) val = kwargs.get('required', default)

@ -10,8 +10,11 @@ from ...interpreterbase import (
InvalidArguments, InvalidArguments,
) )
if T.TYPE_CHECKING:
from ...interpreterbase import SubProject
class RangeHolder(MesonInterpreterObject, IterableObject): class RangeHolder(MesonInterpreterObject, IterableObject):
def __init__(self, start: int, stop: int, step: int, *, subproject: str) -> None: def __init__(self, start: int, stop: int, step: int, *, subproject: 'SubProject') -> None:
super().__init__(subproject=subproject) super().__init__(subproject=subproject)
self.range = range(start, stop, step) self.range = range(start, stop, step)
self.operators.update({ self.operators.update({

@ -57,6 +57,8 @@ __all__ = [
'InterpreterBase', 'InterpreterBase',
'SubProject',
'TV_fw_var', 'TV_fw_var',
'TV_fw_args', 'TV_fw_args',
'TV_fw_kwargs', 'TV_fw_kwargs',
@ -91,6 +93,8 @@ from .baseobjects import (
TYPE_key_resolver, TYPE_key_resolver,
TYPE_HoldableTypes, TYPE_HoldableTypes,
SubProject,
HoldableTypes, HoldableTypes,
) )

@ -39,6 +39,8 @@ TYPE_kwargs = T.Dict[str, TYPE_var]
TYPE_nkwargs = T.Dict[str, TYPE_nvar] TYPE_nkwargs = T.Dict[str, TYPE_nvar]
TYPE_key_resolver = T.Callable[[mparser.BaseNode], str] TYPE_key_resolver = T.Callable[[mparser.BaseNode], str]
SubProject = T.NewType('SubProject', str)
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from typing_extensions import Protocol from typing_extensions import Protocol
__T = T.TypeVar('__T', bound=TYPE_var, contravariant=True) __T = T.TypeVar('__T', bound=TYPE_var, contravariant=True)
@ -47,7 +49,7 @@ if T.TYPE_CHECKING:
def __call__(self, other: __T) -> TYPE_var: ... def __call__(self, other: __T) -> TYPE_var: ...
class InterpreterObject: class InterpreterObject:
def __init__(self, *, subproject: T.Optional[str] = None) -> None: def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None:
self.methods: T.Dict[ self.methods: T.Dict[
str, str,
T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var] T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var]
@ -63,7 +65,7 @@ class InterpreterObject:
# Current node set during a method call. This can be used as location # Current node set during a method call. This can be used as location
# when printing a warning message during a method call. # when printing a warning message during a method call.
self.current_node: mparser.BaseNode = None self.current_node: mparser.BaseNode = None
self.subproject: str = subproject or '' self.subproject = subproject or SubProject('')
# Some default operators supported by all objects # Some default operators supported by all objects
self.operators.update({ self.operators.update({

@ -27,8 +27,9 @@ import copy
import typing as T import typing as T
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from .. import mparser from .. import mparser
from .interpreterbase import SubProject
def get_callee_args(wrapped_args: T.Sequence[T.Any]) -> T.Tuple['mparser.BaseNode', T.List['TYPE_var'], 'TYPE_kwargs', str]: def get_callee_args(wrapped_args: T.Sequence[T.Any]) -> T.Tuple['mparser.BaseNode', T.List['TYPE_var'], 'TYPE_kwargs', 'SubProject']:
# First argument could be InterpreterBase, InterpreterObject or ModuleObject. # First argument could be InterpreterBase, InterpreterObject or ModuleObject.
# In the case of a ModuleObject it is the 2nd argument (ModuleState) that # In the case of a ModuleObject it is the 2nd argument (ModuleState) that
# contains the needed information. # contains the needed information.
@ -600,7 +601,7 @@ class FeatureCheckBase(metaclass=abc.ABCMeta):
def check_version(target_version: str, feature_version: str) -> bool: def check_version(target_version: str, feature_version: str) -> bool:
pass pass
def use(self, subproject: str) -> None: def use(self, subproject: 'SubProject') -> None:
tv = self.get_target_version(subproject) tv = self.get_target_version(subproject)
# No target version # No target version
if tv == '': if tv == '':
@ -668,7 +669,7 @@ class FeatureCheckBase(metaclass=abc.ABCMeta):
return T.cast(TV_func, wrapped) return T.cast(TV_func, wrapped)
@classmethod @classmethod
def single_use(cls, feature_name: str, version: str, subproject: str, def single_use(cls, feature_name: str, version: str, subproject: 'SubProject',
extra_message: str = '', location: T.Optional['mparser.BaseNode'] = None) -> None: extra_message: str = '', location: T.Optional['mparser.BaseNode'] = None) -> None:
"""Oneline version that instantiates and calls use().""" """Oneline version that instantiates and calls use()."""
cls(feature_name, version, extra_message, location).use(subproject) cls(feature_name, version, extra_message, location).use(subproject)

@ -26,6 +26,8 @@ from .baseobjects import (
ObjectHolder, ObjectHolder,
IterableObject, IterableObject,
SubProject,
TYPE_var, TYPE_var,
TYPE_kwargs, TYPE_kwargs,
@ -73,7 +75,7 @@ FunctionType = T.Dict[
] ]
class InterpreterBase: class InterpreterBase:
def __init__(self, source_root: str, subdir: str, subproject: str): def __init__(self, source_root: str, subdir: str, subproject: 'SubProject'):
self.source_root = source_root self.source_root = source_root
self.funcs: FunctionType = {} self.funcs: FunctionType = {}
self.builtin: T.Dict[str, InterpreterObject] = {} self.builtin: T.Dict[str, InterpreterObject] = {}

@ -22,6 +22,7 @@ from . import mlog
from .interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo, permittedKwargs from .interpreterbase import FeatureNew, typed_pos_args, typed_kwargs, ContainerTypeInfo, KwargInfo, permittedKwargs
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
from .interpreterbase import TYPE_var, TYPE_kwargs from .interpreterbase import TYPE_var, TYPE_kwargs
from .interpreterbase import SubProject
from typing_extensions import TypedDict from typing_extensions import TypedDict
FuncOptionArgs = TypedDict('FuncOptionArgs', { FuncOptionArgs = TypedDict('FuncOptionArgs', {
'type': str, 'type': str,
@ -50,7 +51,7 @@ optname_regex = re.compile('[^a-zA-Z0-9_-]')
class OptionInterpreter: class OptionInterpreter:
def __init__(self, subproject: str) -> None: def __init__(self, subproject: 'SubProject') -> None:
self.options: 'coredata.KeyedOptionDictType' = {} self.options: 'coredata.KeyedOptionDictType' = {}
self.subproject = subproject self.subproject = subproject
self.option_types = {'string': self.string_parser, self.option_types = {'string': self.string_parser,

@ -35,6 +35,7 @@ from . import WrapMode
from .. import coredata from .. import coredata
from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree from ..mesonlib import quiet_git, GIT, ProgressBar, MesonException, windows_proof_rmtree
from ..interpreterbase import FeatureNew from ..interpreterbase import FeatureNew
from ..interpreterbase import SubProject
from .. import mesonlib from .. import mesonlib
if T.TYPE_CHECKING: if T.TYPE_CHECKING:
@ -95,7 +96,7 @@ class WrapNotFoundException(WrapException):
class PackageDefinition: class PackageDefinition:
def __init__(self, fname: str, subproject: str = ''): def __init__(self, fname: str, subproject: str = ''):
self.filename = fname self.filename = fname
self.subproject = subproject self.subproject = SubProject(subproject)
self.type = None # type: T.Optional[str] self.type = None # type: T.Optional[str]
self.values = {} # type: T.Dict[str, str] self.values = {} # type: T.Dict[str, str]
self.provided_deps = {} # type: T.Dict[str, T.Optional[str]] self.provided_deps = {} # type: T.Dict[str, T.Optional[str]]

Loading…
Cancel
Save