Bug 2196752
| Summary: | python-mockito fails to build with Python 3.12: SystemError: <class 'functools.partial'> returned a result with an exception set | ||
|---|---|---|---|
| Product: | [Fedora] Fedora | Reporter: | Tomáš Hrnčiar <thrnciar> |
| Component: | python3.12 | Assignee: | Python Maintainers <python-maint> |
| Status: | CLOSED RAWHIDE | QA Contact: | |
| Severity: | unspecified | Docs Contact: | |
| Priority: | unspecified | ||
| Version: | rawhide | CC: | cstratak, libnoon, mhroncok, pviktori, python-maint, python-packagers-sig, thrnciar, torsava, vstinner |
| Target Milestone: | --- | ||
| Target Release: | --- | ||
| Hardware: | Unspecified | ||
| OS: | Unspecified | ||
| Whiteboard: | |||
| Fixed In Version: | Doc Type: | If docs needed, set a value | |
| Doc Text: | Story Points: | --- | |
| Clone Of: | Environment: | ||
| Last Closed: | 2023-08-02 12:19:05 UTC | Type: | Bug |
| Regression: | --- | Mount Type: | --- |
| Documentation: | --- | CRM: | |
| Verified Versions: | Category: | --- | |
| oVirt Team: | --- | RHEL 7.3 requirements from Atomic Host: | |
| Cloudforms Team: | --- | Target Upstream Version: | |
| Embargoed: | |||
| Bug Depends On: | |||
| Bug Blocks: | 2135404 | ||
Hello, I can reproduce this error: SystemError: <class 'functools.partial'> returned a result with an exception set According to https://pythonextensionpatterns.readthedocs.io/en/latest/exceptions.html the correct way for C code to raise an exception is to use both PyErr_* and to return NULL. The error message seems to say that a C function returned non-NULL but on the other hand an exception has been set. And indeed, partial() seems to be implemented in a C file: cpython-3.12.0a7/Modules/_functoolsmodule.c I guess this is a CPython bug, not a python-mockito one. Either the C code wants to raise an exception, and therefore return NULL, or it doesn't, in which case it should not set it. I think this bug should be redirected to CPython rather than python-mockito. Thanks! Best regards Fabrice As explained above, reassigning to the python3.12 component. If anyone gets to this before I do: normally Python checks this after a builtin function returns (when the check is cheap), but not before it's called. It's possible that the exception was set before the partial() call. (But it does look like a CPython bug -- AFAIK Mockito is a pure Python library with minimal dependencies.) To turn on additional asserts, BR /usr/bin/python3-debug and run the tests with that. This is a leaf package, not blocking any other packages during the Python 3.12 rebuild. This is indeed an issue in CPython, and should be fixed in beta 1 (https://github.com/python/cpython/pull/103332). Package now builds. |
python-mockito fails to build with Python 3.12.0a7. =================================== FAILURES =================================== ______________________________ test_deprecated_a _______________________________ args = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} def new_mocked_method(*args, **kwargs): # we throw away the first argument, if it's either self or cls if ( inspect.ismethod(new_mocked_method) or inspect.isclass(self.mocked_obj) and not isinstance(new_mocked_method, staticmethod) ): args = args[1:] > return remembered_invocation_builder( self, method_name, *args, **kwargs) mockito/mocking.py:108: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ mock = <mockito.mocking.Mock object at 0x7feba286d340>, method_name = '__get__' args = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} invoc = __get__(<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) def remembered_invocation_builder(mock, method_name, *args, **kwargs): invoc = invocation.RememberedInvocation(mock, method_name) > return invoc(*args, **kwargs) mockito/mocking.py:48: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __get__(<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) params = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) named_params = {}, matching_invocation = __get__(...) def __call__(self, *params, **named_params): if self.strict: self.ensure_mocked_object_has_method(self.method_name) self.ensure_signature_matches( self.method_name, params, named_params) self._remember_params(params, named_params) self.mock.remember(self) for matching_invocation in self.mock.stubbed_invocations: if matching_invocation.matches(self): matching_invocation.should_answer(self) > return matching_invocation.answer_first( *params, **named_params) mockito/invocation.py:95: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __get__(...) args = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} def answer_first(self, *args, **kwargs): self.used += 1 > return self.answers.answer(*args, **kwargs) mockito/invocation.py:331: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <mockito.invocation.CompositeAnswer object at 0x7feb8dc5c470> args = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} a = functools.partial(<function raise_ at 0x7feba288f880>, <class 'ValueError'>) def answer(self, *args, **kwargs): if len(self.answers) == 0: return None if len(self.answers) == 1: a = self.answers[0] else: a = self.answers.popleft() > return a(*args, **kwargs) mockito/invocation.py:440: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ exception = <class 'ValueError'> a = (<Dummy id=140649672338192>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kw = {} def raise_(exception, *a, **kw): > raise exception E ValueError mockito/invocation.py:383: ValueError The above exception was the direct cause of the following exception: unstub = None def test_deprecated_a(unstub): # Setting on `__class__` is confusing for users m = mock() prop = mock() when(prop).__get__(Ellipsis).thenRaise(ValueError) m.__class__.tx = prop with pytest.raises(ValueError): > m.tx tests/mocking_properties_test.py:13: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <Dummy id=140649672338192>, method_name = 'tx' def __getattr__(self, method_name): if strict: __tracebackhide__ = operator.methodcaller( "errisinstance", AttributeError ) raise AttributeError( "'Dummy' has no attribute %r configured" % method_name) > return functools.partial( remembered_invocation_builder, theMock, method_name) E SystemError: <class 'functools.partial'> returned a result with an exception set mockito/mocking.py:281: SystemError ______________________________ test_deprecated_b _______________________________ a = (<Dummy id=140649669444192>,) def _raise(*a): print(a) > raise ValueError('Boom') E ValueError: Boom tests/mocking_properties_test.py:22: ValueError The above exception was the direct cause of the following exception: unstub = None def test_deprecated_b(unstub): # Setting on `__class__` is confusing for users m = mock() def _raise(*a): print(a) raise ValueError('Boom') m.__class__.tx = property(_raise) with pytest.raises(ValueError): > m.tx tests/mocking_properties_test.py:27: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <Dummy id=140649669444192>, method_name = 'tx' def __getattr__(self, method_name): if strict: __tracebackhide__ = operator.methodcaller( "errisinstance", AttributeError ) raise AttributeError( "'Dummy' has no attribute %r configured" % method_name) > return functools.partial( remembered_invocation_builder, theMock, method_name) E SystemError: <class 'functools.partial'> returned a result with an exception set mockito/mocking.py:281: SystemError ----------------------------- Captured stdout call ----------------------------- (<Dummy id=140649669444192>,) ______________________________ test_deprecated_c _______________________________ args = (<Dummy id=140649673126160>,), kwargs = {} def new_mocked_method(*args, **kwargs): # we throw away the first argument, if it's either self or cls if ( inspect.ismethod(new_mocked_method) or inspect.isclass(self.mocked_obj) and not isinstance(new_mocked_method, staticmethod) ): args = args[1:] > return remembered_invocation_builder( self, method_name, *args, **kwargs) mockito/mocking.py:108: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ mock = <mockito.mocking.Mock object at 0x7feb8dcdfcb0>, method_name = '__call__' args = (<Dummy id=140649673126160>,), kwargs = {} invoc = __call__(<Dummy id=140649673126160>) def remembered_invocation_builder(mock, method_name, *args, **kwargs): invoc = invocation.RememberedInvocation(mock, method_name) > return invoc(*args, **kwargs) mockito/mocking.py:48: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __call__(<Dummy id=140649673126160>) params = (<Dummy id=140649673126160>,), named_params = {} matching_invocation = __call__(...) def __call__(self, *params, **named_params): if self.strict: self.ensure_mocked_object_has_method(self.method_name) self.ensure_signature_matches( self.method_name, params, named_params) self._remember_params(params, named_params) self.mock.remember(self) for matching_invocation in self.mock.stubbed_invocations: if matching_invocation.matches(self): matching_invocation.should_answer(self) > return matching_invocation.answer_first( *params, **named_params) mockito/invocation.py:95: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __call__(...), args = (<Dummy id=140649673126160>,), kwargs = {} def answer_first(self, *args, **kwargs): self.used += 1 > return self.answers.answer(*args, **kwargs) mockito/invocation.py:331: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <mockito.invocation.CompositeAnswer object at 0x7feb8dcddcd0> args = (<Dummy id=140649673126160>,), kwargs = {} a = functools.partial(<function raise_ at 0x7feba288f880>, <class 'ValueError'>) def answer(self, *args, **kwargs): if len(self.answers) == 0: return None if len(self.answers) == 1: a = self.answers[0] else: a = self.answers.popleft() > return a(*args, **kwargs) mockito/invocation.py:440: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ exception = <class 'ValueError'>, a = (<Dummy id=140649673126160>,), kw = {} def raise_(exception, *a, **kw): > raise exception E ValueError mockito/invocation.py:383: ValueError The above exception was the direct cause of the following exception: unstub = None def test_deprecated_c(unstub): # Setting on `__class__` is confusing for users # Wrapping explicitly with `property` as well m = mock() prop = mock(strict=True) when(prop).__call__(Ellipsis).thenRaise(ValueError) m.__class__.tx = property(prop) with pytest.raises(ValueError): > m.tx tests/mocking_properties_test.py:40: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <Dummy id=140649673126160>, method_name = 'tx' def __getattr__(self, method_name): if strict: __tracebackhide__ = operator.methodcaller( "errisinstance", AttributeError ) raise AttributeError( "'Dummy' has no attribute %r configured" % method_name) > return functools.partial( remembered_invocation_builder, theMock, method_name) E SystemError: <class 'functools.partial'> returned a result with an exception set mockito/mocking.py:281: SystemError __________________________ test_recommended_approach ___________________________ args = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} def new_mocked_method(*args, **kwargs): # we throw away the first argument, if it's either self or cls if ( inspect.ismethod(new_mocked_method) or inspect.isclass(self.mocked_obj) and not isinstance(new_mocked_method, staticmethod) ): args = args[1:] > return remembered_invocation_builder( self, method_name, *args, **kwargs) mockito/mocking.py:108: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ mock = <mockito.mocking.Mock object at 0x7feb8d97bda0>, method_name = '__get__' args = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} invoc = __get__(<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) def remembered_invocation_builder(mock, method_name, *args, **kwargs): invoc = invocation.RememberedInvocation(mock, method_name) > return invoc(*args, **kwargs) mockito/mocking.py:48: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __get__(<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) params = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) named_params = {}, matching_invocation = __get__(...) def __call__(self, *params, **named_params): if self.strict: self.ensure_mocked_object_has_method(self.method_name) self.ensure_signature_matches( self.method_name, params, named_params) self._remember_params(params, named_params) self.mock.remember(self) for matching_invocation in self.mock.stubbed_invocations: if matching_invocation.matches(self): matching_invocation.should_answer(self) > return matching_invocation.answer_first( *params, **named_params) mockito/invocation.py:95: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = __get__(...) args = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} def answer_first(self, *args, **kwargs): self.used += 1 > return self.answers.answer(*args, **kwargs) mockito/invocation.py:331: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <mockito.invocation.CompositeAnswer object at 0x7feb8d97bc80> args = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kwargs = {} a = functools.partial(<function raise_ at 0x7feba288f880>, <class 'ValueError'>) def answer(self, *args, **kwargs): if len(self.answers) == 0: return None if len(self.answers) == 1: a = self.answers[0] else: a = self.answers.popleft() > return a(*args, **kwargs) mockito/invocation.py:440: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ exception = <class 'ValueError'> a = (<Dummy id=140649669573280>, <class 'mockito.mocking.mock.<locals>.Dummy'>) kw = {} def raise_(exception, *a, **kw): > raise exception E ValueError mockito/invocation.py:383: ValueError The above exception was the direct cause of the following exception: def test_recommended_approach(): prop = mock(strict=True) when(prop).__get__(Ellipsis).thenRaise(ValueError) m = mock({'tx': prop}) with pytest.raises(ValueError): > m.tx tests/mocking_properties_test.py:49: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ self = <Dummy id=140649669573280>, method_name = 'tx' def __getattr__(self, method_name): if strict: __tracebackhide__ = operator.methodcaller( "errisinstance", AttributeError ) raise AttributeError( "'Dummy' has no attribute %r configured" % method_name) > return functools.partial( remembered_invocation_builder, theMock, method_name) E SystemError: <class 'functools.partial'> returned a result with an exception set mockito/mocking.py:281: SystemError =========================== short test summary info ============================ FAILED tests/mocking_properties_test.py::test_deprecated_a - SystemError: <cl... FAILED tests/mocking_properties_test.py::test_deprecated_b - SystemError: <cl... FAILED tests/mocking_properties_test.py::test_deprecated_c - SystemError: <cl... FAILED tests/mocking_properties_test.py::test_recommended_approach - SystemEr... ================== 4 failed, 1136 passed, 3 xfailed in 1.10s =================== https://docs.python.org/3.12/whatsnew/3.12.html For the build logs, see: https://copr-be.cloud.fedoraproject.org/results/@python/python3.12/fedora-rawhide-x86_64/05902284-python-mockito/ For all our attempts to build python-mockito with Python 3.12, see: https://copr.fedorainfracloud.org/coprs/g/python/python3.12/package/python-mockito/ Testing and mass rebuild of packages is happening in copr. You can follow these instructions to test locally in mock if your package builds with Python 3.12: https://copr.fedorainfracloud.org/coprs/g/python/python3.12/ Let us know here if you have any questions. Python 3.12 is planned to be included in Fedora 39. To make that update smoother, we're building Fedora packages with all pre-releases of Python 3.12. A build failure prevents us from testing all dependent packages (transitive [Build]Requires), so if this package is required a lot, it's important for us to get it fixed soon. We'd appreciate help from the people who know this package best, but if you don't want to work on this now, let us know so we can try to work around it on our side.