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