nbcat 0.12.1__tar.gz → 0.13.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.
Files changed (40) hide show
  1. {nbcat-0.12.1 → nbcat-0.13.1}/PKG-INFO +9 -4
  2. {nbcat-0.12.1 → nbcat-0.13.1}/README.md +6 -3
  3. nbcat-0.13.1/docs/screenshot.png +0 -0
  4. nbcat-0.13.1/docs/screenshot2.png +0 -0
  5. {nbcat-0.12.1 → nbcat-0.13.1}/pyproject.toml +3 -1
  6. nbcat-0.13.1/src/nbcat/__init__.py +1 -0
  7. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/main.py +38 -12
  8. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/markdown.py +11 -10
  9. nbcat-0.13.1/src/nbcat/pager.py +82 -0
  10. nbcat-0.13.1/tests/assets/sqlite.ipynb +666 -0
  11. {nbcat-0.12.1 → nbcat-0.13.1}/uv.lock +114 -1
  12. nbcat-0.12.1/docs/screenshot.png +0 -0
  13. nbcat-0.12.1/src/nbcat/__init__.py +0 -1
  14. {nbcat-0.12.1 → nbcat-0.13.1}/.github/workflows/ci.yml +0 -0
  15. {nbcat-0.12.1 → nbcat-0.13.1}/.github/workflows/homebrew.yml +0 -0
  16. {nbcat-0.12.1 → nbcat-0.13.1}/.gitignore +0 -0
  17. {nbcat-0.12.1 → nbcat-0.13.1}/LICENSE +0 -0
  18. {nbcat-0.12.1 → nbcat-0.13.1}/Makefile +0 -0
  19. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/enums.py +0 -0
  20. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/exceptions.py +0 -0
  21. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/image.py +0 -0
  22. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/py.typed +0 -0
  23. {nbcat-0.12.1 → nbcat-0.13.1}/src/nbcat/schemas.py +0 -0
  24. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/invalid.ipynb +0 -0
  25. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/many_tracebacks.ipynb +0 -0
  26. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/no_min_version.ipynb +0 -0
  27. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test3.ipynb +0 -0
  28. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test3_no_metadata.ipynb +0 -0
  29. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test3_no_min_version.ipynb +0 -0
  30. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test3_no_worksheets.ipynb +0 -0
  31. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test3_worksheet_with_no_cells.ipynb +0 -0
  32. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4.5.ipynb +0 -0
  33. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4.ipynb +0 -0
  34. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4custom.ipynb +0 -0
  35. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4docinfo.ipynb +0 -0
  36. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4jupyter_metadata.ipynb +0 -0
  37. {nbcat-0.12.1 → nbcat-0.13.1}/tests/assets/test4jupyter_metadata_timings.ipynb +0 -0
  38. {nbcat-0.12.1 → nbcat-0.13.1}/tests/conftest.py +0 -0
  39. {nbcat-0.12.1 → nbcat-0.13.1}/tests/test_read_notebook.py +0 -0
  40. {nbcat-0.12.1 → nbcat-0.13.1}/tests/test_render_cell.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbcat
3
- Version: 0.12.1
3
+ Version: 0.13.1
4
4
  Summary: cat for jupyter notebooks
5
5
  Project-URL: Homepage, https://github.com/akopdev/nbcat
6
6
  Project-URL: Repository, https://github.com/akopdev/nbcat
@@ -30,9 +30,11 @@ License: MIT License
30
30
  License-File: LICENSE
31
31
  Requires-Python: >=3.9
32
32
  Requires-Dist: argcomplete
33
+ Requires-Dist: markdownify
33
34
  Requires-Dist: pydantic
34
35
  Requires-Dist: requests
35
36
  Requires-Dist: rich
37
+ Requires-Dist: textual
36
38
  Requires-Dist: timg
37
39
  Provides-Extra: dev
38
40
  Requires-Dist: pytest; extra == 'dev'
@@ -48,13 +50,16 @@ Description-Content-Type: text/markdown
48
50
 
49
51
  <p align="center">
50
52
  <a href="docs/screenshot.png" target="blank"><img src="docs/screenshot.png" width="400" /></a>
53
+ <a href="docs/screenshot2.png" target="blank"><img src="docs/screenshot2.png" width="400" /></a>
51
54
  </p>
52
55
 
53
56
  ## Features
54
57
 
55
- - Very fast and lightweight with minimal dependencies
56
- - Preview remote notebooks without downloading them
57
- - Supports for all Jupyter notebook versions, including old legacy formats
58
+ - Very fast and lightweight with minimal dependencies.
59
+ - Preview remote notebooks without downloading them.
60
+ - Enable paginated view mode with keyboard navigation (similar to `less`).
61
+ - Supports image rendering (some protocols in beta)
62
+ - Supports for all Jupyter notebook versions, including old legacy formats.
58
63
 
59
64
  ## Motivation
60
65
 
@@ -4,13 +4,16 @@
4
4
 
5
5
  <p align="center">
6
6
  <a href="docs/screenshot.png" target="blank"><img src="docs/screenshot.png" width="400" /></a>
7
+ <a href="docs/screenshot2.png" target="blank"><img src="docs/screenshot2.png" width="400" /></a>
7
8
  </p>
8
9
 
9
10
  ## Features
10
11
 
11
- - Very fast and lightweight with minimal dependencies
12
- - Preview remote notebooks without downloading them
13
- - Supports for all Jupyter notebook versions, including old legacy formats
12
+ - Very fast and lightweight with minimal dependencies.
13
+ - Preview remote notebooks without downloading them.
14
+ - Enable paginated view mode with keyboard navigation (similar to `less`).
15
+ - Supports image rendering (some protocols in beta)
16
+ - Supports for all Jupyter notebook versions, including old legacy formats.
14
17
 
15
18
  ## Motivation
16
19
 
Binary file
Binary file
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nbcat"
3
- version = "0.12.1"
3
+ version = "0.13.1"
4
4
  description = "cat for jupyter notebooks"
