diff options
| author | Paolo Bonzini <pbonzini@redhat.com> | 2025-01-08 12:26:07 +0100 |
|---|---|---|
| committer | Jussi Pakkanen <jussi.pakkanen@mailbox.org> | 2025-06-17 12:29:56 +0300 |
| commit | 2f47d0b4b15e27aa62f439c4b65282771149cc7c (patch) | |
| tree | d50002095ec899f5db9d037ff658e575e86c490d /mesonbuild/interpreterbase/baseobjects.py | |
| parent | f3366a7e543f69844bd3658db65c28295912db79 (diff) | |
| download | meson-2f47d0b4b15e27aa62f439c4b65282771149cc7c.tar.gz | |
interpreter: make operators per-class
Do not call update() and Enum.__hash__ a gazillion times; operators
are the same for every instance of the class. In order to access
the class for non-trivial operators, the operators are first marked
using a decorator, and then OPERATORS is built via __init_subclass__.
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
Diffstat (limited to 'mesonbuild/interpreterbase/baseobjects.py')
| -rw-r--r-- | mesonbuild/interpreterbase/baseobjects.py | 40 |
1 files changed, 30 insertions, 10 deletions
diff --git a/mesonbuild/interpreterbase/baseobjects.py b/mesonbuild/interpreterbase/baseobjects.py index 928d03861..ce961bdfb 100644 --- a/mesonbuild/interpreterbase/baseobjects.py +++ b/mesonbuild/interpreterbase/baseobjects.py @@ -43,37 +43,55 @@ class InterpreterObject: ] ] = {} + OPERATORS: T.Dict[MesonOperator, TYPE_op_func] = {} + def __init_subclass__(cls: T.Type[InterpreterObject], **kwargs: T.Any) -> None: super().__init_subclass__(**kwargs) saved_trivial_operators = cls.TRIVIAL_OPERATORS + + cls.OPERATORS = {} cls.TRIVIAL_OPERATORS = {} # Compute inherited operators according to the Python resolution order # Reverse the result of mro() because update() will overwrite operators # that are set by the superclass with those that are set by the subclass for superclass in reversed(cls.mro()[1:]): - if issubclass(superclass, InterpreterObject): + if superclass is InterpreterObject: + # InterpreterObject cannot use @InterpreterObject.operator because + # __init_subclass__ does not operate on InterpreterObject itself + cls.OPERATORS.update({ + MesonOperator.EQUALS: InterpreterObject.op_equals, + MesonOperator.NOT_EQUALS: InterpreterObject.op_not_equals + }) + + elif issubclass(superclass, InterpreterObject): + cls.OPERATORS.update(superclass.OPERATORS) cls.TRIVIAL_OPERATORS.update(superclass.TRIVIAL_OPERATORS) + for name, method in cls.__dict__.items(): + if hasattr(method, 'meson_operator'): + cls.OPERATORS[method.meson_operator] = method cls.TRIVIAL_OPERATORS.update(saved_trivial_operators) + @staticmethod + def operator(op: MesonOperator) -> T.Callable[[TV_func], TV_func]: + '''Decorator that tags a method as the implementation of an operator + for the Meson interpreter''' + def decorator(f: TV_func) -> TV_func: + f.meson_operator = op # type: ignore[attr-defined] + return f + return decorator + def __init__(self, *, subproject: T.Optional['SubProject'] = None) -> None: self.methods: T.Dict[ str, T.Callable[[T.List[TYPE_var], TYPE_kwargs], TYPE_var] ] = {} - self.operators: T.Dict[MesonOperator, TYPE_op_func] = {} # Current node set during a method call. This can be used as location # when printing a warning message during a method call. self.current_node: mparser.BaseNode = None self.subproject = subproject or SubProject('') - # Some default operators supported by all objects - self.operators.update({ - MesonOperator.EQUALS: self.__class__.op_equals, - MesonOperator.NOT_EQUALS: self.__class__.op_not_equals, - }) - # The type of the object that can be printed to the user def display_name(self) -> str: return type(self).__name__ @@ -101,8 +119,8 @@ class InterpreterObject: if op[0] is not None and not isinstance(other, op[0]): raise InvalidArguments(f'The `{operator.value}` operator of {self.display_name()} does not accept objects of type {type(other).__name__} ({other})') return op[1](self, other) - if operator in self.operators: - return self.operators[operator](self, other) + if operator in self.OPERATORS: + return self.OPERATORS[operator](self, other) raise InvalidCode(f'Object {self} of type {self.display_name()} does not support the `{operator.value}` operator.') @@ -165,12 +183,14 @@ class ObjectHolder(InterpreterObject, T.Generic[InterpreterObjectTypeVar]): return type(self.held_object).__name__ # Override default comparison operators for the held object + @InterpreterObject.operator(MesonOperator.EQUALS) def op_equals(self, other: TYPE_var) -> bool: # See the comment from InterpreterObject why we are using `type()` here. if type(self.held_object) is not type(other): self._throw_comp_exception(other, '==') return self.held_object == other + @InterpreterObject.operator(MesonOperator.NOT_EQUALS) def op_not_equals(self, other: TYPE_var) -> bool: if type(self.held_object) is not type(other): self._throw_comp_exception(other, '!=') |
