nbcat 0.11.0__tar.gz → 0.12.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 (36) hide show
  1. nbcat-0.12.1/.github/workflows/homebrew.yml +23 -0
  2. {nbcat-0.11.0 → nbcat-0.12.1}/PKG-INFO +6 -3
  3. {nbcat-0.11.0 → nbcat-0.12.1}/README.md +5 -2
  4. {nbcat-0.11.0 → nbcat-0.12.1}/pyproject.toml +1 -1
  5. nbcat-0.12.1/src/nbcat/__init__.py +1 -0
  6. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/main.py +15 -17
  7. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/markdown.py +29 -1
  8. {nbcat-0.11.0 → nbcat-0.12.1}/tests/test_render_cell.py +1 -21
  9. {nbcat-0.11.0 → nbcat-0.12.1}/uv.lock +1 -1
  10. nbcat-0.11.0/src/nbcat/__init__.py +0 -1
  11. {nbcat-0.11.0 → nbcat-0.12.1}/.github/workflows/ci.yml +0 -0
  12. {nbcat-0.11.0 → nbcat-0.12.1}/.gitignore +0 -0
  13. {nbcat-0.11.0 → nbcat-0.12.1}/LICENSE +0 -0
  14. {nbcat-0.11.0 → nbcat-0.12.1}/Makefile +0 -0
  15. {nbcat-0.11.0 → nbcat-0.12.1}/docs/screenshot.png +0 -0
  16. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/enums.py +0 -0
  17. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/exceptions.py +0 -0
  18. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/image.py +0 -0
  19. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/py.typed +0 -0
  20. {nbcat-0.11.0 → nbcat-0.12.1}/src/nbcat/schemas.py +0 -0
  21. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/invalid.ipynb +0 -0
  22. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/many_tracebacks.ipynb +0 -0
  23. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/no_min_version.ipynb +0 -0
  24. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test3.ipynb +0 -0
  25. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test3_no_metadata.ipynb +0 -0
  26. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test3_no_min_version.ipynb +0 -0
  27. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test3_no_worksheets.ipynb +0 -0
  28. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test3_worksheet_with_no_cells.ipynb +0 -0
  29. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4.5.ipynb +0 -0
  30. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4.ipynb +0 -0
  31. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4custom.ipynb +0 -0
  32. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4docinfo.ipynb +0 -0
  33. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4jupyter_metadata.ipynb +0 -0
  34. {nbcat-0.11.0 → nbcat-0.12.1}/tests/assets/test4jupyter_metadata_timings.ipynb +0 -0
  35. {nbcat-0.11.0 → nbcat-0.12.1}/tests/conftest.py +0 -0
  36. {nbcat-0.11.0 → nbcat-0.12.1}/tests/test_read_notebook.py +0 -0
@@ -0,0 +1,23 @@
1
+ name: Release to Homebrew
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+
7
+ jobs:
8
+ homebrew-releaser:
9
+ runs-on: ubuntu-latest
10
+ steps:
11
+ - name: Release project to Homebrew tap
12
+ uses: Justintime50/homebrew-releaser@v2
13
+ with:
14
+ homebrew_owner: akopdev
15
+ homebrew_tap: homebrew-formulas
16
+ github_token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
17
+ install: 'virtualenv_install_with_resources'
18
+ test: 'assert_match version.to_s, shell_output("#{bin}/nbcat --version")'
19
+ commit_owner: akopdev
20
+ commit_email: devnull@akop.dev
21
+ update_readme_table: true
22
+ formula_includes: 'include Language::Python::Virtualenv'
23
+ depends_on: '"python@3.13"'
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbcat
3
- Version: 0.11.0
3
+ Version: 0.12.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
@@ -68,10 +68,13 @@ Please note, that `nbcat` doesn't aim to replace JupyterLab. If you need a full-
68
68
 
69
69
  ## Installation
70
70
 
71
- From the command line using pip:
72
-
73
71
  ```bash
72
+ # Install from PyPI
74
73
  pip install nbcat
74
+
75
+ # Install via Homebrew
76
+ brew tab akopdev/formulas/nbcat
77
+ brew install nbcat
75
78
  ```
76
79
 
77
80
  ## Quickstart
@@ -24,10 +24,13 @@ Please note, that `nbcat` doesn't aim to replace JupyterLab. If you need a full-
24
24
 
25
25
  ## Installation
26
26
 
27
- From the command line using pip:
28
-
29
27
  ```bash
28
+ # Install from PyPI
30
29
  pip install nbcat
30
+
31
+ # Install via Homebrew
32
+ brew tab akopdev/formulas/nbcat
33
+ brew install nbcat
31
34
  ```
32
35
 
33
36
  ## Quickstart
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "nbcat"
3
- version = "0.11.0"
3
+ version = "0.12.1"
4
4
  description = "cat for jupyter notebooks"
