Skip to content

Commit 720a34a

Browse files
committed
Add support for python 3.13
1 parent 330340a commit 720a34a

File tree

6 files changed

+530
-2
lines changed

6 files changed

+530
-2
lines changed

unittesting/core/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,31 @@
11
import sys
22

3-
if sys.version_info >= (3, 8):
3+
if sys.version_info[:2] >= (3, 13):
4+
from .py313.case import DeferrableMethod
5+
from .py313.case import DeferrableTestCase
6+
from .py313.case import expectedFailure
7+
from .py313.loader import DeferrableTestLoader
8+
from .py313.runner import AWAIT_WORKER
9+
from .py313.runner import DeferringTextTestRunner
10+
from .py313.suite import DeferrableTestSuite
11+
elif sys.version_info[:2] == (3, 8):
412
from .py38.case import DeferrableMethod
513
from .py38.case import DeferrableTestCase
614
from .py38.case import expectedFailure
715
from .py38.loader import DeferrableTestLoader
816
from .py38.runner import AWAIT_WORKER
917
from .py38.runner import DeferringTextTestRunner
1018
from .py38.suite import DeferrableTestSuite
11-
else:
19+
elif sys.version_info[:2] == (3, 3):
1220
from .py33.case import DeferrableMethod
1321
from .py33.case import DeferrableTestCase
1422
from .py33.case import expectedFailure
1523
from .py33.loader import DeferrableTestLoader
1624
from .py33.runner import AWAIT_WORKER
1725
from .py33.runner import DeferringTextTestRunner
1826
from .py33.suite import DeferrableTestSuite
27+
else:
28+
raise ImportError("Unsupported python runtime!")
1929

2030
__all__ = [
2131
"AWAIT_WORKER",

unittesting/core/py313/__init__.py

Whitespace-only changes.

unittesting/core/py313/case.py

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import time
2+
import sys
3+
4+
from collections.abc import Generator as DeferrableMethod
5+
from unittest import TestCase
6+
from unittest.case import _addSkip
7+
from unittest.case import _Outcome
8+
from unittest.case import expectedFailure
9+
10+
from .runner import defer
11+
12+
__all__ = ["DeferrableMethod", "DeferrableTestCase", "expectedFailure"]
13+
14+
15+
class DeferrableTestCase(TestCase):
16+
17+
def _callSetUp(self):
18+
deferred = self.setUp()
19+
if isinstance(deferred, DeferrableMethod):
20+
yield from deferred
21+
22+
def _callTestMethod(self, method):
23+
deferred = method()
24+
if isinstance(deferred, DeferrableMethod):
25+
yield from deferred
26+
27+
def _callTearDown(self):
28+
deferred = self.tearDown()
29+
if isinstance(deferred, DeferrableMethod):
30+
yield from deferred
31+
32+
def _callCleanup(self, function, *args, **kwargs):
33+
deferred = function(*args, **kwargs)
34+
if isinstance(deferred, DeferrableMethod):
35+
yield from deferred
36+
37+
@staticmethod
38+
def defer(delay, callback, *args, **kwargs):
39+
defer(delay, callback, *args, **kwargs)
40+
41+
def run(self, result=None):
42+
if result is None:
43+
result = self.defaultTestResult()
44+
startTestRun = getattr(result, 'startTestRun', None)
45+
stopTestRun = getattr(result, 'stopTestRun', None)
46+
if startTestRun is not None:
47+
startTestRun()
48+
else:
49+
stopTestRun = None
50+
51+
result.startTest(self)
52+
try:
53+
testMethod = getattr(self, self._testMethodName)
54+
if (getattr(self.__class__, "__unittest_skip__", False) or
55+
getattr(testMethod, "__unittest_skip__", False)):
56+
# If the class or method was skipped.
57+
skip_why = (getattr(self.__class__, '__unittest_skip_why__', '')
58+
or getattr(testMethod, '__unittest_skip_why__', ''))
59+
_addSkip(result, self, skip_why)
60+
return result
61+
62+
expecting_failure = (
63+
getattr(self, "__unittest_expecting_failure__", False) or
64+
getattr(testMethod, "__unittest_expecting_failure__", False)
65+
)
66+
outcome = _Outcome(result)
67+
start_time = time.perf_counter()
68+
try:
69+
self._outcome = outcome
70+
71+
with outcome.testPartExecutor(self):
72+
deferred = self._callSetUp()
73+
if isinstance(deferred, DeferrableMethod):
74+
yield from deferred
75+
if outcome.success:
76+
outcome.expecting_failure = expecting_failure
77+
with outcome.testPartExecutor(self):
78+
deferred = self._callTestMethod(testMethod)
79+
if isinstance(deferred, DeferrableMethod):
80+
yield from deferred
81+
outcome.expecting_failure = False
82+
with outcome.testPartExecutor(self):
83+
deferred = self._callTearDown()
84+
if isinstance(deferred, DeferrableMethod):
85+
yield from deferred
86+
deferred = self.doCleanups()
87+
if isinstance(deferred, DeferrableMethod):
88+
yield from deferred
89+
90+
self._addDuration(result, (time.perf_counter() - start_time))
91+
92+
if outcome.success:
93+
if expecting_failure:
94+
if outcome.expectedFailure:
95+
self._addExpectedFailure(result, outcome.expectedFailure)
96+
else:
97+
self._addUnexpectedSuccess(result)
98+
else:
99+
result.addSuccess(self)
100+
return result
101+
finally:
102+
# explicitly break reference cycle:
103+
# outcome.expectedFailure -> frame -> outcome -> outcome.expectedFailure
104+
outcome.expectedFailure = None
105+
outcome = None
106+
107+
# clear the outcome, no more needed
108+
self._outcome = None
109+
110+
finally:
111+
result.stopTest(self)
112+
if stopTestRun is not None:
113+
stopTestRun()
114+
115+
def doCleanups(self):
116+
"""Execute all cleanup functions. Normally called for you after tearDown."""
117+
outcome = self._outcome or _Outcome()
118+
while self._cleanups:
119+
function, args, kwargs = self._cleanups.pop()
120+
with outcome.testPartExecutor(self):
121+
deferred = self._callCleanup(function, *args, **kwargs)
122+
if isinstance(deferred, DeferrableMethod):
123+
yield from deferred
124+
125+
# return this for backwards compatibility
126+
# even though we no longer use it internally
127+
return outcome.success
128+
129+
@classmethod
130+
def doClassCleanups(cls):
131+
"""Execute all class cleanup functions. Normally called for you after tearDownClass."""
132+
cls.tearDown_exceptions = []
133+
while cls._class_cleanups:
134+
function, args, kwargs = cls._class_cleanups.pop()
135+
try:
136+
deferred = function(*args, **kwargs)
137+
if isinstance(deferred, DeferrableMethod):
138+
yield from deferred
139+
except Exception:
140+
cls.tearDown_exceptions.append(sys.exc_info())
141+
142+
def __call__(self, *args, **kwds):
143+
deferred = self.run(*args, **kwds)
144+
if isinstance(deferred, DeferrableMethod):
145+
yield from deferred
146+
else:
147+
return deferred

unittesting/core/py313/loader.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from unittest import TestSuite
2+
from unittest import TestLoader
3+
4+
from .suite import DeferrableTestSuite
5+
6+
7+
class DeferrableTestLoader(TestLoader):
8+
def __init__(self, deferred=False):
9+
if deferred:
10+
self.suiteClass = DeferrableTestSuite
11+
else:
12+
self.suiteClass = TestSuite
13+
super().__init__()

0 commit comments

Comments
 (0)