From 55e1b802be383bbed1bf424afbf7945c0ad7e99a Mon Sep 17 00:00:00 2001 From: Will Shanks Date: Mon, 9 Jan 2023 19:24:07 -0500 Subject: [PATCH] Update `qiskit.utils.wrap_method` for Python 3.11 (Qiskit/qiskit-terra#9310) * Revert "[Test] Pin maximum python version in CI to <3.11.1 (Qiskit/qiskit-terra#9296)" This reverts commit 07e0a2fc79bada7c1fbf0594f4ad33934f70b7e2. * Do not treat __init_subclass__ as a special type method * Release note * Apply suggestions from code review Co-authored-by: Julien Gacon * Use inspect.getattr_static to bypass descriptor call * Update release note * Update wrap_method test * Adjust wording on release note Co-authored-by: Julien Gacon Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- qiskit_algorithms/utils/classtools.py | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/qiskit_algorithms/utils/classtools.py b/qiskit_algorithms/utils/classtools.py index 71c17754..1e58b1ad 100644 --- a/qiskit_algorithms/utils/classtools.py +++ b/qiskit_algorithms/utils/classtools.py @@ -25,11 +25,6 @@ _MAGIC_STATICMETHODS = {"__new__"} _MAGIC_CLASSMETHODS = {"__init_subclass__", "__prepare__"} -# `type` itself has several methods (mostly dunders). When we are wrapping those names, we need to -# make sure that we don't interfere with `type.__getattribute__`'s handling that circumvents the -# normal inheritance rules when appropriate. -_TYPE_METHODS = set(dir(type)) - class _lift_to_method: # pylint: disable=invalid-name """A decorator that ensures that an input callable object implements ``__get__``. It is @@ -146,16 +141,6 @@ def wrap_method(cls: Type, name: str, *, before: Callable = None, after: Callabl # The best time to apply decorators to methods is before they are bound (e.g. by using function # decorators during the class definition), but if we're making a class decorator, we can't do # that. We need the actual definition of the method, so we have to dodge the normal output of - # `type.__getattribute__`, which evalutes descriptors if it finds them, unless the name we're - # looking for is defined on `type` itself. In that case, we need the attribute getter to - # correctly return the underlying object, not the one that `type` defines for its own purposes. - attribute_getter = type.__getattribute__ if name in _TYPE_METHODS else object.__getattribute__ - for cls_ in inspect.getmro(cls): - try: - method = attribute_getter(cls_, name) - break - except AttributeError: - pass - else: - raise ValueError(f"Method '{name}' is not defined for class '{cls.__name__}'") + # `type.__getattribute__`, which evalutes descriptors if it finds them. + method = inspect.getattr_static(cls, name) setattr(cls, name, _WrappedMethod(method, before, after))