5
5
  authors = [
6
6
  { name = "Akop Kesheshyan", email = "devnull@akop.dev" }
@@ -0,0 +1 @@
1
+ __version__ = "0.12.1"
@@ -6,7 +6,6 @@ import argcomplete
6
6
  import requests
7
7
  from argcomplete.completers import FilesCompleter
8
8
  from pydantic import ValidationError
9
- from rich import box
10
9
  from rich.console import Console, Group, RenderableType
11
10
  from rich.padding import Padding
12
11
  from rich.panel import Panel
@@ -87,10 +86,8 @@ def render_cell(cell: Cell) -> RenderableType:
87
86
  def _render_markdown(input: str) -> Markdown:
88
87
  return Markdown(input, code_theme="ansi_dark")
89
88
 
90
- def _render_code(input: str, language: str = "python") -> Panel:
91
- return Panel(
92
- Syntax(input, language, line_numbers=True, theme="ansi_dark", dedent=True), padding=0
93
- )
89
+ def _render_code(input: str, language: str = "python") -> Syntax:
90
+ return Syntax(input, language, theme="ansi_dark", padding=(1, 2), dedent=True)
94
91
 
95
92
  def _render_raw(input: str) -> Text:
96
93
  return Text(input)
@@ -116,16 +113,25 @@ def render_cell(cell: Cell) -> RenderableType:
116
113
  renderer = RENDERERS.get(cell.cell_type)
117
114
  source = renderer(cell.input) if renderer else None
118
115
  if source:
119
- rows.append(Padding(source, (1, 0)))
116
+ s_title = f"[green]In [{cell.execution_count}][/]" if cell.execution_count else None
117
+ if s_title:
118
+ rows.append(Panel(source, title=s_title, title_align="left"))
119
+ else:
120
+ rows.append(Padding(source, (1, 0)))
121
+
120
122
  if not cell.outputs:
121
- return source
123
+ return rows.pop()
122
124
 
123
125
  for o in cell.outputs:
126
+ o_title = f"[blue]Out [{o.execution_count}][/]" if o.execution_count else None
124
127
  if o.output:
125
128
  renderer = RENDERERS.get(o.output.output_type)
126
129
  output = renderer(o.output.text) if renderer else None
127
130
  if output:
128
- rows.append(Panel(output, style="italic", box=box.MINIMAL))
131
+ if o_title:
132
+ rows.append(Panel(output, style="italic", title=o_title, title_align="left"))
133
+ else:
134
+ rows.append(Padding(output, (1, 0), style="italic"))
129
135
  return Group(*rows)
130
136
 
131
137
 
@@ -142,15 +148,7 @@ def print_notebook(nb: Notebook):
142
148
 
143
149
  for cell in nb.cells:
144
150
  rendered = render_cell(cell)
145
- if isinstance(rendered, Group):
146
- out = Panel(
147
- rendered,
148
- title=f"[green][{cell.execution_count}][/]" if cell.execution_count else None,
149
- title_align="left",
150
- )
151
- else:
152
- out = Padding(rendered, (1, 0))
153
- console.print(out)
151
+ console.print(rendered)
154
152
 
155
153
 
156
154
  def main():
@@ -10,12 +10,17 @@
10
10
 
11
11
  from __future__ import annotations
12
12
 
13
+ import base64
14
+ from pathlib import Path
13
15
  from typing import ClassVar
14
16
 
17
+ import requests
15
18
  from rich import markdown as md
16
19
  from rich.console import Console, ConsoleOptions, RenderResult
17
20
  from rich.text import Text
18
21
 
22
+ from .image import Image
23
+
19
24
 
20
25
  class Heading(md.Heading):
21
26
  """A heading."""
@@ -30,6 +35,29 @@ class Heading(md.Heading):
30
35
  yield Text(f"{'#' * indent} {self.text}", style=styles.get(self.tag, "dim white"))
31
36
 
32
37
 
38
+ class ImageItem(md.ImageItem):
39
+ """Renders a placeholder for an image."""
40
+
41
+ def __rich_console__(self, console: Console, options: ConsoleOptions) -> RenderResult:
42
+ image_content = None
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)
54
+ if image_content:
55
+ # TODO: This part can be improved by changing Image class to accept file objects
56
+ image = base64.b64encode(image_content).decode("utf-8")
57
+ return Image(image).__rich_console__(console, options)
58
+ return super().__rich_console__(console, options)
59
+
60
+
33
61
  class Markdown(md.Markdown):
34
62
  elements: ClassVar[dict[str, type[md.MarkdownElement]]] = {
35
63
  "paragraph_open": md.Paragraph,
@@ -41,7 +69,7 @@ class Markdown(md.Markdown):
41
69
  "bullet_list_open": md.ListElement,
42
70
  "ordered_list_open": md.ListElement,
43
71
  "list_item_open": md.ListItem,
44
- "image": md.ImageItem,
72
+ "image": ImageItem,
45
73
  "table_open": md.TableElement,
46
74
  "tbody_open": md.TableBodyElement,
47
75
  "thead_open": md.TableHeaderElement,
@@ -1,29 +1,9 @@
1
- import pytest
2
- from rich.console import Group, RenderableType
3
- from rich.markdown import Markdown
4
- from rich.panel import Panel
5
- from rich.text import Text
1
+ from rich.console import Group
6
2
 
7
3
  from nbcat.main import render_cell
8
4
  from nbcat.schemas import Cell, StreamOutput
9
5
 
10
6
 
11
- @pytest.mark.parametrize(
12
- "cell_type,source,expected",
13
- [
14
- ("markdown", "# Heading", Markdown),
15
- ("code", "print('Hello')", Panel),
16
- ("raw", "Raw content", Text),
17
- ("heading", "Heading text", Markdown),
18
- ],
19
- )
20
- def test_render_cell_input_rendering(cell_type: str, source: str, expected: RenderableType):
21
- cell = Cell(cell_type=cell_type, source=source, execution_count=42, outputs=[])
22
- rendered = render_cell(cell)
23
-
24
- assert isinstance(rendered, expected)
25
-
26
-
27
7
  def test_render_cell_with_outputs():
28
8
  output_1 = StreamOutput(text=["First output"], execution_count=7, output_type="stream")
29
9
  output_2 = StreamOutput(text=["Second output"], output_type="stream")
@@ -237,7 +237,7 @@ wheels = [
237
237
 
238
238
  [[package]]
239
239
  name = "nbcat"
240
- version = "0.11.0"
240
+ version = "0.12.1"
241
241
  source = { editable = "." }
242
242
  dependencies = [
243
243
  { name = "argcomplete" },
@@ -1 +0,0 @@
1
- __version__ = "0.11.0"
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