blob: 550a847616cdc54c90f49a1e9d87f6d51c93ce99 [file] [log] [blame]
Eric Snow6d2cd902018-05-16 15:04:57 -04001from collections import namedtuple
Eric Snow7f8bfc92018-01-29 18:23:44 -07002import contextlib
Eric Snow6d2cd902018-05-16 15:04:57 -04003import itertools
Eric Snow7f8bfc92018-01-29 18:23:44 -07004import os
5import pickle
Eric Snow6d2cd902018-05-16 15:04:57 -04006import sys
Victor Stinner8f4ef3b2019-07-01 18:28:25 +02007from textwrap import dedent
Eric Snow7f8bfc92018-01-29 18:23:44 -07008import threading
Eric Snowf53d9f22018-02-20 16:30:17 -07009import time
Eric Snow7f8bfc92018-01-29 18:23:44 -070010import unittest
11
12from test import support
13from test.support import script_helper
14
Victor Stinnerc4f3cb72018-06-08 02:28:28 +020015
Eric Snow7f8bfc92018-01-29 18:23:44 -070016interpreters = support.import_module('_xxsubinterpreters')
17
18
Eric Snow6d2cd902018-05-16 15:04:57 -040019##################################
20# helpers
21
Eric Snow7f8bfc92018-01-29 18:23:44 -070022def _captured_script(script):
23 r, w = os.pipe()
24 indented = script.replace('\n', '\n ')
25 wrapped = dedent(f"""
26 import contextlib
Eric Snow6d2cd902018-05-16 15:04:57 -040027 with open({w}, 'w') as spipe:
28 with contextlib.redirect_stdout(spipe):
Eric Snow7f8bfc92018-01-29 18:23:44 -070029 {indented}
30 """)
31 return wrapped, open(r)
32
33
34def _run_output(interp, request, shared=None):
Eric Snow6d2cd902018-05-16 15:04:57 -040035 script, rpipe = _captured_script(request)
36 with rpipe:
Eric Snow7f8bfc92018-01-29 18:23:44 -070037 interpreters.run_string(interp, script, shared)
Eric Snow6d2cd902018-05-16 15:04:57 -040038 return rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070039
40
41@contextlib.contextmanager
42def _running(interp):
43 r, w = os.pipe()
44 def run():
45 interpreters.run_string(interp, dedent(f"""
46 # wait for "signal"
Eric Snow6d2cd902018-05-16 15:04:57 -040047 with open({r}) as rpipe:
48 rpipe.read()
Eric Snow7f8bfc92018-01-29 18:23:44 -070049 """))
50
51 t = threading.Thread(target=run)
52 t.start()
53
54 yield
55
Eric Snow6d2cd902018-05-16 15:04:57 -040056 with open(w, 'w') as spipe:
57 spipe.write('done')
Eric Snow7f8bfc92018-01-29 18:23:44 -070058 t.join()
59
60
Eric Snow6d2cd902018-05-16 15:04:57 -040061#@contextmanager
62#def run_threaded(id, source, **shared):
63# def run():
64# run_interp(id, source, **shared)
65# t = threading.Thread(target=run)
66# t.start()
67# yield
68# t.join()
69
70
71def run_interp(id, source, **shared):
72 _run_interp(id, source, shared)
73
74
75def _run_interp(id, source, shared, _mainns={}):
76 source = dedent(source)
77 main = interpreters.get_main()
78 if main == id:
79 if interpreters.get_current() != main:
80 raise RuntimeError
81 # XXX Run a func?
82 exec(source, _mainns)
83 else:
84 interpreters.run_string(id, source, shared)
85
86
Eric Snow6d2cd902018-05-16 15:04:57 -040087class Interpreter(namedtuple('Interpreter', 'name id')):
88
89 @classmethod
90 def from_raw(cls, raw):
91 if isinstance(raw, cls):
92 return raw
93 elif isinstance(raw, str):
94 return cls(raw)
95 else:
96 raise NotImplementedError
97
98 def __new__(cls, name=None, id=None):
99 main = interpreters.get_main()
100 if id == main:
101 if not name:
102 name = 'main'
103 elif name != 'main':
104 raise ValueError(
105 'name mismatch (expected "main", got "{}")'.format(name))
106 id = main
107 elif id is not None:
108 if not name:
109 name = 'interp'
110 elif name == 'main':
111 raise ValueError('name mismatch (unexpected "main")')
112 if not isinstance(id, interpreters.InterpreterID):
113 id = interpreters.InterpreterID(id)
114 elif not name or name == 'main':
115 name = 'main'
116 id = main
117 else:
118 id = interpreters.create()
119 self = super().__new__(cls, name, id)
120 return self
121
122
123# XXX expect_channel_closed() is unnecessary once we improve exc propagation.
124
125@contextlib.contextmanager
126def expect_channel_closed():
127 try:
128 yield
129 except interpreters.ChannelClosedError:
130 pass
131 else:
132 assert False, 'channel not closed'
133
134
135class ChannelAction(namedtuple('ChannelAction', 'action end interp')):
136
137 def __new__(cls, action, end=None, interp=None):
138 if not end:
139 end = 'both'
140 if not interp:
141 interp = 'main'
142 self = super().__new__(cls, action, end, interp)
143 return self
144
145 def __init__(self, *args, **kwargs):
146 if self.action == 'use':
147 if self.end not in ('same', 'opposite', 'send', 'recv'):
148 raise ValueError(self.end)
149 elif self.action in ('close', 'force-close'):
150 if self.end not in ('both', 'same', 'opposite', 'send', 'recv'):
151 raise ValueError(self.end)
152 else:
153 raise ValueError(self.action)
154 if self.interp not in ('main', 'same', 'other', 'extra'):
155 raise ValueError(self.interp)
156
157 def resolve_end(self, end):
158 if self.end == 'same':
159 return end
160 elif self.end == 'opposite':
161 return 'recv' if end == 'send' else 'send'
162 else:
163 return self.end
164
165 def resolve_interp(self, interp, other, extra):
166 if self.interp == 'same':
167 return interp
168 elif self.interp == 'other':
169 if other is None:
170 raise RuntimeError
171 return other
172 elif self.interp == 'extra':
173 if extra is None:
174 raise RuntimeError
175 return extra
176 elif self.interp == 'main':
177 if interp.name == 'main':
178 return interp
179 elif other and other.name == 'main':
180 return other
181 else:
182 raise RuntimeError
183 # Per __init__(), there aren't any others.
184
185
186class ChannelState(namedtuple('ChannelState', 'pending closed')):
187
188 def __new__(cls, pending=0, *, closed=False):
189 self = super().__new__(cls, pending, closed)
190 return self
191
192 def incr(self):
193 return type(self)(self.pending + 1, closed=self.closed)
194
195 def decr(self):
196 return type(self)(self.pending - 1, closed=self.closed)
197
198 def close(self, *, force=True):
199 if self.closed:
200 if not force or self.pending == 0:
201 return self
202 return type(self)(0 if force else self.pending, closed=True)
203
204
205def run_action(cid, action, end, state, *, hideclosed=True):
206 if state.closed:
207 if action == 'use' and end == 'recv' and state.pending:
208 expectfail = False
209 else:
210 expectfail = True
211 else:
212 expectfail = False
213
214 try:
215 result = _run_action(cid, action, end, state)
216 except interpreters.ChannelClosedError:
217 if not hideclosed and not expectfail:
218 raise
219 result = state.close()
220 else:
221 if expectfail:
222 raise ... # XXX
223 return result
224
225
226def _run_action(cid, action, end, state):
227 if action == 'use':
228 if end == 'send':
229 interpreters.channel_send(cid, b'spam')
230 return state.incr()
231 elif end == 'recv':
232 if not state.pending:
233 try:
234 interpreters.channel_recv(cid)
235 except interpreters.ChannelEmptyError:
236 return state
237 else:
238 raise Exception('expected ChannelEmptyError')
239 else:
240 interpreters.channel_recv(cid)
241 return state.decr()
242 else:
243 raise ValueError(end)
244 elif action == 'close':
245 kwargs = {}
246 if end in ('recv', 'send'):
247 kwargs[end] = True
248 interpreters.channel_close(cid, **kwargs)
249 return state.close()
250 elif action == 'force-close':
251 kwargs = {
252 'force': True,
253 }
254 if end in ('recv', 'send'):
255 kwargs[end] = True
256 interpreters.channel_close(cid, **kwargs)
257 return state.close(force=True)
258 else:
259 raise ValueError(action)
260
261
262def clean_up_interpreters():
263 for id in interpreters.list_all():
264 if id == 0: # main
265 continue
266 try:
267 interpreters.destroy(id)
268 except RuntimeError:
269 pass # already destroyed
270
271
272def clean_up_channels():
273 for cid in interpreters.channel_list_all():
274 try:
275 interpreters.channel_destroy(cid)
276 except interpreters.ChannelNotFoundError:
277 pass # already destroyed
278
279
280class TestBase(unittest.TestCase):
281
282 def tearDown(self):
283 clean_up_interpreters()
284 clean_up_channels()
285
286
287##################################
288# misc. tests
289
Eric Snow7f8bfc92018-01-29 18:23:44 -0700290class IsShareableTests(unittest.TestCase):
291
292 def test_default_shareables(self):
293 shareables = [
294 # singletons
295 None,
296 # builtin objects
297 b'spam',
Eric Snow6d2cd902018-05-16 15:04:57 -0400298 'spam',
299 10,
300 -10,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700301 ]
302 for obj in shareables:
303 with self.subTest(obj):
304 self.assertTrue(
305 interpreters.is_shareable(obj))
306
307 def test_not_shareable(self):
308 class Cheese:
309 def __init__(self, name):
310 self.name = name
311 def __str__(self):
312 return self.name
313
314 class SubBytes(bytes):
315 """A subclass of a shareable type."""
316
317 not_shareables = [
318 # singletons
319 True,
320 False,
321 NotImplemented,
322 ...,
323 # builtin types and objects
324 type,
325 object,
326 object(),
327 Exception(),
Eric Snow7f8bfc92018-01-29 18:23:44 -0700328 100.0,
Eric Snow7f8bfc92018-01-29 18:23:44 -0700329 # user-defined types and objects
330 Cheese,
331 Cheese('Wensleydale'),
332 SubBytes(b'spam'),
333 ]
334 for obj in not_shareables:
Eric Snow6d2cd902018-05-16 15:04:57 -0400335 with self.subTest(repr(obj)):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700336 self.assertFalse(
337 interpreters.is_shareable(obj))
338
339
Eric Snow6d2cd902018-05-16 15:04:57 -0400340class ShareableTypeTests(unittest.TestCase):
341
342 def setUp(self):
343 super().setUp()
344 self.cid = interpreters.channel_create()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700345
346 def tearDown(self):
Eric Snow6d2cd902018-05-16 15:04:57 -0400347 interpreters.channel_destroy(self.cid)
348 super().tearDown()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700349
Eric Snow6d2cd902018-05-16 15:04:57 -0400350 def _assert_values(self, values):
351 for obj in values:
352 with self.subTest(obj):
353 interpreters.channel_send(self.cid, obj)
354 got = interpreters.channel_recv(self.cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700355
Eric Snow6d2cd902018-05-16 15:04:57 -0400356 self.assertEqual(got, obj)
357 self.assertIs(type(got), type(obj))
358 # XXX Check the following in the channel tests?
359 #self.assertIsNot(got, obj)
360
361 def test_singletons(self):
362 for obj in [None]:
363 with self.subTest(obj):
364 interpreters.channel_send(self.cid, obj)
365 got = interpreters.channel_recv(self.cid)
366
367 # XXX What about between interpreters?
368 self.assertIs(got, obj)
369
370 def test_types(self):
371 self._assert_values([
372 b'spam',
373 9999,
374 self.cid,
375 ])
376
377 def test_bytes(self):
378 self._assert_values(i.to_bytes(2, 'little', signed=True)
379 for i in range(-1, 258))
380
An Long29c11722020-06-13 20:26:01 +0800381 def test_strs(self):
382 self._assert_values(['hello world', '你好世界', ''])
383
Eric Snow6d2cd902018-05-16 15:04:57 -0400384 def test_int(self):
Alexey Izbyshev16f842d2019-02-12 19:06:43 +0300385 self._assert_values(itertools.chain(range(-1, 258),
386 [sys.maxsize, -sys.maxsize - 1]))
387
388 def test_non_shareable_int(self):
389 ints = [
390 sys.maxsize + 1,
391 -sys.maxsize - 2,
392 2**1000,
393 ]
394 for i in ints:
395 with self.subTest(i):
396 with self.assertRaises(OverflowError):
397 interpreters.channel_send(self.cid, i)
Eric Snow6d2cd902018-05-16 15:04:57 -0400398
399
400##################################
401# interpreter tests
Eric Snow7f8bfc92018-01-29 18:23:44 -0700402
403class ListAllTests(TestBase):
404
405 def test_initial(self):
406 main = interpreters.get_main()
407 ids = interpreters.list_all()
408 self.assertEqual(ids, [main])
409
410 def test_after_creating(self):
411 main = interpreters.get_main()
412 first = interpreters.create()
413 second = interpreters.create()
414 ids = interpreters.list_all()
415 self.assertEqual(ids, [main, first, second])
416
417 def test_after_destroying(self):
418 main = interpreters.get_main()
419 first = interpreters.create()
420 second = interpreters.create()
421 interpreters.destroy(first)
422 ids = interpreters.list_all()
423 self.assertEqual(ids, [main, second])
424
425
426class GetCurrentTests(TestBase):
427
428 def test_main(self):
429 main = interpreters.get_main()
430 cur = interpreters.get_current()
431 self.assertEqual(cur, main)
Eric Snow6d2cd902018-05-16 15:04:57 -0400432 self.assertIsInstance(cur, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700433
434 def test_subinterpreter(self):
435 main = interpreters.get_main()
436 interp = interpreters.create()
437 out = _run_output(interp, dedent("""
438 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400439 cur = _interpreters.get_current()
440 print(cur)
441 assert isinstance(cur, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700442 """))
443 cur = int(out.strip())
444 _, expected = interpreters.list_all()
445 self.assertEqual(cur, expected)
446 self.assertNotEqual(cur, main)
447
448
449class GetMainTests(TestBase):
450
451 def test_from_main(self):
452 [expected] = interpreters.list_all()
453 main = interpreters.get_main()
454 self.assertEqual(main, expected)
Eric Snow6d2cd902018-05-16 15:04:57 -0400455 self.assertIsInstance(main, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700456
457 def test_from_subinterpreter(self):
458 [expected] = interpreters.list_all()
459 interp = interpreters.create()
460 out = _run_output(interp, dedent("""
461 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400462 main = _interpreters.get_main()
463 print(main)
464 assert isinstance(main, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700465 """))
466 main = int(out.strip())
467 self.assertEqual(main, expected)
468
469
470class IsRunningTests(TestBase):
471
472 def test_main(self):
473 main = interpreters.get_main()
474 self.assertTrue(interpreters.is_running(main))
475
476 def test_subinterpreter(self):
477 interp = interpreters.create()
478 self.assertFalse(interpreters.is_running(interp))
479
480 with _running(interp):
481 self.assertTrue(interpreters.is_running(interp))
482 self.assertFalse(interpreters.is_running(interp))
483
484 def test_from_subinterpreter(self):
485 interp = interpreters.create()
486 out = _run_output(interp, dedent(f"""
487 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400488 if _interpreters.is_running({interp}):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700489 print(True)
490 else:
491 print(False)
492 """))
493 self.assertEqual(out.strip(), 'True')
494
495 def test_already_destroyed(self):
496 interp = interpreters.create()
497 interpreters.destroy(interp)
498 with self.assertRaises(RuntimeError):
499 interpreters.is_running(interp)
500
501 def test_does_not_exist(self):
502 with self.assertRaises(RuntimeError):
503 interpreters.is_running(1_000_000)
504
505 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300506 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700507 interpreters.is_running(-1)
508
509
Eric Snow4c6955e2018-02-16 18:53:40 -0700510class InterpreterIDTests(TestBase):
511
512 def test_with_int(self):
513 id = interpreters.InterpreterID(10, force=True)
514
515 self.assertEqual(int(id), 10)
516
517 def test_coerce_id(self):
Eric Snow4c6955e2018-02-16 18:53:40 -0700518 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300519 def __index__(self):
520 return 10
Eric Snow4c6955e2018-02-16 18:53:40 -0700521
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300522 id = interpreters.InterpreterID(Int(), force=True)
523 self.assertEqual(int(id), 10)
Eric Snow4c6955e2018-02-16 18:53:40 -0700524
525 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300526 self.assertRaises(TypeError, interpreters.InterpreterID, object())
527 self.assertRaises(TypeError, interpreters.InterpreterID, 10.0)
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300528 self.assertRaises(TypeError, interpreters.InterpreterID, '10')
Serhiy Storchakabf169912019-09-13 22:50:27 +0300529 self.assertRaises(TypeError, interpreters.InterpreterID, b'10')
530 self.assertRaises(ValueError, interpreters.InterpreterID, -1)
Serhiy Storchakabf169912019-09-13 22:50:27 +0300531 self.assertRaises(OverflowError, interpreters.InterpreterID, 2**64)
Eric Snow4c6955e2018-02-16 18:53:40 -0700532
533 def test_does_not_exist(self):
534 id = interpreters.channel_create()
535 with self.assertRaises(RuntimeError):
536 interpreters.InterpreterID(int(id) + 1) # unforced
537
Eric Snow6d2cd902018-05-16 15:04:57 -0400538 def test_str(self):
539 id = interpreters.InterpreterID(10, force=True)
540 self.assertEqual(str(id), '10')
541
Eric Snow4c6955e2018-02-16 18:53:40 -0700542 def test_repr(self):
543 id = interpreters.InterpreterID(10, force=True)
544 self.assertEqual(repr(id), 'InterpreterID(10)')
545
546 def test_equality(self):
547 id1 = interpreters.create()
548 id2 = interpreters.InterpreterID(int(id1))
549 id3 = interpreters.create()
550
551 self.assertTrue(id1 == id1)
552 self.assertTrue(id1 == id2)
553 self.assertTrue(id1 == int(id1))
Serhiy Storchakabf169912019-09-13 22:50:27 +0300554 self.assertTrue(int(id1) == id1)
555 self.assertTrue(id1 == float(int(id1)))
556 self.assertTrue(float(int(id1)) == id1)
557 self.assertFalse(id1 == float(int(id1)) + 0.1)
558 self.assertFalse(id1 == str(int(id1)))
559 self.assertFalse(id1 == 2**1000)
560 self.assertFalse(id1 == float('inf'))
561 self.assertFalse(id1 == 'spam')
Eric Snow4c6955e2018-02-16 18:53:40 -0700562 self.assertFalse(id1 == id3)
563
564 self.assertFalse(id1 != id1)
565 self.assertFalse(id1 != id2)
566 self.assertTrue(id1 != id3)
567
568
Eric Snow7f8bfc92018-01-29 18:23:44 -0700569class CreateTests(TestBase):
570
571 def test_in_main(self):
572 id = interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400573 self.assertIsInstance(id, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700574
575 self.assertIn(id, interpreters.list_all())
576
577 @unittest.skip('enable this test when working on pystate.c')
578 def test_unique_id(self):
579 seen = set()
580 for _ in range(100):
581 id = interpreters.create()
582 interpreters.destroy(id)
583 seen.add(id)
584
585 self.assertEqual(len(seen), 100)
586
587 def test_in_thread(self):
588 lock = threading.Lock()
589 id = None
590 def f():
591 nonlocal id
592 id = interpreters.create()
593 lock.acquire()
594 lock.release()
595
596 t = threading.Thread(target=f)
597 with lock:
598 t.start()
599 t.join()
600 self.assertIn(id, interpreters.list_all())
601
602 def test_in_subinterpreter(self):
603 main, = interpreters.list_all()
604 id1 = interpreters.create()
605 out = _run_output(id1, dedent("""
606 import _xxsubinterpreters as _interpreters
607 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400608 print(id)
609 assert isinstance(id, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700610 """))
611 id2 = int(out.strip())
612
613 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
614
615 def test_in_threaded_subinterpreter(self):
616 main, = interpreters.list_all()
617 id1 = interpreters.create()
618 id2 = None
619 def f():
620 nonlocal id2
621 out = _run_output(id1, dedent("""
622 import _xxsubinterpreters as _interpreters
623 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400624 print(id)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700625 """))
626 id2 = int(out.strip())
627
628 t = threading.Thread(target=f)
629 t.start()
630 t.join()
631
632 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
633
634 def test_after_destroy_all(self):
635 before = set(interpreters.list_all())
636 # Create 3 subinterpreters.
637 ids = []
638 for _ in range(3):
639 id = interpreters.create()
640 ids.append(id)
641 # Now destroy them.
642 for id in ids:
643 interpreters.destroy(id)
644 # Finally, create another.
645 id = interpreters.create()
646 self.assertEqual(set(interpreters.list_all()), before | {id})
647
648 def test_after_destroy_some(self):
649 before = set(interpreters.list_all())
650 # Create 3 subinterpreters.
651 id1 = interpreters.create()
652 id2 = interpreters.create()
653 id3 = interpreters.create()
654 # Now destroy 2 of them.
655 interpreters.destroy(id1)
656 interpreters.destroy(id3)
657 # Finally, create another.
658 id = interpreters.create()
659 self.assertEqual(set(interpreters.list_all()), before | {id, id2})
660
661
662class DestroyTests(TestBase):
663
664 def test_one(self):
665 id1 = interpreters.create()
666 id2 = interpreters.create()
667 id3 = interpreters.create()
668 self.assertIn(id2, interpreters.list_all())
669 interpreters.destroy(id2)
670 self.assertNotIn(id2, interpreters.list_all())
671 self.assertIn(id1, interpreters.list_all())
672 self.assertIn(id3, interpreters.list_all())
673
674 def test_all(self):
675 before = set(interpreters.list_all())
676 ids = set()
677 for _ in range(3):
678 id = interpreters.create()
679 ids.add(id)
680 self.assertEqual(set(interpreters.list_all()), before | ids)
681 for id in ids:
682 interpreters.destroy(id)
683 self.assertEqual(set(interpreters.list_all()), before)
684
685 def test_main(self):
686 main, = interpreters.list_all()
687 with self.assertRaises(RuntimeError):
688 interpreters.destroy(main)
689
690 def f():
691 with self.assertRaises(RuntimeError):
692 interpreters.destroy(main)
693
694 t = threading.Thread(target=f)
695 t.start()
696 t.join()
697
698 def test_already_destroyed(self):
699 id = interpreters.create()
700 interpreters.destroy(id)
701 with self.assertRaises(RuntimeError):
702 interpreters.destroy(id)
703
704 def test_does_not_exist(self):
705 with self.assertRaises(RuntimeError):
706 interpreters.destroy(1_000_000)
707
708 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300709 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700710 interpreters.destroy(-1)
711
712 def test_from_current(self):
713 main, = interpreters.list_all()
714 id = interpreters.create()
Eric Snow4e9da0d2018-02-02 21:49:49 -0700715 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700716 import _xxsubinterpreters as _interpreters
Eric Snow4e9da0d2018-02-02 21:49:49 -0700717 try:
Eric Snow6d2cd902018-05-16 15:04:57 -0400718 _interpreters.destroy({id})
Eric Snow4e9da0d2018-02-02 21:49:49 -0700719 except RuntimeError:
720 pass
721 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700722
Eric Snow4e9da0d2018-02-02 21:49:49 -0700723 interpreters.run_string(id, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700724 self.assertEqual(set(interpreters.list_all()), {main, id})
725
726 def test_from_sibling(self):
727 main, = interpreters.list_all()
728 id1 = interpreters.create()
729 id2 = interpreters.create()
Eric Snow4c6955e2018-02-16 18:53:40 -0700730 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700731 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400732 _interpreters.destroy({id2})
Eric Snow4c6955e2018-02-16 18:53:40 -0700733 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700734 interpreters.run_string(id1, script)
735
736 self.assertEqual(set(interpreters.list_all()), {main, id1})
737
738 def test_from_other_thread(self):
739 id = interpreters.create()
740 def f():
741 interpreters.destroy(id)
742
743 t = threading.Thread(target=f)
744 t.start()
745 t.join()
746
747 def test_still_running(self):
748 main, = interpreters.list_all()
749 interp = interpreters.create()
750 with _running(interp):
Kyle Stanleyf03a8f82020-01-31 15:07:09 -0500751 self.assertTrue(interpreters.is_running(interp),
752 msg=f"Interp {interp} should be running before destruction.")
753
754 with self.assertRaises(RuntimeError,
755 msg=f"Should not be able to destroy interp {interp} while it's still running."):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700756 interpreters.destroy(interp)
757 self.assertTrue(interpreters.is_running(interp))
758
759
760class RunStringTests(TestBase):
761
Eric Snow7f8bfc92018-01-29 18:23:44 -0700762 def setUp(self):
763 super().setUp()
764 self.id = interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700765
Eric Snow7f8bfc92018-01-29 18:23:44 -0700766 def test_success(self):
767 script, file = _captured_script('print("it worked!", end="")')
768 with file:
769 interpreters.run_string(self.id, script)
770 out = file.read()
771
772 self.assertEqual(out, 'it worked!')
773
774 def test_in_thread(self):
775 script, file = _captured_script('print("it worked!", end="")')
776 with file:
777 def f():
778 interpreters.run_string(self.id, script)
779
780 t = threading.Thread(target=f)
781 t.start()
782 t.join()
783 out = file.read()
784
785 self.assertEqual(out, 'it worked!')
786
787 def test_create_thread(self):
Victor Stinner252346a2020-05-01 11:33:44 +0200788 subinterp = interpreters.create(isolated=False)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700789 script, file = _captured_script("""
790 import threading
791 def f():
792 print('it worked!', end='')
793
794 t = threading.Thread(target=f)
795 t.start()
796 t.join()
797 """)
798 with file:
Victor Stinner252346a2020-05-01 11:33:44 +0200799 interpreters.run_string(subinterp, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700800 out = file.read()
801
802 self.assertEqual(out, 'it worked!')
803
804 @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
805 def test_fork(self):
806 import tempfile
807 with tempfile.NamedTemporaryFile('w+') as file:
808 file.write('')
809 file.flush()
810
811 expected = 'spam spam spam spam spam'
812 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700813 import os
Eric Snow59032962018-09-14 14:17:20 -0700814 try:
815 os.fork()
816 except RuntimeError:
Eric Snow7f8bfc92018-01-29 18:23:44 -0700817 with open('{file.name}', 'w') as out:
818 out.write('{expected}')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700819 """)
820 interpreters.run_string(self.id, script)
821
822 file.seek(0)
823 content = file.read()
824 self.assertEqual(content, expected)
825
826 def test_already_running(self):
827 with _running(self.id):
828 with self.assertRaises(RuntimeError):
829 interpreters.run_string(self.id, 'print("spam")')
830
831 def test_does_not_exist(self):
832 id = 0
833 while id in interpreters.list_all():
834 id += 1
835 with self.assertRaises(RuntimeError):
836 interpreters.run_string(id, 'print("spam")')
837
838 def test_error_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300839 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700840 interpreters.run_string(-1, 'print("spam")')
841
842 def test_bad_id(self):
843 with self.assertRaises(TypeError):
844 interpreters.run_string('spam', 'print("spam")')
845
846 def test_bad_script(self):
847 with self.assertRaises(TypeError):
848 interpreters.run_string(self.id, 10)
849
850 def test_bytes_for_script(self):
851 with self.assertRaises(TypeError):
852 interpreters.run_string(self.id, b'print("spam")')
853
854 @contextlib.contextmanager
855 def assert_run_failed(self, exctype, msg=None):
856 with self.assertRaises(interpreters.RunFailedError) as caught:
857 yield
858 if msg is None:
859 self.assertEqual(str(caught.exception).split(':')[0],
Victor Stinnerf2c3b682020-05-14 18:46:24 +0200860 str(exctype))
Eric Snow7f8bfc92018-01-29 18:23:44 -0700861 else:
862 self.assertEqual(str(caught.exception),
Victor Stinnerf2c3b682020-05-14 18:46:24 +0200863 "{}: {}".format(exctype, msg))
Eric Snow7f8bfc92018-01-29 18:23:44 -0700864
865 def test_invalid_syntax(self):
866 with self.assert_run_failed(SyntaxError):
867 # missing close paren
868 interpreters.run_string(self.id, 'print("spam"')
869
870 def test_failure(self):
871 with self.assert_run_failed(Exception, 'spam'):
872 interpreters.run_string(self.id, 'raise Exception("spam")')
873
874 def test_SystemExit(self):
875 with self.assert_run_failed(SystemExit, '42'):
876 interpreters.run_string(self.id, 'raise SystemExit(42)')
877
878 def test_sys_exit(self):
879 with self.assert_run_failed(SystemExit):
880 interpreters.run_string(self.id, dedent("""
881 import sys
882 sys.exit()
883 """))
884
885 with self.assert_run_failed(SystemExit, '42'):
886 interpreters.run_string(self.id, dedent("""
887 import sys
888 sys.exit(42)
889 """))
890
891 def test_with_shared(self):
892 r, w = os.pipe()
893
894 shared = {
895 'spam': b'ham',
896 'eggs': b'-1',
897 'cheddar': None,
898 }
899 script = dedent(f"""
900 eggs = int(eggs)
901 spam = 42
902 result = spam + eggs
903
904 ns = dict(vars())
905 del ns['__builtins__']
906 import pickle
907 with open({w}, 'wb') as chan:
908 pickle.dump(ns, chan)
909 """)
910 interpreters.run_string(self.id, script, shared)
911 with open(r, 'rb') as chan:
912 ns = pickle.load(chan)
913
914 self.assertEqual(ns['spam'], 42)
915 self.assertEqual(ns['eggs'], -1)
916 self.assertEqual(ns['result'], 41)
917 self.assertIsNone(ns['cheddar'])
918
919 def test_shared_overwrites(self):
920 interpreters.run_string(self.id, dedent("""
921 spam = 'eggs'
922 ns1 = dict(vars())
923 del ns1['__builtins__']
924 """))
925
926 shared = {'spam': b'ham'}
927 script = dedent(f"""
928 ns2 = dict(vars())
929 del ns2['__builtins__']
930 """)
931 interpreters.run_string(self.id, script, shared)
932
933 r, w = os.pipe()
934 script = dedent(f"""
935 ns = dict(vars())
936 del ns['__builtins__']
937 import pickle
938 with open({w}, 'wb') as chan:
939 pickle.dump(ns, chan)
940 """)
941 interpreters.run_string(self.id, script)
942 with open(r, 'rb') as chan:
943 ns = pickle.load(chan)
944
945 self.assertEqual(ns['ns1']['spam'], 'eggs')
946 self.assertEqual(ns['ns2']['spam'], b'ham')
947 self.assertEqual(ns['spam'], b'ham')
948
949 def test_shared_overwrites_default_vars(self):
950 r, w = os.pipe()
951
952 shared = {'__name__': b'not __main__'}
953 script = dedent(f"""
954 spam = 42
955
956 ns = dict(vars())
957 del ns['__builtins__']
958 import pickle
959 with open({w}, 'wb') as chan:
960 pickle.dump(ns, chan)
961 """)
962 interpreters.run_string(self.id, script, shared)
963 with open(r, 'rb') as chan:
964 ns = pickle.load(chan)
965
966 self.assertEqual(ns['__name__'], b'not __main__')
967
968 def test_main_reused(self):
969 r, w = os.pipe()
970 interpreters.run_string(self.id, dedent(f"""
971 spam = True
972
973 ns = dict(vars())
974 del ns['__builtins__']
975 import pickle
976 with open({w}, 'wb') as chan:
977 pickle.dump(ns, chan)
978 del ns, pickle, chan
979 """))
980 with open(r, 'rb') as chan:
981 ns1 = pickle.load(chan)
982
983 r, w = os.pipe()
984 interpreters.run_string(self.id, dedent(f"""
985 eggs = False
986
987 ns = dict(vars())
988 del ns['__builtins__']
989 import pickle
990 with open({w}, 'wb') as chan:
991 pickle.dump(ns, chan)
992 """))
993 with open(r, 'rb') as chan:
994 ns2 = pickle.load(chan)
995
996 self.assertIn('spam', ns1)
997 self.assertNotIn('eggs', ns1)
998 self.assertIn('eggs', ns2)
999 self.assertIn('spam', ns2)
1000
1001 def test_execution_namespace_is_main(self):
1002 r, w = os.pipe()
1003
1004 script = dedent(f"""
1005 spam = 42
1006
1007 ns = dict(vars())
1008 ns['__builtins__'] = str(ns['__builtins__'])
1009 import pickle
1010 with open({w}, 'wb') as chan:
1011 pickle.dump(ns, chan)
1012 """)
1013 interpreters.run_string(self.id, script)
1014 with open(r, 'rb') as chan:
1015 ns = pickle.load(chan)
1016
1017 ns.pop('__builtins__')
1018 ns.pop('__loader__')
1019 self.assertEqual(ns, {
1020 '__name__': '__main__',
1021 '__annotations__': {},
1022 '__doc__': None,
1023 '__package__': None,
1024 '__spec__': None,
1025 'spam': 42,
1026 })
1027
Eric Snow4c6955e2018-02-16 18:53:40 -07001028 # XXX Fix this test!
1029 @unittest.skip('blocking forever')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001030 def test_still_running_at_exit(self):
1031 script = dedent(f"""
1032 from textwrap import dedent
1033 import threading
1034 import _xxsubinterpreters as _interpreters
Eric Snow4c6955e2018-02-16 18:53:40 -07001035 id = _interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -07001036 def f():
1037 _interpreters.run_string(id, dedent('''
1038 import time
1039 # Give plenty of time for the main interpreter to finish.
1040 time.sleep(1_000_000)
1041 '''))
1042
1043 t = threading.Thread(target=f)
1044 t.start()
1045 """)
1046 with support.temp_dir() as dirname:
1047 filename = script_helper.make_script(dirname, 'interp', script)
1048 with script_helper.spawn_python(filename) as proc:
1049 retcode = proc.wait()
1050
1051 self.assertEqual(retcode, 0)
1052
1053
Eric Snow6d2cd902018-05-16 15:04:57 -04001054##################################
1055# channel tests
1056
Eric Snow7f8bfc92018-01-29 18:23:44 -07001057class ChannelIDTests(TestBase):
1058
1059 def test_default_kwargs(self):
1060 cid = interpreters._channel_id(10, force=True)
1061
1062 self.assertEqual(int(cid), 10)
1063 self.assertEqual(cid.end, 'both')
1064
1065 def test_with_kwargs(self):
1066 cid = interpreters._channel_id(10, send=True, force=True)
1067 self.assertEqual(cid.end, 'send')
1068
1069 cid = interpreters._channel_id(10, send=True, recv=False, force=True)
1070 self.assertEqual(cid.end, 'send')
1071
1072 cid = interpreters._channel_id(10, recv=True, force=True)
1073 self.assertEqual(cid.end, 'recv')
1074
1075 cid = interpreters._channel_id(10, recv=True, send=False, force=True)
1076 self.assertEqual(cid.end, 'recv')
1077
1078 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1079 self.assertEqual(cid.end, 'both')
1080
1081 def test_coerce_id(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001082 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001083 def __index__(self):
1084 return 10
Eric Snow7f8bfc92018-01-29 18:23:44 -07001085
Serhiy Storchakabf169912019-09-13 22:50:27 +03001086 cid = interpreters._channel_id(Int(), force=True)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001087 self.assertEqual(int(cid), 10)
1088
1089 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001090 self.assertRaises(TypeError, interpreters._channel_id, object())
1091 self.assertRaises(TypeError, interpreters._channel_id, 10.0)
1092 self.assertRaises(TypeError, interpreters._channel_id, '10')
1093 self.assertRaises(TypeError, interpreters._channel_id, b'10')
1094 self.assertRaises(ValueError, interpreters._channel_id, -1)
1095 self.assertRaises(OverflowError, interpreters._channel_id, 2**64)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001096
1097 def test_bad_kwargs(self):
1098 with self.assertRaises(ValueError):
1099 interpreters._channel_id(10, send=False, recv=False)
1100
1101 def test_does_not_exist(self):
1102 cid = interpreters.channel_create()
1103 with self.assertRaises(interpreters.ChannelNotFoundError):
1104 interpreters._channel_id(int(cid) + 1) # unforced
1105
Eric Snow6d2cd902018-05-16 15:04:57 -04001106 def test_str(self):
1107 cid = interpreters._channel_id(10, force=True)
1108 self.assertEqual(str(cid), '10')
1109
Eric Snow7f8bfc92018-01-29 18:23:44 -07001110 def test_repr(self):
1111 cid = interpreters._channel_id(10, force=True)
1112 self.assertEqual(repr(cid), 'ChannelID(10)')
1113
1114 cid = interpreters._channel_id(10, send=True, force=True)
1115 self.assertEqual(repr(cid), 'ChannelID(10, send=True)')
1116
1117 cid = interpreters._channel_id(10, recv=True, force=True)
1118 self.assertEqual(repr(cid), 'ChannelID(10, recv=True)')
1119
1120 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1121 self.assertEqual(repr(cid), 'ChannelID(10)')
1122
1123 def test_equality(self):
1124 cid1 = interpreters.channel_create()
1125 cid2 = interpreters._channel_id(int(cid1))
1126 cid3 = interpreters.channel_create()
1127
1128 self.assertTrue(cid1 == cid1)
1129 self.assertTrue(cid1 == cid2)
1130 self.assertTrue(cid1 == int(cid1))
Serhiy Storchakabf169912019-09-13 22:50:27 +03001131 self.assertTrue(int(cid1) == cid1)
1132 self.assertTrue(cid1 == float(int(cid1)))
1133 self.assertTrue(float(int(cid1)) == cid1)
1134 self.assertFalse(cid1 == float(int(cid1)) + 0.1)
1135 self.assertFalse(cid1 == str(int(cid1)))
1136 self.assertFalse(cid1 == 2**1000)
1137 self.assertFalse(cid1 == float('inf'))
1138 self.assertFalse(cid1 == 'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001139 self.assertFalse(cid1 == cid3)
1140
1141 self.assertFalse(cid1 != cid1)
1142 self.assertFalse(cid1 != cid2)
1143 self.assertTrue(cid1 != cid3)
1144
1145
1146class ChannelTests(TestBase):
1147
Eric Snow6d2cd902018-05-16 15:04:57 -04001148 def test_create_cid(self):
1149 cid = interpreters.channel_create()
1150 self.assertIsInstance(cid, interpreters.ChannelID)
1151
Eric Snow7f8bfc92018-01-29 18:23:44 -07001152 def test_sequential_ids(self):
1153 before = interpreters.channel_list_all()
1154 id1 = interpreters.channel_create()
1155 id2 = interpreters.channel_create()
1156 id3 = interpreters.channel_create()
1157 after = interpreters.channel_list_all()
1158
1159 self.assertEqual(id2, int(id1) + 1)
1160 self.assertEqual(id3, int(id2) + 1)
1161 self.assertEqual(set(after) - set(before), {id1, id2, id3})
1162
1163 def test_ids_global(self):
1164 id1 = interpreters.create()
1165 out = _run_output(id1, dedent("""
1166 import _xxsubinterpreters as _interpreters
1167 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001168 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001169 """))
1170 cid1 = int(out.strip())
1171
1172 id2 = interpreters.create()
1173 out = _run_output(id2, dedent("""
1174 import _xxsubinterpreters as _interpreters
1175 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001176 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001177 """))
1178 cid2 = int(out.strip())
1179
1180 self.assertEqual(cid2, int(cid1) + 1)
1181
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001182 def test_channel_list_interpreters_none(self):
1183 """Test listing interpreters for a channel with no associations."""
1184 # Test for channel with no associated interpreters.
1185 cid = interpreters.channel_create()
1186 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1187 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1188 self.assertEqual(send_interps, [])
1189 self.assertEqual(recv_interps, [])
1190
1191 def test_channel_list_interpreters_basic(self):
1192 """Test basic listing channel interpreters."""
1193 interp0 = interpreters.get_main()
1194 cid = interpreters.channel_create()
1195 interpreters.channel_send(cid, "send")
1196 # Test for a channel that has one end associated to an interpreter.
1197 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1198 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1199 self.assertEqual(send_interps, [interp0])
1200 self.assertEqual(recv_interps, [])
1201
1202 interp1 = interpreters.create()
1203 _run_output(interp1, dedent(f"""
1204 import _xxsubinterpreters as _interpreters
1205 obj = _interpreters.channel_recv({cid})
1206 """))
1207 # Test for channel that has boths ends associated to an interpreter.
1208 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1209 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1210 self.assertEqual(send_interps, [interp0])
1211 self.assertEqual(recv_interps, [interp1])
1212
1213 def test_channel_list_interpreters_multiple(self):
1214 """Test listing interpreters for a channel with many associations."""
1215 interp0 = interpreters.get_main()
1216 interp1 = interpreters.create()
1217 interp2 = interpreters.create()
1218 interp3 = interpreters.create()
1219 cid = interpreters.channel_create()
1220
1221 interpreters.channel_send(cid, "send")
1222 _run_output(interp1, dedent(f"""
1223 import _xxsubinterpreters as _interpreters
1224 _interpreters.channel_send({cid}, "send")
1225 """))
1226 _run_output(interp2, dedent(f"""
1227 import _xxsubinterpreters as _interpreters
1228 obj = _interpreters.channel_recv({cid})
1229 """))
1230 _run_output(interp3, dedent(f"""
1231 import _xxsubinterpreters as _interpreters
1232 obj = _interpreters.channel_recv({cid})
1233 """))
1234 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1235 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1236 self.assertEqual(set(send_interps), {interp0, interp1})
1237 self.assertEqual(set(recv_interps), {interp2, interp3})
1238
1239 def test_channel_list_interpreters_destroyed(self):
1240 """Test listing channel interpreters with a destroyed interpreter."""
1241 interp0 = interpreters.get_main()
1242 interp1 = interpreters.create()
1243 cid = interpreters.channel_create()
1244 interpreters.channel_send(cid, "send")
1245 _run_output(interp1, dedent(f"""
1246 import _xxsubinterpreters as _interpreters
1247 obj = _interpreters.channel_recv({cid})
1248 """))
1249 # Should be one interpreter associated with each end.
1250 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1251 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1252 self.assertEqual(send_interps, [interp0])
1253 self.assertEqual(recv_interps, [interp1])
1254
1255 interpreters.destroy(interp1)
1256 # Destroyed interpreter should not be listed.
1257 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1258 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1259 self.assertEqual(send_interps, [interp0])
1260 self.assertEqual(recv_interps, [])
1261
1262 def test_channel_list_interpreters_released(self):
1263 """Test listing channel interpreters with a released channel."""
1264 # Set up one channel with main interpreter on the send end and two
1265 # subinterpreters on the receive end.
1266 interp0 = interpreters.get_main()
1267 interp1 = interpreters.create()
1268 interp2 = interpreters.create()
1269 cid = interpreters.channel_create()
1270 interpreters.channel_send(cid, "data")
1271 _run_output(interp1, dedent(f"""
1272 import _xxsubinterpreters as _interpreters
1273 obj = _interpreters.channel_recv({cid})
1274 """))
1275 interpreters.channel_send(cid, "data")
1276 _run_output(interp2, dedent(f"""
1277 import _xxsubinterpreters as _interpreters
1278 obj = _interpreters.channel_recv({cid})
1279 """))
1280 # Check the setup.
1281 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1282 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1283 self.assertEqual(len(send_interps), 1)
1284 self.assertEqual(len(recv_interps), 2)
1285
1286 # Release the main interpreter from the send end.
1287 interpreters.channel_release(cid, send=True)
1288 # Send end should have no associated interpreters.
1289 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1290 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1291 self.assertEqual(len(send_interps), 0)
1292 self.assertEqual(len(recv_interps), 2)
1293
1294 # Release one of the subinterpreters from the receive end.
1295 _run_output(interp2, dedent(f"""
1296 import _xxsubinterpreters as _interpreters
1297 _interpreters.channel_release({cid})
1298 """))
1299 # Receive end should have the released interpreter removed.
1300 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1301 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1302 self.assertEqual(len(send_interps), 0)
1303 self.assertEqual(recv_interps, [interp1])
1304
1305 def test_channel_list_interpreters_closed(self):
1306 """Test listing channel interpreters with a closed channel."""
1307 interp0 = interpreters.get_main()
1308 interp1 = interpreters.create()
1309 cid = interpreters.channel_create()
1310 # Put something in the channel so that it's not empty.
1311 interpreters.channel_send(cid, "send")
1312
1313 # Check initial state.
1314 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1315 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1316 self.assertEqual(len(send_interps), 1)
1317 self.assertEqual(len(recv_interps), 0)
1318
1319 # Force close the channel.
1320 interpreters.channel_close(cid, force=True)
1321 # Both ends should raise an error.
1322 with self.assertRaises(interpreters.ChannelClosedError):
1323 interpreters.channel_list_interpreters(cid, send=True)
1324 with self.assertRaises(interpreters.ChannelClosedError):
1325 interpreters.channel_list_interpreters(cid, send=False)
1326
1327 def test_channel_list_interpreters_closed_send_end(self):
1328 """Test listing channel interpreters with a channel's send end closed."""
1329 interp0 = interpreters.get_main()
1330 interp1 = interpreters.create()
1331 cid = interpreters.channel_create()
1332 # Put something in the channel so that it's not empty.
1333 interpreters.channel_send(cid, "send")
1334
1335 # Check initial state.
1336 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1337 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1338 self.assertEqual(len(send_interps), 1)
1339 self.assertEqual(len(recv_interps), 0)
1340
1341 # Close the send end of the channel.
1342 interpreters.channel_close(cid, send=True)
1343 # Send end should raise an error.
1344 with self.assertRaises(interpreters.ChannelClosedError):
1345 interpreters.channel_list_interpreters(cid, send=True)
1346 # Receive end should not be closed (since channel is not empty).
1347 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1348 self.assertEqual(len(recv_interps), 0)
1349
1350 # Close the receive end of the channel from a subinterpreter.
1351 _run_output(interp1, dedent(f"""
1352 import _xxsubinterpreters as _interpreters
1353 _interpreters.channel_close({cid}, force=True)
1354 """))
1355 # Both ends should raise an error.
1356 with self.assertRaises(interpreters.ChannelClosedError):
1357 interpreters.channel_list_interpreters(cid, send=True)
1358 with self.assertRaises(interpreters.ChannelClosedError):
1359 interpreters.channel_list_interpreters(cid, send=False)
1360
Eric Snow7f8bfc92018-01-29 18:23:44 -07001361 ####################
1362
Eric Snow7f8bfc92018-01-29 18:23:44 -07001363 def test_send_recv_main(self):
1364 cid = interpreters.channel_create()
1365 orig = b'spam'
1366 interpreters.channel_send(cid, orig)
1367 obj = interpreters.channel_recv(cid)
1368
1369 self.assertEqual(obj, orig)
1370 self.assertIsNot(obj, orig)
1371
1372 def test_send_recv_same_interpreter(self):
1373 id1 = interpreters.create()
1374 out = _run_output(id1, dedent("""
1375 import _xxsubinterpreters as _interpreters
1376 cid = _interpreters.channel_create()
1377 orig = b'spam'
1378 _interpreters.channel_send(cid, orig)
1379 obj = _interpreters.channel_recv(cid)
1380 assert obj is not orig
1381 assert obj == orig
1382 """))
1383
1384 def test_send_recv_different_interpreters(self):
1385 cid = interpreters.channel_create()
1386 id1 = interpreters.create()
1387 out = _run_output(id1, dedent(f"""
1388 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -04001389 _interpreters.channel_send({cid}, b'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001390 """))
1391 obj = interpreters.channel_recv(cid)
1392
1393 self.assertEqual(obj, b'spam')
1394
Eric Snowf53d9f22018-02-20 16:30:17 -07001395 def test_send_recv_different_threads(self):
1396 cid = interpreters.channel_create()
1397
1398 def f():
1399 while True:
1400 try:
1401 obj = interpreters.channel_recv(cid)
1402 break
1403 except interpreters.ChannelEmptyError:
1404 time.sleep(0.1)
1405 interpreters.channel_send(cid, obj)
1406 t = threading.Thread(target=f)
1407 t.start()
1408
1409 interpreters.channel_send(cid, b'spam')
1410 t.join()
1411 obj = interpreters.channel_recv(cid)
1412
1413 self.assertEqual(obj, b'spam')
1414
1415 def test_send_recv_different_interpreters_and_threads(self):
1416 cid = interpreters.channel_create()
1417 id1 = interpreters.create()
1418 out = None
1419
1420 def f():
1421 nonlocal out
1422 out = _run_output(id1, dedent(f"""
1423 import time
1424 import _xxsubinterpreters as _interpreters
1425 while True:
1426 try:
Eric Snow6d2cd902018-05-16 15:04:57 -04001427 obj = _interpreters.channel_recv({cid})
Eric Snowf53d9f22018-02-20 16:30:17 -07001428 break
1429 except _interpreters.ChannelEmptyError:
1430 time.sleep(0.1)
1431 assert(obj == b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001432 _interpreters.channel_send({cid}, b'eggs')
Eric Snowf53d9f22018-02-20 16:30:17 -07001433 """))
1434 t = threading.Thread(target=f)
1435 t.start()
1436
1437 interpreters.channel_send(cid, b'spam')
1438 t.join()
1439 obj = interpreters.channel_recv(cid)
1440
1441 self.assertEqual(obj, b'eggs')
1442
Eric Snow7f8bfc92018-01-29 18:23:44 -07001443 def test_send_not_found(self):
1444 with self.assertRaises(interpreters.ChannelNotFoundError):
1445 interpreters.channel_send(10, b'spam')
1446
1447 def test_recv_not_found(self):
1448 with self.assertRaises(interpreters.ChannelNotFoundError):
1449 interpreters.channel_recv(10)
1450
1451 def test_recv_empty(self):
1452 cid = interpreters.channel_create()
1453 with self.assertRaises(interpreters.ChannelEmptyError):
1454 interpreters.channel_recv(cid)
1455
Eric Snow5e8c6912020-04-28 17:11:32 -06001456 def test_recv_default(self):
1457 default = object()
1458 cid = interpreters.channel_create()
1459 obj1 = interpreters.channel_recv(cid, default)
1460 interpreters.channel_send(cid, None)
1461 interpreters.channel_send(cid, 1)
1462 interpreters.channel_send(cid, b'spam')
1463 interpreters.channel_send(cid, b'eggs')
1464 obj2 = interpreters.channel_recv(cid, default)
1465 obj3 = interpreters.channel_recv(cid, default)
1466 obj4 = interpreters.channel_recv(cid)
1467 obj5 = interpreters.channel_recv(cid, default)
1468 obj6 = interpreters.channel_recv(cid, default)
1469
1470 self.assertIs(obj1, default)
1471 self.assertIs(obj2, None)
1472 self.assertEqual(obj3, 1)
1473 self.assertEqual(obj4, b'spam')
1474 self.assertEqual(obj5, b'eggs')
1475 self.assertIs(obj6, default)
1476
Eric Snow6d2cd902018-05-16 15:04:57 -04001477 def test_run_string_arg_unresolved(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001478 cid = interpreters.channel_create()
1479 interp = interpreters.create()
1480
1481 out = _run_output(interp, dedent("""
1482 import _xxsubinterpreters as _interpreters
1483 print(cid.end)
1484 _interpreters.channel_send(cid, b'spam')
1485 """),
1486 dict(cid=cid.send))
1487 obj = interpreters.channel_recv(cid)
1488
1489 self.assertEqual(obj, b'spam')
1490 self.assertEqual(out.strip(), 'send')
1491
Eric Snowab4a1982018-06-13 08:02:39 -06001492 # XXX For now there is no high-level channel into which the
1493 # sent channel ID can be converted...
1494 # Note: this test caused crashes on some buildbots (bpo-33615).
1495 @unittest.skip('disabled until high-level channels exist')
Eric Snow6d2cd902018-05-16 15:04:57 -04001496 def test_run_string_arg_resolved(self):
1497 cid = interpreters.channel_create()
1498 cid = interpreters._channel_id(cid, _resolve=True)
1499 interp = interpreters.create()
1500
1501 out = _run_output(interp, dedent("""
1502 import _xxsubinterpreters as _interpreters
Eric Snowab4a1982018-06-13 08:02:39 -06001503 print(chan.id.end)
1504 _interpreters.channel_send(chan.id, b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001505 """),
1506 dict(chan=cid.send))
1507 obj = interpreters.channel_recv(cid)
1508
1509 self.assertEqual(obj, b'spam')
1510 self.assertEqual(out.strip(), 'send')
1511
1512 # close
1513
1514 def test_close_single_user(self):
1515 cid = interpreters.channel_create()
1516 interpreters.channel_send(cid, b'spam')
1517 interpreters.channel_recv(cid)
1518 interpreters.channel_close(cid)
1519
1520 with self.assertRaises(interpreters.ChannelClosedError):
1521 interpreters.channel_send(cid, b'eggs')
1522 with self.assertRaises(interpreters.ChannelClosedError):
1523 interpreters.channel_recv(cid)
1524
1525 def test_close_multiple_users(self):
1526 cid = interpreters.channel_create()
1527 id1 = interpreters.create()
1528 id2 = interpreters.create()
1529 interpreters.run_string(id1, dedent(f"""
1530 import _xxsubinterpreters as _interpreters
1531 _interpreters.channel_send({cid}, b'spam')
1532 """))
1533 interpreters.run_string(id2, dedent(f"""
1534 import _xxsubinterpreters as _interpreters
1535 _interpreters.channel_recv({cid})
1536 """))
1537 interpreters.channel_close(cid)
1538 with self.assertRaises(interpreters.RunFailedError) as cm:
1539 interpreters.run_string(id1, dedent(f"""
1540 _interpreters.channel_send({cid}, b'spam')
1541 """))
1542 self.assertIn('ChannelClosedError', str(cm.exception))
1543 with self.assertRaises(interpreters.RunFailedError) as cm:
1544 interpreters.run_string(id2, dedent(f"""
1545 _interpreters.channel_send({cid}, b'spam')
1546 """))
1547 self.assertIn('ChannelClosedError', str(cm.exception))
1548
1549 def test_close_multiple_times(self):
1550 cid = interpreters.channel_create()
1551 interpreters.channel_send(cid, b'spam')
1552 interpreters.channel_recv(cid)
1553 interpreters.channel_close(cid)
1554
1555 with self.assertRaises(interpreters.ChannelClosedError):
1556 interpreters.channel_close(cid)
1557
Eric Snow3ab01362018-05-17 10:27:09 -04001558 def test_close_empty(self):
1559 tests = [
1560 (False, False),
1561 (True, False),
1562 (False, True),
1563 (True, True),
1564 ]
1565 for send, recv in tests:
1566 with self.subTest((send, recv)):
1567 cid = interpreters.channel_create()
1568 interpreters.channel_send(cid, b'spam')
1569 interpreters.channel_recv(cid)
1570 interpreters.channel_close(cid, send=send, recv=recv)
1571
1572 with self.assertRaises(interpreters.ChannelClosedError):
1573 interpreters.channel_send(cid, b'eggs')
1574 with self.assertRaises(interpreters.ChannelClosedError):
1575 interpreters.channel_recv(cid)
1576
1577 def test_close_defaults_with_unused_items(self):
Eric Snow6d2cd902018-05-16 15:04:57 -04001578 cid = interpreters.channel_create()
1579 interpreters.channel_send(cid, b'spam')
1580 interpreters.channel_send(cid, b'ham')
Eric Snow6d2cd902018-05-16 15:04:57 -04001581
Eric Snow3ab01362018-05-17 10:27:09 -04001582 with self.assertRaises(interpreters.ChannelNotEmptyError):
1583 interpreters.channel_close(cid)
1584 interpreters.channel_recv(cid)
1585 interpreters.channel_send(cid, b'eggs')
1586
1587 def test_close_recv_with_unused_items_unforced(self):
1588 cid = interpreters.channel_create()
1589 interpreters.channel_send(cid, b'spam')
1590 interpreters.channel_send(cid, b'ham')
1591
1592 with self.assertRaises(interpreters.ChannelNotEmptyError):
1593 interpreters.channel_close(cid, recv=True)
1594 interpreters.channel_recv(cid)
1595 interpreters.channel_send(cid, b'eggs')
1596 interpreters.channel_recv(cid)
1597 interpreters.channel_recv(cid)
1598 interpreters.channel_close(cid, recv=True)
1599
1600 def test_close_send_with_unused_items_unforced(self):
1601 cid = interpreters.channel_create()
1602 interpreters.channel_send(cid, b'spam')
1603 interpreters.channel_send(cid, b'ham')
1604 interpreters.channel_close(cid, send=True)
1605
1606 with self.assertRaises(interpreters.ChannelClosedError):
1607 interpreters.channel_send(cid, b'eggs')
1608 interpreters.channel_recv(cid)
1609 interpreters.channel_recv(cid)
1610 with self.assertRaises(interpreters.ChannelClosedError):
1611 interpreters.channel_recv(cid)
1612
1613 def test_close_both_with_unused_items_unforced(self):
1614 cid = interpreters.channel_create()
1615 interpreters.channel_send(cid, b'spam')
1616 interpreters.channel_send(cid, b'ham')
1617
1618 with self.assertRaises(interpreters.ChannelNotEmptyError):
1619 interpreters.channel_close(cid, recv=True, send=True)
1620 interpreters.channel_recv(cid)
1621 interpreters.channel_send(cid, b'eggs')
1622 interpreters.channel_recv(cid)
1623 interpreters.channel_recv(cid)
1624 interpreters.channel_close(cid, recv=True)
1625
1626 def test_close_recv_with_unused_items_forced(self):
1627 cid = interpreters.channel_create()
1628 interpreters.channel_send(cid, b'spam')
1629 interpreters.channel_send(cid, b'ham')
1630 interpreters.channel_close(cid, recv=True, force=True)
1631
1632 with self.assertRaises(interpreters.ChannelClosedError):
1633 interpreters.channel_send(cid, b'eggs')
1634 with self.assertRaises(interpreters.ChannelClosedError):
1635 interpreters.channel_recv(cid)
1636
1637 def test_close_send_with_unused_items_forced(self):
1638 cid = interpreters.channel_create()
1639 interpreters.channel_send(cid, b'spam')
1640 interpreters.channel_send(cid, b'ham')
1641 interpreters.channel_close(cid, send=True, force=True)
1642
1643 with self.assertRaises(interpreters.ChannelClosedError):
1644 interpreters.channel_send(cid, b'eggs')
1645 with self.assertRaises(interpreters.ChannelClosedError):
1646 interpreters.channel_recv(cid)
1647
1648 def test_close_both_with_unused_items_forced(self):
1649 cid = interpreters.channel_create()
1650 interpreters.channel_send(cid, b'spam')
1651 interpreters.channel_send(cid, b'ham')
1652 interpreters.channel_close(cid, send=True, recv=True, force=True)
1653
1654 with self.assertRaises(interpreters.ChannelClosedError):
1655 interpreters.channel_send(cid, b'eggs')
Eric Snow6d2cd902018-05-16 15:04:57 -04001656 with self.assertRaises(interpreters.ChannelClosedError):
1657 interpreters.channel_recv(cid)
1658
1659 def test_close_never_used(self):
1660 cid = interpreters.channel_create()
1661 interpreters.channel_close(cid)
1662
1663 with self.assertRaises(interpreters.ChannelClosedError):
1664 interpreters.channel_send(cid, b'spam')
1665 with self.assertRaises(interpreters.ChannelClosedError):
1666 interpreters.channel_recv(cid)
1667
1668 def test_close_by_unassociated_interp(self):
1669 cid = interpreters.channel_create()
1670 interpreters.channel_send(cid, b'spam')
1671 interp = interpreters.create()
1672 interpreters.run_string(interp, dedent(f"""
1673 import _xxsubinterpreters as _interpreters
Eric Snow3ab01362018-05-17 10:27:09 -04001674 _interpreters.channel_close({cid}, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001675 """))
1676 with self.assertRaises(interpreters.ChannelClosedError):
1677 interpreters.channel_recv(cid)
1678 with self.assertRaises(interpreters.ChannelClosedError):
1679 interpreters.channel_close(cid)
1680
1681 def test_close_used_multiple_times_by_single_user(self):
1682 cid = interpreters.channel_create()
1683 interpreters.channel_send(cid, b'spam')
1684 interpreters.channel_send(cid, b'spam')
1685 interpreters.channel_send(cid, b'spam')
1686 interpreters.channel_recv(cid)
Eric Snow3ab01362018-05-17 10:27:09 -04001687 interpreters.channel_close(cid, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001688
1689 with self.assertRaises(interpreters.ChannelClosedError):
1690 interpreters.channel_send(cid, b'eggs')
1691 with self.assertRaises(interpreters.ChannelClosedError):
1692 interpreters.channel_recv(cid)
1693
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001694 def test_channel_list_interpreters_invalid_channel(self):
1695 cid = interpreters.channel_create()
1696 # Test for invalid channel ID.
1697 with self.assertRaises(interpreters.ChannelNotFoundError):
1698 interpreters.channel_list_interpreters(1000, send=True)
1699
1700 interpreters.channel_close(cid)
1701 # Test for a channel that has been closed.
1702 with self.assertRaises(interpreters.ChannelClosedError):
1703 interpreters.channel_list_interpreters(cid, send=True)
1704
1705 def test_channel_list_interpreters_invalid_args(self):
1706 # Tests for invalid arguments passed to the API.
1707 cid = interpreters.channel_create()
1708 with self.assertRaises(TypeError):
1709 interpreters.channel_list_interpreters(cid)
1710
Eric Snow6d2cd902018-05-16 15:04:57 -04001711
1712class ChannelReleaseTests(TestBase):
1713
1714 # XXX Add more test coverage a la the tests for close().
1715
1716 """
1717 - main / interp / other
1718 - run in: current thread / new thread / other thread / different threads
1719 - end / opposite
1720 - force / no force
1721 - used / not used (associated / not associated)
1722 - empty / emptied / never emptied / partly emptied
1723 - closed / not closed
1724 - released / not released
1725 - creator (interp) / other
1726 - associated interpreter not running
1727 - associated interpreter destroyed
1728 """
1729
1730 """
1731 use
1732 pre-release
1733 release
1734 after
1735 check
1736 """
1737
1738 """
1739 release in: main, interp1
1740 creator: same, other (incl. interp2)
1741
1742 use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1743 pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1744 pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1745
1746 release: same
1747 release forced: same
1748
1749 use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1750 release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1751 check released: send/recv for same/other(incl. interp2)
1752 check closed: send/recv for same/other(incl. interp2)
1753 """
1754
1755 def test_single_user(self):
1756 cid = interpreters.channel_create()
1757 interpreters.channel_send(cid, b'spam')
1758 interpreters.channel_recv(cid)
1759 interpreters.channel_release(cid, send=True, recv=True)
1760
1761 with self.assertRaises(interpreters.ChannelClosedError):
1762 interpreters.channel_send(cid, b'eggs')
1763 with self.assertRaises(interpreters.ChannelClosedError):
1764 interpreters.channel_recv(cid)
1765
1766 def test_multiple_users(self):
1767 cid = interpreters.channel_create()
1768 id1 = interpreters.create()
1769 id2 = interpreters.create()
1770 interpreters.run_string(id1, dedent(f"""
1771 import _xxsubinterpreters as _interpreters
1772 _interpreters.channel_send({cid}, b'spam')
1773 """))
1774 out = _run_output(id2, dedent(f"""
1775 import _xxsubinterpreters as _interpreters
1776 obj = _interpreters.channel_recv({cid})
1777 _interpreters.channel_release({cid})
1778 print(repr(obj))
1779 """))
1780 interpreters.run_string(id1, dedent(f"""
1781 _interpreters.channel_release({cid})
1782 """))
1783
1784 self.assertEqual(out.strip(), "b'spam'")
1785
1786 def test_no_kwargs(self):
1787 cid = interpreters.channel_create()
1788 interpreters.channel_send(cid, b'spam')
1789 interpreters.channel_recv(cid)
1790 interpreters.channel_release(cid)
1791
1792 with self.assertRaises(interpreters.ChannelClosedError):
1793 interpreters.channel_send(cid, b'eggs')
1794 with self.assertRaises(interpreters.ChannelClosedError):
1795 interpreters.channel_recv(cid)
1796
1797 def test_multiple_times(self):
1798 cid = interpreters.channel_create()
1799 interpreters.channel_send(cid, b'spam')
1800 interpreters.channel_recv(cid)
1801 interpreters.channel_release(cid, send=True, recv=True)
1802
1803 with self.assertRaises(interpreters.ChannelClosedError):
1804 interpreters.channel_release(cid, send=True, recv=True)
1805
1806 def test_with_unused_items(self):
1807 cid = interpreters.channel_create()
1808 interpreters.channel_send(cid, b'spam')
1809 interpreters.channel_send(cid, b'ham')
1810 interpreters.channel_release(cid, send=True, recv=True)
1811
1812 with self.assertRaises(interpreters.ChannelClosedError):
1813 interpreters.channel_recv(cid)
1814
1815 def test_never_used(self):
1816 cid = interpreters.channel_create()
1817 interpreters.channel_release(cid)
1818
1819 with self.assertRaises(interpreters.ChannelClosedError):
1820 interpreters.channel_send(cid, b'spam')
1821 with self.assertRaises(interpreters.ChannelClosedError):
1822 interpreters.channel_recv(cid)
1823
1824 def test_by_unassociated_interp(self):
1825 cid = interpreters.channel_create()
1826 interpreters.channel_send(cid, b'spam')
1827 interp = interpreters.create()
1828 interpreters.run_string(interp, dedent(f"""
1829 import _xxsubinterpreters as _interpreters
1830 _interpreters.channel_release({cid})
1831 """))
1832 obj = interpreters.channel_recv(cid)
1833 interpreters.channel_release(cid)
1834
1835 with self.assertRaises(interpreters.ChannelClosedError):
1836 interpreters.channel_send(cid, b'eggs')
1837 self.assertEqual(obj, b'spam')
1838
1839 def test_close_if_unassociated(self):
1840 # XXX Something's not right with this test...
1841 cid = interpreters.channel_create()
1842 interp = interpreters.create()
1843 interpreters.run_string(interp, dedent(f"""
1844 import _xxsubinterpreters as _interpreters
1845 obj = _interpreters.channel_send({cid}, b'spam')
1846 _interpreters.channel_release({cid})
1847 """))
1848
1849 with self.assertRaises(interpreters.ChannelClosedError):
1850 interpreters.channel_recv(cid)
1851
1852 def test_partially(self):
1853 # XXX Is partial close too weird/confusing?
1854 cid = interpreters.channel_create()
1855 interpreters.channel_send(cid, None)
1856 interpreters.channel_recv(cid)
1857 interpreters.channel_send(cid, b'spam')
1858 interpreters.channel_release(cid, send=True)
1859 obj = interpreters.channel_recv(cid)
1860
1861 self.assertEqual(obj, b'spam')
1862
1863 def test_used_multiple_times_by_single_user(self):
1864 cid = interpreters.channel_create()
1865 interpreters.channel_send(cid, b'spam')
1866 interpreters.channel_send(cid, b'spam')
1867 interpreters.channel_send(cid, b'spam')
1868 interpreters.channel_recv(cid)
1869 interpreters.channel_release(cid, send=True, recv=True)
1870
1871 with self.assertRaises(interpreters.ChannelClosedError):
1872 interpreters.channel_send(cid, b'eggs')
1873 with self.assertRaises(interpreters.ChannelClosedError):
1874 interpreters.channel_recv(cid)
1875
1876
1877class ChannelCloseFixture(namedtuple('ChannelCloseFixture',
1878 'end interp other extra creator')):
1879
1880 # Set this to True to avoid creating interpreters, e.g. when
1881 # scanning through test permutations without running them.
1882 QUICK = False
1883
1884 def __new__(cls, end, interp, other, extra, creator):
1885 assert end in ('send', 'recv')
1886 if cls.QUICK:
1887 known = {}
1888 else:
1889 interp = Interpreter.from_raw(interp)
1890 other = Interpreter.from_raw(other)
1891 extra = Interpreter.from_raw(extra)
1892 known = {
1893 interp.name: interp,
1894 other.name: other,
1895 extra.name: extra,
1896 }
1897 if not creator:
1898 creator = 'same'
1899 self = super().__new__(cls, end, interp, other, extra, creator)
1900 self._prepped = set()
1901 self._state = ChannelState()
1902 self._known = known
1903 return self
1904
1905 @property
1906 def state(self):
1907 return self._state
1908
1909 @property
1910 def cid(self):
1911 try:
1912 return self._cid
1913 except AttributeError:
1914 creator = self._get_interpreter(self.creator)
1915 self._cid = self._new_channel(creator)
1916 return self._cid
1917
1918 def get_interpreter(self, interp):
1919 interp = self._get_interpreter(interp)
1920 self._prep_interpreter(interp)
1921 return interp
1922
1923 def expect_closed_error(self, end=None):
1924 if end is None:
1925 end = self.end
1926 if end == 'recv' and self.state.closed == 'send':
1927 return False
1928 return bool(self.state.closed)
1929
1930 def prep_interpreter(self, interp):
1931 self._prep_interpreter(interp)
1932
1933 def record_action(self, action, result):
1934 self._state = result
1935
1936 def clean_up(self):
1937 clean_up_interpreters()
1938 clean_up_channels()
1939
1940 # internal methods
1941
1942 def _new_channel(self, creator):
1943 if creator.name == 'main':
1944 return interpreters.channel_create()
1945 else:
1946 ch = interpreters.channel_create()
1947 run_interp(creator.id, f"""
1948 import _xxsubinterpreters
1949 cid = _xxsubinterpreters.channel_create()
1950 # We purposefully send back an int to avoid tying the
1951 # channel to the other interpreter.
1952 _xxsubinterpreters.channel_send({ch}, int(cid))
1953 del _xxsubinterpreters
1954 """)
1955 self._cid = interpreters.channel_recv(ch)
1956 return self._cid
1957
1958 def _get_interpreter(self, interp):
1959 if interp in ('same', 'interp'):
1960 return self.interp
1961 elif interp == 'other':
1962 return self.other
1963 elif interp == 'extra':
1964 return self.extra
1965 else:
1966 name = interp
1967 try:
1968 interp = self._known[name]
1969 except KeyError:
1970 interp = self._known[name] = Interpreter(name)
1971 return interp
1972
1973 def _prep_interpreter(self, interp):
1974 if interp.id in self._prepped:
1975 return
1976 self._prepped.add(interp.id)
1977 if interp.name == 'main':
1978 return
1979 run_interp(interp.id, f"""
1980 import _xxsubinterpreters as interpreters
1981 import test.test__xxsubinterpreters as helpers
1982 ChannelState = helpers.ChannelState
1983 try:
1984 cid
1985 except NameError:
1986 cid = interpreters._channel_id({self.cid})
1987 """)
1988
1989
1990@unittest.skip('these tests take several hours to run')
1991class ExhaustiveChannelTests(TestBase):
1992
1993 """
1994 - main / interp / other
1995 - run in: current thread / new thread / other thread / different threads
1996 - end / opposite
1997 - force / no force
1998 - used / not used (associated / not associated)
1999 - empty / emptied / never emptied / partly emptied
2000 - closed / not closed
2001 - released / not released
2002 - creator (interp) / other
2003 - associated interpreter not running
2004 - associated interpreter destroyed
2005
2006 - close after unbound
2007 """
2008
2009 """
2010 use
2011 pre-close
2012 close
2013 after
2014 check
2015 """
2016
2017 """
2018 close in: main, interp1
2019 creator: same, other, extra
2020
2021 use: None,send,recv,send/recv in None,same,other,same+other,all
2022 pre-close: None,send,recv in None,same,other,same+other,all
2023 pre-close forced: None,send,recv in None,same,other,same+other,all
2024
2025 close: same
2026 close forced: same
2027
2028 use after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2029 close after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2030 check closed: send/recv for same/other(incl. interp2)
2031 """
2032
2033 def iter_action_sets(self):
2034 # - used / not used (associated / not associated)
2035 # - empty / emptied / never emptied / partly emptied
2036 # - closed / not closed
2037 # - released / not released
2038
2039 # never used
2040 yield []
2041
2042 # only pre-closed (and possible used after)
2043 for closeactions in self._iter_close_action_sets('same', 'other'):
2044 yield closeactions
2045 for postactions in self._iter_post_close_action_sets():
2046 yield closeactions + postactions
2047 for closeactions in self._iter_close_action_sets('other', 'extra'):
2048 yield closeactions
2049 for postactions in self._iter_post_close_action_sets():
2050 yield closeactions + postactions
2051
2052 # used
2053 for useactions in self._iter_use_action_sets('same', 'other'):
2054 yield useactions
2055 for closeactions in self._iter_close_action_sets('same', 'other'):
2056 actions = useactions + closeactions
2057 yield actions
2058 for postactions in self._iter_post_close_action_sets():
2059 yield actions + postactions
2060 for closeactions in self._iter_close_action_sets('other', 'extra'):
2061 actions = useactions + closeactions
2062 yield actions
2063 for postactions in self._iter_post_close_action_sets():
2064 yield actions + postactions
2065 for useactions in self._iter_use_action_sets('other', 'extra'):
2066 yield useactions
2067 for closeactions in self._iter_close_action_sets('same', 'other'):
2068 actions = useactions + closeactions
2069 yield actions
2070 for postactions in self._iter_post_close_action_sets():
2071 yield actions + postactions
2072 for closeactions in self._iter_close_action_sets('other', 'extra'):
2073 actions = useactions + closeactions
2074 yield actions
2075 for postactions in self._iter_post_close_action_sets():
2076 yield actions + postactions
2077
2078 def _iter_use_action_sets(self, interp1, interp2):
2079 interps = (interp1, interp2)
2080
2081 # only recv end used
2082 yield [
2083 ChannelAction('use', 'recv', interp1),
2084 ]
2085 yield [
2086 ChannelAction('use', 'recv', interp2),
2087 ]
2088 yield [
2089 ChannelAction('use', 'recv', interp1),
2090 ChannelAction('use', 'recv', interp2),
2091 ]
2092
2093 # never emptied
2094 yield [
2095 ChannelAction('use', 'send', interp1),
2096 ]
2097 yield [
2098 ChannelAction('use', 'send', interp2),
2099 ]
2100 yield [
2101 ChannelAction('use', 'send', interp1),
2102 ChannelAction('use', 'send', interp2),
2103 ]
2104
2105 # partially emptied
2106 for interp1 in interps:
2107 for interp2 in interps:
2108 for interp3 in interps:
2109 yield [
2110 ChannelAction('use', 'send', interp1),
2111 ChannelAction('use', 'send', interp2),
2112 ChannelAction('use', 'recv', interp3),
2113 ]
2114
2115 # fully emptied
2116 for interp1 in interps:
2117 for interp2 in interps:
2118 for interp3 in interps:
2119 for interp4 in interps:
2120 yield [
2121 ChannelAction('use', 'send', interp1),
2122 ChannelAction('use', 'send', interp2),
2123 ChannelAction('use', 'recv', interp3),
2124 ChannelAction('use', 'recv', interp4),
2125 ]
2126
2127 def _iter_close_action_sets(self, interp1, interp2):
2128 ends = ('recv', 'send')
2129 interps = (interp1, interp2)
2130 for force in (True, False):
2131 op = 'force-close' if force else 'close'
2132 for interp in interps:
2133 for end in ends:
2134 yield [
2135 ChannelAction(op, end, interp),
2136 ]
2137 for recvop in ('close', 'force-close'):
2138 for sendop in ('close', 'force-close'):
2139 for recv in interps:
2140 for send in interps:
2141 yield [
2142 ChannelAction(recvop, 'recv', recv),
2143 ChannelAction(sendop, 'send', send),
2144 ]
2145
2146 def _iter_post_close_action_sets(self):
2147 for interp in ('same', 'extra', 'other'):
2148 yield [
2149 ChannelAction('use', 'recv', interp),
2150 ]
2151 yield [
2152 ChannelAction('use', 'send', interp),
2153 ]
2154
2155 def run_actions(self, fix, actions):
2156 for action in actions:
2157 self.run_action(fix, action)
2158
2159 def run_action(self, fix, action, *, hideclosed=True):
2160 end = action.resolve_end(fix.end)
2161 interp = action.resolve_interp(fix.interp, fix.other, fix.extra)
2162 fix.prep_interpreter(interp)
2163 if interp.name == 'main':
2164 result = run_action(
2165 fix.cid,
2166 action.action,
2167 end,
2168 fix.state,
2169 hideclosed=hideclosed,
2170 )
2171 fix.record_action(action, result)
2172 else:
2173 _cid = interpreters.channel_create()
2174 run_interp(interp.id, f"""
2175 result = helpers.run_action(
2176 {fix.cid},
2177 {repr(action.action)},
2178 {repr(end)},
2179 {repr(fix.state)},
2180 hideclosed={hideclosed},
2181 )
2182 interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little'))
2183 interpreters.channel_send({_cid}, b'X' if result.closed else b'')
2184 """)
2185 result = ChannelState(
2186 pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'),
2187 closed=bool(interpreters.channel_recv(_cid)),
2188 )
2189 fix.record_action(action, result)
2190
2191 def iter_fixtures(self):
2192 # XXX threads?
2193 interpreters = [
2194 ('main', 'interp', 'extra'),
2195 ('interp', 'main', 'extra'),
2196 ('interp1', 'interp2', 'extra'),
2197 ('interp1', 'interp2', 'main'),
2198 ]
2199 for interp, other, extra in interpreters:
2200 for creator in ('same', 'other', 'creator'):
2201 for end in ('send', 'recv'):
2202 yield ChannelCloseFixture(end, interp, other, extra, creator)
2203
2204 def _close(self, fix, *, force):
2205 op = 'force-close' if force else 'close'
2206 close = ChannelAction(op, fix.end, 'same')
2207 if not fix.expect_closed_error():
2208 self.run_action(fix, close, hideclosed=False)
2209 else:
2210 with self.assertRaises(interpreters.ChannelClosedError):
2211 self.run_action(fix, close, hideclosed=False)
2212
2213 def _assert_closed_in_interp(self, fix, interp=None):
2214 if interp is None or interp.name == 'main':
2215 with self.assertRaises(interpreters.ChannelClosedError):
2216 interpreters.channel_recv(fix.cid)
2217 with self.assertRaises(interpreters.ChannelClosedError):
2218 interpreters.channel_send(fix.cid, b'spam')
2219 with self.assertRaises(interpreters.ChannelClosedError):
2220 interpreters.channel_close(fix.cid)
2221 with self.assertRaises(interpreters.ChannelClosedError):
2222 interpreters.channel_close(fix.cid, force=True)
2223 else:
2224 run_interp(interp.id, f"""
2225 with helpers.expect_channel_closed():
2226 interpreters.channel_recv(cid)
2227 """)
2228 run_interp(interp.id, f"""
2229 with helpers.expect_channel_closed():
2230 interpreters.channel_send(cid, b'spam')
2231 """)
2232 run_interp(interp.id, f"""
2233 with helpers.expect_channel_closed():
2234 interpreters.channel_close(cid)
2235 """)
2236 run_interp(interp.id, f"""
2237 with helpers.expect_channel_closed():
2238 interpreters.channel_close(cid, force=True)
2239 """)
2240
2241 def _assert_closed(self, fix):
2242 self.assertTrue(fix.state.closed)
2243
2244 for _ in range(fix.state.pending):
2245 interpreters.channel_recv(fix.cid)
2246 self._assert_closed_in_interp(fix)
2247
2248 for interp in ('same', 'other'):
2249 interp = fix.get_interpreter(interp)
2250 if interp.name == 'main':
2251 continue
2252 self._assert_closed_in_interp(fix, interp)
2253
2254 interp = fix.get_interpreter('fresh')
2255 self._assert_closed_in_interp(fix, interp)
2256
2257 def _iter_close_tests(self, verbose=False):
2258 i = 0
2259 for actions in self.iter_action_sets():
2260 print()
2261 for fix in self.iter_fixtures():
2262 i += 1
2263 if i > 1000:
2264 return
2265 if verbose:
2266 if (i - 1) % 6 == 0:
2267 print()
2268 print(i, fix, '({} actions)'.format(len(actions)))
2269 else:
2270 if (i - 1) % 6 == 0:
2271 print(' ', end='')
2272 print('.', end=''); sys.stdout.flush()
2273 yield i, fix, actions
2274 if verbose:
2275 print('---')
2276 print()
2277
2278 # This is useful for scanning through the possible tests.
2279 def _skim_close_tests(self):
2280 ChannelCloseFixture.QUICK = True
2281 for i, fix, actions in self._iter_close_tests():
2282 pass
2283
2284 def test_close(self):
2285 for i, fix, actions in self._iter_close_tests():
2286 with self.subTest('{} {} {}'.format(i, fix, actions)):
2287 fix.prep_interpreter(fix.interp)
2288 self.run_actions(fix, actions)
2289
2290 self._close(fix, force=False)
2291
2292 self._assert_closed(fix)
2293 # XXX Things slow down if we have too many interpreters.
2294 fix.clean_up()
2295
2296 def test_force_close(self):
2297 for i, fix, actions in self._iter_close_tests():
2298 with self.subTest('{} {} {}'.format(i, fix, actions)):
2299 fix.prep_interpreter(fix.interp)
2300 self.run_actions(fix, actions)
2301
2302 self._close(fix, force=True)
2303
2304 self._assert_closed(fix)
2305 # XXX Things slow down if we have too many interpreters.
2306 fix.clean_up()
2307
Eric Snow7f8bfc92018-01-29 18:23:44 -07002308
2309if __name__ == '__main__':
2310 unittest.main()