Add CPython generator/contextlib tests (#150796)

Tests:
* test_generator.py
* test_generator_stop.py
* test_contextlib.py

Minor changes were made to each test to run them inside Dynamo. We
intentionally didn't copy the binary files stored in
`python/Lib/test/archivetestdata` for security reasons. There's a single
test that requires a binary file and it is skipped because of that.

The tests were downloaded from CPython 3.13 and the diff was generated
using `git diff` to apply the changes:

```bash
for f in "test_contextlib" "test_generators" "test_generator_stop"; do
	wget -O "test/dynamo/cpython/3_13/${f}.py" "https://raw.githubusercontent.com/python/cpython/refs/heads/3.13/Lib/test/${f}.py"
	git apply "test/dynamo/cpython/3_13/${f}.diff"
done
```

Pull Request resolved: https://github.com/pytorch/pytorch/pull/150796
Approved by: https://github.com/williamwen42
This commit is contained in:
Guilherme Leobas
2025-05-29 23:20:08 +00:00
committed by PyTorch MergeBot
parent fb5a787a8f
commit d5f642211f
130 changed files with 4707 additions and 0 deletions

View File

@ -0,0 +1,2 @@
This empty directory serves as destination for temporary files
created by some tests, in particular, the test_codecmaps_* tests.

View File

@ -0,0 +1,195 @@
diff --git a/test/dynamo/cpython/3_13/test_contextlib.py b/test/dynamo/cpython/3_13/test_contextlib.py
index cf651959803..6a17bc719eb 100644
--- a/test/dynamo/cpython/3_13/test_contextlib.py
+++ b/test/dynamo/cpython/3_13/test_contextlib.py
@@ -1,3 +1,54 @@
+# ======= BEGIN Dynamo patch =======
+# Owner(s): ["module: dynamo"]
+
+# ruff: noqa
+# flake8: noqa
+
+import sys
+import torch
+import torch._dynamo.test_case
+import unittest
+from torch._dynamo.test_case import CPythonTestCase
+from torch.testing._internal.common_utils import run_tests
+
+__TestCase = CPythonTestCase
+
+
+# redirect import statements
+import sys
+import importlib.abc
+
+redirect_imports = (
+ "test.mapping_tests",
+ "test.typinganndata",
+ "test.test_grammar",
+ "test.test_math",
+ "test.test_iter",
+ "test.typinganndata.ann_module",
+)
+
+class RedirectImportFinder(importlib.abc.MetaPathFinder):
+ def find_spec(self, fullname, path, target=None):
+ # Check if the import is the problematic one
+ if fullname in redirect_imports:
+ try:
+ # Attempt to import the standalone module
+ name = fullname.removeprefix("test.")
+ r = importlib.import_module(name)
+ # Redirect the module in sys.modules
+ sys.modules[fullname] = r
+ # Return a module spec from the found module
+ return importlib.util.find_spec(name)
+ except ImportError:
+ return None
+ return None
+
+# Add the custom finder to sys.meta_path
+sys.meta_path.insert(0, RedirectImportFinder())
+
+
+# ======= END DYNAMO PATCH =======
+
"""Unit tests for contextlib.py, and other context managers."""
import io
@@ -14,7 +65,7 @@ from test.support.testcase import ExceptionIsLikeMixin
import weakref
-class TestAbstractContextManager(unittest.TestCase):
+class TestAbstractContextManager(__TestCase):
def test_enter(self):
class DefaultEnter(AbstractContextManager):
@@ -67,7 +118,7 @@ class TestAbstractContextManager(unittest.TestCase):
self.assertFalse(issubclass(NoExit, AbstractContextManager))
-class ContextManagerTestCase(unittest.TestCase):
+class ContextManagerTestCase(__TestCase):
def test_contextmanager_plain(self):
state = []
@@ -396,7 +447,7 @@ def woohoo():
self.assertEqual(depth, 0)
-class ClosingTestCase(unittest.TestCase):
+class ClosingTestCase(__TestCase):
@support.requires_docstrings
def test_instance_docs(self):
@@ -430,7 +481,7 @@ class ClosingTestCase(unittest.TestCase):
self.assertEqual(state, [1])
-class NullcontextTestCase(unittest.TestCase):
+class NullcontextTestCase(__TestCase):
def test_nullcontext(self):
class C:
pass
@@ -439,7 +490,7 @@ class NullcontextTestCase(unittest.TestCase):
self.assertIs(c_in, c)
-class FileContextTestCase(unittest.TestCase):
+class FileContextTestCase(__TestCase):
def testWithOpen(self):
tfn = tempfile.mktemp()
@@ -457,7 +508,7 @@ class FileContextTestCase(unittest.TestCase):
finally:
os_helper.unlink(tfn)
-class LockContextTestCase(unittest.TestCase):
+class LockContextTestCase(__TestCase):
def boilerPlate(self, lock, locked):
self.assertFalse(locked())
@@ -520,7 +571,7 @@ class mycontext(ContextDecorator):
return self.catch
-class TestContextDecorator(unittest.TestCase):
+class TestContextDecorator(__TestCase):
@support.requires_docstrings
def test_instance_docs(self):
@@ -680,7 +731,7 @@ class TestContextDecorator(unittest.TestCase):
self.assertEqual(state, [1, 'something else', 999])
-class TestBaseExitStack:
+class _TestBaseExitStack:
exit_stack = None
@support.requires_docstrings
@@ -1141,7 +1192,7 @@ class TestBaseExitStack:
self.assertIs(exc.__cause__, exc.__context__)
-class TestExitStack(TestBaseExitStack, unittest.TestCase):
+class TestExitStack(_TestBaseExitStack, __TestCase):
exit_stack = ExitStack
callback_error_internal_frames = [
('__exit__', 'raise exc'),
@@ -1149,7 +1200,7 @@ class TestExitStack(TestBaseExitStack, unittest.TestCase):
]
-class TestRedirectStream:
+class _TestRedirectStream:
redirect_stream = None
orig_stream = None
@@ -1206,19 +1257,19 @@ class TestRedirectStream:
self.assertEqual(s, "Hello World!\n")
-class TestRedirectStdout(TestRedirectStream, unittest.TestCase):
+class TestRedirectStdout(_TestRedirectStream, __TestCase):
redirect_stream = redirect_stdout
orig_stream = "stdout"
-class TestRedirectStderr(TestRedirectStream, unittest.TestCase):
+class TestRedirectStderr(_TestRedirectStream, __TestCase):
redirect_stream = redirect_stderr
orig_stream = "stderr"
-class TestSuppress(ExceptionIsLikeMixin, unittest.TestCase):
+class TestSuppress(ExceptionIsLikeMixin, __TestCase):
@support.requires_docstrings
def test_instance_docs(self):
@@ -1315,7 +1366,7 @@ class TestSuppress(ExceptionIsLikeMixin, unittest.TestCase):
)
-class TestChdir(unittest.TestCase):
+class TestChdir(__TestCase):
def make_relative_path(self, *parts):
return os.path.join(
os.path.dirname(os.path.realpath(__file__)),
@@ -1331,6 +1382,7 @@ class TestChdir(unittest.TestCase):
self.assertEqual(os.getcwd(), target)
self.assertEqual(os.getcwd(), old_cwd)
+ @unittest.skip("Missing archivetestdata")
def test_reentrant(self):
old_cwd = os.getcwd()
target1 = self.make_relative_path('data')
@@ -1363,4 +1415,4 @@ class TestChdir(unittest.TestCase):
if __name__ == "__main__":
- unittest.main()
+ run_tests()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
diff --git a/test/dynamo/cpython/3_13/test_generator_stop.py b/test/dynamo/cpython/3_13/test_generator_stop.py
index bc235ceb00e..cb2a85255cb 100644
--- a/test/dynamo/cpython/3_13/test_generator_stop.py
+++ b/test/dynamo/cpython/3_13/test_generator_stop.py
@@ -1,9 +1,60 @@
from __future__ import generator_stop
+# ======= BEGIN Dynamo patch =======
+# Owner(s): ["module: dynamo"]
+
+# ruff: noqa
+# flake8: noqa
+
+import sys
+import torch
+import torch._dynamo.test_case
+import unittest
+from torch._dynamo.test_case import CPythonTestCase
+from torch.testing._internal.common_utils import run_tests
+
+__TestCase = CPythonTestCase
+
+
+# redirect import statements
+import sys
+import importlib.abc
+
+redirect_imports = (
+ "test.mapping_tests",
+ "test.typinganndata",
+ "test.test_grammar",
+ "test.test_math",
+ "test.test_iter",
+ "test.typinganndata.ann_module",
+)
+
+class RedirectImportFinder(importlib.abc.MetaPathFinder):
+ def find_spec(self, fullname, path, target=None):
+ # Check if the import is the problematic one
+ if fullname in redirect_imports:
+ try:
+ # Attempt to import the standalone module
+ name = fullname.removeprefix("test.")
+ r = importlib.import_module(name)
+ # Redirect the module in sys.modules
+ sys.modules[fullname] = r
+ # Return a module spec from the found module
+ return importlib.util.find_spec(name)
+ except ImportError:
+ return None
+ return None
+
+# Add the custom finder to sys.meta_path
+sys.meta_path.insert(0, RedirectImportFinder())
+
+
+# ======= END DYNAMO PATCH =======
+
import unittest
-class TestPEP479(unittest.TestCase):
+class TestPEP479(__TestCase):
def test_stopiteration_wrapping(self):
def f():
raise StopIteration
@@ -30,5 +81,5 @@ class TestPEP479(unittest.TestCase):
'were not properly set')
-if __name__ == '__main__':
- unittest.main()
+if __name__ == "__main__":
+ run_tests()

