aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--mesonbuild/interpreterbase/decorators.py41
-rwxr-xr-xrun_unittests.py12
2 files changed, 53 insertions, 0 deletions
diff --git a/mesonbuild/interpreterbase/decorators.py b/mesonbuild/interpreterbase/decorators.py
index a011b66..b05df5b 100644
--- a/mesonbuild/interpreterbase/decorators.py
+++ b/mesonbuild/interpreterbase/decorators.py
@@ -272,6 +272,11 @@ class ContainerTypeInfo:
_T = T.TypeVar('_T')
+class _NULL_T:
+ """Special null type for evolution, this is an implementation detail."""
+
+
+_NULL = _NULL_T()
class KwargInfo(T.Generic[_T]):
@@ -329,6 +334,42 @@ class KwargInfo(T.Generic[_T]):
self.convertor = convertor
self.not_set_warning = not_set_warning
+ def evolve(self, *,
+ required: T.Union[bool, _NULL_T] = _NULL,
+ listify: T.Union[bool, _NULL_T] = _NULL,
+ default: T.Union[_T, None, _NULL_T] = _NULL,
+ since: T.Union[str, None, _NULL_T] = _NULL,
+ since_values: T.Union[T.Dict[str, str], None, _NULL_T] = _NULL,
+ deprecated: T.Union[str, None, _NULL_T] = _NULL,
+ deprecated_values: T.Union[T.Dict[str, str], None, _NULL_T] = _NULL,
+ validator: T.Union[T.Callable[[_T], T.Optional[str]], None, _NULL_T] = _NULL,
+ convertor: T.Union[T.Callable[[_T], TYPE_var], None, _NULL_T] = _NULL) -> 'KwargInfo':
+ """Create a shallow copy of this KwargInfo, with modifications.
+
+ This allows us to create a new copy of a KwargInfo with modifications.
+ This allows us to use a shared kwarg that implements complex logic, but
+ has slight differences in usage, such as being added to different
+ functions in different versions of Meson.
+
+ The use the _NULL special value here allows us to pass None, which has
+ meaning in many of these cases. _NULL itself is never stored, always
+ being replaced by either the copy in self, or the provided new version.
+ """
+ return type(self)(
+ self.name,
+ self.types,
+ listify=listify if not isinstance(listify, _NULL_T) else self.listify,
+ required=required if not isinstance(required, _NULL_T) else self.required,
+ default=default if not isinstance(default, _NULL_T) else self.default,
+ since=since if not isinstance(since, _NULL_T) else self.since,
+ since_values=since_values if not isinstance(since_values, _NULL_T) else self.since_values,
+ deprecated=deprecated if not isinstance(deprecated, _NULL_T) else self.deprecated,
+ deprecated_values=deprecated_values if not isinstance(deprecated_values, _NULL_T) else self.deprecated_values,
+ validator=validator if not isinstance(validator, _NULL_T) else self.validator,
+ convertor=convertor if not isinstance(convertor, _NULL_T) else self.convertor,
+ )
+
+
def typed_kwargs(name: str, *types: KwargInfo) -> T.Callable[..., T.Any]:
"""Decorator for type checking keyword arguments.
diff --git a/run_unittests.py b/run_unittests.py
index b55ba96..6ca1a81 100755
--- a/run_unittests.py
+++ b/run_unittests.py
@@ -1698,6 +1698,18 @@ class InternalTests(unittest.TestCase):
_(None, mock.Mock(subproject=''), [], {'mode': 'since'})
self.assertRegex(out.getvalue(), r"""WARNING:.Project targeting '1.0'.*introduced in '1.1': "testfunc" keyword argument "mode" value "since".*""")
+ def test_typed_kwarg_evolve(self) -> None:
+ k = KwargInfo('foo', str, required=True, default='foo')
+ v = k.evolve(default='bar')
+ self.assertEqual(k.name, 'foo')
+ self.assertEqual(k.name, v.name)
+ self.assertEqual(k.types, str)
+ self.assertEqual(k.types, v.types)
+ self.assertEqual(k.required, True)
+ self.assertEqual(k.required, v.required)
+ self.assertEqual(k.default, 'foo')
+ self.assertEqual(v.default, 'bar')
+
@unittest.skipIf(is_tarball(), 'Skipping because this is a tarball release')
class DataTests(unittest.TestCase):