diff --git a/azure-pipelines.yml b/azure-pipelines.yml index d04351c270c4..2c3539229277 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -33,9 +33,7 @@ parameters: - name: "supportedPythonVersions" displayName: "All supported versions of Python" type: object - # TODO remove the explicit 3.11.0 pin and set it to 3.11. The pin is currently added - # since 3.11.1 breaks CI, see Qiskit/qiskit-terra#9291. - default: ["3.7", "3.8", "3.9", "3.10", "3.11.0"] + default: ["3.7", "3.8", "3.9", "3.10", "3.11"] - name: "minimumPythonVersion" displayName: "Minimum supported version of Python" @@ -45,7 +43,7 @@ parameters: - name: "maximumPythonVersion" displayName: "Maximum supported version of Python" type: string - default: "3.11.0" + default: "3.11" - name: "minimumRustVersion" displayName: "Minimum supported version of Rust" @@ -111,9 +109,7 @@ stages: - template: ".azure/test-macos.yml" parameters: - # TODO Manually setting this to exclude 3.11 completely, since 3.11.1 breaks - # (see Qiskit/qiskit-terra#9291) and 3.11.0 is not available on azure for MacOS. - pythonVersion: ["3.7", "3.8", "3.9", "3.10"] + pythonVersion: ${{ version }} - template: ".azure/test-windows.yml" parameters: @@ -185,8 +181,7 @@ stages: - template: ".azure/test-macos.yml" parameters: - # TODO remove pin to 3.10 and reset to ${{ parameters.maximumPythonVersion }} - pythonVersion: "3.10" + pythonVersion: ${{ parameters.maximumPythonVersion }} - template: ".azure/test-windows.yml" parameters: diff --git a/qiskit/utils/classtools.py b/qiskit/utils/classtools.py index 71c17754bdad..1e58b1ad2b91 100644 --- a/qiskit/utils/classtools.py +++ b/qiskit/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)) diff --git a/releasenotes/notes/wrap-method-311-147d254d4b40e805.yaml b/releasenotes/notes/wrap-method-311-147d254d4b40e805.yaml new file mode 100644 index 000000000000..146e01b903c5 --- /dev/null +++ b/releasenotes/notes/wrap-method-311-147d254d4b40e805.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixed handling of some ``classmethod``s by + :func:`~qiskit.utils.wrap_method` in Python 3.11. Previously, in Python + 3.11, ``wrap_method`` would wrap the unbounded function associated with the + ``classmethod`` and then fail when invoked because the class object usually + bound to the ``classmethod`` was not passed to the function. Starting in + Python 3.11.1, this issue affected :class:`~qiskit.test.QiskitTestCase`, + preventing it from being imported by other test code. Fixed `#9291 + `__. diff --git a/test/python/utils/test_classtools.py b/test/python/utils/test_classtools.py index 272c3e0a6146..b373675ea569 100644 --- a/test/python/utils/test_classtools.py +++ b/test/python/utils/test_classtools.py @@ -529,5 +529,5 @@ def test_raises_on_invalid_name(self): class Dummy: pass - with self.assertRaisesRegex(ValueError, "Method 'bad' is not defined for class 'Dummy'"): + with self.assertRaisesRegex(AttributeError, "bad"): wrap_method(Dummy, "bad", before=lambda self: None)