diff options
-rw-r--r-- | mesonbuild/interpreterbase/decorators.py | 41 | ||||
-rwxr-xr-x | run_unittests.py | 12 |
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): |