Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix classmethod tests with Python 3.13+ #260

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Commits on Mar 7, 2024

  1. Fix classmethod tests with Python 3.13+

    Fixes GrahamDumpleton#259
    
    The failures were:
    
        =================================== FAILURES ===================================
        _____________ TestCallingOuterClassMethod.test_class_call_function _____________
    
        self = <test_outer_classmethod.TestCallingOuterClassMethod testMethod=test_class_call_function>
    
            def test_class_call_function(self):
                # Test calling classmethod. Prior to Python 3.9, the instance
                # and class passed to the wrapper will both be None because our
                # decorator is surrounded by the classmethod decorator. The
                # classmethod decorator doesn't bind the method and treats it
                # like a normal function, explicitly passing the class as the
                # first argument with the actual arguments following that. This
                # was only finally fixed in Python 3.9. For more details see:
                # https://bugs.python.org/issue19072
    
                _args = (1, 2)
                _kwargs = {'one': 1, 'two': 2}
    
                @wrapt.decorator
                def _decorator(wrapped, instance, args, kwargs):
                    if PYXY < (3, 9):
                        self.assertEqual(instance, None)
                        self.assertEqual(args, (Class,)+_args)
                    else:
                        self.assertEqual(instance, Class)
                        self.assertEqual(args, _args)
    
                    self.assertEqual(kwargs, _kwargs)
                    self.assertEqual(wrapped.__module__, _function.__module__)
                    self.assertEqual(wrapped.__name__, _function.__name__)
    
                    return wrapped(*args, **kwargs)
    
                @_decorator
                def _function(*args, **kwargs):
                    return args, kwargs
    
                class Class(object):
                    @classmethod
                    @_decorator
                    def _function(cls, *args, **kwargs):
                        return (args, kwargs)
    
        >       result = Class._function(*_args, **_kwargs)
    
        tests/test_outer_classmethod.py:160:
        _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
        tests/test_outer_classmethod.py:141: in _decorator
            self.assertEqual(instance, Class)
        E   AssertionError: None != <class 'test_outer_classmethod.TestCallin[54 chars]ass'>
        ___________ TestCallingOuterClassMethod.test_instance_call_function ____________
    
        self = <test_outer_classmethod.TestCallingOuterClassMethod testMethod=test_instance_call_function>
    
            def test_instance_call_function(self):
                # Test calling classmethod via class instance. Prior to Python
                # 3.9, the instance and class passed to the wrapper will both be
                # None because our decorator is surrounded by the classmethod
                # decorator. The classmethod decorator doesn't bind the method
                # and treats it like a normal function, explicitly passing the
                # class as the first argument with the actual arguments
                # following that. This was only finally fixed in Python 3.9. For
                # more details see: https://bugs.python.org/issue19072
    
                _args = (1, 2)
                _kwargs = {'one': 1, 'two': 2}
    
                @wrapt.decorator
                def _decorator(wrapped, instance, args, kwargs):
                    if PYXY < (3, 9):
                        self.assertEqual(instance, None)
                        self.assertEqual(args, (Class,)+_args)
                    else:
                        self.assertEqual(instance, Class)
                        self.assertEqual(args, _args)
    
                    self.assertEqual(kwargs, _kwargs)
                    self.assertEqual(wrapped.__module__, _function.__module__)
                    self.assertEqual(wrapped.__name__, _function.__name__)
    
                    return wrapped(*args, **kwargs)
    
                @_decorator
                def _function(*args, **kwargs):
                    return args, kwargs
    
                class Class(object):
                    @classmethod
                    @_decorator
                    def _function(cls, *args, **kwargs):
                        return (args, kwargs)
    
        >       result = Class()._function(*_args, **_kwargs)
    
        tests/test_outer_classmethod.py:202:
        _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
        tests/test_outer_classmethod.py:183: in _decorator
            self.assertEqual(instance, Class)
        E   AssertionError: None != <class 'test_outer_classmethod.TestCallin[57 chars]ass'>
        _____________ TestSynchronized.test_synchronized_outer_classmethod _____________
    
        self = <test_synchronized_lock.TestSynchronized testMethod=test_synchronized_outer_classmethod>
    
            def test_synchronized_outer_classmethod(self):
                # Prior to Python 3.9 this isn't detected as a class method
                # call, as the classmethod decorator doesn't bind the wrapped
                # function to the class before calling and just calls it direct,
                # explicitly passing the class as first argument. For more
                # details see: https://bugs.python.org/issue19072
    
                if PYXY < (3, 9):
                    _lock0 = getattr(C4.function2, '_synchronized_lock', None)
                else:
                    _lock0 = getattr(C4, '_synchronized_lock', None)
                self.assertEqual(_lock0, None)
    
                c4.function2()
    
                if PYXY < (3, 9):
                    _lock1 = getattr(C4.function2, '_synchronized_lock', None)
                else:
                    _lock1 = getattr(C4, '_synchronized_lock', None)
        >       self.assertNotEqual(_lock1, None)
        E       AssertionError: None == None
    
        tests/test_synchronized_lock.py:181: AssertionError
        ----------------------------- Captured stdout call -----------------------------
        function2
        =========================== short test summary info ============================
        FAILED tests/test_outer_classmethod.py::TestCallingOuterClassMethod::test_class_call_function
        FAILED tests/test_outer_classmethod.py::TestCallingOuterClassMethod::test_instance_call_function
        FAILED tests/test_synchronized_lock.py::TestSynchronized::test_synchronized_outer_classmethod
        ======================== 3 failed, 435 passed in 0.83s =========================
    
    To fix the same failures on Python 3.9,
    they were adjusted in the past. For details see
    GrahamDumpleton#160
    
    However, Python 3.13 reverted the change from 3.9,
    so this adds an upper bound for the conditionals.
    
    To make the conditionals easier to read, the if-else branches were switched.
    hroncok committed Mar 7, 2024
    Configuration menu
    Copy the full SHA
    185f1f2 View commit details
    Browse the repository at this point in the history