jupyterlab-codex-sidebar 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (153) hide show
  1. package/.claude/settings.local.json +9 -0
  2. package/.github/workflows/unit-tests.yml +27 -0
  3. package/.jupyterlab-playwright.log +0 -0
  4. package/README.md +83 -9
  5. package/docs/images/codex-sidebar-screenshot.png +0 -0
  6. package/jupyterlab_codex/handlers.py +938 -297
  7. package/jupyterlab_codex/labextension/package.json +13 -3
  8. package/jupyterlab_codex/labextension/static/525.224526d045c727069de6.js +2 -0
  9. package/jupyterlab_codex/labextension/static/737.e7de3ad9dd6ded798340.js +1 -0
  10. package/jupyterlab_codex/labextension/static/remoteEntry.6ef5e7167763a316c000.js +1 -0
  11. package/jupyterlab_codex/protocol.py +297 -0
  12. package/jupyterlab_codex/runner.py +58 -15
  13. package/jupyterlab_codex/sessions.py +582 -97
  14. package/lib/codexChat.d.ts +13 -0
  15. package/lib/codexChat.js +2506 -0
  16. package/lib/codexChat.js.map +1 -0
  17. package/lib/codexChatAttachmentDedup.d.ts +10 -0
  18. package/lib/codexChatAttachmentDedup.js +35 -0
  19. package/lib/codexChatAttachmentDedup.js.map +1 -0
  20. package/lib/codexChatAttachmentLimit.d.ts +18 -0
  21. package/lib/codexChatAttachmentLimit.js +50 -0
  22. package/lib/codexChatAttachmentLimit.js.map +1 -0
  23. package/lib/codexChatAttachmentState.d.ts +15 -0
  24. package/lib/codexChatAttachmentState.js +16 -0
  25. package/lib/codexChatAttachmentState.js.map +1 -0
  26. package/lib/codexChatDocumentUtils.d.ts +70 -0
  27. package/lib/codexChatDocumentUtils.js +506 -0
  28. package/lib/codexChatDocumentUtils.js.map +1 -0
  29. package/lib/codexChatFormatting.d.ts +11 -0
  30. package/lib/codexChatFormatting.js +83 -0
  31. package/lib/codexChatFormatting.js.map +1 -0
  32. package/lib/codexChatNotice.d.ts +3 -0
  33. package/lib/codexChatNotice.js +74 -0
  34. package/lib/codexChatNotice.js.map +1 -0
  35. package/lib/codexChatPersistence.d.ts +35 -0
  36. package/lib/codexChatPersistence.js +158 -0
  37. package/lib/codexChatPersistence.js.map +1 -0
  38. package/lib/codexChatPrimitives.d.ts +44 -0
  39. package/lib/codexChatPrimitives.js +156 -0
  40. package/lib/codexChatPrimitives.js.map +1 -0
  41. package/lib/codexChatRender.d.ts +24 -0
  42. package/lib/codexChatRender.js +293 -0
  43. package/lib/codexChatRender.js.map +1 -0
  44. package/lib/codexChatSessionFactory.d.ts +15 -0
  45. package/lib/codexChatSessionFactory.js +45 -0
  46. package/lib/codexChatSessionFactory.js.map +1 -0
  47. package/lib/codexChatSessionKey.d.ts +3 -0
  48. package/lib/codexChatSessionKey.js +14 -0
  49. package/lib/codexChatSessionKey.js.map +1 -0
  50. package/lib/codexChatStorage.d.ts +4 -0
  51. package/lib/codexChatStorage.js +37 -0
  52. package/lib/codexChatStorage.js.map +1 -0
  53. package/lib/codexSessionResolver.d.ts +12 -0
  54. package/lib/codexSessionResolver.js +38 -0
  55. package/lib/codexSessionResolver.js.map +1 -0
  56. package/lib/handlers/activitySummarizer.d.ts +15 -0
  57. package/lib/handlers/activitySummarizer.js +327 -0
  58. package/lib/handlers/activitySummarizer.js.map +1 -0
  59. package/lib/handlers/codexMessageTypes.d.ts +30 -0
  60. package/lib/handlers/codexMessageTypes.js +2 -0
  61. package/lib/handlers/codexMessageTypes.js.map +1 -0
  62. package/lib/handlers/codexMessageUtils.d.ts +46 -0
  63. package/lib/handlers/codexMessageUtils.js +144 -0
  64. package/lib/handlers/codexMessageUtils.js.map +1 -0
  65. package/lib/handlers/handleCodexSocketMessage.d.ts +107 -0
  66. package/lib/handlers/handleCodexSocketMessage.js +78 -0
  67. package/lib/handlers/handleCodexSocketMessage.js.map +1 -0
  68. package/lib/handlers/sessionSyncHandler.d.ts +34 -0
  69. package/lib/handlers/sessionSyncHandler.js +181 -0
  70. package/lib/handlers/sessionSyncHandler.js.map +1 -0
  71. package/lib/hooks/useCodexSocket.d.ts +15 -0
  72. package/lib/hooks/useCodexSocket.js +84 -0
  73. package/lib/hooks/useCodexSocket.js.map +1 -0
  74. package/lib/index.js +1 -1
  75. package/lib/index.js.map +1 -1
  76. package/lib/panel.d.ts +1 -11
  77. package/lib/panel.js +1 -2815
  78. package/lib/panel.js.map +1 -1
  79. package/lib/protocol.d.ts +235 -0
  80. package/lib/protocol.js +278 -0
  81. package/lib/protocol.js.map +1 -0
  82. package/package.json +13 -3
  83. package/playwright.config.cjs +27 -0
  84. package/playwright.unit.config.cjs +19 -0
  85. package/pyproject.toml +1 -1
  86. package/release.sh +52 -14
  87. package/scripts/run_playwright_e2e.sh +96 -0
  88. package/scripts/run_playwright_freeze_repro.sh +58 -0
  89. package/scripts/run_playwright_queue_repro.sh +60 -0
  90. package/scripts/run_playwright_repro.sh +55 -0
  91. package/src/codexChat.tsx +3914 -0
  92. package/src/codexChatAttachmentDedup.ts +47 -0
  93. package/src/codexChatAttachmentLimit.ts +81 -0
  94. package/src/codexChatAttachmentState.ts +37 -0
  95. package/src/codexChatDocumentUtils.ts +644 -0
  96. package/src/codexChatFormatting.ts +94 -0
  97. package/src/codexChatNotice.ts +95 -0
  98. package/src/codexChatPersistence.ts +191 -0
  99. package/src/codexChatPrimitives.tsx +446 -0
  100. package/src/codexChatRender.tsx +376 -0
  101. package/src/codexChatSessionFactory.ts +79 -0
  102. package/src/codexChatSessionKey.ts +16 -0
  103. package/src/codexChatStorage.ts +36 -0
  104. package/src/codexSessionResolver.ts +56 -0
  105. package/src/handlers/activitySummarizer.ts +369 -0
  106. package/src/handlers/codexMessageTypes.ts +34 -0
  107. package/src/handlers/codexMessageUtils.ts +217 -0
  108. package/src/handlers/handleCodexSocketMessage.ts +204 -0
  109. package/src/handlers/sessionSyncHandler.ts +308 -0
  110. package/src/hooks/useCodexSocket.ts +109 -0
  111. package/src/index.ts +1 -1
  112. package/src/panel.tsx +1 -4184
  113. package/src/protocol.ts +582 -0
  114. package/style/index.css +480 -11
  115. package/test-results/.last-run.json +4 -0
  116. package/test.py +0 -0
  117. package/tests/e2e/cell-output-error-tail.spec.js +156 -0
  118. package/tests/e2e/codex-ui-test-helpers.js +138 -0
  119. package/tests/e2e/fixtures/notebooks/error-output-tail.ipynb +58 -0
  120. package/tests/e2e/fixtures/notebooks/error-output-tail.py +19 -0
  121. package/tests/e2e/fixtures/notebooks/tab1.ipynb +322 -0
  122. package/tests/e2e/fixtures/notebooks/tab1.py +272 -0
  123. package/tests/e2e/fixtures/notebooks/tab2.ipynb +252 -0
  124. package/tests/e2e/fixtures/notebooks/tab2.py +231 -0
  125. package/tests/e2e/fixtures/notebooks/tab3.ipynb +403 -0
  126. package/tests/e2e/fixtures/notebooks/tab3.py +331 -0
  127. package/tests/e2e/fixtures/notebooks/tab4.py +339 -0
  128. package/tests/e2e/freeze-notebook-tabs-repro.spec.js +295 -0
  129. package/tests/e2e/mock-codex-cli-flood.py +127 -0
  130. package/tests/e2e/mock-codex-cli-prompt-echo.py +88 -0
  131. package/tests/e2e/mock-codex-cli.py +95 -0
  132. package/tests/e2e/queue-multitab-repro.spec.js +189 -0
  133. package/tests/test_handlers.py +116 -0
  134. package/tests/test_protocol.py +169 -0
  135. package/tests/test_session_store_limits.py +50 -0
  136. package/tests/unit/codexChatAttachmentDedup.spec.ts +56 -0
  137. package/tests/unit/codexChatAttachmentLimit.spec.ts +57 -0
  138. package/tests/unit/codexChatAttachmentState.spec.ts +71 -0
  139. package/tests/unit/codexChatDocumentUtils.spec.ts +63 -0
  140. package/tests/unit/codexChatLimit.spec.ts +18 -0
  141. package/tests/unit/codexChatNotice.spec.ts +45 -0
  142. package/tests/unit/codexChatPersistence.spec.ts +199 -0
  143. package/tests/unit/codexChatSessionFactory.spec.ts +94 -0
  144. package/tests/unit/codexChatSessionKey.spec.ts +18 -0
  145. package/tests/unit/codexMessageUtils.spec.ts +89 -0
  146. package/tests/unit/codexSessionResolver.spec.ts +92 -0
  147. package/tests/unit/handleCodexSocketMessage.spec.ts +476 -0
  148. package/tsconfig.tsbuildinfo +1 -1
  149. package/webpack.config.js +6 -0
  150. package/jupyterlab_codex/labextension/static/504.335f3447c84ba3d74517.js +0 -2
  151. package/jupyterlab_codex/labextension/static/972.8e856719e40acc1ef4cb.js +0 -1
  152. package/jupyterlab_codex/labextension/static/remoteEntry.a2982f776a1f0f515640.js +0 -1
  153. /package/jupyterlab_codex/labextension/static/{504.335f3447c84ba3d74517.js.LICENSE.txt → 525.224526d045c727069de6.js.LICENSE.txt} +0 -0
