diff options
author | Dylan Baker <dylan@pnwbakers.com> | 2021-08-10 10:58:46 -0700 |
---|---|---|
committer | Daniel Mensinger <daniel@mensinger-ka.de> | 2021-08-20 18:57:19 +0200 |
commit | c52b454b69414ad60f411ca99bee0348da8e844a (patch) | |
tree | 38112068532d2f7267da17c5f43930bef150f880 /mesonbuild | |
parent | 0f5f265a1677e7492c9bc1d6e1505a9f493bf847 (diff) | |
download | meson-c52b454b69414ad60f411ca99bee0348da8e844a.zip meson-c52b454b69414ad60f411ca99bee0348da8e844a.tar.gz meson-c52b454b69414ad60f411ca99bee0348da8e844a.tar.bz2 |
mesonlib: Fix typing of substitute_values
This is really, really, annoying. What we really want is (psuedocode):
```python
class SubValues(TypedDict[str, str], total=False):
@INPUT@: T.List[str]
@OUTPUT@: T.List[str]
```
Which would specifiy that `@INPUT@` and `@OUTPUT@` *may* be present and
if they are, then they are lists. There may be additional keys, which
have a single string as a value. Of course there is currently no way to
do that with typing, so we have to instead take a union type and then
use asserts to help the type checker unerstand this.
More info: https://github.com/python/mypy/issues/4617
Diffstat (limited to 'mesonbuild')
-rw-r--r-- | mesonbuild/mesonlib/universal.py | 32 |
1 files changed, 27 insertions, 5 deletions
diff --git a/mesonbuild/mesonlib/universal.py b/mesonbuild/mesonlib/universal.py index e7244af..3ce46f7 100644 --- a/mesonbuild/mesonlib/universal.py +++ b/mesonbuild/mesonlib/universal.py @@ -1444,7 +1444,7 @@ def iter_regexin_iter(regexiter: T.Iterable[str], initer: T.Iterable[str]) -> T. return None -def _substitute_values_check_errors(command: T.List[str], values: T.Dict[str, str]) -> None: +def _substitute_values_check_errors(command: T.List[str], values: T.Dict[str, T.Union[str, T.List[str]]]) -> None: # Error checking inregex = ['@INPUT([0-9]+)?@', '@PLAINNAME@', '@BASENAME@'] # type: T.List[str] outregex = ['@OUTPUT([0-9]+)?@', '@OUTDIR@'] # type: T.List[str] @@ -1485,7 +1485,7 @@ def _substitute_values_check_errors(command: T.List[str], values: T.Dict[str, st raise MesonException(m.format(match2.group(), len(values['@OUTPUT@']))) -def substitute_values(command: T.List[str], values: T.Dict[str, str]) -> T.List[str]: +def substitute_values(command: T.List[str], values: T.Dict[str, T.Union[str, T.List[str]]]) -> T.List[str]: ''' Substitute the template strings in the @values dict into the list of strings @command and return a new list. For a full list of the templates, @@ -1494,14 +1494,29 @@ def substitute_values(command: T.List[str], values: T.Dict[str, str]) -> T.List[ If multiple inputs/outputs are given in the @values dictionary, we substitute @INPUT@ and @OUTPUT@ only if they are the entire string, not just a part of it, and in that case we substitute *all* of them. + + The typing of this function is difficult, as only @OUTPUT@ and @INPUT@ can + be lists, everything else is a string. However, TypeDict cannot represent + this, as you can have optional keys, but not extra keys. We end up just + having to us asserts to convince type checkers that this is okay. + + https://github.com/python/mypy/issues/4617 ''' + + def replace(m: T.Match[str]) -> str: + v = values[m.group(0)] + assert isinstance(v, str), 'for mypy' + return v + # Error checking _substitute_values_check_errors(command, values) + # Substitution outcmd = [] # type: T.List[str] rx_keys = [re.escape(key) for key in values if key not in ('@INPUT@', '@OUTPUT@')] value_rx = re.compile('|'.join(rx_keys)) if rx_keys else None for vv in command: + more: T.Optional[str] = None if not isinstance(vv, str): outcmd.append(vv) elif '@INPUT@' in vv: @@ -1522,15 +1537,22 @@ def substitute_values(command: T.List[str], values: T.Dict[str, str]) -> T.List[ else: raise MesonException("Command has '@OUTPUT@' as part of a " "string and more than one output file") + # Append values that are exactly a template string. # This is faster than a string replace. elif vv in values: - outcmd.append(values[vv]) + o = values[vv] + assert isinstance(o, str), 'for mypy' + more = o # Substitute everything else with replacement elif value_rx: - outcmd.append(value_rx.sub(lambda m: values[m.group(0)], vv)) + more = value_rx.sub(replace, vv) else: - outcmd.append(vv) + more = vv + + if more is not None: + outcmd.append(more) + return outcmd |