5
5
  authors = [
6
6
  { name = "Akop Kesheshyan", email = "devnull@akop.dev" }
@@ -17,6 +17,8 @@ dependencies = [
17
17
  "pydantic",
18
18
  "rich",
19
19
  "timg",
20
+ "textual",
21
+ "markdownify"
20
22
  ]
21
23
 
22
24
  [project.optional-dependencies]
@@ -0,0 +1 @@
1
+ __version__ = "0.13.1"
@@ -5,6 +5,7 @@ from pathlib import Path
5
5
  import argcomplete
6
6
  import requests
7
7
  from argcomplete.completers import FilesCompleter
8
+ from markdownify import markdownify
8
9
  from pydantic import ValidationError
9
10
  from rich.console import Console, Group, RenderableType
10
11
  from rich.padding import Padding
@@ -22,6 +23,7 @@ from .exceptions import (
22
23
  )
23
24
  from .image import Image
24
25
  from .markdown import Markdown
26
+ from .pager import Pager
25
27
  from .schemas import Cell, Notebook
26
28
 
27
29
  console = Console()
@@ -84,7 +86,7 @@ def render_cell(cell: Cell) -> RenderableType:
84
86
  """
85
87
 
86
88
  def _render_markdown(input: str) -> Markdown:
87
- return Markdown(input, code_theme="ansi_dark")
89
+ return Markdown(markdownify(input), code_theme="ansi_dark")
88
90
 
89
91
  def _render_code(input: str, language: str = "python") -> Syntax:
90
92
  return Syntax(input, language, theme="ansi_dark", padding=(1, 2), dedent=True)
@@ -135,20 +137,26 @@ def render_cell(cell: Cell) -> RenderableType:
135
137
  return Group(*rows)
136
138
 
137
139
 
138
- def print_notebook(nb: Notebook):
140
+ def render_notebook(nb: Notebook) -> list[RenderableType]:
139
141
  """
140
- Print the notebook to the console with formatted cell inputs and outputs.
142
+ Convert a Notebook object into a list of rich renderables for terminal display.
143
+
144
+ Each cell in the notebook is processed and rendered using `render_cell`,
145
+ producing a sequence of styled input/output blocks suitable for use in a
146
+ Textual or Rich-based terminal UI.
141
147
 
142
148
  Args:
143
- nb (Notebook): A Notebook object containing a list of cells.
144
- """
145
- if not nb.cells:
146
- console.print("[bold red]Notebook contains no cells.")
147
- return
149
+ nb (Notebook): The notebook object containing parsed cells (e.g., from a .ipynb file).
148
150
 
151
+ Returns
152
+ -------
153
+ list[RenderableType]: A list of rich renderable objects representing the notebook cells.
154
+ Returns an empty list if the notebook has no cells.
155
+ """
156
+ rendered: list[RenderableType] = []
149
157
  for cell in nb.cells:
150
- rendered = render_cell(cell)
151
- console.print(rendered)
158
+ rendered.append(render_cell(cell))
159
+ return rendered
152
160
 
153
161
 
154
162
  def main():
@@ -160,20 +168,38 @@ def main():
160
168
  "file", help="Path or URL to a .ipynb notebook", type=str
161
169
  ).completer = FilesCompleter()
162
170
  parser.add_argument(
171
+ "-v",
163
172
  "--version",
164
173
  help="print version information and quite",
165
174
  action="version",
166
175
  version=__version__,
167
176
  )
168
177
  parser.add_argument(
169
- "--debug", help="enable extended error output", action="store_true", default=False
178
+ "-d", "--debug", help="enable extended error output", action="store_true", default=False
179
+ )
180
+ parser.add_argument(
181
+ "-p",
182
+ "--page",
183
+ help="enable paginated view mode (similar to less)",
184
+ action="store_true",
185
+ default=False,
170
186
  )
171
187
 
172
188
  try:
173
189
  argcomplete.autocomplete(parser)
174
190
  args = parser.parse_args()
191
+
175
192
  notebook = read_notebook(args.file, debug=args.debug)
176
- print_notebook(notebook)
193
+ objects = render_notebook(notebook)
194
+
195
+ if not objects:
196
+ console.print("[bold red]Notebook contains no cells.")
197
+ return
198
+
199
+ if args.page:
200
+ Pager(objects).run()
201
+ else:
202
+ console.print(*objects)
177
203
  except Exception as e:
178
204
  sys.exit(f"nbcat: {e}")
179
205
 
@@ -41,16 +41,17 @@ class ImageItem(md.ImageItem):
41
41
  def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
42
42
  image_content = None
43
43
  path = Path(self.destination)
44
- if path.exists():
45
- image_content = path.read_bytes()
46
- elif self.destination.startswith("http://") or self.destination.startswith("https://"):
47
- try:
48
- with requests.Session() as req:
49
- res = req.get(self.destination, timeout=5)
50
- res.raise_for_status()
51
- image_content = res.content
52
- except requests.RequestException:
53
- return super().__rich_console__(console, options)
44
+ if path.suffix in [".png", ".jpeg", ".jpg"]:
45
+ if path.exists():
46
+ image_content = path.read_bytes()
47
+ elif self.destination.startswith("http://") or self.destination.startswith("https://"):
48
+ try:
49
+ with requests.Session() as req:
50
+ res = req.get(self.destination, timeout=5)
51
+ res.raise_for_status()
52
+ image_content = res.content
53
+ except requests.RequestException:
54
+ return super().__rich_console__(console, options)
54
55
  if image_content:
55
56
  # TODO: This part can be improved by changing Image class to accept file objects
56
57
  image = base64.b64encode(image_content).decode("utf-8")