View File

@ -0,0 +1,85 @@
from __future__ import generator_stop
# ======= BEGIN Dynamo patch =======
# Owner(s): ["module: dynamo"]
# ruff: noqa
# flake8: noqa
import sys
import torch
import torch._dynamo.test_case
import unittest
from torch._dynamo.test_case import CPythonTestCase
from torch.testing._internal.common_utils import run_tests
__TestCase = CPythonTestCase
# redirect import statements
import sys
import importlib.abc
redirect_imports = (
"test.mapping_tests",
"test.typinganndata",
"test.test_grammar",
"test.test_math",
"test.test_iter",
"test.typinganndata.ann_module",
)
class RedirectImportFinder(importlib.abc.MetaPathFinder):
def find_spec(self, fullname, path, target=None):
# Check if the import is the problematic one
if fullname in redirect_imports:
try:
# Attempt to import the standalone module
name = fullname.removeprefix("test.")
r = importlib.import_module(name)
# Redirect the module in sys.modules
sys.modules[fullname] = r
# Return a module spec from the found module
return importlib.util.find_spec(name)
except ImportError:
return None
return None
# Add the custom finder to sys.meta_path
sys.meta_path.insert(0, RedirectImportFinder())
# ======= END DYNAMO PATCH =======
import unittest
class TestPEP479(__TestCase):
def test_stopiteration_wrapping(self):
def f():
raise StopIteration
def g():
yield f()
with self.assertRaisesRegex(RuntimeError,
"generator raised StopIteration"):
next(g())
def test_stopiteration_wrapping_context(self):
def f():
raise StopIteration
def g():
yield f()
try:
next(g())
except RuntimeError as exc:
self.assertIs(type(exc.__cause__), StopIteration)
self.assertIs(type(exc.__context__), StopIteration)
self.assertTrue(exc.__suppress_context__)
else:
self.fail('__cause__, __context__, or __suppress_context__ '
'were not properly set')
if __name__ == "__main__":
run_tests()

