notebook-terminal 0.1.1__tar.gz

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.
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ ## 0.1.1
4
+
5
+ - Fixed ANSI status messages being interpreted as PowerShell input.
6
+ - Added programmatic command history and rerun helpers.
7
+ - Added run_many, send_key, wait_until_idle, and restart helpers.
8
+
9
+
10
+ ## 0.1.1
11
+
12
+ Initial public alpha release.
13
+
14
+ ### Included
15
+
16
+ - PTY/ConPTY terminal sessions
17
+ - Jupyter terminal widget
18
+ - interactive and read-only modes
19
+ - real-time output streaming
20
+ - Python command and file execution
21
+ - DataFrame, Matplotlib, Seaborn, Plotly, and Jupyter widget tabs
22
+ - closable rich-output tabs
23
+ - Flask and Django adapters
24
+ - ANSI status helpers
25
+
26
+ ### Fixed before release
27
+
28
+ - Styled messages now use the terminal output channel rather than shell stdin. This prevents PowerShell from interpreting ANSI reset fragments such as `[0m` as commands.
@@ -0,0 +1,15 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND.
@@ -0,0 +1,7 @@
1
+ include README.md
2
+ include LICENSE
3
+ include PUBLISHING.md
4
+ recursive-include src/notebook_terminal/static *
5
+ recursive-include tests *.py
6
+
7
+ include CHANGELOG.md
@@ -0,0 +1,387 @@
1
+ Metadata-Version: 2.4
2
+ Name: notebook-terminal
3
+ Version: 0.1.1
4
+ Summary: A cross-platform embeddable PTY/ConPTY terminal for Jupyter and Python web applications
5
+ Author: Robert Zawadzki
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/ZawadzkiR/notebook-terminal
8
+ Project-URL: Repository, https://github.com/ZawadzkiR/notebook-terminal
9
+ Project-URL: Issues, https://github.com/ZawadzkiR/notebook-terminal/issues
10
+ Keywords: terminal,jupyter,jupyterlab,pty,conpty,xterm,flask,django,notebook,ipywidgets
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Framework :: Jupyter
13
+ Classifier: Operating System :: MacOS
14
+ Classifier: Operating System :: Microsoft :: Windows
15
+ Classifier: Operating System :: POSIX :: Linux
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.9
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Programming Language :: Python :: 3.13
22
+ Classifier: Topic :: System :: Shells
23
+ Classifier: Topic :: Software Development :: User Interfaces
24
+ Requires-Python: >=3.9
25
+ Description-Content-Type: text/markdown
26
+ License-File: LICENSE
27
+ Requires-Dist: anywidget>=0.9.13
28
+ Requires-Dist: ipywidgets>=8.1
29
+ Requires-Dist: ipython>=8
30
+ Requires-Dist: traitlets>=5
31
+ Requires-Dist: jupyterlab_widgets>=3.0
32
+ Requires-Dist: widgetsnbextension>=4.0
33
+ Requires-Dist: pywinpty>=3.0.0; platform_system == "Windows"
34
+ Provides-Extra: flask
35
+ Requires-Dist: Flask>=2.3; extra == "flask"
36
+ Requires-Dist: flask-sock>=0.7; extra == "flask"
37
+ Provides-Extra: django
38
+ Requires-Dist: Django>=4.2; extra == "django"
39
+ Requires-Dist: channels>=4; extra == "django"
40
+ Provides-Extra: plotly
41
+ Requires-Dist: plotly>=5; extra == "plotly"
42
+ Provides-Extra: data
43
+ Requires-Dist: pandas>=1.5; extra == "data"
44
+ Requires-Dist: matplotlib>=3.6; extra == "data"
45
+ Provides-Extra: dev
46
+ Requires-Dist: build>=1.2; extra == "dev"
47
+ Requires-Dist: twine>=5; extra == "dev"
48
+ Requires-Dist: pytest>=8; extra == "dev"
49
+ Dynamic: license-file
50
+
51
+ # Notebook Terminal
52
+
53
+ A cross-platform embeddable terminal for Jupyter Notebook, JupyterLab, Flask, Django, and custom Python frontends.
54
+
55
+ Notebook Terminal provides a persistent PTY/ConPTY session with real-time output, optional keyboard input, programmatic command execution, rich output tabs, interactive DataFrame viewing, Plotly support, and native Jupyter widgets.
56
+
57
+ > Version 0.1.1 is an alpha release. Test carefully before production use.
58
+
59
+ ## Features
60
+
61
+ - POSIX PTY support on Linux and macOS
62
+ - ConPTY support on Windows through `pywinpty`
63
+ - PowerShell, CMD, Bash, Zsh, and other shells
64
+ - Real-time output streaming
65
+ - Interactive or read-only terminal mode
66
+ - Programmatic commands and process input
67
+ - Python code and Python-file execution
68
+ - Matplotlib and Seaborn image tabs
69
+ - Interactive Plotly tabs
70
+ - Interactive DataFrame viewer with filtering, sorting, and pagination
71
+ - Native `ipywidgets` through the active Jupyter kernel
72
+ - Closable rich-output tabs
73
+ - ANSI colors and status helpers
74
+ - Flask WebSocket and Django Channels adapters
75
+ - Framework-independent `TerminalSession`
76
+
77
+ ## Installation
78
+
79
+ ```bash
80
+ python -m pip install notebook-terminal
81
+ ```
82
+
83
+ Install optional integrations:
84
+
85
+ ```bash
86
+ python -m pip install "notebook-terminal[flask]"
87
+ python -m pip install "notebook-terminal[django]"
88
+ python -m pip install "notebook-terminal[data,plotly]"
89
+ ```
90
+
91
+ For local development:
92
+
93
+ ```bash
94
+ git clone https://github.com/YOUR-USERNAME/notebook-terminal.git
95
+ cd notebook-terminal
96
+ python -m pip install -e ".[dev]"
97
+ ```
98
+
99
+ ## Jupyter quick start
100
+
101
+ ```python
102
+ from notebook_terminal import terminal
103
+
104
+ term = terminal(height=450)
105
+ ```
106
+
107
+ Run a command from another cell:
108
+
109
+ ```python
110
+ term.run("python --version")
111
+ ```
112
+
113
+ `term.run()` uses the existing terminal. It does not display a second copy of the widget.
114
+
115
+ ## Interactive and managed modes
116
+
117
+ Interactive terminal:
118
+
119
+ ```python
120
+ term = terminal(interactive=True)
121
+ ```
122
+
123
+ Read-only terminal controlled only through Python:
124
+
125
+ ```python
126
+ term = terminal(interactive=False)
127
+ term.run("python application.py")
128
+ ```
129
+
130
+ You can still send input programmatically:
131
+
132
+ ```python
133
+ term.send_text("answer\r\n")
134
+ ```
135
+
136
+ ## Running commands
137
+
138
+ ```python
139
+ term.run("git status")
140
+ term.run("pip list")
141
+ term.run("python script.py")
142
+ ```
143
+
144
+ Create a separate terminal explicitly:
145
+
146
+ ```python
147
+ second = term.run("python another_script.py", new_window=True)
148
+ ```
149
+
150
+ ## Running Python code
151
+
152
+ ```python
153
+ term.run_python("""
154
+ import time
155
+
156
+ for value in range(5):
157
+ print(value, flush=True)
158
+ time.sleep(1)
159
+ """)
160
+ ```
161
+
162
+ Run an existing file:
163
+
164
+ ```python
165
+ term.run_python_file(
166
+ "analysis.py",
167
+ args=["input.csv", "--limit", "100"],
168
+ )
169
+ ```
170
+
171
+ ## Rich output
172
+
173
+ ### Interactive DataFrame
174
+
175
+ ```python
176
+ term.run_python("""
177
+ import pandas as pd
178
+
179
+ frame = pd.DataFrame({
180
+ "city": ["Warsaw", "Krakow", "Gdansk", "Poznan"],
181
+ "value": [12, 18, 9, 15],
182
+ })
183
+
184
+ display(frame)
185
+ """, rich_output=True, clear_previous=True)
186
+ ```
187
+
188
+ The DataFrame tab supports filtering, sorting, pagination, page-size selection, and horizontal scrolling.
189
+
190
+ ### Matplotlib
191
+
192
+ ```python
193
+ term.run_python("""
194
+ import matplotlib.pyplot as plt
195
+
196
+ plt.plot([1, 2, 3, 4], [3, 7, 4, 8], marker="o")
197
+ plt.title("Example chart")
198
+ plt.show()
199
+ """, rich_output=True, clear_previous=True)
200
+ ```
201
+
202
+ ### Seaborn
203
+
204
+ ```python
205
+ term.run_python("""
206
+ import pandas as pd
207
+ import seaborn as sns
208
+ import matplotlib.pyplot as plt
209
+
210
+ frame = pd.DataFrame({
211
+ "category": ["A", "B", "C"],
212
+ "value": [10, 17, 13],
213
+ })
214
+
215
+ sns.barplot(data=frame, x="category", y="value")
216
+ plt.show()
217
+ """, rich_output=True, clear_previous=True)
218
+ ```
219
+
220
+ ### Plotly
221
+
222
+ ```python
223
+ term.run_python("""
224
+ import pandas as pd
225
+ import plotly.express as px
226
+
227
+ frame = pd.DataFrame({
228
+ "category": ["A", "B", "C", "D"],
229
+ "value": [10, 17, 13, 22],
230
+ })
231
+
232
+ figure = px.bar(frame, x="category", y="value", title="Interactive Plotly chart")
233
+ display(figure)
234
+ """, rich_output=True, clear_previous=True)
235
+ ```
236
+
237
+ ## Native Jupyter widgets
238
+
239
+ Jupyter widgets need the current notebook kernel and its Comm connection. Use `run_kernel()`:
240
+
241
+ ```python
242
+ term.run_kernel("""
243
+ import ipywidgets as widgets
244
+ from IPython.display import display
245
+
246
+ slider = widgets.IntSlider(value=25, min=0, max=100, description="Value:")
247
+ label = widgets.Label()
248
+
249
+ slider.observe(
250
+ lambda change: setattr(label, "value", f"Selected: {change['new']}"),
251
+ names="value",
252
+ )
253
+
254
+ display(widgets.VBox([slider, label]))
255
+ """, clear_previous=True)
256
+ ```
257
+
258
+ For interactive Matplotlib, install `ipympl` and use `%matplotlib widget` inside `run_kernel()`.
259
+
260
+ ## Styled messages
261
+
262
+ ```python
263
+ term.success("Code completed successfully")
264
+ term.error("Operation failed")
265
+ term.warning("Configuration file is missing")
266
+ term.info("Starting analysis")
267
+ term.debug("Current value: 42")
268
+ ```
269
+
270
+ Custom styling:
271
+
272
+ ```python
273
+ from notebook_terminal import Color
274
+
275
+ term.write_line(
276
+ "Custom message",
277
+ color=Color.GREEN,
278
+ bold=True,
279
+ icon="→",
280
+ )
281
+ ```
282
+
283
+ Styled messages are emitted directly to the terminal output channel. They are not written to shell input, so PowerShell does not try to execute ANSI reset sequences such as `[0m`.
284
+
285
+ ## Copy and paste
286
+
287
+ In interactive mode:
288
+
289
+ - select text and press `Ctrl+C` to copy
290
+ - press `Ctrl+C` without a selection to interrupt the active process
291
+ - press `Ctrl+V` to paste
292
+ - use the mouse wheel to scroll
293
+
294
+ In read-only mode, copying remains available, but keyboard input is not forwarded to the process.
295
+
296
+ ## Process control
297
+
298
+ ```python
299
+ term.send_text("answer\r\n")
300
+ term.interrupt()
301
+ term.clear()
302
+ term.clear(clear_tabs=True)
303
+ term.clear_tabs()
304
+ term.close_tab(0)
305
+ term.focus()
306
+ term.close_terminal()
307
+ ```
308
+
309
+ ## Framework-independent session
310
+
311
+ ```python
312
+ from notebook_terminal import TerminalSession
313
+
314
+ session = TerminalSession()
315
+
316
+ session.subscribe(
317
+ lambda chunk: print(chunk.decode("utf-8", errors="replace"), end="")
318
+ )
319
+
320
+ session.run("python --version")
321
+ session.success("Completed")
322
+ ```
323
+
324
+ `session.write()` sends input to the shell. `session.emit_output()` publishes display-only output to attached frontends.
325
+
326
+ ## Flask
327
+
328
+ ```python
329
+ from flask import Flask, render_template
330
+ from notebook_terminal.web import flask_blueprint, manager
331
+
332
+ app = Flask(__name__)
333
+ app.register_blueprint(flask_blueprint(), url_prefix="/terminal")
334
+
335
+ @app.route("/")
336
+ def index():
337
+ session_id, session = manager.create(cwd=".", interactive=True)
338
+ return render_template("terminal.html", session_id=session_id)
339
+ ```
340
+
341
+ Connect an xterm.js frontend to the WebSocket endpoint exposed by the adapter. Programmatic control remains available through the returned session object.
342
+
343
+ ## Django
344
+
345
+ Install Django Channels and add the provided consumer to your ASGI WebSocket routes:
346
+
347
+ ```python
348
+ from django.urls import re_path
349
+ from notebook_terminal.web import django_consumer
350
+
351
+ TerminalConsumer = django_consumer()
352
+
353
+ websocket_urlpatterns = [
354
+ re_path(
355
+ r"^ws/terminal/(?P<sid>[0-9a-f]+)/$",
356
+ TerminalConsumer.as_asgi(),
357
+ ),
358
+ ]
359
+ ```
360
+
361
+ ## Architecture
362
+
363
+ ```text
364
+ TerminalSession
365
+ |
366
+ +-----------------+-----------------+
367
+ | | |
368
+ Jupyter widget Flask WebSocket Django Channels
369
+ | | |
370
+ +-----------------+-----------------+
371
+ |
372
+ PTY / ConPTY
373
+ |
374
+ PowerShell / CMD / Bash / Zsh
375
+ ```
376
+
377
+ ## Security
378
+
379
+ An embedded terminal executes commands with the permissions of the hosting Python process. Do not expose unrestricted terminal access publicly without authentication, authorization, session ownership checks, process limits, automatic cleanup, and operating-system or container isolation.
380
+
381
+ ## Development and publishing
382
+
383
+ See [PUBLISHING.md](PUBLISHING.md) for complete build, GitHub, TestPyPI, and PyPI instructions.
384
+
385
+ ## License
386
+
387
+ MIT License. See [LICENSE](LICENSE).