@@ -0,0 +1,82 @@
1
+ from rich.console import RenderableType
2
+ from textual.app import App, ComposeResult
3
+ from textual.binding import Binding
4
+ from textual.containers import VerticalScroll
5
+ from textual.widgets import Static
6
+
7
+
8
+ class Pager(App):
9
+ BINDINGS = [
10
+ # Exit
11
+ Binding("q", "quit", "Quit"),
12
+ Binding(":q", "quit", "Quit"),
13
+ Binding("Q", "quit", "Quit"),
14
+ Binding(":Q", "quit", "Quit"),
15
+ Binding("ZZ", "quit", "Quit"),
16
+ # One line
17
+ Binding("j", "scroll_down", "Down"),
18
+ Binding("e", "scroll_down", "Down"),
19
+ Binding("^e", "scroll_down", "Down"),
20
+ Binding("^n", "scroll_down", "Down"),
21
+ Binding("cr", "scroll_down", "Down"), # carriage return = Enter
22
+ Binding("k", "scroll_up", "Up"),
23
+ Binding("y", "scroll_up", "Up"),
24
+ Binding("ctrl+y", "scroll_up", "Up"),
25
+ Binding("ctrl+k", "scroll_up", "Up"),
26
+ Binding("ctrl+p", "scroll_up", "Up"),
27
+ # One window
28
+ Binding("f", "page_down", "Page Down"),
29
+ Binding("ctrl+f", "page_down", "Page Down"),
30
+ Binding("ctrl+v", "page_down", "Page Down"),
31
+ Binding("space", "page_down", "Page Down"),
32
+ Binding("b", "page_up", "Page Up"),
33
+ Binding("ctrl+b", "page_up", "Page Up"),
34
+ Binding("escape+v", "page_up", "Page Up"),
35
+ # Extended window control
36
+ Binding("z", "page_down", "Window Down (set N)"),
37
+ Binding("w", "page_up", "Window Up (set N)"),
38
+ Binding("escape+space", "page_down", "Forward one window, no EOF stop"),
39
+ Binding("d", "half_page_down", "Half Page Down"),
40
+ Binding("ctrl+d", "half_page_down", "Half Page Down"),
41
+ # Jumping
42
+ Binding("g", "go_to_top", "Top of File"),
43
+ Binding("<", "go_to_top", "Top of File"),
44
+ Binding("escape+<", "go_to_top", "Top of File"),
45
+ Binding("G", "go_to_bottom", "Bottom of File"),
46
+ Binding(">", "go_to_bottom", "Bottom of File"),
47
+ Binding("escape+>", "go_to_bottom", "Bottom of File"),
48
+ ]
49
+
50
+ def __init__(self, objects: list[RenderableType]):
51
+ super().__init__()
52
+ self._objects = objects
53
+
54
+ def compose(self) -> ComposeResult:
55
+ with VerticalScroll():
56
+ for obj in self._objects:
57
+ yield Static(obj)
58
+
59
+ def on_mount(self) -> None:
60
+ self.theme = "textual-ansi"
61
+ self.viewer = self.query_one(VerticalScroll)
62
+
63
+ def action_scroll_down(self) -> None:
64
+ self.viewer.scroll_to(y=self.viewer.scroll_y + 1)
65
+
66
+ def action_scroll_up(self) -> None:
67
+ self.viewer.scroll_to(y=self.viewer.scroll_y - 1)
68
+
69
+ def action_page_down(self) -> None:
70
+ self.viewer.scroll_to(y=self.viewer.scroll_y + self.viewer.virtual_size.height)
71
+
72
+ def action_page_up(self) -> None:
73
+ self.viewer.scroll_to(y=max(self.viewer.scroll_y - self.viewer.virtual_size.height, 0))
74
+
75
+ def action_half_page_down(self) -> None:
76
+ self.viewer.scroll_to(y=self.viewer.scroll_y + (self.viewer.virtual_size.height / 2))
77
+
78
+ def action_go_to_top(self) -> None:
79
+ self.viewer.scroll_to(y=0)
80
+
81
+ def action_go_to_bottom(self) -> None:
82
+ self.viewer.scroll_to(y=self.viewer.virtual_size.height)
@@ -0,0 +1,666 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "metadata": {},
6
+ "source": [
7
+ "# JupyterLite `xeus-sqlite` Kernel Demo\n",
8
+ "\n",
9
+ "The [`jupyterlite/xeus-sqlite-kernel`](https://github.com/jupyterlite/xeus-sqlite-kernel) wraps the original [`jupyter-xeus/xeus-sqlite`](https://github.com/jupyter-xeus/xeus-sqlite/) kernel for use in JupyterLite.\n",
10
+ "\n",
11
+ "Original kernel docs can be found [here](https://xeus-sqlite.readthedocs.io/en/latest/).\n",
12
+ "\n",
13
+ "The kernel provides cell magic for command line database operations, and native execution of SQL code against a connected database."
14
+ ]
15
+ },
16
+ {
17
+ "cell_type": "markdown",
18
+ "metadata": {},
19
+ "source": [
20
+ "## Creating a Database\n",
21
+ "\n",
22
+ "Line magic is used to create an in-memory database:"
23
+ ]
24
+ },
25
+ {
26
+ "cell_type": "code",
27
+ "execution_count": 1,
28
+ "metadata": {
29
+ "trusted": true
30
+ },
31
+ "outputs": [],
32
+ "source": [
33
+ "%CREATE example_db.db"
34
+ ]
35
+ },
36
+ {
37
+ "cell_type": "markdown",
38
+ "metadata": {},
39
+ "source": [
40
+ "Currently, there is no ability to:\n",
41
+ "\n",
42
+ "- save the database to browser storage;\n",
43
+ "- export the database;\n",
44
+ "- load a database from browser storage;\n",
45
+ "- load a database from a URL;\n",
46
+ "- load a database from the desktop;\n",
47
+ "- connect to a remote sqlite database file."
48
+ ]
49
+ },
50
+ {
51
+ "cell_type": "markdown",
52
+ "metadata": {},
53
+ "source": [
54
+ "## Create and Populate Tables\n",
55
+ "\n",
56
+ "Tables are created and populated using SQL:"
57
+ ]
58
+ },
59
+ {
60
+ "cell_type": "code",
61
+ "execution_count": 2,
62
+ "metadata": {
63
+ "trusted": true
64
+ },
65
+ "outputs": [],
66
+ "source": [
67
+ "CREATE TABLE players (Name STRING, Class STRING, Level INTEGER, Hitpoints INTEGER)"
68
+ ]
69
+ },
70
+ {
71
+ "cell_type": "code",
72
+ "execution_count": 3,
73
+ "metadata": {
74
+ "trusted": true
75
+ },
76
+ "outputs": [],
77
+ "source": [
78
+ "INSERT INTO players (Name, Class, Level, Hitpoints) VALUES (\"Martin Splitskull\", \"Warrior\", 3, 40)"
79
+ ]
80
+ },
81
+ {
82
+ "cell_type": "code",
83
+ "execution_count": 4,
84
+ "metadata": {
85
+ "trusted": true
86
+ },
87
+ "outputs": [
88
+ {
89
+ "data": {
90
+ "text/html": [
91
+ "<table>\n",
92
+ "<tr>\n",
93
+ "<th>rowcount</th>\n",
94
+ "</tr>\n",
95
+ "<tr>\n",
96
+ "<td>1</td>\n",
97
+ "</tr>\n",
98
+ "</table>"
99
+ ],
100
+ "text/plain": [
101
+ "+----------+\n",
102
+ "| rowcount |\n",
103
+ "+----------+\n",
104
+ "| 1 |\n",
105
+ "+----------+"
106
+ ]
107
+ },
108
+ "execution_count": 4,
109
+ "metadata": {},
110
+ "output_type": "execute_result"
111
+ }
112
+ ],
113
+ "source": [
114
+ "SELECT COUNT(*) as rowcount FROM players"
115
+ ]
116
+ },
117
+ {
118
+ "cell_type": "markdown",
119
+ "metadata": {},
120
+ "source": [
121
+ "Only one command can be executed from within a single code cell:"
122
+ ]
123
+ },
124
+ {
125
+ "cell_type": "code",
126
+ "execution_count": 5,
127
+ "metadata": {
128
+ "trusted": true
129
+ },
130
+ "outputs": [],
131
+ "source": [
132
+ "INSERT INTO players (Name, Class, Level, Hitpoints) VALUES (\"Sir Wolf\", \"Cleric\", 2, 20);\n",
133
+ "\n",
134
+ "-- The following will not be inserted\n",
135
+ "INSERT INTO players (Name, Class, Level, Hitpoints) VALUES (\"Sylvain, The Grey\", \"Wizard\", 1, 10);"
136
+ ]
137
+ },
138
+ {
139
+ "cell_type": "code",
140
+ "execution_count": 6,
141
+ "metadata": {
142
+ "trusted": true
143
+ },
144
+ "outputs": [
145
+ {
146
+ "data": {
147
+ "text/html": [
148
+ "<table>\n",
149
+ "<tr>\n",
150
+ "<th>Name</th>\n",
151
+ "<th>Level</th>\n",
152
+ "<th>Hitpoints</th>\n",
153
+ "</tr>\n",
154
+ "<tr>\n",
155
+ "<td>Martin Splitskull</td>\n",
156
+ "<td>3</td>\n",
157
+ "<td>40</td>\n",
158
+ "</tr>\n",
159
+ "<tr>\n",
160
+ "<td>Sir Wolf</td>\n",
161
+ "<td>2</td>\n",
162
+ "<td>20</td>\n",
163
+ "</tr>\n",
164
+ "</table>"
165
+ ],
166
+ "text/plain": [
167
+ "+-------------------+-------+-----------+\n",
168
+ "| Name | Level | Hitpoints |\n",
169
+ "+-------------------+-------+-----------+\n",
170
+ "| Martin Splitskull | 3 | 40 |\n",
171
+ "+-------------------+-------+-----------+\n",
172
+ "| Sir Wolf | 2 | 20 |\n",
173
+ "+-------------------+-------+-----------+"
174
+ ]
175
+ },
176
+ "execution_count": 6,
177
+ "metadata": {},
178
+ "output_type": "execute_result"
179
+ }
180
+ ],
181
+ "source": [
182
+ "SELECT Name, Level, Hitpoints FROM players;"
183
+ ]
184
+ },
185
+ {
186
+ "cell_type": "code",
187
+ "execution_count": 7,
188
+ "metadata": {
189
+ "trusted": true
190
+ },
191
+ "outputs": [],
192
+ "source": [
193
+ "INSERT INTO players (Name, Class, Level, Hitpoints) VALUES (\"Sylvain, The Grey\", \"Wizard\", 1, 10);"
194
+ ]
195
+ },
196
+ {
197
+ "cell_type": "code",
198
+ "execution_count": 8,
199
+ "metadata": {
200
+ "trusted": true
201
+ },
202
+ "outputs": [
203
+ {
204
+ "data": {
205
+ "text/html": [
206
+ "<table>\n",
207
+ "<tr>\n",
208
+ "<th>Name</th>\n",
209
+ "<th>Level</th>\n",
210
+ "<th>Hitpoints</th>\n",
211
+ "</tr>\n",
212
+ "<tr>\n",
213
+ "<td>Martin Splitskull</td>\n",
214
+ "<td>3</td>\n",
215
+ "<td>40</td>\n",
216
+ "</tr>\n",
217
+ "<tr>\n",
218
+ "<td>Sir Wolf</td>\n",
219
+ "<td>2</td>\n",
220
+ "<td>20</td>\n",
221
+ "</tr>\n",
222
+ "<tr>\n",
223
+ "<td>Sylvain, The Grey</td>\n",
224
+ "<td>1</td>\n",
225
+ "<td>10</td>\n",
226
+ "</tr>\n",
227
+ "</table>"
228
+ ],
229
+ "text/plain": [
230
+ "+-------------------+-------+-----------+\n",
231
+ "| Name | Level | Hitpoints |\n",
232
+ "+-------------------+-------+-----------+\n",
233
+ "| Martin Splitskull | 3 | 40 |\n",
234
+ "+-------------------+-------+-----------+\n",
235
+ "| Sir Wolf | 2 | 20 |\n",
236
+ "+-------------------+-------+-----------+\n",
237
+ "| Sylvain, The Grey | 1 | 10 |\n",
238
+ "+-------------------+-------+-----------+"
239
+ ]
240
+ },
241
+ "execution_count": 8,
242
+ "metadata": {},
243
+ "output_type": "execute_result"
244
+ }
245
+ ],
246
+ "source": [
247
+ "SELECT Name, Level, Hitpoints FROM players;"
248
+ ]
249
+ },
250
+ {
251
+ "cell_type": "markdown",
252
+ "metadata": {},
253
+ "source": [
254
+ "## Querying Tables\n",
255
+ "\n",
256
+ "A full range of SQL query commands are supported, including aggregation operations:"
257
+ ]
258
+ },
259
+ {
260
+ "cell_type": "code",
261
+ "execution_count": null,
262
+ "metadata": {
263
+ "trusted": true
264
+ },
265
+ "outputs": [],
266
+ "source": [
267
+ "SELECT SUM (Level) FROM players"
268
+ ]
269
+ },
270
+ {
271
+ "cell_type": "markdown",
272
+ "metadata": {},
273
+ "source": [
274
+ "Grouping also works:"
275
+ ]
276
+ },
277
+ {
278
+ "cell_type": "code",
279
+ "execution_count": 11,
280
+ "metadata": {
281
+ "trusted": true
282
+ },
283
+ "outputs": [
284
+ {
285
+ "data": {
286
+ "text/html": [
287
+ "<table>\n",
288
+ "<tr>\n",
289
+ "<th>Level</th>\n",
290
+ "<th>Total Hitpoints</th>\n",
291
+ "</tr>\n",
292
+ "<tr>\n",
293
+ "<td>3</td>\n",
294
+ "<td>40</td>\n",
295
+ "</tr>\n",
296
+ "<tr>\n",
297
+ "<td>2</td>\n",
298
+ "<td>20</td>\n",
299
+ "</tr>\n",
300
+ "<tr>\n",
301
+ "<td>1</td>\n",
302
+ "<td>10</td>\n",
303
+ "</tr>\n",
304
+ "</table>"
305
+ ],
306
+ "text/plain": [
307
+ "+-------+-----------------+\n",
308
+ "| Level | Total Hitpoints |\n",
309
+ "+-------+-----------------+\n",
310
+ "| 3 | 40 |\n",
311
+ "+-------+-----------------+\n",
312
+ "| 2 | 20 |\n",
313
+ "+-------+-----------------+\n",
314
+ "| 1 | 10 |\n",
315
+ "+-------+-----------------+"
316
+ ]
317
+ },
318
+ "execution_count": 11,
319
+ "metadata": {},
320
+ "output_type": "execute_result"
321
+ }
322
+ ],
323
+ "source": [
324
+ "SELECT Level, SUM(Hitpoints) AS `Total Hitpoints`\n",
325
+ "FROM players\n",
326
+ "GROUP BY Level\n",
327
+ "ORDER BY `Total Hitpoints` DESC;"
328
+ ]
329
+ },
330
+ {
331
+ "cell_type": "markdown",
332
+ "metadata": {},
333
+ "source": [
334
+ "## Charting Using Vega\n",
335
+ "\n",
336
+ "The `jupyter-xeus/xeus-sqlite` kernel also bundles Vega charting components.\n",
337
+ "\n",
338
+ "Vega charts can be generated by piping the result of a SQL query into a Vega line magic command."
339
+ ]
340
+ },
341
+ {
342
+ "cell_type": "code",
343
+ "execution_count": 12,
344
+ "metadata": {
345
+ "trusted": true
346
+ },
347
+ "outputs": [
348
+ {
349
+ "data": {
350
+ "text/html": [
351
+ "<table>\n",
352
+ "<tr>\n",
353
+ "<th>Level</th>\n",
354
+ "<th>Hitpoints</th>\n",
355
+ "</tr>\n",
356
+ "<tr>\n",
357
+ "<td>3</td>\n",
358
+ "<td>40</td>\n",
359
+ "</tr>\n",
360
+ "<tr>\n",
361
+ "<td>2</td>\n",
362
+ "<td>20</td>\n",
363
+ "</tr>\n",
364
+ "<tr>\n",
365
+ "<td>1</td>\n",
366
+ "<td>10</td>\n",
367
+ "</tr>\n",
368
+ "</table>"
369
+ ],
370
+ "text/plain": [
371
+ "+-------+-----------+\n",
372
+ "| Level | Hitpoints |\n",
373
+ "+-------+-----------+\n",
374
+ "| 3 | 40 |\n",
375
+ "+-------+-----------+\n",
376
+ "| 2 | 20 |\n",
377
+ "+-------+-----------+\n",
378
+ "| 1 | 10 |\n",
379
+ "+-------+-----------+"
380
+ ]
381
+ },
382
+ "execution_count": 12,
383
+ "metadata": {},
384
+ "output_type": "execute_result"
385
+ },
386
+ {
387
+ "data": {
388
+ "application/vnd.vegalite.v3+json": {
389
+ "$schema": "https://vega.github.io/schema/vega-lite/v4.json",
390
+ "config": {
391
+ "axis": {
392
+ "grid": true
393
+ }
394
+ },
395
+ "data": {
396
+ "values": [
397
+ {
398
+ "Hitpoints": "name",
399
+ "Level": "name"
400
+ },
401
+ {
402
+ "Hitpoints": "40",
403
+ "Level": "3"
404
+ },
405
+ {
406
+ "Hitpoints": "20",
407
+ "Level": "2"
408
+ },
409
+ {
410
+ "Hitpoints": "10",
411
+ "Level": "1"
412
+ }
413
+ ]
414
+ },
415
+ "encoding": {
416
+ "x": {
417
+ "field": "Level",
418
+ "type": "quantitative"
419
+ },
420
+ "y": {
421
+ "field": "Hitpoints",
422
+ "type": "quantitative"
423
+ }
424
+ },
425
+ "height": 200,
426
+ "mark": {
427
+ "type": "circle"
428
+ },
429
+ "width": 100
430
+ },
431
+ "image/png": "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"
432
+ },
433
+ "execution_count": 12,
434
+ "metadata": {},
435
+ "output_type": "execute_result"
436
+ }
437
+ ],
438
+ "source": [
439
+ "%XVEGA_PLOT\n",
440
+ " X_FIELD Level\n",
441
+ " Y_FIELD Hitpoints\n",
442
+ " MARK circle\n",
443
+ " WIDTH 100\n",
444
+ " HEIGHT 200\n",
445
+ " <>\n",
446
+ " SELECT Level, Hitpoints FROM players"
447
+ ]
448
+ },
449
+ {
450
+ "cell_type": "markdown",
451
+ "metadata": {},
452
+ "source": [
453
+ "## Database Administration\n",
454
+ "\n",
455
+ "Several line magics are defined to support database administration"
456
+ ]
457
+ },
458
+ {
459
+ "cell_type": "code",
460
+ "execution_count": 15,
461
+ "metadata": {
462
+ "trusted": true
463
+ },
464
+ "outputs": [
465
+ {
466
+ "data": {
467
+ "text/plain": [
468
+ "The table players exists."
469
+ ]
470
+ },
471
+ "execution_count": 15,
472
+ "metadata": {},
473
+ "output_type": "execute_result"
474
+ }
475
+ ],
476
+ "source": [
477
+ "%TABLE_EXISTS players"
478
+ ]
479
+ },
480
+ {
481
+ "cell_type": "code",
482
+ "execution_count": 16,
483
+ "metadata": {
484
+ "trusted": true
485
+ },
486
+ "outputs": [
487
+ {
488
+ "data": {
489
+ "text/plain": [
490
+ "The table npcs doesn't exist."
491
+ ]
492
+ },
493
+ "execution_count": 16,
494
+ "metadata": {},
495
+ "output_type": "execute_result"
496
+ }
497
+ ],
498
+ "source": [
499
+ "%TABLE_EXISTS npcs"
500
+ ]
501
+ },
502
+ {
503
+ "cell_type": "code",
504
+ "execution_count": 17,
505
+ "metadata": {
506
+ "trusted": true
507
+ },
508
+ "outputs": [
509
+ {
510
+ "data": {
511
+ "text/plain": [
512
+ "Magic header string: SQLite format 3\n",
513
+ "Page size bytes: 4096\n",
514
+ "File format write version: 1\n",
515
+ "File format read version: 1\n",
516
+ "Reserved space bytes: 0\n",
517
+ "Max embedded payload fraction 64\n",
518
+ "Min embedded payload fraction: 32\n",
519
+ "Leaf payload fraction: 32\n",
520
+ "File change counter: 4\n",
521
+ "Database size pages: 2\n",
522
+ "First freelist trunk page: 0\n",
523
+ "Total freelist trunk pages: 0\n",
524
+ "Schema cookie: 1\n",
525
+ "Schema format number: 4\n",
526
+ "Default page cache size bytes: 0\n",
527
+ "Largest B tree page number: 0\n",
528
+ "Database text encoding: 1\n",
529
+ "User version: 0\n",
530
+ "Incremental vaccum mode: 0\n",
531
+ "Application ID: 0\n",
532
+ "Version valid for: 4\n",
533
+ "SQLite version: 3032003\n"
534
+ ]
535
+ },
536
+ "execution_count": 17,
537
+ "metadata": {},
538
+ "output_type": "execute_result"
539
+ }
540
+ ],
541
+ "source": [
542
+ "%GET_INFO"
543
+ ]
544
+ },
545
+ {
546
+ "cell_type": "markdown",
547
+ "metadata": {},
548
+ "source": [
549
+ "## Connecting to a Different Database\n",
550
+ "\n",
551
+ "Creating a new database will connect the kernel to the new database instance."
552
+ ]
553
+ },
554
+ {
555
+ "cell_type": "code",
556
+ "execution_count": 18,
557
+ "metadata": {
558
+ "trusted": true
559
+ },
560
+ "outputs": [],
561
+ "source": [
562
+ "%CREATE potato.db "
563
+ ]
564
+ },
565
+ {
566
+ "cell_type": "code",
567
+ "execution_count": 19,
568
+ "metadata": {
569
+ "trusted": true
570
+ },
571
+ "outputs": [],
572
+ "source": [
573
+ "CREATE TABLE potaters(production INTEGER)"
574
+ ]
575
+ },
576
+ {
577
+ "cell_type": "code",
578
+ "execution_count": 20,
579
+ "metadata": {
580
+ "trusted": true
581
+ },
582
+ "outputs": [],
583
+ "source": [
584
+ "INSERT INTO potaters (production) VALUES (7)"
585
+ ]
586
+ },
587
+ {
588
+ "cell_type": "code",
589
+ "execution_count": 21,
590
+ "metadata": {
591
+ "trusted": true
592
+ },
593
+ "outputs": [
594
+ {
595
+ "data": {
596
+ "text/html": [
597
+ "<table>\n",
598
+ "<tr>\n",
599
+ "<th>production</th>\n",
600
+ "</tr>\n",
601
+ "<tr>\n",
602
+ "<td>7</td>\n",
603
+ "</tr>\n",
604
+ "</table>"
605
+ ],
606
+ "text/plain": [
607
+ "+------------+\n",
608
+ "| production |\n",
609
+ "+------------+\n",
610
+ "| 7 |\n",
611
+ "+------------+"
612
+ ]
613
+ },
614
+ "execution_count": 21,
615
+ "metadata": {},
616
+ "output_type": "execute_result"
617
+ }
618
+ ],
619
+ "source": [
620
+ "SELECT * FROM potaters"
621
+ ]
622
+ },
623
+ {
624
+ "cell_type": "markdown",
625
+ "metadata": {},
626
+ "source": [
627
+ "The original database is lost:"
628
+ ]
629
+ },
630
+ {
631
+ "cell_type": "code",
632
+ "execution_count": 23,
633
+ "metadata": {
634
+ "trusted": true
635
+ },
636
+ "outputs": [
637
+ {
638
+ "ename": "Error",
639
+ "evalue": "no such table: players",
640
+ "output_type": "error",
641
+ "traceback": [
642
+ "Error: no such table: players"
643
+ ]
644
+ }
645
+ ],
646
+ "source": [
647
+ "SELECT Name, Level, Hitpoints FROM players;"
648
+ ]
649
+ }
650
+ ],
651
+ "metadata": {
652
+ "kernelspec": {
653
+ "display_name": "SQLite",
654
+ "language": "sql",
655
+ "name": "SQLite"
656
+ },
657
+ "language_info": {
658
+ "file_extension": ".sqlite3-console",
659
+ "mimetype": "text/x-sqlite3-console",
660
+ "name": "sqlite3",
661
+ "version": "0.4.0"
662
+ }
663
+ },
664
+ "nbformat": 4,
665
+ "nbformat_minor": 4
666
+ }
@@ -20,6 +20,19 @@ wheels = [
20
20
  { url = "https://files.pythonhosted.org/packages/31/da/e42d7a9d8dd33fa775f467e4028a47936da2f01e4b0e561f9ba0d74cb0ca/argcomplete-3.6.2-py3-none-any.whl", hash = "sha256:65b3133a29ad53fb42c48cf5114752c7ab66c1c38544fdf6460f450c09b42591", size = 43708 },
21
21
  ]
