PageRenderTime 50ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_continuation/test/test_stacklet.py

https://bitbucket.org/dac_io/pypy
Python | 686 lines | 566 code | 38 blank | 82 comment | 21 complexity | 949dd77bac66d145f0b1f39403198475 MD5 | raw file
  1. import os
  2. from pypy.module._continuation.test.support import BaseAppTest
  3. class AppTestStacklet(BaseAppTest):
  4. def setup_class(cls):
  5. BaseAppTest.setup_class.im_func(cls)
  6. cls.w_translated = cls.space.wrap(
  7. os.path.join(os.path.dirname(__file__),
  8. 'test_translated.py'))
  9. def test_new_empty(self):
  10. from _continuation import continulet
  11. #
  12. def empty_callback(c):
  13. never_called
  14. #
  15. c = continulet(empty_callback)
  16. assert type(c) is continulet
  17. def test_call_empty(self):
  18. from _continuation import continulet
  19. #
  20. def empty_callback(c1):
  21. assert c1 is c
  22. seen.append(1)
  23. return 42
  24. #
  25. seen = []
  26. c = continulet(empty_callback)
  27. res = c.switch()
  28. assert res == 42
  29. assert seen == [1]
  30. def test_no_double_init(self):
  31. from _continuation import continulet, error
  32. #
  33. def empty_callback(c1):
  34. never_called
  35. #
  36. c = continulet(empty_callback)
  37. raises(error, c.__init__, empty_callback)
  38. def test_no_init_after_started(self):
  39. from _continuation import continulet, error
  40. #
  41. def empty_callback(c1):
  42. raises(error, c1.__init__, empty_callback)
  43. return 42
  44. #
  45. c = continulet(empty_callback)
  46. res = c.switch()
  47. assert res == 42
  48. def test_no_init_after_finished(self):
  49. from _continuation import continulet, error
  50. #
  51. def empty_callback(c1):
  52. return 42
  53. #
  54. c = continulet(empty_callback)
  55. res = c.switch()
  56. assert res == 42
  57. raises(error, c.__init__, empty_callback)
  58. def test_propagate_exception(self):
  59. from _continuation import continulet
  60. #
  61. def empty_callback(c1):
  62. assert c1 is c
  63. seen.append(42)
  64. raise ValueError
  65. #
  66. seen = []
  67. c = continulet(empty_callback)
  68. raises(ValueError, c.switch)
  69. assert seen == [42]
  70. def test_callback_with_arguments(self):
  71. from _continuation import continulet
  72. #
  73. def empty_callback(c1, *args, **kwds):
  74. seen.append(c1)
  75. seen.append(args)
  76. seen.append(kwds)
  77. return 42
  78. #
  79. seen = []
  80. c = continulet(empty_callback, 42, 43, foo=44, bar=45)
  81. res = c.switch()
  82. assert res == 42
  83. assert seen == [c, (42, 43), {'foo': 44, 'bar': 45}]
  84. def test_switch(self):
  85. from _continuation import continulet
  86. #
  87. def switchbackonce_callback(c):
  88. seen.append(1)
  89. res = c.switch('a')
  90. assert res == 'b'
  91. seen.append(3)
  92. return 'c'
  93. #
  94. seen = []
  95. c = continulet(switchbackonce_callback)
  96. seen.append(0)
  97. res = c.switch()
  98. assert res == 'a'
  99. seen.append(2)
  100. res = c.switch('b')
  101. assert res == 'c'
  102. assert seen == [0, 1, 2, 3]
  103. def test_initial_switch_must_give_None(self):
  104. from _continuation import continulet
  105. #
  106. def empty_callback(c):
  107. return 'ok'
  108. #
  109. c = continulet(empty_callback)
  110. res = c.switch(None)
  111. assert res == 'ok'
  112. #
  113. c = continulet(empty_callback)
  114. raises(TypeError, c.switch, 'foo') # "can't send non-None value"
  115. def test_continuation_error(self):
  116. from _continuation import continulet, error
  117. #
  118. def empty_callback(c):
  119. return 42
  120. #
  121. c = continulet(empty_callback)
  122. c.switch()
  123. e = raises(error, c.switch)
  124. assert str(e.value) == "continulet already finished"
  125. def test_go_depth2(self):
  126. from _continuation import continulet
  127. #
  128. def depth2(c):
  129. seen.append(3)
  130. return 4
  131. #
  132. def depth1(c):
  133. seen.append(1)
  134. c2 = continulet(depth2)
  135. seen.append(2)
  136. res = c2.switch()
  137. seen.append(res)
  138. return 5
  139. #
  140. seen = []
  141. c = continulet(depth1)
  142. seen.append(0)
  143. res = c.switch()
  144. seen.append(res)
  145. assert seen == [0, 1, 2, 3, 4, 5]
  146. def test_exception_depth2(self):
  147. from _continuation import continulet
  148. #
  149. def depth2(c):
  150. seen.append(2)
  151. raise ValueError
  152. #
  153. def depth1(c):
  154. seen.append(1)
  155. try:
  156. continulet(depth2).switch()
  157. except ValueError:
  158. seen.append(3)
  159. return 4
  160. #
  161. seen = []
  162. c = continulet(depth1)
  163. res = c.switch()
  164. seen.append(res)
  165. assert seen == [1, 2, 3, 4]
  166. def test_exception_with_switch(self):
  167. from _continuation import continulet
  168. #
  169. def depth1(c):
  170. seen.append(1)
  171. c.switch()
  172. seen.append(3)
  173. raise ValueError
  174. #
  175. seen = []
  176. c = continulet(depth1)
  177. seen.append(0)
  178. c.switch()
  179. seen.append(2)
  180. raises(ValueError, c.switch)
  181. assert seen == [0, 1, 2, 3]
  182. def test_is_pending(self):
  183. from _continuation import continulet
  184. #
  185. def switchbackonce_callback(c):
  186. assert c.is_pending()
  187. res = c.switch('a')
  188. assert res == 'b'
  189. assert c.is_pending()
  190. return 'c'
  191. #
  192. c = continulet.__new__(continulet)
  193. assert not c.is_pending()
  194. c.__init__(switchbackonce_callback)
  195. assert c.is_pending()
  196. res = c.switch()
  197. assert res == 'a'
  198. assert c.is_pending()
  199. res = c.switch('b')
  200. assert res == 'c'
  201. assert not c.is_pending()
  202. def test_switch_alternate(self):
  203. from _continuation import continulet
  204. #
  205. def func_lower(c):
  206. res = c.switch('a')
  207. assert res == 'b'
  208. res = c.switch('c')
  209. assert res == 'd'
  210. return 'e'
  211. #
  212. def func_upper(c):
  213. res = c.switch('A')
  214. assert res == 'B'
  215. res = c.switch('C')
  216. assert res == 'D'
  217. return 'E'
  218. #
  219. c_lower = continulet(func_lower)
  220. c_upper = continulet(func_upper)
  221. res = c_lower.switch()
  222. assert res == 'a'
  223. res = c_upper.switch()
  224. assert res == 'A'
  225. res = c_lower.switch('b')
  226. assert res == 'c'
  227. res = c_upper.switch('B')
  228. assert res == 'C'
  229. res = c_lower.switch('d')
  230. assert res == 'e'
  231. res = c_upper.switch('D')
  232. assert res == 'E'
  233. def test_switch_not_initialized(self):
  234. from _continuation import continulet
  235. c0 = continulet.__new__(continulet)
  236. res = c0.switch()
  237. assert res is None
  238. res = c0.switch(123)
  239. assert res == 123
  240. raises(ValueError, c0.throw, ValueError)
  241. def test_exception_with_switch_depth2(self):
  242. from _continuation import continulet
  243. #
  244. def depth2(c):
  245. seen.append(4)
  246. c.switch()
  247. seen.append(6)
  248. raise ValueError
  249. #
  250. def depth1(c):
  251. seen.append(1)
  252. c.switch()
  253. seen.append(3)
  254. c2 = continulet(depth2)
  255. c2.switch()
  256. seen.append(5)
  257. raises(ValueError, c2.switch)
  258. assert not c2.is_pending()
  259. seen.append(7)
  260. assert c.is_pending()
  261. raise KeyError
  262. #
  263. seen = []
  264. c = continulet(depth1)
  265. c.switch()
  266. seen.append(2)
  267. raises(KeyError, c.switch)
  268. assert not c.is_pending()
  269. assert seen == [1, 2, 3, 4, 5, 6, 7]
  270. def test_random_switching(self):
  271. from _continuation import continulet
  272. #
  273. def t1(c1):
  274. return c1.switch()
  275. def s1(c1, n):
  276. assert n == 123
  277. c2 = t1(c1)
  278. return c1.switch('a') + 1
  279. #
  280. def s2(c2, c1):
  281. res = c1.switch(c2)
  282. assert res == 'a'
  283. return c2.switch('b') + 2
  284. #
  285. def f():
  286. c1 = continulet(s1, 123)
  287. c2 = continulet(s2, c1)
  288. c1.switch()
  289. res = c2.switch()
  290. assert res == 'b'
  291. res = c1.switch(1000)
  292. assert res == 1001
  293. return c2.switch(2000)
  294. #
  295. res = f()
  296. assert res == 2002
  297. def test_f_back(self):
  298. import sys
  299. from _continuation import continulet
  300. #
  301. def g(c):
  302. c.switch(sys._getframe(0))
  303. c.switch(sys._getframe(0).f_back)
  304. c.switch(sys._getframe(1))
  305. c.switch(sys._getframe(1).f_back)
  306. assert sys._getframe(2) is f3.f_back
  307. c.switch(sys._getframe(2))
  308. def f(c):
  309. g(c)
  310. #
  311. c = continulet(f)
  312. f1 = c.switch()
  313. assert f1.f_code.co_name == 'g'
  314. f2 = c.switch()
  315. assert f2.f_code.co_name == 'f'
  316. f3 = c.switch()
  317. assert f3 is f2
  318. assert f1.f_back is f3
  319. def main():
  320. f4 = c.switch()
  321. assert f4.f_code.co_name == 'main', repr(f4.f_code.co_name)
  322. assert f3.f_back is f1 # not running, so a loop
  323. def main2():
  324. f5 = c.switch()
  325. assert f5.f_code.co_name == 'main2', repr(f5.f_code.co_name)
  326. assert f3.f_back is f1 # not running, so a loop
  327. main()
  328. main2()
  329. res = c.switch()
  330. assert res is None
  331. assert f3.f_back is None
  332. def test_traceback_is_complete(self):
  333. import sys
  334. from _continuation import continulet
  335. #
  336. def g():
  337. raise KeyError
  338. def f(c):
  339. g()
  340. #
  341. def do(c):
  342. c.switch()
  343. #
  344. c = continulet(f)
  345. try:
  346. do(c)
  347. except KeyError:
  348. tb = sys.exc_info()[2]
  349. else:
  350. raise AssertionError("should have raised!")
  351. #
  352. assert tb.tb_next.tb_frame.f_code.co_name == 'do'
  353. assert tb.tb_next.tb_next.tb_frame.f_code.co_name == 'f'
  354. assert tb.tb_next.tb_next.tb_next.tb_frame.f_code.co_name == 'g'
  355. assert tb.tb_next.tb_next.tb_next.tb_next is None
  356. def test_switch2_simple(self):
  357. from _continuation import continulet
  358. #
  359. def f1(c1):
  360. res = c1.switch('started 1')
  361. assert res == 'a'
  362. res = c1.switch('b', to=c2)
  363. assert res == 'c'
  364. return 42
  365. def f2(c2):
  366. res = c2.switch('started 2')
  367. assert res == 'b'
  368. res = c2.switch('c', to=c1)
  369. not_reachable
  370. #
  371. c1 = continulet(f1)
  372. c2 = continulet(f2)
  373. res = c1.switch()
  374. assert res == 'started 1'
  375. res = c2.switch()
  376. assert res == 'started 2'
  377. res = c1.switch('a')
  378. assert res == 42
  379. def test_switch2_pingpong(self):
  380. from _continuation import continulet
  381. #
  382. def f1(c1):
  383. res = c1.switch('started 1')
  384. assert res == 'go'
  385. for i in range(10):
  386. res = c1.switch(i, to=c2)
  387. assert res == 100 + i
  388. return 42
  389. def f2(c2):
  390. res = c2.switch('started 2')
  391. for i in range(10):
  392. assert res == i
  393. res = c2.switch(100 + i, to=c1)
  394. not_reachable
  395. #
  396. c1 = continulet(f1)
  397. c2 = continulet(f2)
  398. res = c1.switch()
  399. assert res == 'started 1'
  400. res = c2.switch()
  401. assert res == 'started 2'
  402. res = c1.switch('go')
  403. assert res == 42
  404. def test_switch2_more_complex(self):
  405. from _continuation import continulet
  406. #
  407. def f1(c1):
  408. res = c1.switch(to=c2)
  409. assert res == 'a'
  410. res = c1.switch('b', to=c2)
  411. assert res == 'c'
  412. return 41
  413. def f2(c2):
  414. res = c2.switch('a', to=c1)
  415. assert res == 'b'
  416. return 42
  417. #
  418. c1 = continulet(f1)
  419. c2 = continulet(f2)
  420. res = c1.switch()
  421. assert res == 42
  422. assert not c2.is_pending() # finished by returning 42
  423. res = c1.switch('c')
  424. assert res == 41
  425. def test_switch2_no_op(self):
  426. from _continuation import continulet
  427. #
  428. def f1(c1):
  429. res = c1.switch('a', to=c1)
  430. assert res == 'a'
  431. return 42
  432. #
  433. c1 = continulet(f1)
  434. res = c1.switch()
  435. assert res == 42
  436. def test_switch2_immediately_away(self):
  437. from _continuation import continulet
  438. #
  439. def f1(c1):
  440. print 'in f1'
  441. return 'm'
  442. #
  443. def f2(c2):
  444. res = c2.switch('z')
  445. print 'got there!'
  446. assert res == 'a'
  447. return None
  448. #
  449. c1 = continulet(f1)
  450. c2 = continulet(f2)
  451. res = c2.switch()
  452. assert res == 'z'
  453. assert c1.is_pending()
  454. assert c2.is_pending()
  455. print 'calling!'
  456. res = c1.switch('a', to=c2)
  457. print 'back'
  458. assert res == 'm'
  459. def test_switch2_immediately_away_corner_case(self):
  460. from _continuation import continulet
  461. #
  462. def f1(c1):
  463. this_is_never_seen
  464. #
  465. def f2(c2):
  466. res = c2.switch('z')
  467. assert res is None
  468. return 'b' # this goes back into the caller, which is f1,
  469. # but f1 didn't start yet, so a None-value value
  470. # has nowhere to go to...
  471. c1 = continulet(f1)
  472. c2 = continulet(f2)
  473. res = c2.switch()
  474. assert res == 'z'
  475. raises(TypeError, c1.switch, to=c2) # "can't send non-None value"
  476. def test_switch2_not_initialized(self):
  477. from _continuation import continulet
  478. c0 = continulet.__new__(continulet)
  479. c0bis = continulet.__new__(continulet)
  480. res = c0.switch(123, to=c0)
  481. assert res == 123
  482. res = c0.switch(123, to=c0bis)
  483. assert res == 123
  484. raises(ValueError, c0.throw, ValueError, to=c0)
  485. raises(ValueError, c0.throw, ValueError, to=c0bis)
  486. #
  487. def f1(c1):
  488. c1.switch('a')
  489. raises(ValueError, c1.switch, 'b')
  490. raises(KeyError, c1.switch, 'c')
  491. return 'd'
  492. c1 = continulet(f1)
  493. res = c0.switch(to=c1)
  494. assert res == 'a'
  495. res = c1.switch(to=c0)
  496. assert res == 'b'
  497. res = c1.throw(ValueError, to=c0)
  498. assert res == 'c'
  499. res = c0.throw(KeyError, to=c1)
  500. assert res == 'd'
  501. def test_switch2_already_finished(self):
  502. from _continuation import continulet, error
  503. #
  504. def f1(c1):
  505. not_reachable
  506. def empty_callback(c):
  507. return 42
  508. #
  509. c1 = continulet(f1)
  510. c2 = continulet(empty_callback)
  511. c2.switch()
  512. e = raises(error, c1.switch, to=c2)
  513. assert str(e.value) == "continulet already finished"
  514. def test_throw(self):
  515. import sys
  516. from _continuation import continulet
  517. #
  518. def f1(c1):
  519. try:
  520. c1.switch()
  521. except KeyError:
  522. res = "got keyerror"
  523. try:
  524. c1.switch(res)
  525. except IndexError, e:
  526. pass
  527. try:
  528. c1.switch(e)
  529. except IndexError, e2:
  530. pass
  531. try:
  532. c1.switch(e2)
  533. except IndexError:
  534. c1.throw(*sys.exc_info())
  535. should_never_reach_here
  536. #
  537. c1 = continulet(f1)
  538. c1.switch()
  539. res = c1.throw(KeyError)
  540. assert res == "got keyerror"
  541. class FooError(IndexError):
  542. pass
  543. foo = FooError()
  544. res = c1.throw(foo)
  545. assert res is foo
  546. res = c1.throw(IndexError, foo)
  547. assert res is foo
  548. #
  549. def main():
  550. def do_raise():
  551. raise foo
  552. try:
  553. do_raise()
  554. except IndexError:
  555. tb = sys.exc_info()[2]
  556. try:
  557. c1.throw(IndexError, foo, tb)
  558. except IndexError:
  559. tb = sys.exc_info()[2]
  560. return tb
  561. #
  562. tb = main()
  563. assert tb.tb_frame.f_code.co_name == 'main'
  564. assert tb.tb_next.tb_frame.f_code.co_name == 'f1'
  565. assert tb.tb_next.tb_next.tb_frame.f_code.co_name == 'main'
  566. assert tb.tb_next.tb_next.tb_next.tb_frame.f_code.co_name == 'do_raise'
  567. assert tb.tb_next.tb_next.tb_next.tb_next is None
  568. def test_throw_to_starting(self):
  569. from _continuation import continulet
  570. #
  571. def f1(c1):
  572. not_reached
  573. #
  574. c1 = continulet(f1)
  575. raises(IndexError, c1.throw, IndexError)
  576. def test_throw2_simple(self):
  577. from _continuation import continulet
  578. #
  579. def f1(c1):
  580. not_reached
  581. def f2(c2):
  582. try:
  583. c2.switch("ready")
  584. except IndexError:
  585. raise ValueError
  586. #
  587. c1 = continulet(f1)
  588. c2 = continulet(f2)
  589. res = c2.switch()
  590. assert res == "ready"
  591. assert c1.is_pending()
  592. assert c2.is_pending()
  593. raises(ValueError, c1.throw, IndexError, to=c2)
  594. assert not c1.is_pending()
  595. assert not c2.is_pending()
  596. def test_throw2_no_op(self):
  597. from _continuation import continulet
  598. #
  599. def f1(c1):
  600. raises(ValueError, c1.throw, ValueError, to=c1)
  601. return "ok"
  602. #
  603. c1 = continulet(f1)
  604. res = c1.switch()
  605. assert res == "ok"
  606. def test_permute(self):
  607. import sys
  608. from _continuation import continulet, permute
  609. #
  610. def f1(c1):
  611. res = c1.switch()
  612. assert res == "ok"
  613. return "done"
  614. #
  615. def f2(c2):
  616. assert sys._getframe(1).f_code.co_name == 'main'
  617. permute(c1, c2)
  618. assert sys._getframe(1).f_code.co_name == 'f1'
  619. return "ok"
  620. #
  621. c1 = continulet(f1)
  622. c2 = continulet(f2)
  623. def main():
  624. c1.switch()
  625. res = c2.switch()
  626. assert res == "done"
  627. main()
  628. def test_permute_noninitialized(self):
  629. from _continuation import continulet, permute
  630. permute(continulet.__new__(continulet)) # ignored
  631. permute(continulet.__new__(continulet), # ignored
  632. continulet.__new__(continulet))
  633. def test_bug_finish_with_already_finished_stacklet(self):
  634. from _continuation import continulet, error
  635. # make an already-finished continulet
  636. c1 = continulet(lambda x: x)
  637. c1.switch()
  638. # make another continulet
  639. c2 = continulet(lambda x: x)
  640. # this switch is forbidden, because it causes a crash when c2 finishes
  641. raises(error, c1.switch, to=c2)
  642. def test_various_depths(self):
  643. skip("may fail on top of CPython")
  644. # run it from test_translated, but not while being actually translated
  645. d = {}
  646. execfile(self.translated, d)
  647. d['set_fast_mode']()
  648. d['test_various_depths']()