diff --git a/mesonbuild/coredata.py b/mesonbuild/coredata.py index 62c81ffd1..38d6fcfe9 100644 --- a/mesonbuild/coredata.py +++ b/mesonbuild/coredata.py @@ -1056,9 +1056,7 @@ def major_versions_differ(v1: str, v2: str) -> bool: def load(build_dir: str) -> CoreData: filename = os.path.join(build_dir, 'meson-private', 'coredata.dat') - obj = pickle_load(filename, 'Coredata', CoreData) - assert isinstance(obj, CoreData), 'for mypy' - return obj + return pickle_load(filename, 'Coredata', CoreData) def save(obj: CoreData, build_dir: str) -> str: diff --git a/mesonbuild/minstall.py b/mesonbuild/minstall.py index 969e09e6d..6922c3872 100644 --- a/mesonbuild/minstall.py +++ b/mesonbuild/minstall.py @@ -131,9 +131,7 @@ class DirMaker: def load_install_data(fname: str) -> InstallData: - obj = pickle_load(fname, 'InstallData', InstallData) - assert isinstance(obj, InstallData), 'fo mypy' - return obj + return pickle_load(fname, 'InstallData', InstallData) def is_executable(path: str, follow_symlinks: bool = False) -> bool: '''Checks whether any of the "x" bits are set in the source file mode.''' diff --git a/mesonbuild/utils/universal.py b/mesonbuild/utils/universal.py index b0cc2522d..bd136ba8c 100644 --- a/mesonbuild/utils/universal.py +++ b/mesonbuild/utils/universal.py @@ -37,13 +37,26 @@ from mesonbuild import mlog from .core import MesonException, HoldableObject if T.TYPE_CHECKING: - from typing_extensions import Literal + from typing_extensions import Literal, Protocol from .._typing import ImmutableListProtocol from ..build import ConfigurationData from ..coredata import KeyedOptionDictType, UserOption + from ..environment import Environment from ..compilers.compilers import Compiler + class _EnvPickleLoadable(Protocol): + + environment: Environment + + class _VerPickleLoadable(Protocol): + + version: str + + # A generic type for pickle_load. This allows any type that has either a + # .version or a .environment to be passed. + _PL = T.TypeVar('_PL', bound=T.Union[_EnvPickleLoadable, _VerPickleLoadable]) + FileOrString = T.Union['File', str] _T = T.TypeVar('_T') @@ -2326,7 +2339,8 @@ class OptionKey: """Convenience method to check if this is a base option.""" return self.type is OptionType.BASE -def pickle_load(filename: str, object_name: str, object_type: T.Type) -> T.Any: + +def pickle_load(filename: str, object_name: str, object_type: T.Type[_PL]) -> _PL: load_fail_msg = f'{object_name} file {filename!r} is corrupted. Try with a fresh build tree.' try: with open(filename, 'rb') as f: @@ -2342,11 +2356,18 @@ def pickle_load(filename: str, object_name: str, object_type: T.Type) -> T.Any: f'meson setup {build_dir} --wipe') if not isinstance(obj, object_type): raise MesonException(load_fail_msg) + + # Because these Protocols are not available at runtime (and cannot be made + # available at runtime until we drop support for Python < 3.8), we have to + # do a bit of hackery so that mypy understands what's going on here + version: str + if hasattr(obj, 'version'): + version = T.cast('_VerPickleLoadable', obj).version + else: + version = T.cast('_EnvPickleLoadable', obj).environment.coredata.version + from ..coredata import version as coredata_version from ..coredata import major_versions_differ, MesonVersionMismatchException - version = getattr(obj, 'version', None) - if version is None: - version = obj.environment.coredata.version if major_versions_differ(version, coredata_version): raise MesonVersionMismatchException(version, coredata_version) return obj