blob: eab8f9f56c82a3cc15100f5485ec89deb2852f07 [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
Joannah Nanjekye6f798382020-06-21 20:59:43 -0300476 @unittest.skip('Fails on FreeBSD')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700477 def test_subinterpreter(self):
478 interp = interpreters.create()
479 self.assertFalse(interpreters.is_running(interp))
480
481 with _running(interp):
482 self.assertTrue(interpreters.is_running(interp))
483 self.assertFalse(interpreters.is_running(interp))
484
485 def test_from_subinterpreter(self):
486 interp = interpreters.create()
487 out = _run_output(interp, dedent(f"""
488 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400489 if _interpreters.is_running({interp}):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700490 print(True)
491 else:
492 print(False)
493 """))
494 self.assertEqual(out.strip(), 'True')
495
496 def test_already_destroyed(self):
497 interp = interpreters.create()
498 interpreters.destroy(interp)
499 with self.assertRaises(RuntimeError):
500 interpreters.is_running(interp)
501
502 def test_does_not_exist(self):
503 with self.assertRaises(RuntimeError):
504 interpreters.is_running(1_000_000)
505
506 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300507 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700508 interpreters.is_running(-1)
509
510
Eric Snow4c6955e2018-02-16 18:53:40 -0700511class InterpreterIDTests(TestBase):
512
513 def test_with_int(self):
514 id = interpreters.InterpreterID(10, force=True)
515
516 self.assertEqual(int(id), 10)
517
518 def test_coerce_id(self):
Eric Snow4c6955e2018-02-16 18:53:40 -0700519 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300520 def __index__(self):
521 return 10
Eric Snow4c6955e2018-02-16 18:53:40 -0700522
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300523 id = interpreters.InterpreterID(Int(), force=True)
524 self.assertEqual(int(id), 10)
Eric Snow4c6955e2018-02-16 18:53:40 -0700525
526 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +0300527 self.assertRaises(TypeError, interpreters.InterpreterID, object())
528 self.assertRaises(TypeError, interpreters.InterpreterID, 10.0)
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300529 self.assertRaises(TypeError, interpreters.InterpreterID, '10')
Serhiy Storchakabf169912019-09-13 22:50:27 +0300530 self.assertRaises(TypeError, interpreters.InterpreterID, b'10')
531 self.assertRaises(ValueError, interpreters.InterpreterID, -1)
Serhiy Storchakabf169912019-09-13 22:50:27 +0300532 self.assertRaises(OverflowError, interpreters.InterpreterID, 2**64)
Eric Snow4c6955e2018-02-16 18:53:40 -0700533
534 def test_does_not_exist(self):
535 id = interpreters.channel_create()
536 with self.assertRaises(RuntimeError):
537 interpreters.InterpreterID(int(id) + 1) # unforced
538
Eric Snow6d2cd902018-05-16 15:04:57 -0400539 def test_str(self):
540 id = interpreters.InterpreterID(10, force=True)
541 self.assertEqual(str(id), '10')
542
Eric Snow4c6955e2018-02-16 18:53:40 -0700543 def test_repr(self):
544 id = interpreters.InterpreterID(10, force=True)
545 self.assertEqual(repr(id), 'InterpreterID(10)')
546
547 def test_equality(self):
548 id1 = interpreters.create()
549 id2 = interpreters.InterpreterID(int(id1))
550 id3 = interpreters.create()
551
552 self.assertTrue(id1 == id1)
553 self.assertTrue(id1 == id2)
554 self.assertTrue(id1 == int(id1))
Serhiy Storchakabf169912019-09-13 22:50:27 +0300555 self.assertTrue(int(id1) == id1)
556 self.assertTrue(id1 == float(int(id1)))
557 self.assertTrue(float(int(id1)) == id1)
558 self.assertFalse(id1 == float(int(id1)) + 0.1)
559 self.assertFalse(id1 == str(int(id1)))
560 self.assertFalse(id1 == 2**1000)
561 self.assertFalse(id1 == float('inf'))
562 self.assertFalse(id1 == 'spam')
Eric Snow4c6955e2018-02-16 18:53:40 -0700563 self.assertFalse(id1 == id3)
564
565 self.assertFalse(id1 != id1)
566 self.assertFalse(id1 != id2)
567 self.assertTrue(id1 != id3)
568
569
Eric Snow7f8bfc92018-01-29 18:23:44 -0700570class CreateTests(TestBase):
571
572 def test_in_main(self):
573 id = interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400574 self.assertIsInstance(id, interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700575
576 self.assertIn(id, interpreters.list_all())
577
578 @unittest.skip('enable this test when working on pystate.c')
579 def test_unique_id(self):
580 seen = set()
581 for _ in range(100):
582 id = interpreters.create()
583 interpreters.destroy(id)
584 seen.add(id)
585
586 self.assertEqual(len(seen), 100)
587
588 def test_in_thread(self):
589 lock = threading.Lock()
590 id = None
591 def f():
592 nonlocal id
593 id = interpreters.create()
594 lock.acquire()
595 lock.release()
596
597 t = threading.Thread(target=f)
598 with lock:
599 t.start()
600 t.join()
601 self.assertIn(id, interpreters.list_all())
602
603 def test_in_subinterpreter(self):
604 main, = interpreters.list_all()
605 id1 = interpreters.create()
606 out = _run_output(id1, dedent("""
607 import _xxsubinterpreters as _interpreters
608 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400609 print(id)
610 assert isinstance(id, _interpreters.InterpreterID)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700611 """))
612 id2 = int(out.strip())
613
614 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
615
616 def test_in_threaded_subinterpreter(self):
617 main, = interpreters.list_all()
618 id1 = interpreters.create()
619 id2 = None
620 def f():
621 nonlocal id2
622 out = _run_output(id1, dedent("""
623 import _xxsubinterpreters as _interpreters
624 id = _interpreters.create()
Eric Snow6d2cd902018-05-16 15:04:57 -0400625 print(id)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700626 """))
627 id2 = int(out.strip())
628
629 t = threading.Thread(target=f)
630 t.start()
631 t.join()
632
633 self.assertEqual(set(interpreters.list_all()), {main, id1, id2})
634
635 def test_after_destroy_all(self):
636 before = set(interpreters.list_all())
637 # Create 3 subinterpreters.
638 ids = []
639 for _ in range(3):
640 id = interpreters.create()
641 ids.append(id)
642 # Now destroy them.
643 for id in ids:
644 interpreters.destroy(id)
645 # Finally, create another.
646 id = interpreters.create()
647 self.assertEqual(set(interpreters.list_all()), before | {id})
648
649 def test_after_destroy_some(self):
650 before = set(interpreters.list_all())
651 # Create 3 subinterpreters.
652 id1 = interpreters.create()
653 id2 = interpreters.create()
654 id3 = interpreters.create()
655 # Now destroy 2 of them.
656 interpreters.destroy(id1)
657 interpreters.destroy(id3)
658 # Finally, create another.
659 id = interpreters.create()
660 self.assertEqual(set(interpreters.list_all()), before | {id, id2})
661
662
663class DestroyTests(TestBase):
664
665 def test_one(self):
666 id1 = interpreters.create()
667 id2 = interpreters.create()
668 id3 = interpreters.create()
669 self.assertIn(id2, interpreters.list_all())
670 interpreters.destroy(id2)
671 self.assertNotIn(id2, interpreters.list_all())
672 self.assertIn(id1, interpreters.list_all())
673 self.assertIn(id3, interpreters.list_all())
674
675 def test_all(self):
676 before = set(interpreters.list_all())
677 ids = set()
678 for _ in range(3):
679 id = interpreters.create()
680 ids.add(id)
681 self.assertEqual(set(interpreters.list_all()), before | ids)
682 for id in ids:
683 interpreters.destroy(id)
684 self.assertEqual(set(interpreters.list_all()), before)
685
686 def test_main(self):
687 main, = interpreters.list_all()
688 with self.assertRaises(RuntimeError):
689 interpreters.destroy(main)
690
691 def f():
692 with self.assertRaises(RuntimeError):
693 interpreters.destroy(main)
694
695 t = threading.Thread(target=f)
696 t.start()
697 t.join()
698
699 def test_already_destroyed(self):
700 id = interpreters.create()
701 interpreters.destroy(id)
702 with self.assertRaises(RuntimeError):
703 interpreters.destroy(id)
704
705 def test_does_not_exist(self):
706 with self.assertRaises(RuntimeError):
707 interpreters.destroy(1_000_000)
708
709 def test_bad_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300710 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700711 interpreters.destroy(-1)
712
713 def test_from_current(self):
714 main, = interpreters.list_all()
715 id = interpreters.create()
Eric Snow4e9da0d2018-02-02 21:49:49 -0700716 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700717 import _xxsubinterpreters as _interpreters
Eric Snow4e9da0d2018-02-02 21:49:49 -0700718 try:
Eric Snow6d2cd902018-05-16 15:04:57 -0400719 _interpreters.destroy({id})
Eric Snow4e9da0d2018-02-02 21:49:49 -0700720 except RuntimeError:
721 pass
722 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700723
Eric Snow4e9da0d2018-02-02 21:49:49 -0700724 interpreters.run_string(id, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700725 self.assertEqual(set(interpreters.list_all()), {main, id})
726
727 def test_from_sibling(self):
728 main, = interpreters.list_all()
729 id1 = interpreters.create()
730 id2 = interpreters.create()
Eric Snow4c6955e2018-02-16 18:53:40 -0700731 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700732 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -0400733 _interpreters.destroy({id2})
Eric Snow4c6955e2018-02-16 18:53:40 -0700734 """)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700735 interpreters.run_string(id1, script)
736
737 self.assertEqual(set(interpreters.list_all()), {main, id1})
738
739 def test_from_other_thread(self):
740 id = interpreters.create()
741 def f():
742 interpreters.destroy(id)
743
744 t = threading.Thread(target=f)
745 t.start()
746 t.join()
747
748 def test_still_running(self):
749 main, = interpreters.list_all()
750 interp = interpreters.create()
751 with _running(interp):
Kyle Stanleyf03a8f82020-01-31 15:07:09 -0500752 self.assertTrue(interpreters.is_running(interp),
753 msg=f"Interp {interp} should be running before destruction.")
754
755 with self.assertRaises(RuntimeError,
756 msg=f"Should not be able to destroy interp {interp} while it's still running."):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700757 interpreters.destroy(interp)
758 self.assertTrue(interpreters.is_running(interp))
759
760
761class RunStringTests(TestBase):
762
Eric Snow7f8bfc92018-01-29 18:23:44 -0700763 def setUp(self):
764 super().setUp()
765 self.id = interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -0700766
Eric Snow7f8bfc92018-01-29 18:23:44 -0700767 def test_success(self):
768 script, file = _captured_script('print("it worked!", end="")')
769 with file:
770 interpreters.run_string(self.id, script)
771 out = file.read()
772
773 self.assertEqual(out, 'it worked!')
774
775 def test_in_thread(self):
776 script, file = _captured_script('print("it worked!", end="")')
777 with file:
778 def f():
779 interpreters.run_string(self.id, script)
780
781 t = threading.Thread(target=f)
782 t.start()
783 t.join()
784 out = file.read()
785
786 self.assertEqual(out, 'it worked!')
787
788 def test_create_thread(self):
Victor Stinner252346a2020-05-01 11:33:44 +0200789 subinterp = interpreters.create(isolated=False)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700790 script, file = _captured_script("""
791 import threading
792 def f():
793 print('it worked!', end='')
794
795 t = threading.Thread(target=f)
796 t.start()
797 t.join()
798 """)
799 with file:
Victor Stinner252346a2020-05-01 11:33:44 +0200800 interpreters.run_string(subinterp, script)
Eric Snow7f8bfc92018-01-29 18:23:44 -0700801 out = file.read()
802
803 self.assertEqual(out, 'it worked!')
804
805 @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()")
806 def test_fork(self):
807 import tempfile
808 with tempfile.NamedTemporaryFile('w+') as file:
809 file.write('')
810 file.flush()
811
812 expected = 'spam spam spam spam spam'
813 script = dedent(f"""
Eric Snow7f8bfc92018-01-29 18:23:44 -0700814 import os
Eric Snow59032962018-09-14 14:17:20 -0700815 try:
816 os.fork()
817 except RuntimeError:
Eric Snow7f8bfc92018-01-29 18:23:44 -0700818 with open('{file.name}', 'w') as out:
819 out.write('{expected}')
Eric Snow7f8bfc92018-01-29 18:23:44 -0700820 """)
821 interpreters.run_string(self.id, script)
822
823 file.seek(0)
824 content = file.read()
825 self.assertEqual(content, expected)
826
827 def test_already_running(self):
828 with _running(self.id):
829 with self.assertRaises(RuntimeError):
830 interpreters.run_string(self.id, 'print("spam")')
831
832 def test_does_not_exist(self):
833 id = 0
834 while id in interpreters.list_all():
835 id += 1
836 with self.assertRaises(RuntimeError):
837 interpreters.run_string(id, 'print("spam")')
838
839 def test_error_id(self):
Serhiy Storchaka543a3952019-09-25 18:35:57 +0300840 with self.assertRaises(ValueError):
Eric Snow7f8bfc92018-01-29 18:23:44 -0700841 interpreters.run_string(-1, 'print("spam")')
842
843 def test_bad_id(self):
844 with self.assertRaises(TypeError):
845 interpreters.run_string('spam', 'print("spam")')
846
847 def test_bad_script(self):
848 with self.assertRaises(TypeError):
849 interpreters.run_string(self.id, 10)
850
851 def test_bytes_for_script(self):
852 with self.assertRaises(TypeError):
853 interpreters.run_string(self.id, b'print("spam")')
854
855 @contextlib.contextmanager
856 def assert_run_failed(self, exctype, msg=None):
857 with self.assertRaises(interpreters.RunFailedError) as caught:
858 yield
859 if msg is None:
860 self.assertEqual(str(caught.exception).split(':')[0],
Victor Stinnerf2c3b682020-05-14 18:46:24 +0200861 str(exctype))
Eric Snow7f8bfc92018-01-29 18:23:44 -0700862 else:
863 self.assertEqual(str(caught.exception),
Victor Stinnerf2c3b682020-05-14 18:46:24 +0200864 "{}: {}".format(exctype, msg))
Eric Snow7f8bfc92018-01-29 18:23:44 -0700865
866 def test_invalid_syntax(self):
867 with self.assert_run_failed(SyntaxError):
868 # missing close paren
869 interpreters.run_string(self.id, 'print("spam"')
870
871 def test_failure(self):
872 with self.assert_run_failed(Exception, 'spam'):
873 interpreters.run_string(self.id, 'raise Exception("spam")')
874
875 def test_SystemExit(self):
876 with self.assert_run_failed(SystemExit, '42'):
877 interpreters.run_string(self.id, 'raise SystemExit(42)')
878
879 def test_sys_exit(self):
880 with self.assert_run_failed(SystemExit):
881 interpreters.run_string(self.id, dedent("""
882 import sys
883 sys.exit()
884 """))
885
886 with self.assert_run_failed(SystemExit, '42'):
887 interpreters.run_string(self.id, dedent("""
888 import sys
889 sys.exit(42)
890 """))
891
892 def test_with_shared(self):
893 r, w = os.pipe()
894
895 shared = {
896 'spam': b'ham',
897 'eggs': b'-1',
898 'cheddar': None,
899 }
900 script = dedent(f"""
901 eggs = int(eggs)
902 spam = 42
903 result = spam + eggs
904
905 ns = dict(vars())
906 del ns['__builtins__']
907 import pickle
908 with open({w}, 'wb') as chan:
909 pickle.dump(ns, chan)
910 """)
911 interpreters.run_string(self.id, script, shared)
912 with open(r, 'rb') as chan:
913 ns = pickle.load(chan)
914
915 self.assertEqual(ns['spam'], 42)
916 self.assertEqual(ns['eggs'], -1)
917 self.assertEqual(ns['result'], 41)
918 self.assertIsNone(ns['cheddar'])
919
920 def test_shared_overwrites(self):
921 interpreters.run_string(self.id, dedent("""
922 spam = 'eggs'
923 ns1 = dict(vars())
924 del ns1['__builtins__']
925 """))
926
927 shared = {'spam': b'ham'}
928 script = dedent(f"""
929 ns2 = dict(vars())
930 del ns2['__builtins__']
931 """)
932 interpreters.run_string(self.id, script, shared)
933
934 r, w = os.pipe()
935 script = dedent(f"""
936 ns = dict(vars())
937 del ns['__builtins__']
938 import pickle
939 with open({w}, 'wb') as chan:
940 pickle.dump(ns, chan)
941 """)
942 interpreters.run_string(self.id, script)
943 with open(r, 'rb') as chan:
944 ns = pickle.load(chan)
945
946 self.assertEqual(ns['ns1']['spam'], 'eggs')
947 self.assertEqual(ns['ns2']['spam'], b'ham')
948 self.assertEqual(ns['spam'], b'ham')
949
950 def test_shared_overwrites_default_vars(self):
951 r, w = os.pipe()
952
953 shared = {'__name__': b'not __main__'}
954 script = dedent(f"""
955 spam = 42
956
957 ns = dict(vars())
958 del ns['__builtins__']
959 import pickle
960 with open({w}, 'wb') as chan:
961 pickle.dump(ns, chan)
962 """)
963 interpreters.run_string(self.id, script, shared)
964 with open(r, 'rb') as chan:
965 ns = pickle.load(chan)
966
967 self.assertEqual(ns['__name__'], b'not __main__')
968
969 def test_main_reused(self):
970 r, w = os.pipe()
971 interpreters.run_string(self.id, dedent(f"""
972 spam = True
973
974 ns = dict(vars())
975 del ns['__builtins__']
976 import pickle
977 with open({w}, 'wb') as chan:
978 pickle.dump(ns, chan)
979 del ns, pickle, chan
980 """))
981 with open(r, 'rb') as chan:
982 ns1 = pickle.load(chan)
983
984 r, w = os.pipe()
985 interpreters.run_string(self.id, dedent(f"""
986 eggs = False
987
988 ns = dict(vars())
989 del ns['__builtins__']
990 import pickle
991 with open({w}, 'wb') as chan:
992 pickle.dump(ns, chan)
993 """))
994 with open(r, 'rb') as chan:
995 ns2 = pickle.load(chan)
996
997 self.assertIn('spam', ns1)
998 self.assertNotIn('eggs', ns1)
999 self.assertIn('eggs', ns2)
1000 self.assertIn('spam', ns2)
1001
1002 def test_execution_namespace_is_main(self):
1003 r, w = os.pipe()
1004
1005 script = dedent(f"""
1006 spam = 42
1007
1008 ns = dict(vars())
1009 ns['__builtins__'] = str(ns['__builtins__'])
1010 import pickle
1011 with open({w}, 'wb') as chan:
1012 pickle.dump(ns, chan)
1013 """)
1014 interpreters.run_string(self.id, script)
1015 with open(r, 'rb') as chan:
1016 ns = pickle.load(chan)
1017
1018 ns.pop('__builtins__')
1019 ns.pop('__loader__')
1020 self.assertEqual(ns, {
1021 '__name__': '__main__',
1022 '__annotations__': {},
1023 '__doc__': None,
1024 '__package__': None,
1025 '__spec__': None,
1026 'spam': 42,
1027 })
1028
Eric Snow4c6955e2018-02-16 18:53:40 -07001029 # XXX Fix this test!
1030 @unittest.skip('blocking forever')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001031 def test_still_running_at_exit(self):
1032 script = dedent(f"""
1033 from textwrap import dedent
1034 import threading
1035 import _xxsubinterpreters as _interpreters
Eric Snow4c6955e2018-02-16 18:53:40 -07001036 id = _interpreters.create()
Eric Snow7f8bfc92018-01-29 18:23:44 -07001037 def f():
1038 _interpreters.run_string(id, dedent('''
1039 import time
1040 # Give plenty of time for the main interpreter to finish.
1041 time.sleep(1_000_000)
1042 '''))
1043
1044 t = threading.Thread(target=f)
1045 t.start()
1046 """)
1047 with support.temp_dir() as dirname:
1048 filename = script_helper.make_script(dirname, 'interp', script)
1049 with script_helper.spawn_python(filename) as proc:
1050 retcode = proc.wait()
1051
1052 self.assertEqual(retcode, 0)
1053
1054
Eric Snow6d2cd902018-05-16 15:04:57 -04001055##################################
1056# channel tests
1057
Eric Snow7f8bfc92018-01-29 18:23:44 -07001058class ChannelIDTests(TestBase):
1059
1060 def test_default_kwargs(self):
1061 cid = interpreters._channel_id(10, force=True)
1062
1063 self.assertEqual(int(cid), 10)
1064 self.assertEqual(cid.end, 'both')
1065
1066 def test_with_kwargs(self):
1067 cid = interpreters._channel_id(10, send=True, force=True)
1068 self.assertEqual(cid.end, 'send')
1069
1070 cid = interpreters._channel_id(10, send=True, recv=False, force=True)
1071 self.assertEqual(cid.end, 'send')
1072
1073 cid = interpreters._channel_id(10, recv=True, force=True)
1074 self.assertEqual(cid.end, 'recv')
1075
1076 cid = interpreters._channel_id(10, recv=True, send=False, force=True)
1077 self.assertEqual(cid.end, 'recv')
1078
1079 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1080 self.assertEqual(cid.end, 'both')
1081
1082 def test_coerce_id(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001083 class Int(str):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001084 def __index__(self):
1085 return 10
Eric Snow7f8bfc92018-01-29 18:23:44 -07001086
Serhiy Storchakabf169912019-09-13 22:50:27 +03001087 cid = interpreters._channel_id(Int(), force=True)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001088 self.assertEqual(int(cid), 10)
1089
1090 def test_bad_id(self):
Serhiy Storchakabf169912019-09-13 22:50:27 +03001091 self.assertRaises(TypeError, interpreters._channel_id, object())
1092 self.assertRaises(TypeError, interpreters._channel_id, 10.0)
1093 self.assertRaises(TypeError, interpreters._channel_id, '10')
1094 self.assertRaises(TypeError, interpreters._channel_id, b'10')
1095 self.assertRaises(ValueError, interpreters._channel_id, -1)
1096 self.assertRaises(OverflowError, interpreters._channel_id, 2**64)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001097
1098 def test_bad_kwargs(self):
1099 with self.assertRaises(ValueError):
1100 interpreters._channel_id(10, send=False, recv=False)
1101
1102 def test_does_not_exist(self):
1103 cid = interpreters.channel_create()
1104 with self.assertRaises(interpreters.ChannelNotFoundError):
1105 interpreters._channel_id(int(cid) + 1) # unforced
1106
Eric Snow6d2cd902018-05-16 15:04:57 -04001107 def test_str(self):
1108 cid = interpreters._channel_id(10, force=True)
1109 self.assertEqual(str(cid), '10')
1110
Eric Snow7f8bfc92018-01-29 18:23:44 -07001111 def test_repr(self):
1112 cid = interpreters._channel_id(10, force=True)
1113 self.assertEqual(repr(cid), 'ChannelID(10)')
1114
1115 cid = interpreters._channel_id(10, send=True, force=True)
1116 self.assertEqual(repr(cid), 'ChannelID(10, send=True)')
1117
1118 cid = interpreters._channel_id(10, recv=True, force=True)
1119 self.assertEqual(repr(cid), 'ChannelID(10, recv=True)')
1120
1121 cid = interpreters._channel_id(10, send=True, recv=True, force=True)
1122 self.assertEqual(repr(cid), 'ChannelID(10)')
1123
1124 def test_equality(self):
1125 cid1 = interpreters.channel_create()
1126 cid2 = interpreters._channel_id(int(cid1))
1127 cid3 = interpreters.channel_create()
1128
1129 self.assertTrue(cid1 == cid1)
1130 self.assertTrue(cid1 == cid2)
1131 self.assertTrue(cid1 == int(cid1))
Serhiy Storchakabf169912019-09-13 22:50:27 +03001132 self.assertTrue(int(cid1) == cid1)
1133 self.assertTrue(cid1 == float(int(cid1)))
1134 self.assertTrue(float(int(cid1)) == cid1)
1135 self.assertFalse(cid1 == float(int(cid1)) + 0.1)
1136 self.assertFalse(cid1 == str(int(cid1)))
1137 self.assertFalse(cid1 == 2**1000)
1138 self.assertFalse(cid1 == float('inf'))
1139 self.assertFalse(cid1 == 'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001140 self.assertFalse(cid1 == cid3)
1141
1142 self.assertFalse(cid1 != cid1)
1143 self.assertFalse(cid1 != cid2)
1144 self.assertTrue(cid1 != cid3)
1145
1146
1147class ChannelTests(TestBase):
1148
Eric Snow6d2cd902018-05-16 15:04:57 -04001149 def test_create_cid(self):
1150 cid = interpreters.channel_create()
1151 self.assertIsInstance(cid, interpreters.ChannelID)
1152
Eric Snow7f8bfc92018-01-29 18:23:44 -07001153 def test_sequential_ids(self):
1154 before = interpreters.channel_list_all()
1155 id1 = interpreters.channel_create()
1156 id2 = interpreters.channel_create()
1157 id3 = interpreters.channel_create()
1158 after = interpreters.channel_list_all()
1159
1160 self.assertEqual(id2, int(id1) + 1)
1161 self.assertEqual(id3, int(id2) + 1)
1162 self.assertEqual(set(after) - set(before), {id1, id2, id3})
1163
1164 def test_ids_global(self):
1165 id1 = interpreters.create()
1166 out = _run_output(id1, dedent("""
1167 import _xxsubinterpreters as _interpreters
1168 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001169 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001170 """))
1171 cid1 = int(out.strip())
1172
1173 id2 = interpreters.create()
1174 out = _run_output(id2, dedent("""
1175 import _xxsubinterpreters as _interpreters
1176 cid = _interpreters.channel_create()
Eric Snow6d2cd902018-05-16 15:04:57 -04001177 print(cid)
Eric Snow7f8bfc92018-01-29 18:23:44 -07001178 """))
1179 cid2 = int(out.strip())
1180
1181 self.assertEqual(cid2, int(cid1) + 1)
1182
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001183 def test_channel_list_interpreters_none(self):
1184 """Test listing interpreters for a channel with no associations."""
1185 # Test for channel with no associated interpreters.
1186 cid = interpreters.channel_create()
1187 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1188 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1189 self.assertEqual(send_interps, [])
1190 self.assertEqual(recv_interps, [])
1191
1192 def test_channel_list_interpreters_basic(self):
1193 """Test basic listing channel interpreters."""
1194 interp0 = interpreters.get_main()
1195 cid = interpreters.channel_create()
1196 interpreters.channel_send(cid, "send")
1197 # Test for a channel that has one end associated to an interpreter.
1198 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1199 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1200 self.assertEqual(send_interps, [interp0])
1201 self.assertEqual(recv_interps, [])
1202
1203 interp1 = interpreters.create()
1204 _run_output(interp1, dedent(f"""
1205 import _xxsubinterpreters as _interpreters
1206 obj = _interpreters.channel_recv({cid})
1207 """))
1208 # Test for channel that has boths ends associated to an interpreter.
1209 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1210 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1211 self.assertEqual(send_interps, [interp0])
1212 self.assertEqual(recv_interps, [interp1])
1213
1214 def test_channel_list_interpreters_multiple(self):
1215 """Test listing interpreters for a channel with many associations."""
1216 interp0 = interpreters.get_main()
1217 interp1 = interpreters.create()
1218 interp2 = interpreters.create()
1219 interp3 = interpreters.create()
1220 cid = interpreters.channel_create()
1221
1222 interpreters.channel_send(cid, "send")
1223 _run_output(interp1, dedent(f"""
1224 import _xxsubinterpreters as _interpreters
1225 _interpreters.channel_send({cid}, "send")
1226 """))
1227 _run_output(interp2, dedent(f"""
1228 import _xxsubinterpreters as _interpreters
1229 obj = _interpreters.channel_recv({cid})
1230 """))
1231 _run_output(interp3, dedent(f"""
1232 import _xxsubinterpreters as _interpreters
1233 obj = _interpreters.channel_recv({cid})
1234 """))
1235 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1236 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1237 self.assertEqual(set(send_interps), {interp0, interp1})
1238 self.assertEqual(set(recv_interps), {interp2, interp3})
1239
1240 def test_channel_list_interpreters_destroyed(self):
1241 """Test listing channel interpreters with a destroyed interpreter."""
1242 interp0 = interpreters.get_main()
1243 interp1 = interpreters.create()
1244 cid = interpreters.channel_create()
1245 interpreters.channel_send(cid, "send")
1246 _run_output(interp1, dedent(f"""
1247 import _xxsubinterpreters as _interpreters
1248 obj = _interpreters.channel_recv({cid})
1249 """))
1250 # Should be one interpreter associated with each end.
1251 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1252 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1253 self.assertEqual(send_interps, [interp0])
1254 self.assertEqual(recv_interps, [interp1])
1255
1256 interpreters.destroy(interp1)
1257 # Destroyed interpreter should not be listed.
1258 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1259 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1260 self.assertEqual(send_interps, [interp0])
1261 self.assertEqual(recv_interps, [])
1262
1263 def test_channel_list_interpreters_released(self):
1264 """Test listing channel interpreters with a released channel."""
1265 # Set up one channel with main interpreter on the send end and two
1266 # subinterpreters on the receive end.
1267 interp0 = interpreters.get_main()
1268 interp1 = interpreters.create()
1269 interp2 = interpreters.create()
1270 cid = interpreters.channel_create()
1271 interpreters.channel_send(cid, "data")
1272 _run_output(interp1, dedent(f"""
1273 import _xxsubinterpreters as _interpreters
1274 obj = _interpreters.channel_recv({cid})
1275 """))
1276 interpreters.channel_send(cid, "data")
1277 _run_output(interp2, dedent(f"""
1278 import _xxsubinterpreters as _interpreters
1279 obj = _interpreters.channel_recv({cid})
1280 """))
1281 # Check the setup.
1282 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1283 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1284 self.assertEqual(len(send_interps), 1)
1285 self.assertEqual(len(recv_interps), 2)
1286
1287 # Release the main interpreter from the send end.
1288 interpreters.channel_release(cid, send=True)
1289 # Send end should have no associated interpreters.
1290 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1291 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1292 self.assertEqual(len(send_interps), 0)
1293 self.assertEqual(len(recv_interps), 2)
1294
1295 # Release one of the subinterpreters from the receive end.
1296 _run_output(interp2, dedent(f"""
1297 import _xxsubinterpreters as _interpreters
1298 _interpreters.channel_release({cid})
1299 """))
1300 # Receive end should have the released interpreter removed.
1301 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1302 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1303 self.assertEqual(len(send_interps), 0)
1304 self.assertEqual(recv_interps, [interp1])
1305
1306 def test_channel_list_interpreters_closed(self):
1307 """Test listing channel interpreters with a closed channel."""
1308 interp0 = interpreters.get_main()
1309 interp1 = interpreters.create()
1310 cid = interpreters.channel_create()
1311 # Put something in the channel so that it's not empty.
1312 interpreters.channel_send(cid, "send")
1313
1314 # Check initial state.
1315 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1316 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1317 self.assertEqual(len(send_interps), 1)
1318 self.assertEqual(len(recv_interps), 0)
1319
1320 # Force close the channel.
1321 interpreters.channel_close(cid, force=True)
1322 # Both ends should raise an error.
1323 with self.assertRaises(interpreters.ChannelClosedError):
1324 interpreters.channel_list_interpreters(cid, send=True)
1325 with self.assertRaises(interpreters.ChannelClosedError):
1326 interpreters.channel_list_interpreters(cid, send=False)
1327
1328 def test_channel_list_interpreters_closed_send_end(self):
1329 """Test listing channel interpreters with a channel's send end closed."""
1330 interp0 = interpreters.get_main()
1331 interp1 = interpreters.create()
1332 cid = interpreters.channel_create()
1333 # Put something in the channel so that it's not empty.
1334 interpreters.channel_send(cid, "send")
1335
1336 # Check initial state.
1337 send_interps = interpreters.channel_list_interpreters(cid, send=True)
1338 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1339 self.assertEqual(len(send_interps), 1)
1340 self.assertEqual(len(recv_interps), 0)
1341
1342 # Close the send end of the channel.
1343 interpreters.channel_close(cid, send=True)
1344 # Send end should raise an error.
1345 with self.assertRaises(interpreters.ChannelClosedError):
1346 interpreters.channel_list_interpreters(cid, send=True)
1347 # Receive end should not be closed (since channel is not empty).
1348 recv_interps = interpreters.channel_list_interpreters(cid, send=False)
1349 self.assertEqual(len(recv_interps), 0)
1350
1351 # Close the receive end of the channel from a subinterpreter.
1352 _run_output(interp1, dedent(f"""
1353 import _xxsubinterpreters as _interpreters
1354 _interpreters.channel_close({cid}, force=True)
1355 """))
1356 # Both ends should raise an error.
1357 with self.assertRaises(interpreters.ChannelClosedError):
1358 interpreters.channel_list_interpreters(cid, send=True)
1359 with self.assertRaises(interpreters.ChannelClosedError):
1360 interpreters.channel_list_interpreters(cid, send=False)
1361
Eric Snow7f8bfc92018-01-29 18:23:44 -07001362 ####################
1363
Eric Snow7f8bfc92018-01-29 18:23:44 -07001364 def test_send_recv_main(self):
1365 cid = interpreters.channel_create()
1366 orig = b'spam'
1367 interpreters.channel_send(cid, orig)
1368 obj = interpreters.channel_recv(cid)
1369
1370 self.assertEqual(obj, orig)
1371 self.assertIsNot(obj, orig)
1372
1373 def test_send_recv_same_interpreter(self):
1374 id1 = interpreters.create()
1375 out = _run_output(id1, dedent("""
1376 import _xxsubinterpreters as _interpreters
1377 cid = _interpreters.channel_create()
1378 orig = b'spam'
1379 _interpreters.channel_send(cid, orig)
1380 obj = _interpreters.channel_recv(cid)
1381 assert obj is not orig
1382 assert obj == orig
1383 """))
1384
1385 def test_send_recv_different_interpreters(self):
1386 cid = interpreters.channel_create()
1387 id1 = interpreters.create()
1388 out = _run_output(id1, dedent(f"""
1389 import _xxsubinterpreters as _interpreters
Eric Snow6d2cd902018-05-16 15:04:57 -04001390 _interpreters.channel_send({cid}, b'spam')
Eric Snow7f8bfc92018-01-29 18:23:44 -07001391 """))
1392 obj = interpreters.channel_recv(cid)
1393
1394 self.assertEqual(obj, b'spam')
1395
Eric Snowf53d9f22018-02-20 16:30:17 -07001396 def test_send_recv_different_threads(self):
1397 cid = interpreters.channel_create()
1398
1399 def f():
1400 while True:
1401 try:
1402 obj = interpreters.channel_recv(cid)
1403 break
1404 except interpreters.ChannelEmptyError:
1405 time.sleep(0.1)
1406 interpreters.channel_send(cid, obj)
1407 t = threading.Thread(target=f)
1408 t.start()
1409
1410 interpreters.channel_send(cid, b'spam')
1411 t.join()
1412 obj = interpreters.channel_recv(cid)
1413
1414 self.assertEqual(obj, b'spam')
1415
1416 def test_send_recv_different_interpreters_and_threads(self):
1417 cid = interpreters.channel_create()
1418 id1 = interpreters.create()
1419 out = None
1420
1421 def f():
1422 nonlocal out
1423 out = _run_output(id1, dedent(f"""
1424 import time
1425 import _xxsubinterpreters as _interpreters
1426 while True:
1427 try:
Eric Snow6d2cd902018-05-16 15:04:57 -04001428 obj = _interpreters.channel_recv({cid})
Eric Snowf53d9f22018-02-20 16:30:17 -07001429 break
1430 except _interpreters.ChannelEmptyError:
1431 time.sleep(0.1)
1432 assert(obj == b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001433 _interpreters.channel_send({cid}, b'eggs')
Eric Snowf53d9f22018-02-20 16:30:17 -07001434 """))
1435 t = threading.Thread(target=f)
1436 t.start()
1437
1438 interpreters.channel_send(cid, b'spam')
1439 t.join()
1440 obj = interpreters.channel_recv(cid)
1441
1442 self.assertEqual(obj, b'eggs')
1443
Eric Snow7f8bfc92018-01-29 18:23:44 -07001444 def test_send_not_found(self):
1445 with self.assertRaises(interpreters.ChannelNotFoundError):
1446 interpreters.channel_send(10, b'spam')
1447
1448 def test_recv_not_found(self):
1449 with self.assertRaises(interpreters.ChannelNotFoundError):
1450 interpreters.channel_recv(10)
1451
1452 def test_recv_empty(self):
1453 cid = interpreters.channel_create()
1454 with self.assertRaises(interpreters.ChannelEmptyError):
1455 interpreters.channel_recv(cid)
1456
Eric Snow5e8c6912020-04-28 17:11:32 -06001457 def test_recv_default(self):
1458 default = object()
1459 cid = interpreters.channel_create()
1460 obj1 = interpreters.channel_recv(cid, default)
1461 interpreters.channel_send(cid, None)
1462 interpreters.channel_send(cid, 1)
1463 interpreters.channel_send(cid, b'spam')
1464 interpreters.channel_send(cid, b'eggs')
1465 obj2 = interpreters.channel_recv(cid, default)
1466 obj3 = interpreters.channel_recv(cid, default)
1467 obj4 = interpreters.channel_recv(cid)
1468 obj5 = interpreters.channel_recv(cid, default)
1469 obj6 = interpreters.channel_recv(cid, default)
1470
1471 self.assertIs(obj1, default)
1472 self.assertIs(obj2, None)
1473 self.assertEqual(obj3, 1)
1474 self.assertEqual(obj4, b'spam')
1475 self.assertEqual(obj5, b'eggs')
1476 self.assertIs(obj6, default)
1477
Eric Snow6d2cd902018-05-16 15:04:57 -04001478 def test_run_string_arg_unresolved(self):
Eric Snow7f8bfc92018-01-29 18:23:44 -07001479 cid = interpreters.channel_create()
1480 interp = interpreters.create()
1481
1482 out = _run_output(interp, dedent("""
1483 import _xxsubinterpreters as _interpreters
1484 print(cid.end)
1485 _interpreters.channel_send(cid, b'spam')
1486 """),
1487 dict(cid=cid.send))
1488 obj = interpreters.channel_recv(cid)
1489
1490 self.assertEqual(obj, b'spam')
1491 self.assertEqual(out.strip(), 'send')
1492
Eric Snowab4a1982018-06-13 08:02:39 -06001493 # XXX For now there is no high-level channel into which the
1494 # sent channel ID can be converted...
1495 # Note: this test caused crashes on some buildbots (bpo-33615).
1496 @unittest.skip('disabled until high-level channels exist')
Eric Snow6d2cd902018-05-16 15:04:57 -04001497 def test_run_string_arg_resolved(self):
1498 cid = interpreters.channel_create()
1499 cid = interpreters._channel_id(cid, _resolve=True)
1500 interp = interpreters.create()
1501
1502 out = _run_output(interp, dedent("""
1503 import _xxsubinterpreters as _interpreters
Eric Snowab4a1982018-06-13 08:02:39 -06001504 print(chan.id.end)
1505 _interpreters.channel_send(chan.id, b'spam')
Eric Snow6d2cd902018-05-16 15:04:57 -04001506 """),
1507 dict(chan=cid.send))
1508 obj = interpreters.channel_recv(cid)
1509
1510 self.assertEqual(obj, b'spam')
1511 self.assertEqual(out.strip(), 'send')
1512
1513 # close
1514
1515 def test_close_single_user(self):
1516 cid = interpreters.channel_create()
1517 interpreters.channel_send(cid, b'spam')
1518 interpreters.channel_recv(cid)
1519 interpreters.channel_close(cid)
1520
1521 with self.assertRaises(interpreters.ChannelClosedError):
1522 interpreters.channel_send(cid, b'eggs')
1523 with self.assertRaises(interpreters.ChannelClosedError):
1524 interpreters.channel_recv(cid)
1525
1526 def test_close_multiple_users(self):
1527 cid = interpreters.channel_create()
1528 id1 = interpreters.create()
1529 id2 = interpreters.create()
1530 interpreters.run_string(id1, dedent(f"""
1531 import _xxsubinterpreters as _interpreters
1532 _interpreters.channel_send({cid}, b'spam')
1533 """))
1534 interpreters.run_string(id2, dedent(f"""
1535 import _xxsubinterpreters as _interpreters
1536 _interpreters.channel_recv({cid})
1537 """))
1538 interpreters.channel_close(cid)
1539 with self.assertRaises(interpreters.RunFailedError) as cm:
1540 interpreters.run_string(id1, dedent(f"""
1541 _interpreters.channel_send({cid}, b'spam')
1542 """))
1543 self.assertIn('ChannelClosedError', str(cm.exception))
1544 with self.assertRaises(interpreters.RunFailedError) as cm:
1545 interpreters.run_string(id2, dedent(f"""
1546 _interpreters.channel_send({cid}, b'spam')
1547 """))
1548 self.assertIn('ChannelClosedError', str(cm.exception))
1549
1550 def test_close_multiple_times(self):
1551 cid = interpreters.channel_create()
1552 interpreters.channel_send(cid, b'spam')
1553 interpreters.channel_recv(cid)
1554 interpreters.channel_close(cid)
1555
1556 with self.assertRaises(interpreters.ChannelClosedError):
1557 interpreters.channel_close(cid)
1558
Eric Snow3ab01362018-05-17 10:27:09 -04001559 def test_close_empty(self):
1560 tests = [
1561 (False, False),
1562 (True, False),
1563 (False, True),
1564 (True, True),
1565 ]
1566 for send, recv in tests:
1567 with self.subTest((send, recv)):
1568 cid = interpreters.channel_create()
1569 interpreters.channel_send(cid, b'spam')
1570 interpreters.channel_recv(cid)
1571 interpreters.channel_close(cid, send=send, recv=recv)
1572
1573 with self.assertRaises(interpreters.ChannelClosedError):
1574 interpreters.channel_send(cid, b'eggs')
1575 with self.assertRaises(interpreters.ChannelClosedError):
1576 interpreters.channel_recv(cid)
1577
1578 def test_close_defaults_with_unused_items(self):
Eric Snow6d2cd902018-05-16 15:04:57 -04001579 cid = interpreters.channel_create()
1580 interpreters.channel_send(cid, b'spam')
1581 interpreters.channel_send(cid, b'ham')
Eric Snow6d2cd902018-05-16 15:04:57 -04001582
Eric Snow3ab01362018-05-17 10:27:09 -04001583 with self.assertRaises(interpreters.ChannelNotEmptyError):
1584 interpreters.channel_close(cid)
1585 interpreters.channel_recv(cid)
1586 interpreters.channel_send(cid, b'eggs')
1587
1588 def test_close_recv_with_unused_items_unforced(self):
1589 cid = interpreters.channel_create()
1590 interpreters.channel_send(cid, b'spam')
1591 interpreters.channel_send(cid, b'ham')
1592
1593 with self.assertRaises(interpreters.ChannelNotEmptyError):
1594 interpreters.channel_close(cid, recv=True)
1595 interpreters.channel_recv(cid)
1596 interpreters.channel_send(cid, b'eggs')
1597 interpreters.channel_recv(cid)
1598 interpreters.channel_recv(cid)
1599 interpreters.channel_close(cid, recv=True)
1600
1601 def test_close_send_with_unused_items_unforced(self):
1602 cid = interpreters.channel_create()
1603 interpreters.channel_send(cid, b'spam')
1604 interpreters.channel_send(cid, b'ham')
1605 interpreters.channel_close(cid, send=True)
1606
1607 with self.assertRaises(interpreters.ChannelClosedError):
1608 interpreters.channel_send(cid, b'eggs')
1609 interpreters.channel_recv(cid)
1610 interpreters.channel_recv(cid)
1611 with self.assertRaises(interpreters.ChannelClosedError):
1612 interpreters.channel_recv(cid)
1613
1614 def test_close_both_with_unused_items_unforced(self):
1615 cid = interpreters.channel_create()
1616 interpreters.channel_send(cid, b'spam')
1617 interpreters.channel_send(cid, b'ham')
1618
1619 with self.assertRaises(interpreters.ChannelNotEmptyError):
1620 interpreters.channel_close(cid, recv=True, send=True)
1621 interpreters.channel_recv(cid)
1622 interpreters.channel_send(cid, b'eggs')
1623 interpreters.channel_recv(cid)
1624 interpreters.channel_recv(cid)
1625 interpreters.channel_close(cid, recv=True)
1626
1627 def test_close_recv_with_unused_items_forced(self):
1628 cid = interpreters.channel_create()
1629 interpreters.channel_send(cid, b'spam')
1630 interpreters.channel_send(cid, b'ham')
1631 interpreters.channel_close(cid, recv=True, force=True)
1632
1633 with self.assertRaises(interpreters.ChannelClosedError):
1634 interpreters.channel_send(cid, b'eggs')
1635 with self.assertRaises(interpreters.ChannelClosedError):
1636 interpreters.channel_recv(cid)
1637
1638 def test_close_send_with_unused_items_forced(self):
1639 cid = interpreters.channel_create()
1640 interpreters.channel_send(cid, b'spam')
1641 interpreters.channel_send(cid, b'ham')
1642 interpreters.channel_close(cid, send=True, force=True)
1643
1644 with self.assertRaises(interpreters.ChannelClosedError):
1645 interpreters.channel_send(cid, b'eggs')
1646 with self.assertRaises(interpreters.ChannelClosedError):
1647 interpreters.channel_recv(cid)
1648
1649 def test_close_both_with_unused_items_forced(self):
1650 cid = interpreters.channel_create()
1651 interpreters.channel_send(cid, b'spam')
1652 interpreters.channel_send(cid, b'ham')
1653 interpreters.channel_close(cid, send=True, recv=True, force=True)
1654
1655 with self.assertRaises(interpreters.ChannelClosedError):
1656 interpreters.channel_send(cid, b'eggs')
Eric Snow6d2cd902018-05-16 15:04:57 -04001657 with self.assertRaises(interpreters.ChannelClosedError):
1658 interpreters.channel_recv(cid)
1659
1660 def test_close_never_used(self):
1661 cid = interpreters.channel_create()
1662 interpreters.channel_close(cid)
1663
1664 with self.assertRaises(interpreters.ChannelClosedError):
1665 interpreters.channel_send(cid, b'spam')
1666 with self.assertRaises(interpreters.ChannelClosedError):
1667 interpreters.channel_recv(cid)
1668
1669 def test_close_by_unassociated_interp(self):
1670 cid = interpreters.channel_create()
1671 interpreters.channel_send(cid, b'spam')
1672 interp = interpreters.create()
1673 interpreters.run_string(interp, dedent(f"""
1674 import _xxsubinterpreters as _interpreters
Eric Snow3ab01362018-05-17 10:27:09 -04001675 _interpreters.channel_close({cid}, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001676 """))
1677 with self.assertRaises(interpreters.ChannelClosedError):
1678 interpreters.channel_recv(cid)
1679 with self.assertRaises(interpreters.ChannelClosedError):
1680 interpreters.channel_close(cid)
1681
1682 def test_close_used_multiple_times_by_single_user(self):
1683 cid = interpreters.channel_create()
1684 interpreters.channel_send(cid, b'spam')
1685 interpreters.channel_send(cid, b'spam')
1686 interpreters.channel_send(cid, b'spam')
1687 interpreters.channel_recv(cid)
Eric Snow3ab01362018-05-17 10:27:09 -04001688 interpreters.channel_close(cid, force=True)
Eric Snow6d2cd902018-05-16 15:04:57 -04001689
1690 with self.assertRaises(interpreters.ChannelClosedError):
1691 interpreters.channel_send(cid, b'eggs')
1692 with self.assertRaises(interpreters.ChannelClosedError):
1693 interpreters.channel_recv(cid)
1694
Lewis Gaulf7bbf582020-04-29 01:18:42 +01001695 def test_channel_list_interpreters_invalid_channel(self):
1696 cid = interpreters.channel_create()
1697 # Test for invalid channel ID.
1698 with self.assertRaises(interpreters.ChannelNotFoundError):
1699 interpreters.channel_list_interpreters(1000, send=True)
1700
1701 interpreters.channel_close(cid)
1702 # Test for a channel that has been closed.
1703 with self.assertRaises(interpreters.ChannelClosedError):
1704 interpreters.channel_list_interpreters(cid, send=True)
1705
1706 def test_channel_list_interpreters_invalid_args(self):
1707 # Tests for invalid arguments passed to the API.
1708 cid = interpreters.channel_create()
1709 with self.assertRaises(TypeError):
1710 interpreters.channel_list_interpreters(cid)
1711
Eric Snow6d2cd902018-05-16 15:04:57 -04001712
1713class ChannelReleaseTests(TestBase):
1714
1715 # XXX Add more test coverage a la the tests for close().
1716
1717 """
1718 - main / interp / other
1719 - run in: current thread / new thread / other thread / different threads
1720 - end / opposite
1721 - force / no force
1722 - used / not used (associated / not associated)
1723 - empty / emptied / never emptied / partly emptied
1724 - closed / not closed
1725 - released / not released
1726 - creator (interp) / other
1727 - associated interpreter not running
1728 - associated interpreter destroyed
1729 """
1730
1731 """
1732 use
1733 pre-release
1734 release
1735 after
1736 check
1737 """
1738
1739 """
1740 release in: main, interp1
1741 creator: same, other (incl. interp2)
1742
1743 use: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1744 pre-release: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1745 pre-release forced: None,send,recv,both in None,same,other(incl. interp2),same+other(incl. interp2),all
1746
1747 release: same
1748 release forced: same
1749
1750 use after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1751 release after: None,send,recv,send/recv in None,same,other(incl. interp2),same+other(incl. interp2),all
1752 check released: send/recv for same/other(incl. interp2)
1753 check closed: send/recv for same/other(incl. interp2)
1754 """
1755
1756 def test_single_user(self):
1757 cid = interpreters.channel_create()
1758 interpreters.channel_send(cid, b'spam')
1759 interpreters.channel_recv(cid)
1760 interpreters.channel_release(cid, send=True, recv=True)
1761
1762 with self.assertRaises(interpreters.ChannelClosedError):
1763 interpreters.channel_send(cid, b'eggs')
1764 with self.assertRaises(interpreters.ChannelClosedError):
1765 interpreters.channel_recv(cid)
1766
1767 def test_multiple_users(self):
1768 cid = interpreters.channel_create()
1769 id1 = interpreters.create()
1770 id2 = interpreters.create()
1771 interpreters.run_string(id1, dedent(f"""
1772 import _xxsubinterpreters as _interpreters
1773 _interpreters.channel_send({cid}, b'spam')
1774 """))
1775 out = _run_output(id2, dedent(f"""
1776 import _xxsubinterpreters as _interpreters
1777 obj = _interpreters.channel_recv({cid})
1778 _interpreters.channel_release({cid})
1779 print(repr(obj))
1780 """))
1781 interpreters.run_string(id1, dedent(f"""
1782 _interpreters.channel_release({cid})
1783 """))
1784
1785 self.assertEqual(out.strip(), "b'spam'")
1786
1787 def test_no_kwargs(self):
1788 cid = interpreters.channel_create()
1789 interpreters.channel_send(cid, b'spam')
1790 interpreters.channel_recv(cid)
1791 interpreters.channel_release(cid)
1792
1793 with self.assertRaises(interpreters.ChannelClosedError):
1794 interpreters.channel_send(cid, b'eggs')
1795 with self.assertRaises(interpreters.ChannelClosedError):
1796 interpreters.channel_recv(cid)
1797
1798 def test_multiple_times(self):
1799 cid = interpreters.channel_create()
1800 interpreters.channel_send(cid, b'spam')
1801 interpreters.channel_recv(cid)
1802 interpreters.channel_release(cid, send=True, recv=True)
1803
1804 with self.assertRaises(interpreters.ChannelClosedError):
1805 interpreters.channel_release(cid, send=True, recv=True)
1806
1807 def test_with_unused_items(self):
1808 cid = interpreters.channel_create()
1809 interpreters.channel_send(cid, b'spam')
1810 interpreters.channel_send(cid, b'ham')
1811 interpreters.channel_release(cid, send=True, recv=True)
1812
1813 with self.assertRaises(interpreters.ChannelClosedError):
1814 interpreters.channel_recv(cid)
1815
1816 def test_never_used(self):
1817 cid = interpreters.channel_create()
1818 interpreters.channel_release(cid)
1819
1820 with self.assertRaises(interpreters.ChannelClosedError):
1821 interpreters.channel_send(cid, b'spam')
1822 with self.assertRaises(interpreters.ChannelClosedError):
1823 interpreters.channel_recv(cid)
1824
1825 def test_by_unassociated_interp(self):
1826 cid = interpreters.channel_create()
1827 interpreters.channel_send(cid, b'spam')
1828 interp = interpreters.create()
1829 interpreters.run_string(interp, dedent(f"""
1830 import _xxsubinterpreters as _interpreters
1831 _interpreters.channel_release({cid})
1832 """))
1833 obj = interpreters.channel_recv(cid)
1834 interpreters.channel_release(cid)
1835
1836 with self.assertRaises(interpreters.ChannelClosedError):
1837 interpreters.channel_send(cid, b'eggs')
1838 self.assertEqual(obj, b'spam')
1839
1840 def test_close_if_unassociated(self):
1841 # XXX Something's not right with this test...
1842 cid = interpreters.channel_create()
1843 interp = interpreters.create()
1844 interpreters.run_string(interp, dedent(f"""
1845 import _xxsubinterpreters as _interpreters
1846 obj = _interpreters.channel_send({cid}, b'spam')
1847 _interpreters.channel_release({cid})
1848 """))
1849
1850 with self.assertRaises(interpreters.ChannelClosedError):
1851 interpreters.channel_recv(cid)
1852
1853 def test_partially(self):
1854 # XXX Is partial close too weird/confusing?
1855 cid = interpreters.channel_create()
1856 interpreters.channel_send(cid, None)
1857 interpreters.channel_recv(cid)
1858 interpreters.channel_send(cid, b'spam')
1859 interpreters.channel_release(cid, send=True)
1860 obj = interpreters.channel_recv(cid)
1861
1862 self.assertEqual(obj, b'spam')
1863
1864 def test_used_multiple_times_by_single_user(self):
1865 cid = interpreters.channel_create()
1866 interpreters.channel_send(cid, b'spam')
1867 interpreters.channel_send(cid, b'spam')
1868 interpreters.channel_send(cid, b'spam')
1869 interpreters.channel_recv(cid)
1870 interpreters.channel_release(cid, send=True, recv=True)
1871
1872 with self.assertRaises(interpreters.ChannelClosedError):
1873 interpreters.channel_send(cid, b'eggs')
1874 with self.assertRaises(interpreters.ChannelClosedError):
1875 interpreters.channel_recv(cid)
1876
1877
1878class ChannelCloseFixture(namedtuple('ChannelCloseFixture',
1879 'end interp other extra creator')):
1880
1881 # Set this to True to avoid creating interpreters, e.g. when
1882 # scanning through test permutations without running them.
1883 QUICK = False
1884
1885 def __new__(cls, end, interp, other, extra, creator):
1886 assert end in ('send', 'recv')
1887 if cls.QUICK:
1888 known = {}
1889 else:
1890 interp = Interpreter.from_raw(interp)
1891 other = Interpreter.from_raw(other)
1892 extra = Interpreter.from_raw(extra)
1893 known = {
1894 interp.name: interp,
1895 other.name: other,
1896 extra.name: extra,
1897 }
1898 if not creator:
1899 creator = 'same'
1900 self = super().__new__(cls, end, interp, other, extra, creator)
1901 self._prepped = set()
1902 self._state = ChannelState()
1903 self._known = known
1904 return self
1905
1906 @property
1907 def state(self):
1908 return self._state
1909
1910 @property
1911 def cid(self):
1912 try:
1913 return self._cid
1914 except AttributeError:
1915 creator = self._get_interpreter(self.creator)
1916 self._cid = self._new_channel(creator)
1917 return self._cid
1918
1919 def get_interpreter(self, interp):
1920 interp = self._get_interpreter(interp)
1921 self._prep_interpreter(interp)
1922 return interp
1923
1924 def expect_closed_error(self, end=None):
1925 if end is None:
1926 end = self.end
1927 if end == 'recv' and self.state.closed == 'send':
1928 return False
1929 return bool(self.state.closed)
1930
1931 def prep_interpreter(self, interp):
1932 self._prep_interpreter(interp)
1933
1934 def record_action(self, action, result):
1935 self._state = result
1936
1937 def clean_up(self):
1938 clean_up_interpreters()
1939 clean_up_channels()
1940
1941 # internal methods
1942
1943 def _new_channel(self, creator):
1944 if creator.name == 'main':
1945 return interpreters.channel_create()
1946 else:
1947 ch = interpreters.channel_create()
1948 run_interp(creator.id, f"""
1949 import _xxsubinterpreters
1950 cid = _xxsubinterpreters.channel_create()
1951 # We purposefully send back an int to avoid tying the
1952 # channel to the other interpreter.
1953 _xxsubinterpreters.channel_send({ch}, int(cid))
1954 del _xxsubinterpreters
1955 """)
1956 self._cid = interpreters.channel_recv(ch)
1957 return self._cid
1958
1959 def _get_interpreter(self, interp):
1960 if interp in ('same', 'interp'):
1961 return self.interp
1962 elif interp == 'other':
1963 return self.other
1964 elif interp == 'extra':
1965 return self.extra
1966 else:
1967 name = interp
1968 try:
1969 interp = self._known[name]
1970 except KeyError:
1971 interp = self._known[name] = Interpreter(name)
1972 return interp
1973
1974 def _prep_interpreter(self, interp):
1975 if interp.id in self._prepped:
1976 return
1977 self._prepped.add(interp.id)
1978 if interp.name == 'main':
1979 return
1980 run_interp(interp.id, f"""
1981 import _xxsubinterpreters as interpreters
1982 import test.test__xxsubinterpreters as helpers
1983 ChannelState = helpers.ChannelState
1984 try:
1985 cid
1986 except NameError:
1987 cid = interpreters._channel_id({self.cid})
1988 """)
1989
1990
1991@unittest.skip('these tests take several hours to run')
1992class ExhaustiveChannelTests(TestBase):
1993
1994 """
1995 - main / interp / other
1996 - run in: current thread / new thread / other thread / different threads
1997 - end / opposite
1998 - force / no force
1999 - used / not used (associated / not associated)
2000 - empty / emptied / never emptied / partly emptied
2001 - closed / not closed
2002 - released / not released
2003 - creator (interp) / other
2004 - associated interpreter not running
2005 - associated interpreter destroyed
2006
2007 - close after unbound
2008 """
2009
2010 """
2011 use
2012 pre-close
2013 close
2014 after
2015 check
2016 """
2017
2018 """
2019 close in: main, interp1
2020 creator: same, other, extra
2021
2022 use: None,send,recv,send/recv in None,same,other,same+other,all
2023 pre-close: None,send,recv in None,same,other,same+other,all
2024 pre-close forced: None,send,recv in None,same,other,same+other,all
2025
2026 close: same
2027 close forced: same
2028
2029 use after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2030 close after: None,send,recv,send/recv in None,same,other,extra,same+other,all
2031 check closed: send/recv for same/other(incl. interp2)
2032 """
2033
2034 def iter_action_sets(self):
2035 # - used / not used (associated / not associated)
2036 # - empty / emptied / never emptied / partly emptied
2037 # - closed / not closed
2038 # - released / not released
2039
2040 # never used
2041 yield []
2042
2043 # only pre-closed (and possible used after)
2044 for closeactions in self._iter_close_action_sets('same', 'other'):
2045 yield closeactions
2046 for postactions in self._iter_post_close_action_sets():
2047 yield closeactions + postactions
2048 for closeactions in self._iter_close_action_sets('other', 'extra'):
2049 yield closeactions
2050 for postactions in self._iter_post_close_action_sets():
2051 yield closeactions + postactions
2052
2053 # used
2054 for useactions in self._iter_use_action_sets('same', 'other'):
2055 yield useactions
2056 for closeactions in self._iter_close_action_sets('same', 'other'):
2057 actions = useactions + closeactions
2058 yield actions
2059 for postactions in self._iter_post_close_action_sets():
2060 yield actions + postactions
2061 for closeactions in self._iter_close_action_sets('other', 'extra'):
2062 actions = useactions + closeactions
2063 yield actions
2064 for postactions in self._iter_post_close_action_sets():
2065 yield actions + postactions
2066 for useactions in self._iter_use_action_sets('other', 'extra'):
2067 yield useactions
2068 for closeactions in self._iter_close_action_sets('same', 'other'):
2069 actions = useactions + closeactions
2070 yield actions
2071 for postactions in self._iter_post_close_action_sets():
2072 yield actions + postactions
2073 for closeactions in self._iter_close_action_sets('other', 'extra'):
2074 actions = useactions + closeactions
2075 yield actions
2076 for postactions in self._iter_post_close_action_sets():
2077 yield actions + postactions
2078
2079 def _iter_use_action_sets(self, interp1, interp2):
2080 interps = (interp1, interp2)
2081
2082 # only recv end used
2083 yield [
2084 ChannelAction('use', 'recv', interp1),
2085 ]
2086 yield [
2087 ChannelAction('use', 'recv', interp2),
2088 ]
2089 yield [
2090 ChannelAction('use', 'recv', interp1),
2091 ChannelAction('use', 'recv', interp2),
2092 ]
2093
2094 # never emptied
2095 yield [
2096 ChannelAction('use', 'send', interp1),
2097 ]
2098 yield [
2099 ChannelAction('use', 'send', interp2),
2100 ]
2101 yield [
2102 ChannelAction('use', 'send', interp1),
2103 ChannelAction('use', 'send', interp2),
2104 ]
2105
2106 # partially emptied
2107 for interp1 in interps:
2108 for interp2 in interps:
2109 for interp3 in interps:
2110 yield [
2111 ChannelAction('use', 'send', interp1),
2112 ChannelAction('use', 'send', interp2),
2113 ChannelAction('use', 'recv', interp3),
2114 ]
2115
2116 # fully emptied
2117 for interp1 in interps:
2118 for interp2 in interps:
2119 for interp3 in interps:
2120 for interp4 in interps:
2121 yield [
2122 ChannelAction('use', 'send', interp1),
2123 ChannelAction('use', 'send', interp2),
2124 ChannelAction('use', 'recv', interp3),
2125 ChannelAction('use', 'recv', interp4),
2126 ]
2127
2128 def _iter_close_action_sets(self, interp1, interp2):
2129 ends = ('recv', 'send')
2130 interps = (interp1, interp2)
2131 for force in (True, False):
2132 op = 'force-close' if force else 'close'
2133 for interp in interps:
2134 for end in ends:
2135 yield [
2136 ChannelAction(op, end, interp),
2137 ]
2138 for recvop in ('close', 'force-close'):
2139 for sendop in ('close', 'force-close'):
2140 for recv in interps:
2141 for send in interps:
2142 yield [
2143 ChannelAction(recvop, 'recv', recv),
2144 ChannelAction(sendop, 'send', send),
2145 ]
2146
2147 def _iter_post_close_action_sets(self):
2148 for interp in ('same', 'extra', 'other'):
2149 yield [
2150 ChannelAction('use', 'recv', interp),
2151 ]
2152 yield [
2153 ChannelAction('use', 'send', interp),
2154 ]
2155
2156 def run_actions(self, fix, actions):
2157 for action in actions:
2158 self.run_action(fix, action)
2159
2160 def run_action(self, fix, action, *, hideclosed=True):
2161 end = action.resolve_end(fix.end)
2162 interp = action.resolve_interp(fix.interp, fix.other, fix.extra)
2163 fix.prep_interpreter(interp)
2164 if interp.name == 'main':
2165 result = run_action(
2166 fix.cid,
2167 action.action,
2168 end,
2169 fix.state,
2170 hideclosed=hideclosed,
2171 )
2172 fix.record_action(action, result)
2173 else:
2174 _cid = interpreters.channel_create()
2175 run_interp(interp.id, f"""
2176 result = helpers.run_action(
2177 {fix.cid},
2178 {repr(action.action)},
2179 {repr(end)},
2180 {repr(fix.state)},
2181 hideclosed={hideclosed},
2182 )
2183 interpreters.channel_send({_cid}, result.pending.to_bytes(1, 'little'))
2184 interpreters.channel_send({_cid}, b'X' if result.closed else b'')
2185 """)
2186 result = ChannelState(
2187 pending=int.from_bytes(interpreters.channel_recv(_cid), 'little'),
2188 closed=bool(interpreters.channel_recv(_cid)),
2189 )
2190 fix.record_action(action, result)
2191
2192 def iter_fixtures(self):
2193 # XXX threads?
2194 interpreters = [
2195 ('main', 'interp', 'extra'),
2196 ('interp', 'main', 'extra'),
2197 ('interp1', 'interp2', 'extra'),
2198 ('interp1', 'interp2', 'main'),
2199 ]
2200 for interp, other, extra in interpreters:
2201 for creator in ('same', 'other', 'creator'):
2202 for end in ('send', 'recv'):
2203 yield ChannelCloseFixture(end, interp, other, extra, creator)
2204
2205 def _close(self, fix, *, force):
2206 op = 'force-close' if force else 'close'
2207 close = ChannelAction(op, fix.end, 'same')
2208 if not fix.expect_closed_error():
2209 self.run_action(fix, close, hideclosed=False)
2210 else:
2211 with self.assertRaises(interpreters.ChannelClosedError):
2212 self.run_action(fix, close, hideclosed=False)
2213
2214 def _assert_closed_in_interp(self, fix, interp=None):
2215 if interp is None or interp.name == 'main':
2216 with self.assertRaises(interpreters.ChannelClosedError):
2217 interpreters.channel_recv(fix.cid)
2218 with self.assertRaises(interpreters.ChannelClosedError):
2219 interpreters.channel_send(fix.cid, b'spam')
2220 with self.assertRaises(interpreters.ChannelClosedError):
2221 interpreters.channel_close(fix.cid)
2222 with self.assertRaises(interpreters.ChannelClosedError):
2223 interpreters.channel_close(fix.cid, force=True)
2224 else:
2225 run_interp(interp.id, f"""
2226 with helpers.expect_channel_closed():
2227 interpreters.channel_recv(cid)
2228 """)
2229 run_interp(interp.id, f"""
2230 with helpers.expect_channel_closed():
2231 interpreters.channel_send(cid, b'spam')
2232 """)
2233 run_interp(interp.id, f"""
2234 with helpers.expect_channel_closed():
2235 interpreters.channel_close(cid)
2236 """)
2237 run_interp(interp.id, f"""
2238 with helpers.expect_channel_closed():
2239 interpreters.channel_close(cid, force=True)
2240 """)
2241
2242 def _assert_closed(self, fix):
2243 self.assertTrue(fix.state.closed)
2244
2245 for _ in range(fix.state.pending):
2246 interpreters.channel_recv(fix.cid)
2247 self._assert_closed_in_interp(fix)
2248
2249 for interp in ('same', 'other'):
2250 interp = fix.get_interpreter(interp)
2251 if interp.name == 'main':
2252 continue
2253 self._assert_closed_in_interp(fix, interp)
2254
2255 interp = fix.get_interpreter('fresh')
2256 self._assert_closed_in_interp(fix, interp)
2257
2258 def _iter_close_tests(self, verbose=False):
2259 i = 0
2260 for actions in self.iter_action_sets():
2261 print()
2262 for fix in self.iter_fixtures():
2263 i += 1
2264 if i > 1000:
2265 return
2266 if verbose:
2267 if (i - 1) % 6 == 0:
2268 print()
2269 print(i, fix, '({} actions)'.format(len(actions)))
2270 else:
2271 if (i - 1) % 6 == 0:
2272 print(' ', end='')
2273 print('.', end=''); sys.stdout.flush()
2274 yield i, fix, actions
2275 if verbose:
2276 print('---')
2277 print()
2278
2279 # This is useful for scanning through the possible tests.
2280 def _skim_close_tests(self):
2281 ChannelCloseFixture.QUICK = True
2282 for i, fix, actions in self._iter_close_tests():
2283 pass
2284
2285 def test_close(self):
2286 for i, fix, actions in self._iter_close_tests():
2287 with self.subTest('{} {} {}'.format(i, fix, actions)):
2288 fix.prep_interpreter(fix.interp)
2289 self.run_actions(fix, actions)
2290
2291 self._close(fix, force=False)
2292
2293 self._assert_closed(fix)
2294 # XXX Things slow down if we have too many interpreters.
2295 fix.clean_up()
2296
2297 def test_force_close(self):
2298 for i, fix, actions in self._iter_close_tests():
2299 with self.subTest('{} {} {}'.format(i, fix, actions)):
2300 fix.prep_interpreter(fix.interp)
2301 self.run_actions(fix, actions)
2302
2303 self._close(fix, force=True)
2304
2305 self._assert_closed(fix)
2306 # XXX Things slow down if we have too many interpreters.
2307 fix.clean_up()
2308
Eric Snow7f8bfc92018-01-29 18:23:44 -07002309
2310if __name__ == '__main__':
2311 unittest.main()