diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2020-09-24 09:53:41 -0700 |
---|---|---|
committer | Dylan Baker <dylan@pnwbakers.com> | 2021-06-02 15:53:17 -0700 |
commit | 113a1595149b72ee0a572ed215db616c5a6d8a20 (patch) | |
tree | 2ef6d92f919aaea3fb83b06631b38ebbe56aa9ce | |
parent | f8be4f8fc747772d9639fdbb6e84ddbcfb61593a (diff) | |
download | meson-113a1595149b72ee0a572ed215db616c5a6d8a20.zip meson-113a1595149b72ee0a572ed215db616c5a6d8a20.tar.gz meson-113a1595149b72ee0a572ed215db616c5a6d8a20.tar.bz2 |
use an immutable list for an lru_cached functions
When mutable items are stored in an lru cache, changing the returned
items changes the cached items as well. Therefore we want to ensure that
we're not mutating them. Using the ImmutableListProtocol allows mypy to
find mutations and reject them. This doesn't solve the problem of
mutable values inside the values, so you could have to do things like:
```python
ImmutableListProtocol[ImmutableListProtocol[str]]
```
or equally hacky. It can also be used for input types and acts a bit
like C's const:
```python
def foo(arg: ImmutableListProtocol[str]) -> T.List[str]:
arg[1] = 'foo' # works while running, but mypy errors
```
-rw-r--r-- | mesonbuild/backend/backends.py | 12 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 16 | ||||
-rw-r--r-- | mesonbuild/build.py | 5 | ||||
-rw-r--r-- | mesonbuild/cmake/interpreter.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/compilers.py | 1 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/clike.py | 5 | ||||
-rw-r--r-- | mesonbuild/compilers/mixins/gnu.py | 5 | ||||
-rw-r--r-- | mesonbuild/mesonlib/universal.py | 3 |
8 files changed, 31 insertions, 21 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 1e5a13e..484e4cc 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -36,6 +36,7 @@ from ..mesonlib import ( ) if T.TYPE_CHECKING: + from .._typing import ImmutableListProtocol from ..arglist import CompilerArgs from ..compilers import Compiler from ..interpreter import Interpreter, Test @@ -1103,21 +1104,20 @@ class Backend: return result @lru_cache(maxsize=None) - def get_custom_target_provided_by_generated_source(self, generated_source): - libs = [] + def get_custom_target_provided_by_generated_source(self, generated_source: build.CustomTarget) -> 'ImmutableListProtocol[str]': + libs: T.List[str] = [] for f in generated_source.get_outputs(): if self.environment.is_library(f): libs.append(os.path.join(self.get_target_dir(generated_source), f)) return libs @lru_cache(maxsize=None) - def get_custom_target_provided_libraries(self, target): - libs = [] + def get_custom_target_provided_libraries(self, target: T.Union[build.BuildTarget, build.CustomTarget]) -> 'ImmutableListProtocol[str]': + libs: T.List[str] = [] for t in target.get_generated_sources(): if not isinstance(t, build.CustomTarget): continue - l = self.get_custom_target_provided_by_generated_source(t) - libs = libs + l + libs.extend(self.get_custom_target_provided_by_generated_source(t)) return libs def is_unity(self, target): diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 4e826ee..463c4c6 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -49,6 +49,7 @@ from ..interpreter import Interpreter from ..mesonmain import need_setup_vsenv if T.TYPE_CHECKING: + from .._typing import ImmutableListProtocol from ..linkers import StaticLinker from ..compilers.cs import CsCompiler @@ -2329,7 +2330,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return (rel_obj, rel_src) @lru_cache(maxsize=None) - def generate_inc_dir(self, compiler, d, basedir, is_system): + def generate_inc_dir(self, compiler: 'Compiler', d: str, basedir: str, is_system: bool) -> \ + T.Tuple['ImmutableListProtocol[str]', 'ImmutableListProtocol[str]']: # Avoid superfluous '/.' at the end of paths when d is '.' if d not in ('', '.'): expdir = os.path.normpath(os.path.join(basedir, d)) @@ -2349,12 +2351,13 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) bargs = [] return (sargs, bargs) - def _generate_single_compile(self, target, compiler, is_generated=False): - commands = self._generate_single_compile_base_args(target, compiler, is_generated) + def _generate_single_compile(self, target: build.BuildTarget, compiler: 'Compiler', + is_generated: bool = False) -> 'CompilerArgs': + commands = self._generate_single_compile_base_args(target, compiler) commands += self._generate_single_compile_target_args(target, compiler, is_generated) return commands - def _generate_single_compile_base_args(self, target, compiler, is_generated): + def _generate_single_compile_base_args(self, target: build.BuildTarget, compiler: 'Compiler') -> 'CompilerArgs': base_proxy = self.get_base_options_for_target(target) # Create an empty commands list, and start adding arguments from # various sources in the order in which they must override each other @@ -2369,7 +2372,8 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) return commands @lru_cache(maxsize=None) - def _generate_single_compile_target_args(self, target, compiler, is_generated): + def _generate_single_compile_target_args(self, target: build.BuildTarget, compiler: 'Compiler', + is_generated: bool = False) -> 'ImmutableListProtocol[str]': # The code generated by valac is usually crap and has tons of unused # variables and such, so disable warnings for Vala C sources. no_warn_args = (is_generated == 'vala') @@ -2439,7 +2443,7 @@ https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485''')) raise AssertionError(f'BUG: sources should not contain headers {src!r}') compiler = get_compiler_for_source(target.compilers.values(), src) - commands = self._generate_single_compile_base_args(target, compiler, is_generated) + commands = self._generate_single_compile_base_args(target, compiler) # Include PCH header as first thing as it must be the first one or it will be # ignored by gcc https://gcc.gnu.org/bugzilla/show_bug.cgi?id=100462 diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 13eb0c1..aec9ae3 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -40,6 +40,7 @@ from .linkers import StaticLinker from .interpreterbase import FeatureNew if T.TYPE_CHECKING: + from ._typing import ImmutableListProtocol from .interpreter.interpreter import Test, SourceOutputs from .mesonlib import FileMode, FileOrString from .backend.backends import Backend @@ -909,8 +910,8 @@ class BuildTarget(Target): return self.get_transitive_link_deps() @lru_cache(maxsize=None) - def get_transitive_link_deps(self): - result = [] + def get_transitive_link_deps(self) -> 'ImmutableListProtocol[Target]': + result: T.List[Target] = [] for i in self.link_targets: result += i.get_all_link_deps() return result diff --git a/mesonbuild/cmake/interpreter.py b/mesonbuild/cmake/interpreter.py index cc6adf1..f79f7d2 100644 --- a/mesonbuild/cmake/interpreter.py +++ b/mesonbuild/cmake/interpreter.py @@ -51,6 +51,7 @@ from ..mparser import ( if T.TYPE_CHECKING: + from .._typing import ImmutableListProtocol from ..build import Build from ..backend.backends import Backend from ..environment import Environment @@ -582,14 +583,14 @@ class ConverterTarget: self.compile_opts[lang] += [x for x in opts if x not in self.compile_opts[lang]] @lru_cache(maxsize=None) - def _all_source_suffixes(self) -> T.List[str]: + def _all_source_suffixes(self) -> 'ImmutableListProtocol[str]': suffixes = [] # type: T.List[str] for exts in lang_suffixes.values(): suffixes += [x for x in exts] return suffixes @lru_cache(maxsize=None) - def _all_lang_stds(self, lang: str) -> T.List[str]: + def _all_lang_stds(self, lang: str) -> 'ImmutableListProtocol[str]': try: res = self.env.coredata.options[OptionKey('std', machine=MachineChoice.BUILD, lang=lang)].choices except KeyError: diff --git a/mesonbuild/compilers/compilers.py b/mesonbuild/compilers/compilers.py index 4eeaec7..361f5d6 100644 --- a/mesonbuild/compilers/compilers.py +++ b/mesonbuild/compilers/compilers.py @@ -1023,6 +1023,7 @@ class Compiler(metaclass=abc.ABCMeta): raise EnvironmentException('This compiler does not have a preprocessor') def get_default_include_dirs(self) -> T.List[str]: + # TODO: This is a candidate for returning an immutable list return [] def get_largefile_args(self) -> T.List[str]: diff --git a/mesonbuild/compilers/mixins/clike.py b/mesonbuild/compilers/mixins/clike.py index 0637439..0c0ade3 100644 --- a/mesonbuild/compilers/mixins/clike.py +++ b/mesonbuild/compilers/mixins/clike.py @@ -42,6 +42,7 @@ from .visualstudio import VisualStudioLikeCompiler if T.TYPE_CHECKING: from ...dependencies import Dependency + from ..._typing import ImmutableListProtocol from ...environment import Environment from ...compilers.compilers import Compiler from ...programs import ExternalProgram @@ -207,7 +208,7 @@ class CLikeCompiler(Compiler): @functools.lru_cache() def _get_library_dirs(self, env: 'Environment', - elf_class: T.Optional[int] = None) -> T.List[str]: + elf_class: T.Optional[int] = None) -> 'ImmutableListProtocol[str]': # TODO: replace elf_class with enum dirs = self.get_compiler_dirs(env, 'libraries') if elf_class is None or elf_class == 0: @@ -253,7 +254,7 @@ class CLikeCompiler(Compiler): return self._get_library_dirs(env, elf_class).copy() @functools.lru_cache() - def _get_program_dirs(self, env: 'Environment') -> T.List[str]: + def _get_program_dirs(self, env: 'Environment') -> 'ImmutableListProtocol[str]': ''' Programs used by the compiler. Also where toolchain DLLs such as libstdc++-6.dll are found with MinGW. diff --git a/mesonbuild/compilers/mixins/gnu.py b/mesonbuild/compilers/mixins/gnu.py index b007ff0..9a94703 100644 --- a/mesonbuild/compilers/mixins/gnu.py +++ b/mesonbuild/compilers/mixins/gnu.py @@ -28,6 +28,7 @@ from ... import mlog from ...mesonlib import OptionKey if T.TYPE_CHECKING: + from ..._typing import ImmutableListProtocol from ...environment import Environment from ..compilers import Compiler else: @@ -92,7 +93,7 @@ gnu_color_args = { @functools.lru_cache(maxsize=None) -def gnulike_default_include_dirs(compiler: T.Tuple[str], lang: str) -> T.List[str]: +def gnulike_default_include_dirs(compiler: T.Tuple[str, ...], lang: str) -> 'ImmutableListProtocol[str]': lang_map = { 'c': 'c', 'cpp': 'c++', @@ -189,7 +190,7 @@ class GnuLikeCompiler(Compiler, metaclass=abc.ABCMeta): return gnulike_instruction_set_args.get(instruction_set, None) def get_default_include_dirs(self) -> T.List[str]: - return gnulike_default_include_dirs(tuple(self.exelist), self.language) + return gnulike_default_include_dirs(tuple(self.exelist), self.language).copy() @abc.abstractmethod def openmp_flags(self) -> T.List[str]: diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index 66149f9..b8fc763 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -30,6 +30,7 @@ import textwrap from mesonbuild import mlog if T.TYPE_CHECKING: + from .._typing import ImmutableListProtocol from ..build import ConfigurationData from ..coredata import KeyedOptionDictType, UserOption from ..compilers.compilers import CompilerType @@ -659,7 +660,7 @@ def exe_exists(arglist: T.List[str]) -> bool: @lru_cache(maxsize=None) -def darwin_get_object_archs(objpath: str) -> T.List[str]: +def darwin_get_object_archs(objpath: str) -> 'ImmutableListProtocol[str]': ''' For a specific object (executable, static library, dylib, etc), run `lipo` to fetch the list of archs supported by it. Supports both thin objects and |