@@ -0,0 +1,156 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const { test, expect } = require('@playwright/test');
4
+
5
+ const NOTEBOOK_RELATIVE_PATH = 'tests/e2e/fixtures/notebooks/error-output-tail.ipynb';
6
+ const NOTEBOOK_NAME = 'error-output-tail.ipynb';
7
+ const NOTEBOOK_PY_PATH = path.resolve(__dirname, 'fixtures/notebooks/error-output-tail.py');
8
+ const NOTEBOOK_IPYNB_PATH = path.resolve(__dirname, 'fixtures/notebooks/error-output-tail.ipynb');
9
+ const TRACE_HEAD_MARKER = 'TRACE_HEAD_MARKER_ABCDEFGHIJ';
10
+ const TRACE_TAIL_MARKER = 'TRACE_TAIL_MARKER_ZYXWVUTSRQ';
11
+
12
+ function buildInitialNotebookUrl(baseLabUrl, notebookPath) {
13
+ const parsed = new URL(baseLabUrl);
14
+ const basePath = parsed.pathname.replace(/\/$/, '');
15
+ parsed.pathname = `${basePath}/tree/${notebookPath}`;
16
+ parsed.searchParams.set('reset', '1');
17
+ return parsed.toString();
18
+ }
19
+
20
+ async function dismissBlockingDialogs(page) {
21
+ for (let attempt = 0; attempt < 8; attempt += 1) {
22
+ const dialog = page.locator('dialog.jp-Dialog, [role="dialog"].jp-Dialog, [role="dialog"]').first();
23
+ if (!(await dialog.count()) || !(await dialog.isVisible())) {
24
+ return;
25
+ }
26
+
27
+ const noKernelButton = page.getByRole('button', { name: /^No Kernel$/ }).first();
28
+ if ((await noKernelButton.count()) > 0 && (await noKernelButton.isVisible())) {
29
+ await noKernelButton.click();
30
+ await page.waitForTimeout(150);
31
+ continue;
32
+ }
33
+
34
+ await page.keyboard.press('Escape');
35
+ await page.waitForTimeout(150);
36
+ }
37
+ }
38
+
39
+ async function ensureCodexPanel(page) {
40
+ await dismissBlockingDialogs(page);
41
+ const composer = page.locator('.jp-CodexPanel .jp-CodexComposer textarea');
42
+ if ((await composer.count()) > 0 && (await composer.first().isVisible())) {
43
+ return;
44
+ }
45
+
46
+ const codexSideTab = page
47
+ .locator('.jp-SideBar.jp-mod-right .lm-TabBar-tab[title="Codex"], .jp-SideBar.jp-mod-right .lm-TabBar-tab')
48
+ .filter({ hasText: 'Codex' })
49
+ .first();
50
+ await expect(codexSideTab).toBeVisible({ timeout: 20000 });
51
+ await codexSideTab.click();
52
+ await expect(composer.first()).toBeVisible({ timeout: 20000 });
53
+ }
54
+
55
+ function writeNotebookFixture() {
56
+ const traceback = [
57
+ 'Traceback (most recent call last):',
58
+ ` ${TRACE_HEAD_MARKER}`,
59
+ `${'A'.repeat(22000)}`,
60
+ `ValueError: ${TRACE_TAIL_MARKER}`
61
+ ];
62
+ const notebook = {
63
+ cells: [
64
+ {
65
+ cell_type: 'code',
66
+ execution_count: 1,
67
+ id: 'error-output-tail-cell',
68
+ metadata: {},
69
+ outputs: [
70
+ {
71
+ output_type: 'error',
72
+ ename: 'ValueError',
73
+ evalue: TRACE_TAIL_MARKER,
74
+ traceback
75
+ }
76
+ ],
77
+ source: ['raise ValueError("synthetic traceback for tail truncation test")\n']
78
+ }
79
+ ],
80
+ metadata: {
81
+ jupytext: {
82
+ formats: 'ipynb,py:percent'
83
+ },
84
+ kernelspec: {
85
+ display_name: 'Python 3 (ipykernel)',
86
+ language: 'python',
87
+ name: 'python3'
88
+ },
89
+ language_info: {
90
+ codemirror_mode: {
91
+ name: 'ipython',
92
+ version: 3
93
+ },
94
+ file_extension: '.py',
95
+ mimetype: 'text/x-python',
96
+ name: 'python',
97
+ nbconvert_exporter: 'python',
98
+ pygments_lexer: 'ipython3',
99
+ version: '3.13.9'
100
+ }
101
+ },
102
+ nbformat: 4,
103
+ nbformat_minor: 5
104
+ };
105
+
106
+ fs.writeFileSync(NOTEBOOK_IPYNB_PATH, `${JSON.stringify(notebook, null, 2)}\n`, 'utf8');
107
+ fs.writeFileSync(
108
+ NOTEBOOK_PY_PATH,
109
+ [
110
+ '# %%',
111
+ 'raise ValueError("synthetic traceback for tail truncation test")',
112
+ ''
113
+ ].join('\n'),
114
+ 'utf8'
115
+ );
116
+ }
117
+
118
+ test('error cell output sends the tail of the traceback to Codex', async ({ page, baseURL }) => {
119
+ writeNotebookFixture();
120
+ const codexCommandPath =
121
+ process.env.PLAYWRIGHT_CODEX_COMMAND || path.resolve(__dirname, 'mock-codex-cli-prompt-echo.py');
122
+ const targetUrl = baseURL || process.env.JUPYTERLAB_URL || 'http://127.0.0.1:8888/lab';
123
+
124
+ await page.addInitScript(commandPath => {
125
+ window.localStorage.setItem('jupyterlab-codex:command-path', commandPath);
126
+ window.localStorage.setItem('jupyterlab-codex:include-active-cell', 'true');
127
+ window.localStorage.setItem('jupyterlab-codex:include-active-cell-output', 'true');
128
+ }, codexCommandPath);
129
+
130
+ await page.goto(buildInitialNotebookUrl(targetUrl, NOTEBOOK_RELATIVE_PATH), { waitUntil: 'domcontentloaded' });
131
+ await page.waitForSelector('main[aria-label="Main Content"], .jp-LabShell, .lm-DockPanel', {
132
+ timeout: 30000
133
+ });
134
+ await dismissBlockingDialogs(page);
135
+ await ensureCodexPanel(page);
136
+
137
+ const notebookCell = page.locator('.jp-Notebook .jp-Cell').first();
138
+ await expect(notebookCell).toBeVisible({ timeout: 20000 });
139
+ await notebookCell.click();
140
+
141
+ const composer = page.locator('.jp-CodexComposer textarea');
142
+ const sendBtn = page.locator('.jp-CodexSendBtn');
143
+ await composer.fill('Inspect the attached output.');
144
+ await expect(sendBtn).toBeEnabled({ timeout: 15000 });
145
+ await sendBtn.click();
146
+ await expect(page.locator('.jp-CodexSendBtn.is-stop')).toHaveCount(0, { timeout: 30000 });
147
+
148
+ const assistantMessage = page.locator('.jp-CodexChat-message.jp-mod-assistant').last();
149
+ await expect(assistantMessage).toContainText('PROMPT_TAIL_START', { timeout: 30000 });
150
+ const responseText = await assistantMessage.innerText();
151
+
152
+ const tailSection = responseText.split('PROMPT_TAIL_START\n')[1]?.split('\nPROMPT_TAIL_END')[0] || '';
153
+ expect(tailSection).toContain(TRACE_TAIL_MARKER);
154
+ expect(tailSection).not.toContain(TRACE_HEAD_MARKER);
155
+ expect(responseText).toContain('Current Cell Output:');
156
+ });
@@ -0,0 +1,138 @@
1
+ const path = require('node:path');
2
+
3
+ function buildNotebookUrl(baseLabUrl, notebookPath) {
4
+ const parsed = new URL(baseLabUrl);
5
+ const basePath = parsed.pathname.replace(/\/$/, '');
6
+ parsed.pathname = `${basePath}/tree/${notebookPath}`;
7
+ parsed.searchParams.set('reset', '1');
8
+ return parsed.toString();
9
+ }
10
+
11
+ function escapeRegExp(value) {
12
+ return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
13
+ }
14
+
15
+ async function dismissBlockingDialogs(page) {
16
+ for (let attempt = 0; attempt < 12; attempt += 1) {
17
+ const dialog = page.locator('dialog.jp-Dialog, [role="dialog"].jp-Dialog, [role="dialog"]').first();
18
+ if (!(await dialog.count()) || !(await dialog.isVisible())) {
19
+ return;
20
+ }
21
+
22
+ const noKernelButton = page.getByRole('button', { name: /^No Kernel$/ }).first();
23
+ if ((await noKernelButton.count()) > 0 && (await noKernelButton.isVisible())) {
24
+ await noKernelButton.click();
25
+ await page.waitForTimeout(120);
26
+ continue;
27
+ }
28
+
29
+ const selectKernelButton = page.getByRole('button', { name: /^Select$/ }).first();
30
+ if ((await selectKernelButton.count()) > 0 && (await selectKernelButton.isVisible())) {
31
+ await selectKernelButton.click();
32
+ await page.waitForTimeout(120);
33
+ continue;
34
+ }
35
+
36
+ const cancelButton = page.getByRole('button', { name: /^Cancel$/ }).first();
37
+ if ((await cancelButton.count()) > 0 && (await cancelButton.isVisible())) {
38
+ await cancelButton.click();
39
+ await page.waitForTimeout(120);
40
+ continue;
41
+ }
42
+
43
+ await page.keyboard.press('Escape');
44
+ await page.waitForTimeout(120);
45
+ }
46
+ }
47
+
48
+ async function ensureFileBrowserVisible(page) {
49
+ await dismissBlockingDialogs(page);
50
+ const fileBrowserTab = page.getByRole('tab', { name: /File Browser/i }).first();
51
+ if (await fileBrowserTab.count()) {
52
+ const isActiveBefore = await fileBrowserTab.evaluate(
53
+ element =>
54
+ element.getAttribute('aria-selected') === 'true' ||
55
+ (typeof element.className === 'string' && element.className.includes('lm-mod-current'))
56
+ );
57
+ if (!isActiveBefore) {
58
+ await fileBrowserTab.click();
59
+ }
60
+ const isActiveAfter = await fileBrowserTab.evaluate(
61
+ element =>
62
+ element.getAttribute('aria-selected') === 'true' ||
63
+ (typeof element.className === 'string' && element.className.includes('lm-mod-current'))
64
+ );
65
+ if (!isActiveAfter) {
66
+ await fileBrowserTab.click();
67
+ }
68
+ }
69
+ await page.getByRole('list', { name: /files/i }).first().waitFor({ state: 'visible', timeout: 10000 }).catch(() => {});
70
+ }
71
+
72
+ async function openDocumentFromFileBrowser(page, documentName) {
73
+ await ensureFileBrowserVisible(page);
74
+ let item = page
75
+ .getByRole('listitem', { name: new RegExp(`\\b${escapeRegExp(documentName)}\\b`, 'i') })
76
+ .first();
77
+ if (!(await item.count())) {
78
+ item = page.locator('.jp-DirListing-item', { hasText: documentName }).first();
79
+ }
80
+ if (!(await item.count())) {
81
+ item = page.locator('.jp-FileBrowser').getByText(documentName, { exact: true }).first();
82
+ }
83
+ await item.first().waitFor({ state: 'visible', timeout: 20000 });
84
+ await item.first().dblclick();
85
+ await dismissBlockingDialogs(page);
86
+ }
87
+
88
+ async function activateDocumentTab(page, documentName) {
89
+ await dismissBlockingDialogs(page);
90
+ const exactTab = page
91
+ .getByRole('tab', { name: new RegExp(`^${escapeRegExp(documentName)}$`) })
92
+ .first();
93
+ await exactTab.click();
94
+ }
95
+
96
+ async function ensureCodexPanel(page) {
97
+ await dismissBlockingDialogs(page);
98
+ const composer = page.locator('.jp-CodexPanel .jp-CodexComposer textarea');
99
+ if ((await composer.count()) > 0 && (await composer.first().isVisible())) {
100
+ return;
101
+ }
102
+
103
+ const codexSideTab = page
104
+ .locator('.jp-SideBar.jp-mod-right .lm-TabBar-tab[title="Codex"], .jp-SideBar.jp-mod-right .lm-TabBar-tab')
105
+ .filter({ hasText: 'Codex' })
106
+ .first();
107
+ await codexSideTab.click();
108
+ await expect(composer.first()).toBeVisible({ timeout: 20000 });
109
+ }
110
+
111
+ async function openNotebookInCodex(page, baseURL, notebook) {
112
+ const commandPath = notebook.commandPath || path.resolve(__dirname, 'mock-codex-cli.py');
113
+ await page.addInitScript(cmd => {
114
+ window.localStorage.setItem('jupyterlab-codex:command-path', cmd);
115
+ if (!window.localStorage.getItem('jupyterlab-codex:model')) {
116
+ window.localStorage.removeItem('jupyterlab-codex:model');
117
+ }
118
+ }, commandPath);
119
+
120
+ const targetUrl = buildNotebookUrl(baseURL, notebook.path);
121
+ await page.goto(targetUrl, { waitUntil: 'domcontentloaded' });
122
+ await page.waitForSelector('main[aria-label="Main Content"], .jp-LabShell, .lm-DockPanel', { timeout: 30000 });
123
+ await dismissBlockingDialogs(page);
124
+ await ensureCodexPanel(page);
125
+
126
+ await openDocumentFromFileBrowser(page, notebook.name);
127
+ await activateDocumentTab(page, notebook.name);
128
+ }
129
+
130
+ module.exports = {
131
+ buildNotebookUrl,
132
+ dismissBlockingDialogs,
133
+ ensureFileBrowserVisible,
134
+ openDocumentFromFileBrowser,
135
+ activateDocumentTab,
136
+ ensureCodexPanel,
137
+ openNotebookInCodex
138
+ };
@@ -0,0 +1,58 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 1,
6
+ "id": "error-output-tail-cell",
7
+ "metadata": {},
8
+ "outputs": [
9
+ {
10
+ "ename": "ValueError",
11
+ "evalue": "synthetic traceback for tail truncation test",
12
+ "output_type": "error",
13
+ "traceback": [
14
+ "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
15
+ "\u001b[0;31mValueError\u001b[0m Traceback (most recent call last)",
16
+ "Cell \u001b[0;32mIn[1], line 1\u001b[0m\n\u001b[0;32m----> 1\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mValueError\u001b[39;00m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msynthetic traceback for tail truncation test\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n",
17
+ "\u001b[0;31mValueError\u001b[0m: synthetic traceback for tail truncation test"
18
+ ]
19
+ }
20
+ ],
21
+ "source": [
22
+ "raise ValueError(\"synthetic traceback for tail truncation test\")"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": null,
28
+ "id": "05fa12e1-3ea7-4081-b658-7e2799c32d07",
29
+ "metadata": {},
30
+ "outputs": [],
31
+ "source": []
32
+ }
33
+ ],
34
+ "metadata": {
35
+ "jupytext": {
36
+ "formats": "ipynb,py:percent"
37
+ },
38
+ "kernelspec": {
39
+ "display_name": "Python 3 (ipykernel)",
40
+ "language": "python",
41
+ "name": "python3"
42
+ },
43
+ "language_info": {
44
+ "codemirror_mode": {
45
+ "name": "ipython",
46
+ "version": 3
47
+ },
48
+ "file_extension": ".py",
49
+ "mimetype": "text/x-python",
50
+ "name": "python",
51
+ "nbconvert_exporter": "python",
52
+ "pygments_lexer": "ipython3",
53
+ "version": "3.12.2"
54
+ }
55
+ },
56
+ "nbformat": 4,
57
+ "nbformat_minor": 5
58
+ }
@@ -0,0 +1,19 @@
1
+ # ---
2
+ # jupyter:
3
+ # jupytext:
4
+ # formats: ipynb,py:percent
5
+ # text_representation:
6
+ # extension: .py
7
+ # format_name: percent
8
+ # format_version: '1.3'
9
+ # jupytext_version: 1.17.1
10
+ # kernelspec:
11
+ # display_name: Python 3 (ipykernel)
12
+ # language: python
13
+ # name: python3
14
+ # ---
15
+
16
+ # %%
17
+ raise ValueError("synthetic traceback for tail truncation test")
18
+
19
+ # %%