22
22
 
23
+ [[package]]
24
+ name = "beautifulsoup4"
25
+ version = "4.13.4"
26
+ source = { registry = "https://pypi.org/simple" }
27
+ dependencies = [
28
+ { name = "soupsieve" },
29
+ { name = "typing-extensions" },
30
+ ]
31
+ sdist = { url = "https://files.pythonhosted.org/packages/d8/e4/0c4c39e18fd76d6a628d4dd8da40543d136ce2d1752bd6eeeab0791f4d6b/beautifulsoup4-4.13.4.tar.gz", hash = "sha256:dbb3c4e1ceae6aefebdaf2423247260cd062430a410e38c66f2baa50a8437195", size = 621067 }
32
+ wheels = [
33
+ { url = "https://files.pythonhosted.org/packages/50/cd/30110dc0ffcf3b131156077b90e9f60ed75711223f306da4db08eff8403b/beautifulsoup4-4.13.4-py3-none-any.whl", hash = "sha256:9bbbb14bfde9d79f38b8cd5f8c7c85f4b8f2523190ebed90e950a8dea4cb1c4b", size = 187285 },
34
+ ]
35
+
23
36
  [[package]]
24
37
  name = "certifi"
25
38
  version = "2025.4.26"
@@ -214,6 +227,18 @@ wheels = [
214
227
  { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
215
228
  ]
216
229
 
230
+ [[package]]
231
+ name = "linkify-it-py"
232
+ version = "2.0.3"
233
+ source = { registry = "https://pypi.org/simple" }
234
+ dependencies = [
235
+ { name = "uc-micro-py" },
236
+ ]
237
+ sdist = { url = "https://files.pythonhosted.org/packages/2a/ae/bb56c6828e4797ba5a4821eec7c43b8bf40f69cda4d4f5f8c8a2810ec96a/linkify-it-py-2.0.3.tar.gz", hash = "sha256:68cda27e162e9215c17d786649d1da0021a451bdc436ef9e0fa0ba5234b9b048", size = 27946 }
238
+ wheels = [
239
+ { url = "https://files.pythonhosted.org/packages/04/1e/b832de447dee8b582cac175871d2f6c3d5077cc56d5575cadba1fd1cccfa/linkify_it_py-2.0.3-py3-none-any.whl", hash = "sha256:6bcbc417b0ac14323382aef5c5192c0075bf8a9d6b41820a2b66371eac6b6d79", size = 19820 },
240
+ ]
241
+
217
242
  [[package]]
218
243
  name = "markdown-it-py"
219
244
  version = "3.0.0"
@@ -226,6 +251,39 @@ wheels = [
226
251
  { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528 },
227
252
  ]
228
253
 
254
+ [package.optional-dependencies]
255
+ linkify = [
256
+ { name = "linkify-it-py" },
257
+ ]
258
+ plugins = [
259
+ { name = "mdit-py-plugins" },
260
+ ]
261
+
262
+ [[package]]
263
+ name = "markdownify"
264
+ version = "1.1.0"
265
+ source = { registry = "https://pypi.org/simple" }
266
+ dependencies = [
267
+ { name = "beautifulsoup4" },
268
+ { name = "six" },
269
+ ]
270
+ sdist = { url = "https://files.pythonhosted.org/packages/2f/78/c48fed23c7aebc2c16049062e72de1da3220c274de59d28c942acdc9ffb2/markdownify-1.1.0.tar.gz", hash = "sha256:449c0bbbf1401c5112379619524f33b63490a8fa479456d41de9dc9e37560ebd", size = 17127 }
271
+ wheels = [
272
+ { url = "https://files.pythonhosted.org/packages/64/11/b751af7ad41b254a802cf52f7bc1fca7cabe2388132f2ce60a1a6b9b9622/markdownify-1.1.0-py3-none-any.whl", hash = "sha256:32a5a08e9af02c8a6528942224c91b933b4bd2c7d078f9012943776fc313eeef", size = 13901 },
273
+ ]
274
+
275
+ [[package]]
276
+ name = "mdit-py-plugins"
277
+ version = "0.4.2"
278
+ source = { registry = "https://pypi.org/simple" }
279
+ dependencies = [
280
+ { name = "markdown-it-py" },
281
+ ]
282
+ sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542 }
283
+ wheels = [
284
+ { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316 },
285
+ ]
286
+
229
287
  [[package]]