View File

@ -0,0 +1,165 @@
diff --git a/test/dynamo/cpython/3_13/test_generators.py b/test/dynamo/cpython/3_13/test_generators.py
index a6c1dd62a23..8e1fc7c3308 100644
--- a/test/dynamo/cpython/3_13/test_generators.py
+++ b/test/dynamo/cpython/3_13/test_generators.py
@@ -1,3 +1,53 @@
+# ======= BEGIN Dynamo patch =======
+# Owner(s): ["module: dynamo"]
+
+# ruff: noqa
+# flake8: noqa
+
+import sys
+import torch
+import torch._dynamo.test_case
+import unittest
+from torch._dynamo.test_case import CPythonTestCase
+from torch.testing._internal.common_utils import run_tests
+
+__TestCase = CPythonTestCase
+
+# redirect import statements
+import sys
+import importlib.abc
+
+redirect_imports = (
+ "test.mapping_tests",
+ "test.typinganndata",
+ "test.test_grammar",
+ "test.test_math",
+ "test.test_iter",
+ "test.typinganndata.ann_module",
+)
+
+class RedirectImportFinder(importlib.abc.MetaPathFinder):
+ def find_spec(self, fullname, path, target=None):
+ # Check if the import is the problematic one
+ if fullname in redirect_imports:
+ try:
+ # Attempt to import the standalone module
+ name = fullname.removeprefix("test.")
+ r = importlib.import_module(name)
+ # Redirect the module in sys.modules
+ sys.modules[fullname] = r
+ # Return a module spec from the found module
+ return importlib.util.find_spec(name)
+ except ImportError:
+ return None
+ return None
+
+# Add the custom finder to sys.meta_path
+sys.meta_path.insert(0, RedirectImportFinder())
+
+
+# ======= END DYNAMO PATCH =======
+
import copy
import gc
import pickle
@@ -22,7 +72,7 @@ except ImportError:
@unittest.skipUnless(_testcapi is not None and
hasattr(_testcapi, "raise_SIGINT_then_send_None"),
"needs _testcapi.raise_SIGINT_then_send_None")
-class SignalAndYieldFromTest(unittest.TestCase):
+class SignalAndYieldFromTest(__TestCase):
def generator1(self):
return (yield from self.generator2())
@@ -46,7 +96,7 @@ class SignalAndYieldFromTest(unittest.TestCase):
self.assertEqual(exc.value, "PASSED")
-class FinalizationTest(unittest.TestCase):
+class FinalizationTest(__TestCase):
def test_frame_resurrect(self):
# A generator frame can be resurrected by a generator's finalization.
@@ -113,7 +163,7 @@ class FinalizationTest(unittest.TestCase):
self.assertEqual(cm.exception.value, 2)
-class GeneratorTest(unittest.TestCase):
+class GeneratorTest(__TestCase):
def test_name(self):
def func():
@@ -246,6 +296,7 @@ class GeneratorTest(unittest.TestCase):
#This should not raise
loop()
+ @unittest.expectedFailure
def test_genexpr_only_calls_dunder_iter_once(self):
class Iterator:
@@ -269,7 +320,7 @@ class GeneratorTest(unittest.TestCase):
self.assertEqual([1,2], list(i for i in C()))
-class ModifyUnderlyingIterableTest(unittest.TestCase):
+class ModifyUnderlyingIterableTest(__TestCase):
iterables = [
range(0),
range(20),
@@ -341,7 +392,7 @@ class ModifyUnderlyingIterableTest(unittest.TestCase):
self.process_tests(get_generator_genfunc)
-class ExceptionTest(unittest.TestCase):
+class ExceptionTest(__TestCase):
# Tests for the issue #23353: check that the currently handled exception
# is correctly saved/restored in PyEval_EvalFrameEx().
@@ -550,7 +601,7 @@ class ExceptionTest(unittest.TestCase):
self.assertEqual(cm.exception.value.value, 2)
-class GeneratorCloseTest(unittest.TestCase):
+class GeneratorCloseTest(__TestCase):
def test_close_no_return_value(self):
def f():
@@ -652,7 +703,7 @@ class GeneratorCloseTest(unittest.TestCase):
self.assertIsNone(f_wr())
-class GeneratorThrowTest(unittest.TestCase):
+class GeneratorThrowTest(__TestCase):
def test_exception_context_with_yield(self):
def f():
@@ -751,7 +802,7 @@ class GeneratorThrowTest(unittest.TestCase):
gen.throw(ValueError)
-class GeneratorStackTraceTest(unittest.TestCase):
+class GeneratorStackTraceTest(__TestCase):
def check_stack_names(self, frame, expected):
names = []
@@ -800,7 +851,7 @@ class GeneratorStackTraceTest(unittest.TestCase):
self.check_yield_from_example(call_throw)
-class YieldFromTests(unittest.TestCase):
+class YieldFromTests(__TestCase):
def test_generator_gi_yieldfrom(self):
def a():
self.assertEqual(inspect.getgeneratorstate(gen_b), inspect.GEN_RUNNING)
@@ -2703,9 +2754,15 @@ __test__ = {"tut": tutorial_tests,
}
def load_tests(loader, tests, pattern):
- tests.addTest(doctest.DocTestSuite())
+ # ======= BEGIN Dynamo patch =======
+ suite = doctest.DocTestSuite()
+ for test in suite:
+ # Dynamically change base class
+ test.__class__ = type(test.__class__.__name__, (__TestCase, test.__class__), {})
+ tests.addTests(suite)
+ # ======= END DYNAMO PATCH =======
return tests
if __name__ == "__main__":
- unittest.main()
+ run_tests()

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More