aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDylan Baker <dylan@pnwbakers.com>2021-08-10 10:58:46 -0700
committerDaniel Mensinger <daniel@mensinger-ka.de>2021-08-20 18:57:19 +0200
commitc52b454b69414ad60f411ca99bee0348da8e844a (patch)
tree38112068532d2f7267da17c5f43930bef150f880
parent0f5f265a1677e7492c9bc1d6e1505a9f493bf847 (diff)
downloadmeson-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
-rw-r--r--mesonbuild/mesonlib/universal.py32
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