230
288
  name = "mdurl"
231
289
  version = "0.1.2"
@@ -237,13 +295,15 @@ wheels = [
237
295
 
238
296
  [[package]]
239
297
  name = "nbcat"
240
- version = "0.12.1"
298
+ version = "0.13.1"
241
299
  source = { editable = "." }
242
300
  dependencies = [
243
301
  { name = "argcomplete" },
302
+ { name = "markdownify" },
244
303
  { name = "pydantic" },
245
304
  { name = "requests" },
246
305
  { name = "rich" },
306
+ { name = "textual" },
247
307
  { name = "timg" },
248
308
  ]
249
309
 
@@ -259,6 +319,7 @@ dev = [
259
319
  [package.metadata]
260
320
  requires-dist = [
261
321
  { name = "argcomplete" },
322
+ { name = "markdownify" },
262
323
  { name = "pydantic" },
263
324
  { name = "pytest", marker = "extra == 'dev'" },
264
325
  { name = "pytest-cov", marker = "extra == 'dev'" },
@@ -267,6 +328,7 @@ requires-dist = [
267
328
  { name = "requests" },
268
329
  { name = "rich" },
269
330
  { name = "ruff", marker = "extra == 'dev'" },
331
+ { name = "textual" },
270
332
  { name = "timg" },
271
333
  ]
272
334
  provides-extras = ["dev"]
@@ -368,6 +430,15 @@ wheels = [
368
430
  { url = "https://files.pythonhosted.org/packages/21/2c/5e05f58658cf49b6667762cca03d6e7d85cededde2caf2ab37b81f80e574/pillow-11.2.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:208653868d5c9ecc2b327f9b9ef34e0e42a4cdd172c2988fd81d62d2bc9bc044", size = 2674751 },
369
431
  ]
370
432
 
433
+ [[package]]
434
+ name = "platformdirs"
435
+ version = "4.3.8"
436
+ source = { registry = "https://pypi.org/simple" }
437
+ sdist = { url = "https://files.pythonhosted.org/packages/fe/8b/3c73abc9c759ecd3f1f7ceff6685840859e8070c4d947c93fae71f6a0bf2/platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc", size = 21362 }
438
+ wheels = [
439
+ { url = "https://files.pythonhosted.org/packages/fe/39/979e8e21520d4e47a0bbe349e2713c0aac6f3d853d0e5b34d76206c439aa/platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4", size = 18567 },
440
+ ]
441
+
371
442
  [[package]]
372
443
  name = "pluggy"
373
444
  version = "1.5.0"
@@ -685,6 +756,39 @@ wheels = [
685
756
  { url = "https://files.pythonhosted.org/packages/68/ca/69d7c7752bce162d1516e5592b1cc6b6668e9328c0d270609ddbeeadd7cf/ruff-0.11.7-py3-none-win_arm64.whl", hash = "sha256:778c1e5d6f9e91034142dfd06110534ca13220bfaad5c3735f6cb844654f6177", size = 10677936 },
686
757
  ]
687
758
 
759
+ [[package]]
760
+ name = "six"
761
+ version = "1.17.0"
762
+ source = { registry = "https://pypi.org/simple" }
763
+ sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031 }
764
+ wheels = [
765
+ { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050 },
766
+ ]
767
+
768
+ [[package]]
769
+ name = "soupsieve"
770
+ version = "2.7"
771
+ source = { registry = "https://pypi.org/simple" }
772
+ sdist = { url = "https://files.pythonhosted.org/packages/3f/f4/4a80cd6ef364b2e8b65b15816a843c0980f7a5a2b4dc701fc574952aa19f/soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a", size = 103418 }
773
+ wheels = [
774
+ { url = "https://files.pythonhosted.org/packages/e7/9c/0e6afc12c269578be5c0c1c9f4b49a8d32770a080260c333ac04cc1c832d/soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4", size = 36677 },
775
+ ]
776
+
777
+ [[package]]
778
+ name = "textual"
779
+ version = "3.2.0"
780
+ source = { registry = "https://pypi.org/simple" }
781
+ dependencies = [
782
+ { name = "markdown-it-py", extra = ["linkify", "plugins"] },
783
+ { name = "platformdirs" },
784
+ { name = "rich" },
785
+ { name = "typing-extensions" },
786
+ ]
787
+ sdist = { url = "https://files.pythonhosted.org/packages/34/99/8408761a1a1076b2bb69d4859ec110d74be7515552407ac1cb6b68630eb6/textual-3.2.0.tar.gz", hash = "sha256:d2f3b0c39e02535bb5f2aec1c45e10bd3ee7508ed1e240b7505c3cf02a6f00ed", size = 1607281 }
788
+ wheels = [
789
+ { url = "https://files.pythonhosted.org/packages/10/d0/5869999e03ec4ad3a80f36298eb018556faaae0d3309dc305ecd93450b06/textual-3.2.0-py3-none-any.whl", hash = "sha256:c857c6d8dfc9aa915e09df99d227cbe1da3a7ea500b45af9f6b3ecb810c00d77", size = 685595 },
790
+ ]
791
+
688
792
  [[package]]
689
793
  name = "timg"
690
794
  version = "1.1.6"
@@ -757,6 +861,15 @@ wheels = [
757
861
  { url = "https://files.pythonhosted.org/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
758
862
  ]
759
863
 
864
+ [[package]]
865
+ name = "uc-micro-py"
866
+ version = "1.0.3"
867
+ source = { registry = "https://pypi.org/simple" }
868
+ sdist = { url = "https://files.pythonhosted.org/packages/91/7a/146a99696aee0609e3712f2b44c6274566bc368dfe8375191278045186b8/uc-micro-py-1.0.3.tar.gz", hash = "sha256:d321b92cff673ec58027c04015fcaa8bb1e005478643ff4a500882eaab88c48a", size = 6043 }
869
+ wheels = [
870
+ { url = "https://files.pythonhosted.org/packages/37/87/1f677586e8ac487e29672e4b17455758fce261de06a0d086167bb760361a/uc_micro_py-1.0.3-py3-none-any.whl", hash = "sha256:db1dffff340817673d7b466ec86114a9dc0e9d4d9b5ba229d9d60e5c12600cd5", size = 6229 },
871
+ ]
872
+
760
873
  [[package]]
761
874
  name = "urllib3"
762
875
  version = "2.4.0"
Binary file
@@ -1 +0,0 @@
1
- __version__ = "0.12.1"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes