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