nbsync 0.3.11__tar.gz → 0.4.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: nbsync
3
- Version: 0.3.11
3
+ Version: 0.4.0
4
4
  Summary: A core library to synchronize Jupyter notebooks and Markdown documents, enabling seamless integration and dynamic content execution
5
5
  Keywords: jupyter,notebook,documentation,markdown,python,visualization,dynamic-execution,real-time-sync
6
6
  Author: daizutabi
@@ -38,7 +38,9 @@ Classifier: Topic :: Software Development :: Documentation
38
38
  Classifier: Topic :: Text Processing :: Markup :: Markdown
39
39
  Requires-Dist: nbstore>=0.5.2
40
40
  Requires-Python: >=3.10
41
+ Project-URL: Changelog, https://github.com/daizutabi/nbsync/releases
41
42
  Project-URL: Documentation, https://daizutabi.github.io/nbsync/
43
+ Project-URL: Homepage, https://daizutabi.github.io/nbsync/
42
44
  Project-URL: Issues, https://github.com/daizutabi/nbsync/issues
43
45
  Project-URL: Source, https://github.com/daizutabi/nbsync
44
46
  Description-Content-Type: text/markdown
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "nbsync"
7
- version = "0.3.11"
7
+ version = "0.4.0"
8
8
  description = "A core library to synchronize Jupyter notebooks and Markdown documents, enabling seamless integration and dynamic content execution"
9
9
  readme = "README.md"
10
10
  license = { file = "LICENSE" }
@@ -35,19 +35,26 @@ requires-python = ">=3.10"
35
35
  dependencies = ["nbstore>=0.5.2"]
36
36
 
37
37
  [project.urls]
38
+ Changelog = "https://github.com/daizutabi/nbsync/releases"
38
39
  Documentation = "https://daizutabi.github.io/nbsync/"
39
- Source = "https://github.com/daizutabi/nbsync"
40
+ Homepage = "https://daizutabi.github.io/nbsync/"
40
41
  Issues = "https://github.com/daizutabi/nbsync/issues"
42
+ Source = "https://github.com/daizutabi/nbsync"
41
43
 
42
44
  [dependency-groups]
43
45
  dev = [
46
+ "basedpyright>=1.31.7",
44
47
  "ipykernel>=6",
45
48
  "matplotlib>=3",
46
49
  "nbconvert>=7",
50
+ "prek>=0.2.12",
47
51
  "pytest-cov>=6",
52
+ "pytest-mock>=3.15.1",
48
53
  "pytest-randomly>=3",
54
+ "ruff>=0.14.0",
55
+ "ty>=0.0.1a22",
49
56
  ]
50
- docs = ["mkapi", "mkdocs-material"]
57
+ docs = ["mkapi", "mkdocs-material", "mkdocs-nbsync>=0.1.5"]
51
58
 
52
59
  [tool.pytest.ini_options]
53
60
  testpaths = ["src", "tests"]
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import shlex
3
4
  import textwrap
4
5
  from typing import TYPE_CHECKING, TypeAlias
5
6
 
@@ -40,14 +41,19 @@ def _convert_code_block_attrs(code_block: CodeBlock) -> CodeBlock | Image:
40
41
  if exec_ != "1" or not code_block.classes:
41
42
  return code_block
42
43
 
43
- if code_block.classes[0] != "python":
44
+ if code_block.classes[0] == "python":
45
+ classes = code_block.classes[1:]
46
+ elif code_block.classes[0] == "console":
47
+ classes = code_block.classes
48
+ else:
44
49
  return code_block
45
50
 
46
51
  del code_block.attributes["exec"]
52
+
47
53
  return Image(
48
54
  code_block.indent,
49
55
  "",
50
- code_block.classes[1:],
56
+ classes,
51
57
  code_block.attributes,
52
58
  code_block.source,
53
59
  url=".md",
@@ -62,7 +68,7 @@ def convert_image(image: Image, index: int | None = None) -> Iterator[Element]:
62
68
  raise ValueError(msg)
63
69
 
64
70
  image.identifier = image.identifier or f"image-nbsync-{index}"
65
- yield CodeBlock("", image.identifier, [], {}, image.source, image.url)
71
+ yield create_code_block(image)
66
72
  yield image
67
73
 
68
74
  elif image.identifier:
@@ -72,6 +78,29 @@ def convert_image(image: Image, index: int | None = None) -> Iterator[Element]:
72
78
  yield image.text
73
79
 
74
80
 
81
+ def create_code_block(image: Image) -> CodeBlock:
82
+ if "console" in image.classes:
83
+ source = create_subprocess_source(image.source)
84
+ else:
85
+ source = image.source
86
+
87
+ return CodeBlock("", image.identifier, [], {}, source, image.url)
88
+
89
+
90
+ def create_subprocess_source(source: str) -> str:
91
+ """Create a Python source that runs the command in subprocess."""
92
+ args = shlex.split(source)
93
+ if not args:
94
+ return ""
95
+
96
+ if args[0] in ["$", "#", ">"]:
97
+ args = args[1:]
98
+
99
+ return textwrap.dedent(f"""\
100
+ import subprocess
101
+ print(subprocess.check_output({args}, text=True).rstrip())""")
102
+
103
+
75
104
  SUPPORTED_EXTENSIONS = (".ipynb", ".md", ".py")
76
105
 
77
106
 
@@ -101,6 +101,10 @@ def convert(
101
101
  return ""
102
102
 
103
103
  nb = notebooks[elem.url].nb
104
+
105
+ if "console" in elem.classes:
106
+ return convert_console(elem, nb)
107
+
104
108
  return convert_image(elem, nb)
105
109
 
106
110
  return convert_code_block(elem)
@@ -121,6 +125,24 @@ def convert_image(image: Image, nb: NotebookNode) -> Cell:
121
125
  return Cell(image, get_language(nb), *mime_content)
122
126
 
123
127
 
128
+ def remove_ansi(text: str) -> str:
129
+ return re.sub(r"\x1B\[[0-?]*[ -/]*[@-~]", "", text)
130
+
131
+
132
+ def convert_console(image: Image, nb: NotebookNode) -> str:
133
+ result = image.attributes.get("result", "bash")
134
+ if result == "1":
135
+ result = "bash"
136
+
137
+ source = image.attributes.get("source", None)
138
+ source = f"{image.source}\n" if is_truelike(source) else ""
139
+
140
+ _, content = get_mime_content(nb, image.identifier)
141
+ if isinstance(content, str):
142
+ content = remove_ansi(content)
143
+ return f"```{result}\n{source}{content}```"
144
+
145
+
124
146
  def convert_code_block(code_block: CodeBlock) -> str:
125
147
  source = code_block.attributes.pop("source", None)
126
148
  if not is_truelike(source):
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes