Skip to main content

Split/rejoin Jupyter notebooks effortlessly.

Project description

jupyflow

Split & rejoin Jupyter notebooks, simply.


Overview

jupyflow is a tiny, zero‑dependency library that helps you move code in and out of Jupyter notebooks with confidence:

  • split(notebook_path) -> list[str]: extract all code cells from a .ipynb into a Python list of strings.
  • rejoin(*files, ...) -> str: take one or more .py files (each file = one cell) and rebuild a valid .ipynb JSON string.

It’s ideal for lightweight refactors, quick round‑trips, or preparing notebooks for CI/CD and code reviews—without imposing a custom file layout or heavy dependencies.

Python ≥ 3.8. No third‑party dependencies.


Installation

pip install jupyflow

Quick Start

1) Split a notebook into code strings

from pathlib import Path
import jupyflow as jf

cells = jf.split("Your_filename.ipynb", skip_empty=True)  # List[str]
print(f"Found {len(cells)} cells")
print(cells[0][:120])  # peek the first 120 chars

2) Rejoin multiple .py files into one notebook

from pathlib import Path
import jupyflow as jf

ipynb_text = jf.rejoin("cells/001_intro.py", "cells/002_calc.py")
Path("combined.ipynb").write_text(ipynb_text, encoding="utf-8")

3) Rejoin all .py files in a folder (natural sort)

from pathlib import Path
import re
import jupyflow as jf

def natural_key(name: str):
    # Ensure 2 < 10 when sorting: ['1','2','10'] not ['1','10','2']
    return [int(s) if s.isdigit() else s.lower() for s in re.split(r"(\d+)", name)]

src = Path("./Your_filename_py")
py_files = sorted(src.glob("*.py"), key=lambda p: natural_key(p.name))
ipynb_text = jf.rejoin(py_files)          # respects passed order by default
Path("Your_filename_combined.ipynb").write_text(ipynb_text, encoding="utf-8")

4) Round‑trip idea (optional)

# Split → write each cell to a file → rejoin later
import jupyflow as jf
from pathlib import Path

cells = jf.split("demo.ipynb")
out = Path("cells"); out.mkdir(exist_ok=True)
for i, code in enumerate(cells, start=1):
    (out / f"{i:03d}.py").write_text(code, encoding="utf-8")

# ...edit files... then rejoin:
from sortedcontainers import SortedList  # or use your own sorting if desired
# (Note: this line is only illustrative; jupyflow itself has no deps.)

API Reference

split(notebook_path: str | Path, skip_empty: bool = True) -> list[str]

Extract the code cells (in order) from a notebook.

  • Parameters
    • notebook_path: path to .ipynb
    • skip_empty (default True): skip cells that are empty or whitespace‑only
  • Returns: list[str] — each element is a code cell’s source
  • Raises: FileNotFoundError if the notebook path does not exist

rejoin(*files, metadata: dict | None = None, encoding: str = "utf-8", sort: bool = False) -> str

Build a valid .ipynb JSON string from one or more .py files (each file becomes one code cell).

  • Parameters
    • *files: pass multiple paths or a single sequence (list/tuple) of paths
    • metadata: optional dict to merge into the notebook’s metadata (e.g. custom kernelspec)
    • encoding: file encoding for reading .py (default utf-8)
    • sort: True to lexicographically sort inputs; by default the call order is preserved
  • Returns: str — the full notebook JSON (ready to write to disk)
  • Raises: FileNotFoundError if any source file is missing
  • Defaults
    • nbformat=4, nbformat_minor=5
    • Basic kernelspec & language_info (Python 3) included
    • Each code cell keeps original line endings via splitlines(keepends=True)

Customize metadata example

ipynb_text = jupyflow.rejoin(
    "a.py", "b.py",
    metadata={"kernelspec": {"display_name": "Python 3.11", "language": "python", "name": "python3"}}
)

Minimal Tests (pytest example)

# tests/test_basic.py
import json
from pathlib import Path
import jupyflow as jf

def make_nb(tmp: Path):
    nb = {
        "cells": [
            {"cell_type": "markdown", "source": ["# Title"]},
            {"cell_type": "code", "source": ["print('A')\n"]},
            {"cell_type": "code", "source": ["x = 42\n"]},
            {"cell_type": "code", "source": ["   \n"]},
        ],
        "metadata": {},
        "nbformat": 4,
        "nbformat_minor": 5,
    }
    p = tmp / "mini.ipynb"
    p.write_text(json.dumps(nb), encoding="utf-8")
    return p

def test_split(tmp_path: Path):
    nbp = make_nb(tmp_path)
    cells = jf.split(nbp, skip_empty=True)
    assert len(cells) == 2
    assert "print('A')" in cells[0]
    assert "x = 42" in cells[1]

License

MIT. See LICENSE.


简介

jupyflow 是一个零依赖的小工具,用来在 Jupyter 笔记本与纯代码之间轻盈往返

  • split(notebook_path) -> list[str]:把 .ipynb 里的代码单元抽取为 Python 字符串列表。
  • rejoin(*files, ...) -> str:把一个或多个 .py 文件(每个文件 = 一个单元)拼回合法的 .ipynb JSON 字符串。

适合做轻量重构、把 Notebook 纳入 CI/CD、或作为代码评审的过渡形态,简单直接。

需要 Python ≥ 3.8。无第三方依赖。

安装

pip install jupyflow

快速上手

1)把 .ipynb 拆成代码字符串

import jupyflow as jf
cells = jf.split("Your_filename.ipynb", skip_empty=True)
print(len(cells), cells[0][:120])

2)把多个 .py 合成一个 Notebook

from pathlib import Path
import jupyflow as jf

ipynb_text = jf.rejoin("cells/001_intro.py", "cells/002_calc.py")
Path("combined.ipynb").write_text(ipynb_text, encoding="utf-8")

3)把文件夹中的所有 .py 合成(自然排序)

from pathlib import Path
import re
import jupyflow as jf

def natural_key(name: str):
    return [int(s) if s.isdigit() else s.lower() for s in re.split(r"(\d+)", name)]

src = Path("./Your_filename_py")
py_files = sorted(src.glob("*.py"), key=lambda p: natural_key(p.name))
ipynb_text = jf.rejoin(py_files)
Path("Your_filename_combined.ipynb").write_text(ipynb_text, encoding="utf-8")

API 参考

split(notebook_path: str | Path, skip_empty: bool = True) -> list[str]

  • 参数
    • notebook_path.ipynb 路径
    • skip_empty:是否跳过空白单元,默认 True
  • 返回list[str](每个元素是一段代码)
  • 异常:路径不存在则抛 FileNotFoundError

rejoin(*files, metadata: dict | None = None, encoding: str = "utf-8", sort: bool = False) -> str

  • 参数
    • *files:可传多个路径,或传入一个列表/元组
    • metadata:可选,合并到生成的 notebook 元数据里(如自定义 kernelspec)
    • encoding:读取 .py 的编码,默认 utf-8
    • sort:若为 True,对输入路径按字典序排序;默认按传入顺序生成
  • 返回.ipynbJSON 字符串
  • 异常:任何源文件缺失会抛 FileNotFoundError
  • 默认设置
    • nbformat=4nbformat_minor=5
    • 默认包含 Python 3 的 kernelspeclanguage_info
    • 代码以逐行列表形式保存,保留换行

自定义元数据示例

ipynb_text = jupyflow.rejoin(
    "a.py", "b.py",
    metadata={"kernelspec": {"display_name": "Python 3.11", "language": "python", "name": "python3"}}
)

简单测试(pytest 示例)

# tests/test_basic.py
import json
from pathlib import Path
import jupyflow as jf

def make_nb(tmp: Path):
    nb = {
        "cells": [
            {"cell_type": "markdown", "source": ["# Title"]},
            {"cell_type": "code", "source": ["print('A')\n"]},
            {"cell_type": "code", "source": ["x = 42\n"]},
            {"cell_type": "code", "source": ["   \n"]},
        ],
        "metadata": {},
        "nbformat": 4,
        "nbformat_minor": 5,
    }
    p = tmp / "mini.ipynb"
    p.write_text(json.dumps(nb), encoding="utf-8")
    return p

def test_split(tmp_path: Path):
    nbp = make_nb(tmp_path)
    cells = jf.split(nbp, skip_empty=True)
    assert len(cells) == 2
    assert "print('A')" in cells[0]
    assert "x = 42" in cells[1]

许可

MIT(可在 LICENSE 中查看)。


Your notebooks, now fluid.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

jupyflow-1.0.1.tar.gz (6.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

jupyflow-1.0.1-py3-none-any.whl (6.2 kB view details)

Uploaded Python 3

File details

Details for the file jupyflow-1.0.1.tar.gz.

File metadata

  • Download URL: jupyflow-1.0.1.tar.gz
  • Upload date:
  • Size: 6.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for jupyflow-1.0.1.tar.gz
Algorithm Hash digest
SHA256 67d4438320e2bbb999a992c942280f757f4fa64a5a163098a762412a23200bb2
MD5 5686c7d47d52826f3689bf66beec7e92
BLAKE2b-256 52f15bf3bda3a51c022f58d01ff4eb865dbd2a6ed21ea4c52840727090335081

See more details on using hashes here.

File details

Details for the file jupyflow-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: jupyflow-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 6.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.11.9

File hashes

Hashes for jupyflow-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 760fe00cd17a627876f7cfbe1076081ec34ac7cabad937a1342b5fac071aec95
MD5 552b2d5deb3d0251482bfb87e9d9f3d5
BLAKE2b-256 5fd3acce4295d36cd32115a8f641124c82067f7209bbc53d00215a2e0123216c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page