test_adapter.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. """Tests for adapting Jupyter msg spec versions"""
  2. # Copyright (c) Jupyter Development Team.
  3. # Distributed under the terms of the Modified BSD License.
  4. import copy
  5. import json
  6. from unittest import TestCase
  7. from jupyter_client.adapter import adapt, V4toV5, V5toV4, code_to_line
  8. from jupyter_client.session import Session
  9. def test_default_version():
  10. s = Session()
  11. msg = s.msg("msg_type")
  12. msg['header'].pop('version')
  13. original = copy.deepcopy(msg)
  14. adapted = adapt(original)
  15. assert adapted['header']['version'] == V4toV5.version
  16. def test_code_to_line_no_code():
  17. line, pos = code_to_line("", 0)
  18. assert line == ""
  19. assert pos == 0
  20. class AdapterTest(TestCase):
  21. def setUp(self):
  22. self.session = Session()
  23. def adapt(self, msg, version=None):
  24. original = copy.deepcopy(msg)
  25. adapted = adapt(msg, version or self.to_version)
  26. return original, adapted
  27. def check_header(self, msg):
  28. pass
  29. class V4toV5TestCase(AdapterTest):
  30. from_version = 4
  31. to_version = 5
  32. def msg(self, msg_type, content):
  33. """Create a v4 msg (same as v5, minus version header)"""
  34. msg = self.session.msg(msg_type, content)
  35. msg['header'].pop('version')
  36. return msg
  37. def test_same_version(self):
  38. msg = self.msg("execute_result",
  39. content={'status' : 'ok'}
  40. )
  41. original, adapted = self.adapt(msg, self.from_version)
  42. self.assertEqual(original, adapted)
  43. def test_no_adapt(self):
  44. msg = self.msg("input_reply", {'value' : 'some text'})
  45. v4, v5 = self.adapt(msg)
  46. self.assertEqual(v5['header']['version'], V4toV5.version)
  47. v5['header'].pop('version')
  48. self.assertEqual(v4, v5)
  49. def test_rename_type(self):
  50. for v5_type, v4_type in [
  51. ('execute_result', 'pyout'),
  52. ('execute_input', 'pyin'),
  53. ('error', 'pyerr'),
  54. ]:
  55. msg = self.msg(v4_type, {'key' : 'value'})
  56. v4, v5 = self.adapt(msg)
  57. self.assertEqual(v5['header']['version'], V4toV5.version)
  58. self.assertEqual(v5['header']['msg_type'], v5_type)
  59. self.assertEqual(v4['content'], v5['content'])
  60. def test_execute_request(self):
  61. msg = self.msg("execute_request", {
  62. 'code' : 'a=5',
  63. 'silent' : False,
  64. 'user_expressions' : {'a' : 'apple'},
  65. 'user_variables' : ['b'],
  66. })
  67. v4, v5 = self.adapt(msg)
  68. self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
  69. v4c = v4['content']
  70. v5c = v5['content']
  71. self.assertEqual(v5c['user_expressions'], {'a' : 'apple', 'b': 'b'})
  72. self.assertNotIn('user_variables', v5c)
  73. self.assertEqual(v5c['code'], v4c['code'])
  74. def test_execute_reply(self):
  75. msg = self.msg("execute_reply", {
  76. 'status': 'ok',
  77. 'execution_count': 7,
  78. 'user_variables': {'a': 1},
  79. 'user_expressions': {'a+a': 2},
  80. 'payload': [{'source':'page', 'text':'blah'}]
  81. })
  82. v4, v5 = self.adapt(msg)
  83. v5c = v5['content']
  84. self.assertNotIn('user_variables', v5c)
  85. self.assertEqual(v5c['user_expressions'], {'a': 1, 'a+a': 2})
  86. self.assertEqual(v5c['payload'], [{'source': 'page',
  87. 'data': {'text/plain': 'blah'}}
  88. ])
  89. def test_complete_request(self):
  90. msg = self.msg("complete_request", {
  91. 'text' : 'a.is',
  92. 'line' : 'foo = a.is',
  93. 'block' : None,
  94. 'cursor_pos' : 10,
  95. })
  96. v4, v5 = self.adapt(msg)
  97. v4c = v4['content']
  98. v5c = v5['content']
  99. for key in ('text', 'line', 'block'):
  100. self.assertNotIn(key, v5c)
  101. self.assertEqual(v5c['cursor_pos'], v4c['cursor_pos'])
  102. self.assertEqual(v5c['code'], v4c['line'])
  103. def test_complete_reply(self):
  104. msg = self.msg("complete_reply", {
  105. 'matched_text' : 'a.is',
  106. 'matches' : ['a.isalnum',
  107. 'a.isalpha',
  108. 'a.isdigit',
  109. 'a.islower',
  110. ],
  111. })
  112. v4, v5 = self.adapt(msg)
  113. v4c = v4['content']
  114. v5c = v5['content']
  115. self.assertEqual(v5c['matches'], v4c['matches'])
  116. self.assertEqual(v5c['metadata'], {})
  117. self.assertEqual(v5c['cursor_start'], -4)
  118. self.assertEqual(v5c['cursor_end'], None)
  119. def test_object_info_request(self):
  120. msg = self.msg("object_info_request", {
  121. 'oname' : 'foo',
  122. 'detail_level' : 1,
  123. })
  124. v4, v5 = self.adapt(msg)
  125. self.assertEqual(v5['header']['msg_type'], 'inspect_request')
  126. v4c = v4['content']
  127. v5c = v5['content']
  128. self.assertEqual(v5c['code'], v4c['oname'])
  129. self.assertEqual(v5c['cursor_pos'], len(v4c['oname']))
  130. self.assertEqual(v5c['detail_level'], v4c['detail_level'])
  131. def test_object_info_reply(self):
  132. msg = self.msg("object_info_reply", {
  133. 'name' : 'foo',
  134. 'found' : True,
  135. 'status' : 'ok',
  136. 'definition' : 'foo(a=5)',
  137. 'docstring' : "the docstring",
  138. })
  139. v4, v5 = self.adapt(msg)
  140. self.assertEqual(v5['header']['msg_type'], 'inspect_reply')
  141. v4c = v4['content']
  142. v5c = v5['content']
  143. self.assertEqual(sorted(v5c), [ 'data', 'found', 'metadata', 'status'])
  144. text = v5c['data']['text/plain']
  145. self.assertEqual(text, '\n'.join([v4c['definition'], v4c['docstring']]))
  146. def test_object_info_reply_not_found(self):
  147. msg = self.msg("object_info_reply", {
  148. 'name' : 'foo',
  149. 'found' : False,
  150. })
  151. v4, v5 = self.adapt(msg)
  152. self.assertEqual(v5['header']['msg_type'], 'inspect_reply')
  153. v4c = v4['content']
  154. v5c = v5['content']
  155. self.assertEqual(v5c, {
  156. 'status': 'ok',
  157. 'found': False,
  158. 'data': {},
  159. 'metadata': {},
  160. })
  161. def test_kernel_info_reply(self):
  162. msg = self.msg("kernel_info_reply", {
  163. 'language': 'python',
  164. 'language_version': [2,8,0],
  165. 'ipython_version': [1,2,3],
  166. })
  167. v4, v5 = self.adapt(msg)
  168. v4c = v4['content']
  169. v5c = v5['content']
  170. self.assertEqual(v5c, {
  171. 'protocol_version': '4.1',
  172. 'implementation': 'ipython',
  173. 'implementation_version': '1.2.3',
  174. 'language_info': {
  175. 'name': 'python',
  176. 'version': '2.8.0',
  177. },
  178. 'banner' : '',
  179. })
  180. # iopub channel
  181. def test_display_data(self):
  182. jsondata = dict(a=5)
  183. msg = self.msg("display_data", {
  184. 'data' : {
  185. 'text/plain' : 'some text',
  186. 'application/json' : json.dumps(jsondata)
  187. },
  188. 'metadata' : {'text/plain' : { 'key' : 'value' }},
  189. })
  190. v4, v5 = self.adapt(msg)
  191. v4c = v4['content']
  192. v5c = v5['content']
  193. self.assertEqual(v5c['metadata'], v4c['metadata'])
  194. self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
  195. self.assertEqual(v5c['data']['application/json'], jsondata)
  196. # stdin channel
  197. def test_input_request(self):
  198. msg = self.msg('input_request', {'prompt': "$>"})
  199. v4, v5 = self.adapt(msg)
  200. self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
  201. self.assertFalse(v5['content']['password'])
  202. class V5toV4TestCase(AdapterTest):
  203. from_version = 5
  204. to_version = 4
  205. def msg(self, msg_type, content):
  206. return self.session.msg(msg_type, content)
  207. def test_same_version(self):
  208. msg = self.msg("execute_result",
  209. content={'status' : 'ok'}
  210. )
  211. original, adapted = self.adapt(msg, self.from_version)
  212. self.assertEqual(original, adapted)
  213. def test_no_adapt(self):
  214. msg = self.msg("input_reply", {'value' : 'some text'})
  215. v5, v4 = self.adapt(msg)
  216. self.assertNotIn('version', v4['header'])
  217. v5['header'].pop('version')
  218. self.assertEqual(v4, v5)
  219. def test_rename_type(self):
  220. for v5_type, v4_type in [
  221. ('execute_result', 'pyout'),
  222. ('execute_input', 'pyin'),
  223. ('error', 'pyerr'),
  224. ]:
  225. msg = self.msg(v5_type, {'key' : 'value'})
  226. v5, v4 = self.adapt(msg)
  227. self.assertEqual(v4['header']['msg_type'], v4_type)
  228. assert 'version' not in v4['header']
  229. self.assertEqual(v4['content'], v5['content'])
  230. def test_execute_request(self):
  231. msg = self.msg("execute_request", {
  232. 'code' : 'a=5',
  233. 'silent' : False,
  234. 'user_expressions' : {'a' : 'apple'},
  235. })
  236. v5, v4 = self.adapt(msg)
  237. self.assertEqual(v4['header']['msg_type'], v5['header']['msg_type'])
  238. v4c = v4['content']
  239. v5c = v5['content']
  240. self.assertEqual(v4c['user_variables'], [])
  241. self.assertEqual(v5c['code'], v4c['code'])
  242. def test_complete_request(self):
  243. msg = self.msg("complete_request", {
  244. 'code' : 'def foo():\n'
  245. ' a.is\n'
  246. 'foo()',
  247. 'cursor_pos': 19,
  248. })
  249. v5, v4 = self.adapt(msg)
  250. v4c = v4['content']
  251. v5c = v5['content']
  252. self.assertNotIn('code', v4c)
  253. self.assertEqual(v4c['line'], v5c['code'].splitlines(True)[1])
  254. self.assertEqual(v4c['cursor_pos'], 8)
  255. self.assertEqual(v4c['text'], '')
  256. self.assertEqual(v4c['block'], None)
  257. def test_complete_reply(self):
  258. msg = self.msg("complete_reply", {
  259. 'cursor_start' : 10,
  260. 'cursor_end' : 14,
  261. 'matches' : ['a.isalnum',
  262. 'a.isalpha',
  263. 'a.isdigit',
  264. 'a.islower',
  265. ],
  266. 'metadata' : {},
  267. })
  268. v5, v4 = self.adapt(msg)
  269. v4c = v4['content']
  270. v5c = v5['content']
  271. self.assertEqual(v4c['matched_text'], 'a.is')
  272. self.assertEqual(v4c['matches'], v5c['matches'])
  273. def test_inspect_request(self):
  274. msg = self.msg("inspect_request", {
  275. 'code' : 'def foo():\n'
  276. ' apple\n'
  277. 'bar()',
  278. 'cursor_pos': 18,
  279. 'detail_level' : 1,
  280. })
  281. v5, v4 = self.adapt(msg)
  282. self.assertEqual(v4['header']['msg_type'], 'object_info_request')
  283. v4c = v4['content']
  284. v5c = v5['content']
  285. self.assertEqual(v4c['oname'], 'apple')
  286. self.assertEqual(v5c['detail_level'], v4c['detail_level'])
  287. def test_inspect_request_token(self):
  288. line = 'something(range(10), kwarg=smth) ; xxx.xxx.xxx( firstarg, rand(234,23), kwarg1=2,'
  289. msg = self.msg("inspect_request", {
  290. 'code' : line,
  291. 'cursor_pos': len(line)-1,
  292. 'detail_level' : 1,
  293. })
  294. v5, v4 = self.adapt(msg)
  295. self.assertEqual(v4['header']['msg_type'], 'object_info_request')
  296. v4c = v4['content']
  297. v5c = v5['content']
  298. self.assertEqual(v4c['oname'], 'xxx.xxx.xxx')
  299. self.assertEqual(v5c['detail_level'], v4c['detail_level'])
  300. def test_inspect_reply(self):
  301. msg = self.msg("inspect_reply", {
  302. 'name' : 'foo',
  303. 'found' : True,
  304. 'data' : {'text/plain' : 'some text'},
  305. 'metadata' : {},
  306. })
  307. v5, v4 = self.adapt(msg)
  308. self.assertEqual(v4['header']['msg_type'], 'object_info_reply')
  309. v4c = v4['content']
  310. v5c = v5['content']
  311. self.assertEqual(sorted(v4c), ['found', 'oname'])
  312. self.assertEqual(v4c['found'], False)
  313. def test_kernel_info_reply(self):
  314. msg = self.msg("kernel_info_reply", {
  315. 'protocol_version': '5.0',
  316. 'implementation': 'ipython',
  317. 'implementation_version': '1.2.3',
  318. 'language_info': {
  319. 'name': 'python',
  320. 'version': '2.8.0',
  321. 'mimetype': 'text/x-python',
  322. },
  323. 'banner' : 'the banner',
  324. })
  325. v5, v4 = self.adapt(msg)
  326. v4c = v4['content']
  327. v5c = v5['content']
  328. info = v5c['language_info']
  329. self.assertEqual(v4c, {
  330. 'protocol_version': [5,0],
  331. 'language': 'python',
  332. 'language_version': [2,8,0],
  333. 'ipython_version': [1,2,3],
  334. })
  335. # iopub channel
  336. def test_display_data(self):
  337. jsondata = dict(a=5)
  338. msg = self.msg("display_data", {
  339. 'data' : {
  340. 'text/plain' : 'some text',
  341. 'application/json' : jsondata,
  342. },
  343. 'metadata' : {'text/plain' : { 'key' : 'value' }},
  344. })
  345. v5, v4 = self.adapt(msg)
  346. v4c = v4['content']
  347. v5c = v5['content']
  348. self.assertEqual(v5c['metadata'], v4c['metadata'])
  349. self.assertEqual(v5c['data']['text/plain'], v4c['data']['text/plain'])
  350. self.assertEqual(v4c['data']['application/json'], json.dumps(jsondata))
  351. # stdin channel
  352. def test_input_request(self):
  353. msg = self.msg('input_request', {'prompt': "$>", 'password' : True})
  354. v5, v4 = self.adapt(msg)
  355. self.assertEqual(v5['content']['prompt'], v4['content']['prompt'])
  356. self.assertNotIn('password', v4['content'])