summaryrefslogtreecommitdiffstats
path: root/Lib/test
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test')
-rw-r--r--Lib/test/support/interpreters/__init__.py8
-rw-r--r--Lib/test/test__xxsubinterpreters.py4
-rw-r--r--Lib/test/test_capi/test_misc.py83
-rw-r--r--Lib/test/test_import/__init__.py2
-rw-r--r--Lib/test/test_importlib/test_util.py4
-rw-r--r--Lib/test/test_interpreters/test_api.py211
-rw-r--r--Lib/test/test_interpreters/test_queues.py6
-rw-r--r--Lib/test/test_interpreters/utils.py21
8 files changed, 277 insertions, 62 deletions
diff --git a/Lib/test/support/interpreters/__init__.py b/Lib/test/support/interpreters/__init__.py
index d8e6654..8316b5e 100644
--- a/Lib/test/support/interpreters/__init__.py
+++ b/Lib/test/support/interpreters/__init__.py
@@ -73,7 +73,7 @@ class ExecutionFailed(RuntimeError):
def create():
"""Return a new (idle) Python interpreter."""
- id = _interpreters.create(isolated=True)
+ id = _interpreters.create(reqrefs=True)
return Interpreter(id)
@@ -109,13 +109,13 @@ class Interpreter:
assert hasattr(self, '_ownsref')
except KeyError:
# This may raise InterpreterNotFoundError:
- _interpreters._incref(id)
+ _interpreters.incref(id)
try:
self = super().__new__(cls)
self._id = id
self._ownsref = True
except BaseException:
- _interpreters._deccref(id)
+ _interpreters.decref(id)
raise
_known[id] = self
return self
@@ -142,7 +142,7 @@ class Interpreter:
return
self._ownsref = False
try:
- _interpreters._decref(self.id)
+ _interpreters.decref(self.id)
except InterpreterNotFoundError:
pass
diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py
index 35d7355..f674771 100644
--- a/Lib/test/test__xxsubinterpreters.py
+++ b/Lib/test/test__xxsubinterpreters.py
@@ -584,7 +584,7 @@ class RunStringTests(TestBase):
def test_create_daemon_thread(self):
with self.subTest('isolated'):
expected = 'spam spam spam spam spam'
- subinterp = interpreters.create(isolated=True)
+ subinterp = interpreters.create('isolated')
script, file = _captured_script(f"""
import threading
def f():
@@ -604,7 +604,7 @@ class RunStringTests(TestBase):
self.assertEqual(out, expected)
with self.subTest('not isolated'):
- subinterp = interpreters.create(isolated=False)
+ subinterp = interpreters.create('legacy')
script, file = _captured_script("""
import threading
def f():
diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py
index 34311af..2f2bf03 100644
--- a/Lib/test/test_capi/test_misc.py
+++ b/Lib/test/test_capi/test_misc.py
@@ -2204,6 +2204,7 @@ class SubinterpreterTest(unittest.TestCase):
self.assertEqual(main_attr_id, subinterp_attr_id)
+@requires_subinterpreters
class InterpreterConfigTests(unittest.TestCase):
supported = {
@@ -2277,11 +2278,11 @@ class InterpreterConfigTests(unittest.TestCase):
expected = self.supported[expected]
args = (name,) if name else ()
- config1 = _testinternalcapi.new_interp_config(*args)
+ config1 = _interpreters.new_config(*args)
self.assert_ns_equal(config1, expected)
self.assertIsNot(config1, expected)
- config2 = _testinternalcapi.new_interp_config(*args)
+ config2 = _interpreters.new_config(*args)
self.assert_ns_equal(config2, expected)
self.assertIsNot(config2, expected)
self.assertIsNot(config2, config1)
@@ -2298,7 +2299,7 @@ class InterpreterConfigTests(unittest.TestCase):
with self.subTest(f'noop ({name})'):
expected = vanilla
overrides = vars(vanilla)
- config = _testinternalcapi.new_interp_config(name, **overrides)
+ config = _interpreters.new_config(name, **overrides)
self.assert_ns_equal(config, expected)
with self.subTest(f'change all ({name})'):
@@ -2308,7 +2309,7 @@ class InterpreterConfigTests(unittest.TestCase):
continue
overrides['gil'] = gil
expected = types.SimpleNamespace(**overrides)
- config = _testinternalcapi.new_interp_config(
+ config = _interpreters.new_config(
name, **overrides)
self.assert_ns_equal(config, expected)
@@ -2324,14 +2325,14 @@ class InterpreterConfigTests(unittest.TestCase):
expected = types.SimpleNamespace(
**dict(vars(vanilla), **overrides),
)
- config = _testinternalcapi.new_interp_config(
+ config = _interpreters.new_config(
name, **overrides)
self.assert_ns_equal(config, expected)
with self.subTest('unsupported field'):
for name in self.supported:
with self.assertRaises(ValueError):
- _testinternalcapi.new_interp_config(name, spam=True)
+ _interpreters.new_config(name, spam=True)
# Bad values for bool fields.
for field, value in vars(self.supported['empty']).items():
@@ -2341,19 +2342,18 @@ class InterpreterConfigTests(unittest.TestCase):
for value in [1, '', 'spam', 1.0, None, object()]:
with self.subTest(f'unsupported value ({field}={value!r})'):
with self.assertRaises(TypeError):
- _testinternalcapi.new_interp_config(**{field: value})
+ _interpreters.new_config(**{field: value})
# Bad values for .gil.
for value in [True, 1, 1.0, None, object()]:
with self.subTest(f'unsupported value(gil={value!r})'):
with self.assertRaises(TypeError):
- _testinternalcapi.new_interp_config(gil=value)
+ _interpreters.new_config(gil=value)
for value in ['', 'spam']:
with self.subTest(f'unsupported value (gil={value!r})'):
with self.assertRaises(ValueError):
- _testinternalcapi.new_interp_config(gil=value)
+ _interpreters.new_config(gil=value)
- @requires_subinterpreters
def test_interp_init(self):
questionable = [
# strange
@@ -2412,11 +2412,10 @@ class InterpreterConfigTests(unittest.TestCase):
with self.subTest(f'valid: {config}'):
check(config)
- @requires_subinterpreters
def test_get_config(self):
@contextlib.contextmanager
def new_interp(config):
- interpid = _testinternalcapi.new_interpreter(config)
+ interpid = _interpreters.create(config, reqrefs=False)
try:
yield interpid
finally:
@@ -2426,32 +2425,32 @@ class InterpreterConfigTests(unittest.TestCase):
pass
with self.subTest('main'):
- expected = _testinternalcapi.new_interp_config('legacy')
+ expected = _interpreters.new_config('legacy')
expected.gil = 'own'
interpid = _interpreters.get_main()
- config = _testinternalcapi.get_interp_config(interpid)
+ config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, expected)
with self.subTest('isolated'):
- expected = _testinternalcapi.new_interp_config('isolated')
+ expected = _interpreters.new_config('isolated')
with new_interp('isolated') as interpid:
- config = _testinternalcapi.get_interp_config(interpid)
+ config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, expected)
with self.subTest('legacy'):
- expected = _testinternalcapi.new_interp_config('legacy')
+ expected = _interpreters.new_config('legacy')
with new_interp('legacy') as interpid:
- config = _testinternalcapi.get_interp_config(interpid)
+ config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, expected)
with self.subTest('custom'):
- orig = _testinternalcapi.new_interp_config(
+ orig = _interpreters.new_config(
'empty',
use_main_obmalloc=True,
gil='shared',
)
with new_interp(orig) as interpid:
- config = _testinternalcapi.get_interp_config(interpid)
+ config = _interpreters.get_config(interpid)
self.assert_ns_equal(config, orig)
@@ -2529,14 +2528,19 @@ class InterpreterIDTests(unittest.TestCase):
self.assertFalse(
_testinternalcapi.interpreter_exists(interpid))
+ def get_refcount_helpers(self):
+ return (
+ _testinternalcapi.get_interpreter_refcount,
+ (lambda id: _interpreters.incref(id, implieslink=False)),
+ _interpreters.decref,
+ )
+
def test_linked_lifecycle_does_not_exist(self):
exists = _testinternalcapi.interpreter_exists
is_linked = _testinternalcapi.interpreter_refcount_linked
link = _testinternalcapi.link_interpreter_refcount
unlink = _testinternalcapi.unlink_interpreter_refcount
- get_refcount = _testinternalcapi.get_interpreter_refcount
- incref = _testinternalcapi.interpreter_incref
- decref = _testinternalcapi.interpreter_decref
+ get_refcount, incref, decref = self.get_refcount_helpers()
with self.subTest('never existed'):
interpid = _testinternalcapi.unused_interpreter_id()
@@ -2578,8 +2582,7 @@ class InterpreterIDTests(unittest.TestCase):
get_refcount = _testinternalcapi.get_interpreter_refcount
# A new interpreter will start out not linked, with a refcount of 0.
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
linked = is_linked(interpid)
refcount = get_refcount(interpid)
@@ -2589,12 +2592,9 @@ class InterpreterIDTests(unittest.TestCase):
def test_linked_lifecycle_never_linked(self):
exists = _testinternalcapi.interpreter_exists
is_linked = _testinternalcapi.interpreter_refcount_linked
- get_refcount = _testinternalcapi.get_interpreter_refcount
- incref = _testinternalcapi.interpreter_incref
- decref = _testinternalcapi.interpreter_decref
+ get_refcount, incref, decref = self.get_refcount_helpers()
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
# Incref will not automatically link it.
incref(interpid)
@@ -2618,8 +2618,7 @@ class InterpreterIDTests(unittest.TestCase):
link = _testinternalcapi.link_interpreter_refcount
unlink = _testinternalcapi.unlink_interpreter_refcount
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
# Linking at refcount 0 does not destroy the interpreter.
link(interpid)
@@ -2639,12 +2638,9 @@ class InterpreterIDTests(unittest.TestCase):
exists = _testinternalcapi.interpreter_exists
is_linked = _testinternalcapi.interpreter_refcount_linked
link = _testinternalcapi.link_interpreter_refcount
- get_refcount = _testinternalcapi.get_interpreter_refcount
- incref = _testinternalcapi.interpreter_incref
- decref = _testinternalcapi.interpreter_decref
+ get_refcount, incref, decref = self.get_refcount_helpers()
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
# Linking it will not change the refcount.
link(interpid)
@@ -2666,11 +2662,9 @@ class InterpreterIDTests(unittest.TestCase):
def test_linked_lifecycle_incref_link(self):
is_linked = _testinternalcapi.interpreter_refcount_linked
link = _testinternalcapi.link_interpreter_refcount
- get_refcount = _testinternalcapi.get_interpreter_refcount
- incref = _testinternalcapi.interpreter_incref
+ get_refcount, incref, _ = self.get_refcount_helpers()
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
incref(interpid)
self.assertEqual(
@@ -2688,12 +2682,9 @@ class InterpreterIDTests(unittest.TestCase):
is_linked = _testinternalcapi.interpreter_refcount_linked
link = _testinternalcapi.link_interpreter_refcount
unlink = _testinternalcapi.unlink_interpreter_refcount
- get_refcount = _testinternalcapi.get_interpreter_refcount
- incref = _testinternalcapi.interpreter_incref
- decref = _testinternalcapi.interpreter_decref
+ get_refcount, incref, decref = self.get_refcount_helpers()
- interpid = _testinternalcapi.new_interpreter()
- self.add_interp_cleanup(interpid)
+ interpid = self.new_interpreter()
link(interpid)
self.assertTrue(
diff --git a/Lib/test/test_import/__init__.py b/Lib/test/test_import/__init__.py
index 81ec700..3c387d9 100644
--- a/Lib/test/test_import/__init__.py
+++ b/Lib/test/test_import/__init__.py
@@ -2163,7 +2163,7 @@ class SinglephaseInitTests(unittest.TestCase):
# subinterpreters
def add_subinterpreter(self):
- interpid = _interpreters.create(isolated=False)
+ interpid = _interpreters.create('legacy')
def ensure_destroyed():
try:
_interpreters.destroy(interpid)
diff --git a/Lib/test/test_importlib/test_util.py b/Lib/test/test_importlib/test_util.py
index a6a76e5..115cb7a 100644
--- a/Lib/test/test_importlib/test_util.py
+++ b/Lib/test/test_importlib/test_util.py
@@ -656,7 +656,7 @@ class MagicNumberTests(unittest.TestCase):
class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):
def run_with_own_gil(self, script):
- interpid = _interpreters.create(isolated=True)
+ interpid = _interpreters.create('isolated')
def ensure_destroyed():
try:
_interpreters.destroy(interpid)
@@ -669,7 +669,7 @@ class IncompatibleExtensionModuleRestrictionsTests(unittest.TestCase):
raise ImportError(excsnap.msg)
def run_with_shared_gil(self, script):
- interpid = _interpreters.create(isolated=False)
+ interpid = _interpreters.create('legacy')
def ensure_destroyed():
try:
_interpreters.destroy(interpid)
diff --git a/Lib/test/test_interpreters/test_api.py b/Lib/test/test_interpreters/test_api.py
index 3cde9bd..2aa7f9b 100644
--- a/Lib/test/test_interpreters/test_api.py
+++ b/Lib/test/test_interpreters/test_api.py
@@ -1,13 +1,14 @@
import os
import pickle
-import threading
from textwrap import dedent
+import threading
+import types
import unittest
from test import support
from test.support import import_helper
# Raise SkipTest if subinterpreters not supported.
-import_helper.import_module('_xxsubinterpreters')
+_interpreters = import_helper.import_module('_xxsubinterpreters')
from test.support import interpreters
from test.support.interpreters import InterpreterNotFoundError
from .utils import _captured_script, _run_output, _running, TestBase
@@ -932,6 +933,212 @@ class TestIsShareable(TestBase):
interpreters.is_shareable(obj))
+class LowLevelTests(TestBase):
+
+ # The behaviors in the low-level module are important in as much
+ # as they are exercised by the high-level module. Therefore the
+ # most important testing happens in the high-level tests.
+ # These low-level tests cover corner cases that are not
+ # encountered by the high-level module, thus they
+ # mostly shouldn't matter as much.
+
+ def test_new_config(self):
+ # This test overlaps with
+ # test.test_capi.test_misc.InterpreterConfigTests.
+
+ default = _interpreters.new_config('isolated')
+ with self.subTest('no arg'):
+ config = _interpreters.new_config()
+ self.assert_ns_equal(config, default)
+ self.assertIsNot(config, default)
+
+ with self.subTest('default'):
+ config1 = _interpreters.new_config('default')
+ self.assert_ns_equal(config1, default)
+ self.assertIsNot(config1, default)
+
+ config2 = _interpreters.new_config('default')
+ self.assert_ns_equal(config2, config1)
+ self.assertIsNot(config2, config1)
+
+ for arg in ['', 'default']:
+ with self.subTest(f'default ({arg!r})'):
+ config = _interpreters.new_config(arg)
+ self.assert_ns_equal(config, default)
+ self.assertIsNot(config, default)
+
+ supported = {
+ 'isolated': types.SimpleNamespace(
+ use_main_obmalloc=False,
+ allow_fork=False,
+ allow_exec=False,
+ allow_threads=True,
+ allow_daemon_threads=False,
+ check_multi_interp_extensions=True,
+ gil='own',
+ ),
+ 'legacy': types.SimpleNamespace(
+ use_main_obmalloc=True,
+ allow_fork=True,
+ allow_exec=True,
+ allow_threads=True,
+ allow_daemon_threads=True,
+ check_multi_interp_extensions=False,
+ gil='shared',
+ ),
+ 'empty': types.SimpleNamespace(
+ use_main_obmalloc=False,
+ allow_fork=False,
+ allow_exec=False,
+ allow_threads=False,
+ allow_daemon_threads=False,
+ check_multi_interp_extensions=False,
+ gil='default',
+ ),
+ }
+ gil_supported = ['default', 'shared', 'own']
+
+ for name, vanilla in supported.items():
+ with self.subTest(f'supported ({name})'):
+ expected = vanilla
+ config1 = _interpreters.new_config(name)
+ self.assert_ns_equal(config1, expected)
+ self.assertIsNot(config1, expected)
+
+ config2 = _interpreters.new_config(name)
+ self.assert_ns_equal(config2, config1)
+ self.assertIsNot(config2, config1)
+
+ with self.subTest(f'noop override ({name})'):
+ expected = vanilla
+ overrides = vars(vanilla)
+ config = _interpreters.new_config(name, **overrides)
+ self.assert_ns_equal(config, expected)
+
+ with self.subTest(f'override all ({name})'):
+ overrides = {k: not v for k, v in vars(vanilla).items()}
+ for gil in gil_supported:
+ if vanilla.gil == gil:
+ continue
+ overrides['gil'] = gil
+ expected = types.SimpleNamespace(**overrides)
+ config = _interpreters.new_config(name, **overrides)
+ self.assert_ns_equal(config, expected)
+
+ # Override individual fields.
+ for field, old in vars(vanilla).items():
+ if field == 'gil':
+ values = [v for v in gil_supported if v != old]
+ else:
+ values = [not old]
+ for val in values:
+ with self.subTest(f'{name}.{field} ({old!r} -> {val!r})'):
+ overrides = {field: val}
+ expected = types.SimpleNamespace(
+ **dict(vars(vanilla), **overrides),
+ )
+ config = _interpreters.new_config(name, **overrides)
+ self.assert_ns_equal(config, expected)
+
+ with self.subTest('extra override'):
+ with self.assertRaises(ValueError):
+ _interpreters.new_config(spam=True)
+
+ # Bad values for bool fields.
+ for field, value in vars(supported['empty']).items():
+ if field == 'gil':
+ continue
+ assert isinstance(value, bool)
+ for value in [1, '', 'spam', 1.0, None, object()]:
+ with self.subTest(f'bad override ({field}={value!r})'):
+ with self.assertRaises(TypeError):
+ _interpreters.new_config(**{field: value})
+
+ # Bad values for .gil.
+ for value in [True, 1, 1.0, None, object()]:
+ with self.subTest(f'bad override (gil={value!r})'):
+ with self.assertRaises(TypeError):
+ _interpreters.new_config(gil=value)
+ for value in ['', 'spam']:
+ with self.subTest(f'bad override (gil={value!r})'):
+ with self.assertRaises(ValueError):
+ _interpreters.new_config(gil=value)
+
+ def test_get_config(self):
+ # This test overlaps with
+ # test.test_capi.test_misc.InterpreterConfigTests.
+
+ with self.subTest('main'):
+ expected = _interpreters.new_config('legacy')
+ expected.gil = 'own'
+ interpid = _interpreters.get_main()
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, expected)
+
+ with self.subTest('isolated'):
+ expected = _interpreters.new_config('isolated')
+ interpid = _interpreters.create('isolated')
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, expected)
+
+ with self.subTest('legacy'):
+ expected = _interpreters.new_config('legacy')
+ interpid = _interpreters.create('legacy')
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, expected)
+
+ def test_create(self):
+ isolated = _interpreters.new_config('isolated')
+ legacy = _interpreters.new_config('legacy')
+ default = isolated
+
+ with self.subTest('no arg'):
+ interpid = _interpreters.create()
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, default)
+
+ with self.subTest('arg: None'):
+ interpid = _interpreters.create(None)
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, default)
+
+ with self.subTest('arg: \'empty\''):
+ with self.assertRaises(RuntimeError):
+ # The "empty" config isn't viable on its own.
+ _interpreters.create('empty')
+
+ for arg, expected in {
+ '': default,
+ 'default': default,
+ 'isolated': isolated,
+ 'legacy': legacy,
+ }.items():
+ with self.subTest(f'str arg: {arg!r}'):
+ interpid = _interpreters.create(arg)
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, expected)
+
+ with self.subTest('custom'):
+ orig = _interpreters.new_config('empty')
+ orig.use_main_obmalloc = True
+ orig.gil = 'shared'
+ interpid = _interpreters.create(orig)
+ config = _interpreters.get_config(interpid)
+ self.assert_ns_equal(config, orig)
+
+ with self.subTest('missing fields'):
+ orig = _interpreters.new_config()
+ del orig.gil
+ with self.assertRaises(ValueError):
+ _interpreters.create(orig)
+
+ with self.subTest('extra fields'):
+ orig = _interpreters.new_config()
+ orig.spam = True
+ with self.assertRaises(ValueError):
+ _interpreters.create(orig)
+
+
if __name__ == '__main__':
# Test needs to be a package, so we can do relative imports.
unittest.main()
diff --git a/Lib/test/test_interpreters/test_queues.py b/Lib/test/test_interpreters/test_queues.py
index d16d294..8ab9ebb 100644
--- a/Lib/test/test_interpreters/test_queues.py
+++ b/Lib/test/test_interpreters/test_queues.py
@@ -28,9 +28,9 @@ class TestBase(_TestBase):
class LowLevelTests(TestBase):
- # The behaviors in the low-level module is important in as much
- # as it is exercised by the high-level module. Therefore the
- # most # important testing happens in the high-level tests.
+ # The behaviors in the low-level module are important in as much
+ # as they are exercised by the high-level module. Therefore the
+ # most important testing happens in the high-level tests.
# These low-level tests cover corner cases that are not
# encountered by the high-level module, thus they
# mostly shouldn't matter as much.
diff --git a/Lib/test/test_interpreters/utils.py b/Lib/test/test_interpreters/utils.py
index 973d05d..5ade676 100644
--- a/Lib/test/test_interpreters/utils.py
+++ b/Lib/test/test_interpreters/utils.py
@@ -68,6 +68,9 @@ def _running(interp):
class TestBase(unittest.TestCase):
+ def tearDown(self):
+ clean_up_interpreters()
+
def pipe(self):
def ensure_closed(fd):
try:
@@ -156,5 +159,19 @@ class TestBase(unittest.TestCase):
self.assertNotEqual(exitcode, 0)
return stdout, stderr
- def tearDown(self):
- clean_up_interpreters()
+ def assert_ns_equal(self, ns1, ns2, msg=None):
+ # This is mostly copied from TestCase.assertDictEqual.
+ self.assertEqual(type(ns1), type(ns2))
+ if ns1 == ns2:
+ return
+
+ import difflib
+ import pprint
+ from unittest.util import _common_shorten_repr
+ standardMsg = '%s != %s' % _common_shorten_repr(ns1, ns2)
+ diff = ('\n' + '\n'.join(difflib.ndiff(
+ pprint.pformat(vars(ns1)).splitlines(),
+ pprint.pformat(vars(ns2)).splitlines())))
+ diff = f'namespace({diff})'
+ standardMsg = self._truncateMessage(standardMsg, diff)
+ self.fail(self._formatMessage(msg, standardMsg))