diff --git a/Lib/test/support/interpreters.py b/Lib/test/support/interpreters.py index 09508e1bbeca03..2935708f9df1a5 100644 --- a/Lib/test/support/interpreters.py +++ b/Lib/test/support/interpreters.py @@ -1,5 +1,6 @@ """Subinterpreters High Level Module.""" +import time import _xxsubinterpreters as _interpreters # aliases: @@ -19,47 +20,51 @@ def create(*, isolated=True): - """ - Initialize a new (idle) Python interpreter. - """ + """Return a new (idle) Python interpreter.""" id = _interpreters.create(isolated=isolated) return Interpreter(id, isolated=isolated) def list_all(): - """ - Get all existing interpreters. - """ - return [Interpreter(id) for id in - _interpreters.list_all()] + """Return all existing interpreters.""" + return [Interpreter(id) for id in _interpreters.list_all()] def get_current(): - """ - Get the currently running interpreter. - """ + """Return the currently running interpreter.""" id = _interpreters.get_current() return Interpreter(id) def get_main(): - """ - Get the main interpreter. - """ + """Return the main interpreter.""" id = _interpreters.get_main() return Interpreter(id) class Interpreter: - """ - The Interpreter object represents - a single interpreter. - """ + """A single Python interpreter.""" def __init__(self, id, *, isolated=None): + if not isinstance(id, (int, _interpreters.InterpreterID)): + raise TypeError(f'id must be an int, got {id!r}') self._id = id self._isolated = isolated + def __repr__(self): + data = dict(id=int(self._id), isolated=self._isolated) + kwargs = (f'{k}={v!r}' for k, v in data.items()) + return f'{type(self).__name__}({", ".join(kwargs)})' + + def __hash__(self): + return hash(self._id) + + def __eq__(self, other): + if not isinstance(other, Interpreter): + return NotImplemented + else: + return other._id == self._id + @property def id(self): return self._id @@ -67,84 +72,98 @@ def id(self): @property def isolated(self): if self._isolated is None: + # XXX The low-level function has not been added yet. + # See bpo-.... self._isolated = _interpreters.is_isolated(self._id) return self._isolated def is_running(self): - """ - Return whether or not the identified - interpreter is running. - """ + """Return whether or not the identified interpreter is running.""" return _interpreters.is_running(self._id) def close(self): - """ - Finalize and destroy the interpreter. + """Finalize and destroy the interpreter. - Attempting to destroy the current - interpreter results in a RuntimeError. + Attempting to destroy the current interpreter results + in a RuntimeError. """ return _interpreters.destroy(self._id) def run(self, src_str, /, *, channels=None): - """ - Run the given source code in the interpreter. + """Run the given source code in the interpreter. + This blocks the current Python thread until done. """ - _interpreters.run_string(self._id, src_str) + _interpreters.run_string(self._id, src_str, channels) def create_channel(): - """ - Create a new channel for passing data between - interpreters. - """ + """Return (recv, send) for a new cross-interpreter channel. + The channel may be used to pass data safely between interpreters. + """ cid = _interpreters.channel_create() - return (RecvChannel(cid), SendChannel(cid)) + recv, send = RecvChannel(cid), SendChannel(cid) + return recv, send def list_all_channels(): - """ - Get all open channels. - """ + """Return a list of (recv, send) for all open channels.""" return [(RecvChannel(cid), SendChannel(cid)) for cid in _interpreters.channel_list_all()] +class _ChannelEnd: + """The base class for RecvChannel and SendChannel.""" + + def __init__(self, id): + if not isinstance(id, (int, _interpreters.ChannelID)): + raise TypeError(f'id must be an int, got {id!r}') + self._id = id + + def __repr__(self): + return f'{type(self).__name__}(id={int(self._id)})' + + def __hash__(self): + return hash(self._id) + + def __eq__(self, other): + if isinstance(self, RecvChannel): + if not isinstance(other, RecvChannel): + return NotImplemented + elif not isinstance(other, SendChannel): + return NotImplemented + return other._id == self._id + + @property + def id(self): + return self._id + + _NOT_SET = object() -class RecvChannel: - """ - The RecvChannel object represents - a receiving channel. - """ +class RecvChannel(_ChannelEnd): + """The receiving end of a cross-interpreter channel.""" - def __init__(self, id): - self._id = id + def recv(self, *, _sentinel=object(), _delay=10 / 1000): # 10 milliseconds + """Return the next object from the channel. - def recv(self, *, _delay=10 / 1000): # 10 milliseconds - """ - Get the next object from the channel, - and wait if none have been sent. - Associate the interpreter with the channel. + This blocks until an object has been sent, if none have been + sent already. """ - import time - sentinel = object() - obj = _interpreters.channel_recv(self._id, sentinel) - while obj is sentinel: + obj = _interpreters.channel_recv(self._id, _sentinel) + while obj is _sentinel: time.sleep(_delay) - obj = _interpreters.channel_recv(self._id, sentinel) + obj = _interpreters.channel_recv(self._id, _sentinel) return obj def recv_nowait(self, default=_NOT_SET): - """ - Like recv(), but return the default - instead of waiting. + """Return the next object from the channel. - This function is blocked by a missing low-level - implementation of channel_recv_wait(). + If none have been sent then return the default if one + is provided or fail with ChannelEmptyError. Otherwise this + is the same as recv(). """ if default is _NOT_SET: return _interpreters.channel_recv(self._id) @@ -152,32 +171,27 @@ def recv_nowait(self, default=_NOT_SET): return _interpreters.channel_recv(self._id, default) -class SendChannel: - """ - The SendChannel object represents - a sending channel. - """ - - def __init__(self, id): - self._id = id +class SendChannel(_ChannelEnd): + """The sending end of a cross-interpreter channel.""" def send(self, obj): + """Send the object (i.e. its data) to the channel's receiving end. + + This blocks until the object is received. """ - Send the object (i.e. its data) to the receiving - end of the channel and wait. Associate the interpreter - with the channel. - """ - import time _interpreters.channel_send(self._id, obj) + # XXX We are missing a low-level channel_send_wait(). + # See bpo-32604 and gh-19829. + # Until that shows up we fake it: time.sleep(2) def send_nowait(self, obj): - """ - Like send(), but return False if not received. + """Send the object to the channel's receiving end. - This function is blocked by a missing low-level - implementation of channel_send_wait(). + If the object is immediately received then return True + (else False). Otherwise this is the same as send(). """ - - _interpreters.channel_send(self._id, obj) - return False + # XXX Note that at the moment channel_send() only ever returns + # None. This should be fixed when channel_send_wait() is added. + # See bpo-32604 and gh-19829. + return _interpreters.channel_send(self._id, obj) diff --git a/Lib/test/test__xxsubinterpreters.py b/Lib/test/test__xxsubinterpreters.py index 7aec021fb19a5e..550a847616cdc5 100644 --- a/Lib/test/test__xxsubinterpreters.py +++ b/Lib/test/test__xxsubinterpreters.py @@ -759,21 +759,9 @@ def test_still_running(self): class RunStringTests(TestBase): - SCRIPT = dedent(""" - with open('{}', 'w') as out: - out.write('{}') - """) - FILENAME = 'spam' - def setUp(self): super().setUp() self.id = interpreters.create() - self._fs = None - - def tearDown(self): - if self._fs is not None: - self._fs.close() - super().tearDown() def test_success(self): script, file = _captured_script('print("it worked!", end="")') diff --git a/Lib/test/test_interpreters.py b/Lib/test/test_interpreters.py index 3451a4c8759d8b..58258bb66af8a8 100644 --- a/Lib/test/test_interpreters.py +++ b/Lib/test/test_interpreters.py @@ -31,10 +31,10 @@ def clean_up_interpreters(): pass # already destroyed -def _run_output(interp, request, shared=None): +def _run_output(interp, request, channels=None): script, rpipe = _captured_script(request) with rpipe: - interp.run(script) + interp.run(script, channels=channels) return rpipe.read() @@ -68,25 +68,22 @@ class CreateTests(TestBase): def test_in_main(self): interp = interpreters.create() - lst = interpreters.list_all() - self.assertEqual(interp.id, lst[1].id) + self.assertIsInstance(interp, interpreters.Interpreter) + self.assertIn(interp, interpreters.list_all()) def test_in_thread(self): lock = threading.Lock() - id = None - interp = interpreters.create() - lst = interpreters.list_all() + interp = None def f(): - nonlocal id - id = interp.id + nonlocal interp + interp = interpreters.create() lock.acquire() lock.release() - t = threading.Thread(target=f) with lock: t.start() t.join() - self.assertEqual(interp.id, lst[1].id) + self.assertIn(interp, interpreters.list_all()) def test_in_subinterpreter(self): main, = interpreters.list_all() @@ -94,11 +91,10 @@ def test_in_subinterpreter(self): out = _run_output(interp, dedent(""" from test.support import interpreters interp = interpreters.create() - print(interp) + print(interp.id) """)) - interp2 = out.strip() - - self.assertEqual(len(set(interpreters.list_all())), len({main, interp, interp2})) + interp2 = interpreters.Interpreter(int(out)) + self.assertEqual(interpreters.list_all(), [main, interp, interp2]) def test_after_destroy_all(self): before = set(interpreters.list_all()) @@ -112,7 +108,7 @@ def test_after_destroy_all(self): interp.close() # Finally, create another. interp = interpreters.create() - self.assertEqual(len(set(interpreters.list_all())), len(before | {interp})) + self.assertEqual(set(interpreters.list_all()), before | {interp}) def test_after_destroy_some(self): before = set(interpreters.list_all()) @@ -125,15 +121,15 @@ def test_after_destroy_some(self): interp2.close() # Finally, create another. interp = interpreters.create() - self.assertEqual(len(set(interpreters.list_all())), len(before | {interp3, interp})) + self.assertEqual(set(interpreters.list_all()), before | {interp3, interp}) class GetCurrentTests(TestBase): def test_main(self): - main_interp_id = _interpreters.get_main() - cur_interp_id = interpreters.get_current().id - self.assertEqual(cur_interp_id, main_interp_id) + main = interpreters.get_main() + current = interpreters.get_current() + self.assertEqual(current, main) def test_subinterpreter(self): main = _interpreters.get_main() @@ -141,10 +137,10 @@ def test_subinterpreter(self): out = _run_output(interp, dedent(""" from test.support import interpreters cur = interpreters.get_current() - print(cur) + print(cur.id) """)) - cur = out.strip() - self.assertNotEqual(cur, main) + current = interpreters.Interpreter(int(out)) + self.assertNotEqual(current, main) class ListAllTests(TestBase): @@ -177,26 +173,75 @@ def test_after_destroying(self): self.assertEqual(ids, [main.id, second.id]) -class TestInterpreterId(TestBase): +class TestInterpreterAttrs(TestBase): - def test_in_main(self): - main = interpreters.get_current() - self.assertEqual(0, main.id) + def test_id_type(self): + main = interpreters.get_main() + current = interpreters.get_current() + interp = interpreters.create() + self.assertIsInstance(main.id, _interpreters.InterpreterID) + self.assertIsInstance(current.id, _interpreters.InterpreterID) + self.assertIsInstance(interp.id, _interpreters.InterpreterID) - def test_with_custom_num(self): + def test_main_id(self): + main = interpreters.get_main() + self.assertEqual(main.id, 0) + + def test_custom_id(self): interp = interpreters.Interpreter(1) - self.assertEqual(1, interp.id) + self.assertEqual(interp.id, 1) + + with self.assertRaises(TypeError): + interpreters.Interpreter('1') - def test_for_readonly_property(self): + def test_id_readonly(self): interp = interpreters.Interpreter(1) with self.assertRaises(AttributeError): interp.id = 2 + @unittest.skip('not ready yet (see bpo-32604)') + def test_main_isolated(self): + main = interpreters.get_main() + self.assertFalse(main.isolated) + + @unittest.skip('not ready yet (see bpo-32604)') + def test_subinterpreter_isolated_default(self): + interp = interpreters.create() + self.assertFalse(interp.isolated) + + def test_subinterpreter_isolated_explicit(self): + interp1 = interpreters.create(isolated=True) + interp2 = interpreters.create(isolated=False) + self.assertTrue(interp1.isolated) + self.assertFalse(interp2.isolated) + + @unittest.skip('not ready yet (see bpo-32604)') + def test_custom_isolated_default(self): + interp = interpreters.Interpreter(1) + self.assertFalse(interp.isolated) + + def test_custom_isolated_explicit(self): + interp1 = interpreters.Interpreter(1, isolated=True) + interp2 = interpreters.Interpreter(1, isolated=False) + self.assertTrue(interp1.isolated) + self.assertFalse(interp2.isolated) + + def test_isolated_readonly(self): + interp = interpreters.Interpreter(1) + with self.assertRaises(AttributeError): + interp.isolated = True + + def test_equality(self): + interp1 = interpreters.create() + interp2 = interpreters.create() + self.assertEqual(interp1, interp1) + self.assertNotEqual(interp1, interp2) + class TestInterpreterIsRunning(TestBase): def test_main(self): - main = interpreters.get_current() + main = interpreters.get_main() self.assertTrue(main.is_running()) def test_subinterpreter(self): @@ -224,16 +269,29 @@ def test_already_destroyed(self): with self.assertRaises(RuntimeError): interp.is_running() + def test_does_not_exist(self): + interp = interpreters.Interpreter(1_000_000) + with self.assertRaises(RuntimeError): + interp.is_running() + + def test_bad_id(self): + interp = interpreters.Interpreter(-1) + with self.assertRaises(ValueError): + interp.is_running() -class TestInterpreterDestroy(TestBase): + +class TestInterpreterClose(TestBase): def test_basic(self): + main = interpreters.get_main() interp1 = interpreters.create() interp2 = interpreters.create() interp3 = interpreters.create() - self.assertEqual(4, len(interpreters.list_all())) + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp2, interp3}) interp2.close() - self.assertEqual(3, len(interpreters.list_all())) + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp3}) def test_all(self): before = set(interpreters.list_all()) @@ -241,10 +299,10 @@ def test_all(self): for _ in range(3): interp = interpreters.create() interps.add(interp) - self.assertEqual(len(set(interpreters.list_all())), len(before | interps)) + self.assertEqual(set(interpreters.list_all()), before | interps) for interp in interps: interp.close() - self.assertEqual(len(set(interpreters.list_all())), len(before)) + self.assertEqual(set(interpreters.list_all()), before) def test_main(self): main, = interpreters.list_all() @@ -265,32 +323,44 @@ def test_already_destroyed(self): with self.assertRaises(RuntimeError): interp.close() + def test_does_not_exist(self): + interp = interpreters.Interpreter(1_000_000) + with self.assertRaises(RuntimeError): + interp.close() + + def test_bad_id(self): + interp = interpreters.Interpreter(-1) + with self.assertRaises(ValueError): + interp.close() + def test_from_current(self): main, = interpreters.list_all() interp = interpreters.create() - script = dedent(f""" + out = _run_output(interp, dedent(f""" from test.support import interpreters + interp = interpreters.Interpreter({int(interp.id)}) try: - main = interpreters.get_current() - main.close() + interp.close() except RuntimeError: - pass - """) - - interp.run(script) - self.assertEqual(len(set(interpreters.list_all())), len({main, interp})) + print('failed') + """)) + self.assertEqual(out.strip(), 'failed') + self.assertEqual(set(interpreters.list_all()), {main, interp}) def test_from_sibling(self): main, = interpreters.list_all() interp1 = interpreters.create() - script = dedent(f""" + interp2 = interpreters.create() + self.assertEqual(set(interpreters.list_all()), + {main, interp1, interp2}) + interp1.run(dedent(f""" from test.support import interpreters - interp2 = interpreters.create() + interp2 = interpreters.Interpreter(int({interp2.id})) interp2.close() - """) - interp1.run(script) - - self.assertEqual(len(set(interpreters.list_all())), len({main, interp1})) + interp3 = interpreters.create() + interp3.close() + """)) + self.assertEqual(set(interpreters.list_all()), {main, interp1}) def test_from_other_thread(self): interp = interpreters.create() @@ -312,41 +382,21 @@ def test_still_running(self): class TestInterpreterRun(TestBase): - SCRIPT = dedent(""" - with open('{}', 'w') as out: - out.write('{}') - """) - FILENAME = 'spam' - - def setUp(self): - super().setUp() - self.interp = interpreters.create() - self._fs = None - - def tearDown(self): - if self._fs is not None: - self._fs.close() - super().tearDown() - - @property - def fs(self): - if self._fs is None: - self._fs = FSFixture(self) - return self._fs - def test_success(self): + interp = interpreters.create() script, file = _captured_script('print("it worked!", end="")') with file: - self.interp.run(script) + interp.run(script) out = file.read() self.assertEqual(out, 'it worked!') def test_in_thread(self): + interp = interpreters.create() script, file = _captured_script('print("it worked!", end="")') with file: def f(): - self.interp.run(script) + interp.run(script) t = threading.Thread(target=f) t.start() @@ -357,6 +407,7 @@ def f(): @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") def test_fork(self): + interp = interpreters.create() import tempfile with tempfile.NamedTemporaryFile('w+') as file: file.write('') @@ -371,24 +422,39 @@ def test_fork(self): with open('{file.name}', 'w') as out: out.write('{expected}') """) - self.interp.run(script) + interp.run(script) file.seek(0) content = file.read() self.assertEqual(content, expected) def test_already_running(self): - with _running(self.interp): + interp = interpreters.create() + with _running(interp): with self.assertRaises(RuntimeError): - self.interp.run('print("spam")') + interp.run('print("spam")') + + def test_does_not_exist(self): + interp = interpreters.Interpreter(1_000_000) + with self.assertRaises(RuntimeError): + interp.run('print("spam")') + + def test_bad_id(self): + interp = interpreters.Interpreter(-1) + with self.assertRaises(ValueError): + interp.run('print("spam")') def test_bad_script(self): + interp = interpreters.create() with self.assertRaises(TypeError): - self.interp.run(10) + interp.run(10) def test_bytes_for_script(self): + interp = interpreters.create() with self.assertRaises(TypeError): - self.interp.run(b'print("spam")') + interp.run(b'print("spam")') + + # test_xxsubinterpreters covers the remaining Interpreter.run() behavior. class TestIsShareable(TestBase): @@ -405,8 +471,8 @@ def test_default_shareables(self): ] for obj in shareables: with self.subTest(obj): - self.assertTrue( - interpreters.is_shareable(obj)) + shareable = interpreters.is_shareable(obj) + self.assertTrue(shareable) def test_not_shareable(self): class Cheese: @@ -441,22 +507,71 @@ class SubBytes(bytes): interpreters.is_shareable(obj)) -class TestChannel(TestBase): +class TestChannels(TestBase): - def test_create_cid(self): + def test_create(self): r, s = interpreters.create_channel() self.assertIsInstance(r, interpreters.RecvChannel) self.assertIsInstance(s, interpreters.SendChannel) - def test_sequential_ids(self): - before = interpreters.list_all_channels() - channels1 = interpreters.create_channel() - channels2 = interpreters.create_channel() - channels3 = interpreters.create_channel() - after = interpreters.list_all_channels() + def test_list_all(self): + self.assertEqual(interpreters.list_all_channels(), []) + created = set() + for _ in range(3): + ch = interpreters.create_channel() + created.add(ch) + after = set(interpreters.list_all_channels()) + self.assertEqual(after, created) + + +class TestRecvChannelAttrs(TestBase): + + def test_id_type(self): + rch, _ = interpreters.create_channel() + self.assertIsInstance(rch.id, _interpreters.ChannelID) + + def test_custom_id(self): + rch = interpreters.RecvChannel(1) + self.assertEqual(rch.id, 1) + + with self.assertRaises(TypeError): + interpreters.RecvChannel('1') + + def test_id_readonly(self): + rch = interpreters.RecvChannel(1) + with self.assertRaises(AttributeError): + rch.id = 2 + + def test_equality(self): + ch1, _ = interpreters.create_channel() + ch2, _ = interpreters.create_channel() + self.assertEqual(ch1, ch1) + self.assertNotEqual(ch1, ch2) + + +class TestSendChannelAttrs(TestBase): + + def test_id_type(self): + _, sch = interpreters.create_channel() + self.assertIsInstance(sch.id, _interpreters.ChannelID) - self.assertEqual(len(set(after) - set(before)), - len({channels1, channels2, channels3})) + def test_custom_id(self): + sch = interpreters.SendChannel(1) + self.assertEqual(sch.id, 1) + + with self.assertRaises(TypeError): + interpreters.SendChannel('1') + + def test_id_readonly(self): + sch = interpreters.SendChannel(1) + with self.assertRaises(AttributeError): + sch.id = 2 + + def test_equality(self): + _, ch1 = interpreters.create_channel() + _, ch2 = interpreters.create_channel() + self.assertEqual(ch1, ch1) + self.assertNotEqual(ch1, ch2) class TestSendRecv(TestBase): @@ -464,7 +579,7 @@ class TestSendRecv(TestBase): def test_send_recv_main(self): r, s = interpreters.create_channel() orig = b'spam' - s.send(orig) + s.send_nowait(orig) obj = r.recv() self.assertEqual(obj, orig) @@ -472,16 +587,40 @@ def test_send_recv_main(self): def test_send_recv_same_interpreter(self): interp = interpreters.create() - out = _run_output(interp, dedent(""" + interp.run(dedent(""" from test.support import interpreters r, s = interpreters.create_channel() orig = b'spam' - s.send(orig) + s.send_nowait(orig) obj = r.recv() - assert obj is not orig - assert obj == orig + assert obj == orig, 'expected: obj == orig' + assert obj is not orig, 'expected: obj is not orig' """)) + @unittest.skip('broken (see BPO-...)') + def test_send_recv_different_interpreters(self): + r1, s1 = interpreters.create_channel() + r2, s2 = interpreters.create_channel() + orig1 = b'spam' + s1.send_nowait(orig1) + out = _run_output( + interpreters.create(), + dedent(f""" + obj1 = r.recv() + assert obj1 == b'spam', 'expected: obj1 == orig1' + # When going to another interpreter we get a copy. + assert id(obj1) != {id(orig1)}, 'expected: obj1 is not orig1' + orig2 = b'eggs' + print(id(orig2)) + s.send_nowait(orig2) + """), + channels=dict(r=r1, s=s2), + ) + obj2 = r2.recv() + + self.assertEqual(obj2, b'eggs') + self.assertNotEqual(id(obj2), int(out)) + def test_send_recv_different_threads(self): r, s = interpreters.create_channel() @@ -496,40 +635,108 @@ def f(): t = threading.Thread(target=f) t.start() - s.send(b'spam') + orig = b'spam' + s.send(orig) t.join() obj = r.recv() - self.assertEqual(obj, b'spam') + self.assertEqual(obj, orig) + self.assertIsNot(obj, orig) def test_send_recv_nowait_main(self): r, s = interpreters.create_channel() orig = b'spam' - s.send(orig) + s.send_nowait(orig) obj = r.recv_nowait() self.assertEqual(obj, orig) self.assertIsNot(obj, orig) + def test_send_recv_nowait_main_with_default(self): + r, _ = interpreters.create_channel() + obj = r.recv_nowait(None) + + self.assertIsNone(obj) + def test_send_recv_nowait_same_interpreter(self): interp = interpreters.create() - out = _run_output(interp, dedent(""" + interp.run(dedent(""" from test.support import interpreters r, s = interpreters.create_channel() orig = b'spam' - s.send(orig) + s.send_nowait(orig) obj = r.recv_nowait() - assert obj is not orig - assert obj == orig + assert obj == orig, 'expected: obj == orig' + # When going back to the same interpreter we get the same object. + assert obj is not orig, 'expected: obj is not orig' """)) - r, s = interpreters.create_channel() - - def f(): - while True: - try: - obj = r.recv_nowait() - break - except _interpreters.ChannelEmptyError: - time.sleep(0.1) - s.send(obj) + @unittest.skip('broken (see BPO-...)') + def test_send_recv_nowait_different_interpreters(self): + r1, s1 = interpreters.create_channel() + r2, s2 = interpreters.create_channel() + orig1 = b'spam' + s1.send_nowait(orig1) + out = _run_output( + interpreters.create(), + dedent(f""" + obj1 = r.recv_nowait() + assert obj1 == b'spam', 'expected: obj1 == orig1' + # When going to another interpreter we get a copy. + assert id(obj1) != {id(orig1)}, 'expected: obj1 is not orig1' + orig2 = b'eggs' + print(id(orig2)) + s.send_nowait(orig2) + """), + channels=dict(r=r1, s=s2), + ) + obj2 = r2.recv_nowait() + + self.assertEqual(obj2, b'eggs') + self.assertNotEqual(id(obj2), int(out)) + + def test_recv_channel_does_not_exist(self): + ch = interpreters.RecvChannel(1_000_000) + with self.assertRaises(interpreters.ChannelNotFoundError): + ch.recv() + + def test_send_channel_does_not_exist(self): + ch = interpreters.SendChannel(1_000_000) + with self.assertRaises(interpreters.ChannelNotFoundError): + ch.send(b'spam') + + def test_recv_nowait_channel_does_not_exist(self): + ch = interpreters.RecvChannel(1_000_000) + with self.assertRaises(interpreters.ChannelNotFoundError): + ch.recv_nowait() + + def test_send_nowait_channel_does_not_exist(self): + ch = interpreters.SendChannel(1_000_000) + with self.assertRaises(interpreters.ChannelNotFoundError): + ch.send_nowait(b'spam') + + def test_recv_nowait_empty(self): + ch, _ = interpreters.create_channel() + with self.assertRaises(interpreters.ChannelEmptyError): + ch.recv_nowait() + + def test_recv_nowait_default(self): + default = object() + rch, sch = interpreters.create_channel() + obj1 = rch.recv_nowait(default) + sch.send_nowait(None) + sch.send_nowait(1) + sch.send_nowait(b'spam') + sch.send_nowait(b'eggs') + obj2 = rch.recv_nowait(default) + obj3 = rch.recv_nowait(default) + obj4 = rch.recv_nowait() + obj5 = rch.recv_nowait(default) + obj6 = rch.recv_nowait(default) + + self.assertIs(obj1, default) + self.assertIs(obj2, None) + self.assertEqual(obj3, 1) + self.assertEqual(obj4, b'spam') + self.assertEqual(obj5, b'eggs') + self.assertIs(obj6, default)
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: