nbsync 0.1.4__tar.gz → 0.2.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.
Files changed (39) hide show
  1. {nbsync-0.1.4 → nbsync-0.2.0}/.github/workflows/docs.yaml +1 -1
  2. {nbsync-0.1.4 → nbsync-0.2.0}/PKG-INFO +29 -19
  3. {nbsync-0.1.4 → nbsync-0.2.0}/README.md +25 -14
  4. nbsync-0.2.0/docs/index.md +1 -0
  5. {nbsync-0.1.4 → nbsync-0.2.0}/mkdocs.yaml +3 -8
  6. {nbsync-0.1.4 → nbsync-0.2.0}/pyproject.toml +5 -21
  7. {nbsync-0.1.4 → nbsync-0.2.0}/src/nbsync/cell.py +1 -2
  8. nbsync-0.2.0/src/nbsync/logger.py +32 -0
  9. {nbsync-0.1.4 → nbsync-0.2.0}/src/nbsync/sync.py +1 -2
  10. nbsync-0.2.0/tests/test_logger.py +64 -0
  11. {nbsync-0.1.4 → nbsync-0.2.0}/tests/test_sync.py +7 -0
  12. nbsync-0.1.4/docs/getting-started/configuration.md +0 -35
  13. nbsync-0.1.4/docs/getting-started/first-steps.md +0 -186
  14. nbsync-0.1.4/docs/getting-started/installation.md +0 -35
  15. nbsync-0.1.4/docs/index.md +0 -138
  16. nbsync-0.1.4/notebooks/analysis.ipynb +0 -69
  17. nbsync-0.1.4/scripts/plot.py +0 -15
  18. nbsync-0.1.4/scripts/plotting.py +0 -19
  19. nbsync-0.1.4/src/nbsync/logger.py +0 -5
  20. nbsync-0.1.4/src/nbsync/plugin.py +0 -96
  21. nbsync-0.1.4/tests/__init__.py +0 -0
  22. nbsync-0.1.4/tests/test_plugin.py +0 -108
  23. {nbsync-0.1.4 → nbsync-0.2.0}/.devcontainer/devcontainer.json +0 -0
  24. {nbsync-0.1.4 → nbsync-0.2.0}/.devcontainer/postCreate.sh +0 -0
  25. {nbsync-0.1.4 → nbsync-0.2.0}/.devcontainer/starship.toml +0 -0
  26. {nbsync-0.1.4 → nbsync-0.2.0}/.gitattributes +0 -0
  27. {nbsync-0.1.4 → nbsync-0.2.0}/.github/workflows/ci.yaml +0 -0
  28. {nbsync-0.1.4 → nbsync-0.2.0}/.github/workflows/publish.yaml +0 -0
  29. {nbsync-0.1.4 → nbsync-0.2.0}/.gitignore +0 -0
  30. {nbsync-0.1.4 → nbsync-0.2.0}/LICENSE +0 -0
  31. {nbsync-0.1.4/scripts → nbsync-0.2.0/src/nbsync}/__init__.py +0 -0
  32. {nbsync-0.1.4 → nbsync-0.2.0}/src/nbsync/markdown.py +0 -0
  33. {nbsync-0.1.4 → nbsync-0.2.0}/src/nbsync/notebook.py +0 -0
  34. {nbsync-0.1.4 → nbsync-0.2.0}/src/nbsync/py.typed +0 -0
  35. {nbsync-0.1.4/src/nbsync → nbsync-0.2.0/tests}/__init__.py +0 -0
  36. {nbsync-0.1.4 → nbsync-0.2.0}/tests/conftest.py +0 -0
  37. {nbsync-0.1.4 → nbsync-0.2.0}/tests/test_cell.py +0 -0
  38. {nbsync-0.1.4 → nbsync-0.2.0}/tests/test_markdown.py +0 -0
  39. {nbsync-0.1.4 → nbsync-0.2.0}/tests/test_notebook.py +0 -0
@@ -24,6 +24,6 @@ jobs:
24
24
  - name: Install uv
25
25
  run: pip install uv
26
26
  - name: Install the project
27
- run: uv sync
27
+ run: uv sync --group docs
28
28
  - name: Deploy documentation
29
29
  run: uv run mkdocs gh-deploy --force
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nbsync
3
- Version: 0.1.4
4
- Summary: MkDocs plugin treating Jupyter notebooks, Python scripts and Markdown files as first-class citizens for documentation with dynamic execution and real-time synchronization
3
+ Version: 0.2.0
4
+ Summary: A core library to synchronize Jupyter notebooks and Markdown documents, enabling seamless integration and dynamic content execution
5
5
  Project-URL: Documentation, https://daizutabi.github.io/nbsync/
6
6
  Project-URL: Source, https://github.com/daizutabi/nbsync
7
7
  Project-URL: Issues, https://github.com/daizutabi/nbsync/issues
@@ -28,7 +28,7 @@ License: MIT License
28
28
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
29
  SOFTWARE.
30
30
  License-File: LICENSE
31
- Keywords: documentation,dynamic-execution,jupyter,markdown,mkdocs,notebook,python,real-time-sync,visualization
31
+ Keywords: documentation,dynamic-execution,jupyter,markdown,notebook,python,real-time-sync,visualization
32
32
  Classifier: Development Status :: 4 - Beta
33
33
  Classifier: Programming Language :: Python
34
34
  Classifier: Programming Language :: Python :: 3.10
@@ -39,8 +39,7 @@ Classifier: Topic :: Documentation
39
39
  Classifier: Topic :: Software Development :: Documentation
40
40
  Classifier: Topic :: Text Processing :: Markup :: Markdown
41
41
  Requires-Python: >=3.10
42
- Requires-Dist: mkdocs>=1.6
43
- Requires-Dist: nbstore>=0.4.7
42
+ Requires-Dist: nbstore>=0.4.11
44
43
  Description-Content-Type: text/markdown
45
44
 
46
45
  # nbsync
@@ -50,11 +49,9 @@ Description-Content-Type: text/markdown
50
49
  [![Build Status][GHAction-image]][GHAction-link]
51
50
  [![Coverage Status][codecov-image]][codecov-link]
52
51
 
53
- <strong>Connect Jupyter notebooks to your MkDocs documentation</strong>
52
+ <strong>Connect Jupyter notebooks and Markdown documents</strong>
54
53
 
55
- nbsync is a MkDocs plugin that seamlessly embeds Jupyter notebook
56
- visualizations in your documentation, solving the disconnect between
57
- code development and documentation.
54
+ nbsync is a core library that seamlessly bridges Jupyter notebooks and Markdown documents, enabling dynamic content synchronization and execution.
58
55
 
59
56
  ## Why Use nbsync?
60
57
 
@@ -68,7 +65,7 @@ Data scientists, researchers, and technical writers face a common dilemma:
68
65
 
69
66
  ### Our Solution
70
67
 
71
- This plugin creates a live bridge between your notebooks and documentation by:
68
+ nbsync creates a live bridge between your notebooks and markdown documents by:
72
69
 
73
70
  - **Keeping environments separate** - work in the tool best suited for each task
74
71
  - **Maintaining connections** - reference specific figures from notebooks
@@ -86,7 +83,7 @@ This plugin creates a live bridge between your notebooks and documentation by:
86
83
 
87
84
  - **Automatic Updates**:
88
85
  When you modify your notebooks, your documentation updates
89
- automatically in MkDocs serve mode.
86
+ automatically.
90
87
 
91
88
  - **Clean Source Documents**:
92
89
  Your markdown remains readable and focused on content, without
@@ -104,14 +101,27 @@ This plugin creates a live bridge between your notebooks and documentation by:
104
101
  pip install nbsync
105
102
  ```
106
103
 
107
- ### 2. Configuration
104
+ ### 2. Basic Usage
108
105
 
109
- Add to your `mkdocs.yml`:
106
+ ```python
107
+ from nbsync.sync import Synchronizer
108
+ from nbstore import Store
109
+
110
+ # Initialize with a notebook store
111
+ store = Store("path/to/notebooks")
112
+ sync = Synchronizer(store)
113
+
114
+ # Process markdown with notebook references
115
+ markdown_text = """
116
+ # My Document
117
+
118
+ ![Chart description](my-notebook.ipynb){#my-figure}
119
+ """
110
120
 
111
- ```yaml
112
- plugins:
113
- - nbsync:
114
- src_dir: ../notebooks
121
+ # Convert markdown with notebook references to final output
122
+ for element in sync.convert(markdown_text):
123
+ # Process each element (string or Cell objects)
124
+ print(element)
115
125
  ```
116
126
 
117
127
  ### 3. Mark Figures in Your Notebook
@@ -140,14 +150,14 @@ Creating documentation and developing visualizations involve different
140
150
  workflows and timeframes. When building visualizations in Jupyter notebooks,
141
151
  you need rapid cycles of execution, verification, and modification.
142
152
 
143
- This plugin is designed specifically to address these separation of
153
+ nbsync is designed specifically to address these separation of
144
154
  concerns, allowing you to:
145
155
 
146
156
  - **Focus on code** in notebooks without documentation distractions
147
157
  - **Focus on narrative** in markdown without code interruptions
148
158
  - **Maintain powerful connections** between both environments
149
159
 
150
- Each environment is optimized for its purpose, while the plugin
160
+ Each environment is optimized for its purpose, while nbsync
151
161
  handles the integration automatically.
152
162
 
153
163
  ## Contributing
@@ -5,11 +5,9 @@
5
5
  [![Build Status][GHAction-image]][GHAction-link]
6
6
  [![Coverage Status][codecov-image]][codecov-link]
7
7
 
8
- <strong>Connect Jupyter notebooks to your MkDocs documentation</strong>
8
+ <strong>Connect Jupyter notebooks and Markdown documents</strong>
9
9
 
10
- nbsync is a MkDocs plugin that seamlessly embeds Jupyter notebook
11
- visualizations in your documentation, solving the disconnect between
12
- code development and documentation.
10
+ nbsync is a core library that seamlessly bridges Jupyter notebooks and Markdown documents, enabling dynamic content synchronization and execution.
13
11
 
14
12
  ## Why Use nbsync?
15
13
 
@@ -23,7 +21,7 @@ Data scientists, researchers, and technical writers face a common dilemma:
23
21
 
24
22
  ### Our Solution
25
23
 
26
- This plugin creates a live bridge between your notebooks and documentation by:
24
+ nbsync creates a live bridge between your notebooks and markdown documents by:
27
25
 
28
26
  - **Keeping environments separate** - work in the tool best suited for each task
29
27
  - **Maintaining connections** - reference specific figures from notebooks
@@ -41,7 +39,7 @@ This plugin creates a live bridge between your notebooks and documentation by:
41
39
 
42
40
  - **Automatic Updates**:
43
41
  When you modify your notebooks, your documentation updates
44
- automatically in MkDocs serve mode.
42
+ automatically.
45
43
 
46
44
  - **Clean Source Documents**:
47
45
  Your markdown remains readable and focused on content, without
@@ -59,14 +57,27 @@ This plugin creates a live bridge between your notebooks and documentation by:
59
57
  pip install nbsync
60
58
  ```
61
59
 
62
- ### 2. Configuration
60
+ ### 2. Basic Usage
63
61
 
64
- Add to your `mkdocs.yml`:
62
+ ```python
63
+ from nbsync.sync import Synchronizer
64
+ from nbstore import Store
65
+
66
+ # Initialize with a notebook store
67
+ store = Store("path/to/notebooks")
68
+ sync = Synchronizer(store)
69
+
70
+ # Process markdown with notebook references
71
+ markdown_text = """
72
+ # My Document
73
+
74
+ ![Chart description](my-notebook.ipynb){#my-figure}
75
+ """
65
76
 
66
- ```yaml
67
- plugins:
68
- - nbsync:
69
- src_dir: ../notebooks
77
+ # Convert markdown with notebook references to final output
78
+ for element in sync.convert(markdown_text):
79
+ # Process each element (string or Cell objects)
80
+ print(element)
70
81
  ```
71
82
 
72
83
  ### 3. Mark Figures in Your Notebook
@@ -95,14 +106,14 @@ Creating documentation and developing visualizations involve different
95
106
  workflows and timeframes. When building visualizations in Jupyter notebooks,
96
107
  you need rapid cycles of execution, verification, and modification.
97
108
 
98
- This plugin is designed specifically to address these separation of
109
+ nbsync is designed specifically to address these separation of
99
110
  concerns, allowing you to:
100
111
 
101
112
  - **Focus on code** in notebooks without documentation distractions
102
113
  - **Focus on narrative** in markdown without code interruptions
103
114
  - **Maintain powerful connections** between both environments
104
115
 
105
- Each environment is optimized for its purpose, while the plugin
116
+ Each environment is optimized for its purpose, while nbsync
106
117
  handles the integration automatically.
107
118
 
108
119
  ## Contributing
@@ -0,0 +1 @@
1
+ # nbsync
@@ -41,10 +41,8 @@ theme:
41
41
  - search.suggest
42
42
  plugins:
43
43
  - search
44
- - nbsync:
45
- src_dir:
46
- - ../notebooks
47
- - ../scripts
44
+ - mkapi:
45
+ debug: true
48
46
  markdown_extensions:
49
47
  - admonition
50
48
  - pymdownx.emoji:
@@ -60,7 +58,4 @@ markdown_extensions:
60
58
  alternate_style: true
61
59
  nav:
62
60
  - Home: index.md
63
- - Getting Started:
64
- - getting-started/installation.md
65
- - getting-started/configuration.md
66
- - getting-started/first-steps.md
61
+ - API: $api/nbsync.***
@@ -4,15 +4,14 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nbsync"
7
- version = "0.1.4"
8
- description = "MkDocs plugin treating Jupyter notebooks, Python scripts and Markdown files as first-class citizens for documentation with dynamic execution and real-time synchronization"
7
+ version = "0.2.0"
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" }
11
11
  authors = [{ name = "daizutabi", email = "daizutabi@gmail.com" }]
12
12
  keywords = [
13
13
  "jupyter",
14
14
  "notebook",
15
- "mkdocs",
16
15
  "documentation",
17
16
  "markdown",
18
17
  "python",
@@ -32,31 +31,23 @@ classifiers = [
32
31
  "Topic :: Text Processing :: Markup :: Markdown",
33
32
  ]
34
33
  requires-python = ">=3.10"
35
- dependencies = ["mkdocs>=1.6", "nbstore>=0.4.7"]
34
+ dependencies = ["nbstore>=0.4.11"]
36
35
 
37
36
  [project.urls]
38
37
  Documentation = "https://daizutabi.github.io/nbsync/"
39
38
  Source = "https://github.com/daizutabi/nbsync"
40
39
  Issues = "https://github.com/daizutabi/nbsync/issues"
41
40
 
42
- [project.entry-points."mkdocs.plugins"]
43
- nbsync = "nbsync.plugin:Plugin"
44
-
45
41
  [dependency-groups]
46
42
  dev = [
47
43
  "ipykernel>=6.29.5",
48
44
  "matplotlib>=3.10.1",
49
- "mkdocs-material>=9.6.11",
50
45
  "nbconvert>=7.16.6",
51
- "pandas>=2.2",
52
- "polars>=1.26",
53
- "pytest-clarity>=1.0.1",
54
46
  "pytest-cov>=6.1.0",
55
47
  "pytest-randomly>=3.16.0",
56
- "pytest-xdist>=3.6.1",
57
48
  "ruff>=0.11.4",
58
- "seaborn>=0.13.2",
59
49
  ]
50
+ docs = ["mkapi", "mkdocs-material"]
60
51
 
61
52
  [tool.pytest.ini_options]
62
53
  testpaths = ["src", "tests"]
@@ -81,11 +72,4 @@ unfixable = ["F401"]
81
72
  ignore = ["ANN401", "ARG001", "ARG002", "C901", "D", "PLR0911", "S105"]
82
73
 
83
74
  [tool.ruff.lint.per-file-ignores]
84
- "**/tests/*" = ["ANN", "ARG", "D", "FBT", "PLR", "RUF", "S", "PGH003"]
85
- "**/{notebooks,scripts}/*" = ["ANN", "ERA001", "NPY", "T201"]
86
-
87
- [tool.pyright]
88
- include = ["src", "tests"]
89
- strictDictionaryInference = true
90
- strictListInference = true
91
- strictSetInference = true
75
+ "**/tests/*" = ["ANN", "ARG", "D", "FBT", "PGH003", "PLR", "RUF", "S", "SLF"]
@@ -5,10 +5,9 @@ import uuid
5
5
  from dataclasses import dataclass
6
6
  from typing import TYPE_CHECKING
7
7
 
8
+ from nbsync import logger
8
9
  from nbsync.markdown import is_truelike
9
10
 
10
- from .logger import logger
11
-
12
11
  if TYPE_CHECKING:
13
12
  from nbstore.markdown import Image
14
13
 
@@ -0,0 +1,32 @@
1
+ from __future__ import annotations
2
+
3
+ import logging
4
+ from logging import Logger
5
+ from typing import Any
6
+
7
+ _logger = logging.getLogger("nbsync")
8
+
9
+
10
+ def configure(logger: Logger | None = None) -> Logger:
11
+ global _logger # noqa: PLW0603
12
+
13
+ if logger:
14
+ _logger = logger
15
+
16
+ return _logger
17
+
18
+
19
+ def debug(msg: str, *args: Any, **kwargs: Any) -> None:
20
+ _logger.debug(msg, *args, **kwargs)
21
+
22
+
23
+ def info(msg: str, *args: Any, **kwargs: Any) -> None:
24
+ _logger.info(msg, *args, **kwargs)
25
+
26
+
27
+ def warning(msg: str, *args: Any, **kwargs: Any) -> None:
28
+ _logger.warning(msg, *args, **kwargs)
29
+
30
+
31
+ def error(msg: str, *args: Any, **kwargs: Any) -> None:
32
+ _logger.error(msg, *args, **kwargs)
@@ -10,12 +10,11 @@ from nbstore.markdown import CodeBlock, Image
10
10
  from nbstore.notebook import get_language, get_mime_content, get_source
11
11
 
12
12
  import nbsync.markdown
13
+ from nbsync import logger
13
14
  from nbsync.cell import Cell
14
15
  from nbsync.markdown import is_truelike
15
16
  from nbsync.notebook import Notebook
16
17
 
17
- from .logger import logger
18
-
19
18
  if TYPE_CHECKING:
20
19
  from collections.abc import Iterator
21
20
 
@@ -0,0 +1,64 @@
1
+ import logging
2
+ from logging import Logger
3
+
4
+ import pytest
5
+
6
+ from nbsync import logger
7
+
8
+
9
+ @pytest.fixture
10
+ def reset_logger():
11
+ original_logger = logger._logger
12
+
13
+ yield
14
+
15
+ logger._logger = original_logger
16
+
17
+
18
+ def test_default_logger():
19
+ assert isinstance(logger._logger, Logger)
20
+ assert logger._logger.name == "nbsync"
21
+
22
+
23
+ def test_configure(reset_logger):
24
+ custom_logger = logging.getLogger("custom_logger")
25
+
26
+ result = logger.configure(custom_logger)
27
+
28
+ assert result is custom_logger
29
+ assert logger._logger is custom_logger
30
+ assert logger._logger.name == "custom_logger"
31
+
32
+
33
+ def test_configure_no_args(reset_logger):
34
+ custom_logger = logging.getLogger("custom_logger")
35
+ logger._logger = custom_logger
36
+
37
+ result = logger.configure()
38
+
39
+ assert result is custom_logger
40
+ assert logger._logger is custom_logger
41
+
42
+
43
+ def test_logging_methods(reset_logger, caplog):
44
+ caplog.set_level(logging.DEBUG, logger="nbsync")
45
+
46
+ debug_msg = "Debug message"
47
+ info_msg = "Info message"
48
+ warning_msg = "Warning message"
49
+ error_msg = "Error message"
50
+
51
+ logger.debug(debug_msg)
52
+ logger.info(info_msg)
53
+ logger.warning(warning_msg)
54
+ logger.error(error_msg)
55
+
56
+ assert debug_msg in caplog.text
57
+ assert info_msg in caplog.text
58
+ assert warning_msg in caplog.text
59
+ assert error_msg in caplog.text
60
+
61
+ assert (logger._logger.name, logging.DEBUG, debug_msg) in caplog.record_tuples
62
+ assert (logger._logger.name, logging.INFO, info_msg) in caplog.record_tuples
63
+ assert (logger._logger.name, logging.WARNING, warning_msg) in caplog.record_tuples
64
+ assert (logger._logger.name, logging.ERROR, error_msg) in caplog.record_tuples
@@ -167,3 +167,10 @@ def test_sync_notebook_not_found():
167
167
 
168
168
  image = Image("abc", "id", [], {}, "", "a.ipynb")
169
169
  assert convert(image, {}) == ""
170
+
171
+
172
+ def test_convert_no_id():
173
+ from nbsync.sync import convert
174
+
175
+ image = Image("abc", ".", [], {}, "", "a.ipynb")
176
+ assert convert(image, {}) == ""
@@ -1,35 +0,0 @@
1
- # Configuration
2
-
3
- Configuring nbsync for your MkDocs site is simple but powerful, allowing you to
4
- customize how notebooks and Python files are integrated with your documentation.
5
-
6
- ## Basic Configuration
7
-
8
- To use nbsync with MkDocs, add it to your `mkdocs.yml` file:
9
-
10
- ```yaml
11
- plugins:
12
- - search
13
- - nbsync
14
- ```
15
-
16
- This minimal configuration uses all the default settings.
17
-
18
- ## Source Directory Configuration
19
-
20
- Specify where nbsync should look for notebooks and Python files:
21
-
22
- ```yaml
23
- plugins:
24
- - search
25
- - nbsync:
26
- src_dir:
27
- - ../notebooks # Path to notebooks directory
28
- - ../scripts # Path to Python scripts
29
- ```
30
-
31
- The `src_dir` option can be:
32
-
33
- - A single path as a string
34
- - A list of paths
35
- - Relative to your docs directory
@@ -1,186 +0,0 @@
1
- # First Steps
2
-
3
- Let's walk through a quick example of using nbsync to integrate notebooks with
4
- your MkDocs documentation.
5
-
6
- ## Setting Up Your Project
7
-
8
- Start with a typical MkDocs project structure:
9
-
10
- ```
11
- my-project/
12
- ├── docs/
13
- │ ├── index.md
14
- │ └── ...
15
- ├── notebooks/
16
- │ ├── analysis.ipynb
17
- │ └── ...
18
- ├── scripts/
19
- │ ├── plotting.py
20
- │ └── ...
21
- └── mkdocs.yml
22
- ```
23
-
24
- ## Configure MkDocs
25
-
26
- Update your `mkdocs.yml` to include nbsync:
27
-
28
- ```yaml
29
- site_name: My Documentation
30
- theme:
31
- name: material
32
-
33
- plugins:
34
- - search
35
- - nbsync:
36
- src_dir:
37
- - ../notebooks
38
- - ../scripts
39
- ```
40
-
41
- ## Creating Your First Integration
42
-
43
- ### 1. Prepare a Jupyter Notebook
44
-
45
- Create or use an existing notebook with visualizations.
46
- Tag cells you want to reference with a comment:
47
-
48
- ![](analysis.ipynb){#simple-plot source="only" identifier="1" title="notebooks/analysis.ipynb"}
49
-
50
- ### 2. Reference in Your Documentation
51
-
52
- In one of your markdown files (e.g., `docs/index.md`), add:
53
-
54
- ```markdown
55
- # My Project Documentation
56
-
57
- Here's a visualization from our analysis:
58
-
59
- ![Sine wave plot](analysis.ipynb){#simple-plot}
60
- ```
61
-
62
- ![Sine wave plot](analysis.ipynb){#simple-plot}
63
-
64
- ### 3. Create a Python Script
65
-
66
- Create a file `scripts/plotting.py` with visualization functions:
67
-
68
- ```python title="scripts/plotting.py"
69
- --8<-- "scripts/plotting.py"
70
- ```
71
-
72
- ### 4. Use Functions in Your Documentation
73
-
74
- Create a new file `docs/examples.md`:
75
-
76
- ```markdown
77
- # Examples
78
-
79
- Let's demonstrate different plots:
80
-
81
- ![](plotting.py){#.}
82
-
83
- ## Sine Waves
84
-
85
- | Frequency = 1 | Frequency = 2 |
86
- | :-------------------: | :-------------------: |
87
- | ![](){`plot_sine(1)`} | ![](){`plot_sine(2)`} |
88
-
89
- ## Histogram Examples
90
-
91
- | 20 Bins | 50 Bins |
92
- | :-------------------------: | :-------------------------: |
93
- | ![](){`plot_histogram(20)`} | ![](){`plot_histogram(50)`} |
94
- ```
95
-
96
- ![](plotting.py){#.}
97
-
98
- | Frequency = 1 | Frequency = 2 |
99
- | :-------------------: | :-------------------: |
100
- | ![](){`plot_sine(1)`} | ![](){`plot_sine(2)`} |
101
-
102
- | 20 Bins | 50 Bins |
103
- | :-------------------------: | :-------------------------: |
104
- | ![](){`plot_histogram(20)`} | ![](){`plot_histogram(50)`} |
105
-
106
- ### 5. Create a Markdown-Based Notebook
107
-
108
- Create a file `docs/custom.md`:
109
-
110
- ````markdown
111
- # Custom Analysis
112
-
113
- Here's an analysis created directly in markdown:
114
-
115
- ```python .md#_
116
- import numpy as np
117
- import pandas as pd
118
-
119
- # Generate sample data
120
- data = pd.DataFrame({
121
- 'x': np.random.randn(100),
122
- 'y': np.random.randn(100),
123
- 'group': np.random.choice(['A', 'B', 'C'], 100)
124
- })
125
- ```
126
-
127
- ```python .md#scatter
128
- import matplotlib.pyplot as plt
129
- import seaborn as sns
130
-
131
- plt.figure(figsize=(3, 2))
132
- sns.scatterplot(data=data, x='x', y='y', hue='group')
133
- plt.title('Scatter Plot by Group')
134
- ```
135
-
136
- ![Scatter plot](.md){#scatter}
137
- ````
138
-
139
- ```python .md#_
140
- import numpy as np
141
- import pandas as pd
142
-
143
- # Generate sample data
144
- data = pd.DataFrame({
145
- 'x': np.random.randn(100),
146
- 'y': np.random.randn(100),
147
- 'group': np.random.choice(['A', 'B', 'C'], 100)
148
- })
149
- ```
150
-
151
- ```python .md#scatter
152
- import matplotlib.pyplot as plt
153
- import seaborn as sns
154
-
155
- plt.figure(figsize=(3, 2))
156
- sns.scatterplot(data=data, x='x', y='y', hue='group')
157
- plt.title('Scatter Plot by Group')
158
- ```
159
-
160
- ![Scatter plot](){#scatter}
161
-
162
- ### 7. Run Your Documentation
163
-
164
- Start the MkDocs development server:
165
-
166
- ```bash
167
- mkdocs serve --open
168
- ```
169
-
170
- ## Troubleshooting
171
-
172
- ### Common Issues
173
-
174
- 1. **Images Not Showing**:
175
- - Check paths in your configuration
176
- - Ensure notebooks have correctly tagged cells
177
- - Verify Python dependencies are installed
178
-
179
- 2. **Execution Errors**:
180
- - Check the console output for error messages
181
- - Ensure your environment has all required packages
182
-
183
- 3. **Changes Not Reflecting**:
184
- - Hard refresh your browser
185
- - Restart the MkDocs server
186
- - Check file paths and identifiers
@@ -1,35 +0,0 @@
1
- # Installation
2
-
3
- Installing nbsync is straightforward and can be done using uv or pip,
4
- the Python package manager.
5
-
6
- ## Prerequisites
7
-
8
- Before installing nbsync, ensure you have the following:
9
-
10
- - Python 3.10 or higher
11
- - uv or pip (Python package manager)
12
- - MkDocs 1.6 or higher (documentation generator)
13
-
14
- ## Basic Installation
15
-
16
- Install nbsync using uv or pip:
17
-
18
- ```bash
19
- uv pip install nbsync
20
- # or
21
- pip install nbsync
22
- ```
23
-
24
- This command installs the latest stable version of nbsync and its core
25
- dependencies.
26
-
27
- ## Installation of nbconvert
28
-
29
- For dynamic execution functionality, install nbconvert:
30
-
31
- ```bash
32
- uv pip install nbconvert
33
- # or
34
- pip install nbconvert
35
- ```
@@ -1,138 +0,0 @@
1
- # nbsync
2
-
3
- <div class="grid cards" markdown>
4
-
5
- - :material-notebook-edit: **Notebooks from Markdown**
6
- Extend standard markdown syntax to automatically generate notebooks from
7
- documentation
8
- [:octicons-arrow-right-24: Markdown Features](#notebooks-from-markdown)
9
-
10
- - :material-language-python: **Python File Integration**
11
- Directly reference external Python files and reuse functions or classes
12
- [:octicons-arrow-right-24: Python Integration](#python-file-integration)
13
-
14
- - :material-image-edit: **Code Execution in Images**
15
- Execute code within image notation for dynamic visualizations
16
- [:octicons-arrow-right-24: Dynamic Visualization](#code-execution-in-images)
17
-
18
- - :material-refresh-auto: **Dynamic Updates**
19
- Real-time synchronization between notebooks and documentation
20
- [:octicons-arrow-right-24: Dynamic Updates](#dynamic-updates-and-execution)
21
-
22
- </div>
23
-
24
- ## What is nbsync?
25
-
26
- nbsync is an innovative MkDocs plugin that treats Jupyter notebooks,
27
- Python scripts, and Markdown files as first-class citizens for
28
- documentation. Unlike traditional approaches, nbsync provides equal
29
- capabilities across all file formats, enabling seamless integration
30
- and dynamic execution with real-time synchronization.
31
-
32
- It solves common challenges faced by data scientists, researchers, and technical
33
- writers:
34
-
35
- - **Development happens in notebooks** - ideal for experimentation and visualization
36
- - **Documentation lives in markdown** - perfect for narrative and explanation
37
- - **Code resides in Python files** - organized and version-controlled
38
- - **Traditional integration is challenging** - screenshots break, exports get outdated
39
-
40
- ## Inspiration & Comparison
41
-
42
- nbsync was inspired by and builds upon the excellent work of two MkDocs
43
- plugins:
44
-
45
- - [**markdown-exec**](https://pawamoy.github.io/markdown-exec/) - Provides utilities to execute code blocks in Markdown files
46
- - [**mkdocs-jupyter**](https://mkdocs-jupyter.danielfrg.com/) - Enables embedding Jupyter notebooks in MkDocs
47
-
48
- While these plugins offer great functionality, nbsync takes a unified
49
- approach by:
50
-
51
- 1. **Equal treatment** - Unlike other solutions that prioritize one format, nbsync treats Jupyter notebooks, Python scripts, and Markdown files equally as first-class citizens
52
- 2. **Real-time synchronization** - Changes to source files are immediately reflected in documentation
53
- 3. **Seamless integration** - Consistent syntax across all file formats allows for flexible documentation workflows
54
- 4. **Image syntax code execution** - Unique ability to execute code and embed visualizations anywhere Markdown image syntax (`![alt](url)`) is valid, including tables, lists, and complex layouts
55
-
56
- ## Acknowledgements
57
-
58
- The development of nbsync would not have been possible without the
59
- groundwork laid by markdown-exec and mkdocs-jupyter. We extend our
60
- sincere gratitude to the developers of these projects for their
61
- innovative contributions to the documentation ecosystem.
62
-
63
- ## Key Features
64
-
65
- ### Notebooks from Markdown
66
-
67
- Extend standard markdown syntax to define notebook cells within your
68
- documentation. Present code and its output results concisely with tabbed
69
- display.
70
-
71
- ````markdown source="tabbed-nbsync"
72
- ```python .md#plot
73
- import matplotlib.pyplot as plt
74
-
75
- fig, ax = plt.subplots(figsize=(2, 1))
76
- ax.plot([1, 3, 3, 4])
77
- ```
78
-
79
- ![Plot result](){#plot source="above"}
80
- ````
81
-
82
- ### Python File Integration
83
-
84
- Directly reference external Python files and reuse defined functions or
85
- classes. Avoid code duplication and improve maintainability.
86
-
87
- ```python title="plot.py"
88
- --8<-- "scripts/plot.py"
89
- ```
90
-
91
- ```markdown source="tabbed-nbsync"
92
- ![Plot result](plot.py){#sqrt source="on"}
93
- ```
94
-
95
- ### Code Execution in Images
96
-
97
- Execute Python code directly within image notation and display the results.
98
- This enables easy placement of dynamic visualizations in tables or complex
99
- layouts.
100
-
101
- ```markdown source="tabbed-nbsync"
102
- | Sine | Cosine |
103
- | :-------------------: | :-------------------: |
104
- | ![](){`plot(np.sin)`} | ![](){`plot(np.cos)`} |
105
- ```
106
-
107
- ### Code Execution with markdown-exec Style Syntax
108
-
109
- nbsync supports the markdown-exec style code blocks with the `exec="1"`
110
- attribute as a compatible approach to code execution. While this syntax
111
- is familiar to markdown-exec users, nbsync executes it through the
112
- Jupyter Notebook engine instead, providing the ability to render diverse
113
- MIME content types (HTML, SVG, images, etc.) directly in your
114
- documentation. This enables richer and more complex outputs than
115
- traditional execution methods.
116
-
117
- ````markdown source="tabbed-nbsync"
118
- ```python exec="1" source="tabbed-left"
119
- import numpy as np
120
- from PIL import Image
121
- x = np.random.randint(0, 255, (50, 200), dtype=np.uint8)
122
- Image.fromarray(x)
123
- ```
124
- ````
125
-
126
- ### Dynamic Updates and Execution
127
-
128
- Automatic synchronization between notebooks and documentation ensures code
129
- changes are reflected in real-time. View changes instantly in MkDocs serve
130
- mode.
131
-
132
- ## Getting Started
133
-
134
- Follow these steps to get started with nbsync:
135
-
136
- 1. [Installation](getting-started/installation.md)
137
- 2. [Configuration](getting-started/configuration.md)
138
- 3. [First Steps](getting-started/first-steps.md)
@@ -1,69 +0,0 @@
1
- {
2
- "cells": [
3
- {
4
- "cell_type": "code",
5
- "execution_count": 5,
6
- "metadata": {},
7
- "outputs": [
8
- {
9
- "data": {
10
- "text/plain": [
11
- "Text(0.5, 1.0, 'Simple Sine Wave')"
12
- ]
13
- },
14
- "execution_count": 5,
15
- "metadata": {},
16
- "output_type": "execute_result"
17
- },
18
- {
19
- "data": {
20
- "image/png": "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",
21
- "text/plain": [
22
- "<Figure size 300x150 with 1 Axes>"
23
- ]
24
- },
25
- "metadata": {},
26
- "output_type": "display_data"
27
- }
28
- ],
29
- "source": [
30
- "# #simple-plot\n",
31
- "import matplotlib.pyplot as plt\n",
32
- "import numpy as np\n",
33
- "\n",
34
- "x = np.linspace(0, 10, 100)\n",
35
- "plt.figure(figsize=(3, 1.5))\n",
36
- "plt.plot(x, np.sin(x))\n",
37
- "plt.title(\"Simple Sine Wave\")"
38
- ]
39
- },
40
- {
41
- "cell_type": "code",
42
- "execution_count": null,
43
- "metadata": {},
44
- "outputs": [],
45
- "source": []
46
- }
47
- ],
48
- "metadata": {
49
- "kernelspec": {
50
- "display_name": ".venv",
51
- "language": "python",
52
- "name": "python3"
53
- },
54
- "language_info": {
55
- "codemirror_mode": {
56
- "name": "ipython",
57
- "version": 3
58
- },
59
- "file_extension": ".py",
60
- "mimetype": "text/x-python",
61
- "name": "python",
62
- "nbconvert_exporter": "python",
63
- "pygments_lexer": "ipython3",
64
- "version": "3.13.3"
65
- }
66
- },
67
- "nbformat": 4,
68
- "nbformat_minor": 2
69
- }
@@ -1,15 +0,0 @@
1
- import matplotlib.pyplot as plt
2
- import numpy as np
3
-
4
-
5
- def plot(func):
6
- x = np.linspace(0, 360)
7
- y = func(np.radians(x))
8
- fig, ax = plt.subplots(figsize=(2, 1))
9
- ax.plot(x, y)
10
- ax.set_title(f"Plot {func.__name__}")
11
-
12
-
13
- if __name__ == "__main__":
14
- # %% #sqrt
15
- plot(np.sqrt)
@@ -1,19 +0,0 @@
1
- import matplotlib.pyplot as plt
2
- import numpy as np
3
-
4
-
5
- def plot_sine(frequency=1):
6
- """Plot a sine wave with given frequency."""
7
- x = np.linspace(0, 10, 100)
8
- plt.figure(figsize=(2, 1.2))
9
- plt.plot(x, np.sin(frequency * x))
10
- plt.title(f"Sine Wave (f={frequency})")
11
- plt.ylim(-1.2, 1.2)
12
-
13
-
14
- def plot_histogram(bins=20):
15
- """Plot a histogram of random data."""
16
- data = np.random.randn(1000)
17
- plt.figure(figsize=(2, 1.2))
18
- plt.hist(data, bins=bins)
19
- plt.title(f"Histogram (bins={bins})")
@@ -1,5 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from mkdocs.plugins import get_plugin_logger
4
-
5
- logger = get_plugin_logger("nbsync")
@@ -1,96 +0,0 @@
1
- from __future__ import annotations
2
-
3
- from pathlib import Path
4
- from typing import TYPE_CHECKING, ClassVar
5
-
6
- from mkdocs.config import Config as BaseConfig
7
- from mkdocs.config import config_options
8
- from mkdocs.plugins import BasePlugin
9
- from mkdocs.structure.files import File
10
- from nbstore.store import Store
11
-
12
- from .logger import logger
13
- from .sync import Synchronizer
14
-
15
- if TYPE_CHECKING:
16
- from typing import Any
17
-
18
- from mkdocs.config.defaults import MkDocsConfig
19
- from mkdocs.structure.files import Files
20
- from mkdocs.structure.pages import Page
21
-
22
- from .cell import Cell
23
-
24
-
25
- class Config(BaseConfig):
26
- """Configuration for Nbstore plugin."""
27
-
28
- src_dir = config_options.Type((str, list), default=".")
29
-
30
-
31
- class Plugin(BasePlugin[Config]):
32
- store: ClassVar[Store | None] = None
33
- syncs: ClassVar[dict[str, Synchronizer]] = {}
34
- files: Files
35
-
36
- def on_config(self, config: MkDocsConfig, **kwargs: Any) -> MkDocsConfig:
37
- if isinstance(self.config.src_dir, str):
38
- src_dirs = [self.config.src_dir]
39
- else:
40
- src_dirs = self.config.src_dir
41
-
42
- src_dirs = [(Path(config.docs_dir) / s).resolve() for s in src_dirs]
43
-
44
- store = self.__class__.store
45
-
46
- if store is None or store.src_dirs != src_dirs:
47
- self.__class__.store = Store(src_dirs)
48
- config.watch.extend(x.as_posix() for x in src_dirs)
49
-
50
- for name in ["attr_list", "md_in_html"]:
51
- if name not in config.markdown_extensions:
52
- config.markdown_extensions.append(name)
53
-
54
- return config
55
-
56
- def on_files(self, files: Files, config: MkDocsConfig, **kwargs: Any) -> Files:
57
- self.files = files
58
- return files
59
-
60
- def on_page_markdown(
61
- self,
62
- markdown: str,
63
- page: Page,
64
- config: MkDocsConfig,
65
- **kwargs: Any,
66
- ) -> str:
67
- if self.__class__.store is None:
68
- msg = "Store must be initialized before processing markdown"
69
- logger.error(msg)
70
- return markdown
71
-
72
- src_uri = page.file.src_uri
73
- syncs = self.__class__.syncs
74
-
75
- if src_uri not in syncs:
76
- syncs[src_uri] = Synchronizer(self.__class__.store)
77
-
78
- markdowns = []
79
-
80
- for elem in syncs[src_uri].convert(markdown):
81
- if isinstance(elem, str):
82
- markdowns.append(elem)
83
-
84
- elif markdown := elem.convert():
85
- markdowns.append(markdown)
86
-
87
- if elem.image.url and elem.content:
88
- file = generate_file(elem, src_uri, config)
89
- self.files.append(file)
90
-
91
- return "".join(markdowns)
92
-
93
-
94
- def generate_file(cell: Cell, page_uri: str, config: MkDocsConfig) -> File:
95
- src_uri = (Path(page_uri).parent / cell.image.url).as_posix()
96
- return File.generated(config, src_uri, content=cell.content)
File without changes
@@ -1,108 +0,0 @@
1
- import os
2
- import shutil
3
- import sys
4
- from pathlib import Path
5
-
6
- import pytest
7
- from mkdocs.commands.build import build
8
- from mkdocs.config import load_config
9
- from mkdocs.config.defaults import MkDocsConfig
10
-
11
- from nbsync.plugin import Config, Plugin
12
-
13
-
14
- @pytest.fixture(scope="module")
15
- def config_file():
16
- return Path(__file__).parent.parent / "mkdocs.yaml"
17
-
18
-
19
- def test_config_file_exists(config_file: Path):
20
- assert config_file.exists()
21
-
22
-
23
- @pytest.fixture(scope="module")
24
- def mkdocs_config(config_file: Path):
25
- return load_config(str(config_file))
26
-
27
-
28
- @pytest.fixture(scope="module")
29
- def nbstore_plugin(mkdocs_config: MkDocsConfig):
30
- return mkdocs_config.plugins["nbsync"]
31
-
32
-
33
- def test_nbstore_plugin(nbstore_plugin: Plugin):
34
- assert isinstance(nbstore_plugin, Plugin)
35
- assert isinstance(nbstore_plugin.config, Config)
36
-
37
-
38
- @pytest.fixture(scope="module")
39
- def nbstore_config(nbstore_plugin: Plugin):
40
- return nbstore_plugin.config
41
-
42
-
43
- def test_nbstore_config(nbstore_config: Config):
44
- config = nbstore_config
45
- assert config.src_dir == ["../notebooks", "../scripts"]
46
-
47
-
48
- @pytest.fixture
49
- def config_plugin(tmp_path):
50
- dest = Path(tmp_path)
51
- root = Path(__file__).parent.parent
52
- config_file = root / "mkdocs.yaml"
53
- shutil.copy(config_file, dest)
54
- for src in ["docs", "notebooks", "scripts", "src", "tests"]:
55
- src_dir = root / src
56
- shutil.copytree(src_dir, dest / src)
57
- curdir = Path(os.curdir).absolute()
58
- os.chdir(dest)
59
- sys.path.insert(0, ".")
60
- config = load_config("mkdocs.yaml")
61
- plugin = config.plugins["nbsync"]
62
- assert isinstance(plugin, Plugin)
63
- plugin.__init__()
64
-
65
- yield config, plugin
66
-
67
- config.plugins.on_shutdown()
68
- sys.path.pop(0)
69
- os.chdir(curdir)
70
-
71
-
72
- def test_on_config(config_plugin: tuple[MkDocsConfig, Plugin]):
73
- config, plugin = config_plugin
74
- plugin.on_config(config)
75
- assert plugin.store is not None
76
-
77
-
78
- @pytest.fixture
79
- def config(config_plugin):
80
- return config_plugin[0]
81
-
82
-
83
- def test_build(config: MkDocsConfig):
84
- config.plugins.on_startup(command="build", dirty=False)
85
- plugin = config.plugins["nbsync"]
86
- assert isinstance(plugin, Plugin)
87
-
88
- build(config, dirty=False)
89
-
90
-
91
- def test_on_page_markdown_fallback():
92
- class FakePlugin(Plugin):
93
- pass
94
-
95
- plugin = FakePlugin()
96
- plugin.__class__.store = None
97
- assert plugin.on_page_markdown("abc", None, None) == "abc" # type: ignore
98
-
99
-
100
- def test_src_dir_list(config_plugin: tuple[MkDocsConfig, Plugin]):
101
- config, plugin = config_plugin
102
- src_dir = plugin.config.src_dir
103
- plugin.config.src_dir = ["a", "b"]
104
- plugin.on_config(config)
105
- assert plugin.store
106
- assert plugin.store.src_dirs[0].name == "a"
107
- assert plugin.store.src_dirs[1].name == "b"
108
- plugin.config.src_dir = src_dir
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