tagrefsorter 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
tagrefsorter/cli.py ADDED
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env python3
2
+ import argparse
3
+ import pathlib
4
+ import sys
5
+ import nbformat
6
+ import re
7
+ from typing import Callable
8
+ from dataclasses import dataclass, field
9
+
10
+
11
+ def parse_args():
12
+ parser = argparse.ArgumentParser(
13
+ description="Jupyter Notebook (.ipynb) を読み込み、LaTeX の tag を整理します"
14
+ )
15
+ parser.add_argument(
16
+ "notebook",
17
+ type=pathlib.Path,
18
+ help="変更する .ipynb ファイルのパス",
19
+ )
20
+ return parser.parse_args()
21
+
22
+
23
+ _LATEX_BLOCK = re.compile(r"\$\$(.*?)\$\$", flags=re.DOTALL)
24
+ _LATEX_ALIGN = re.compile(r"\\begin\{align\}(.*?)\\end\{align\}", flags=re.DOTALL)
25
+ _LATEX_TAG = re.compile(r"\\tag\{(.*?)\}")
26
+
27
+
28
+ @dataclass
29
+ class EditState:
30
+ update_map: dict[str, str] = field(default_factory=dict)
31
+ next_tag: int = 1
32
+
33
+
34
+ def update_nb(nb: nbformat.NotebookNode) -> nbformat.NotebookNode:
35
+ state = EditState()
36
+
37
+ def sub_editor(subtex: str) -> str:
38
+ match = _LATEX_TAG.search(subtex)
39
+ if match:
40
+ old_tag = match.group(1)
41
+ new_tag = str(state.next_tag)
42
+ if old_tag != new_tag:
43
+ state.update_map[old_tag] = new_tag
44
+ subtex = _LATEX_TAG.sub(rf"\\\\tag{{{new_tag}}}", subtex)
45
+ else:
46
+ new_tag = str(state.next_tag)
47
+ subtex += rf"\\tag{{{new_tag}}}"
48
+ state.next_tag += 1
49
+ return subtex
50
+
51
+ def editor(latex: str) -> str:
52
+ match = _LATEX_ALIGN.search(latex)
53
+ if match:
54
+ align_body = match.group(1)
55
+ parts = re.split(r"(\\\\)", align_body)
56
+ new_parts = []
57
+ for part in parts:
58
+ if part == r"\\":
59
+ new_parts.append(r"\\\\")
60
+ else:
61
+ new_parts.append(sub_editor(part))
62
+ new_align_body = "".join(new_parts)
63
+ latex = _LATEX_ALIGN.sub(
64
+ rf"\\begin{{align}}{new_align_body}\\end{{align}}",
65
+ latex,
66
+ )
67
+ else:
68
+ latex = sub_editor(latex)
69
+ return latex
70
+
71
+ def edit_latex_blocks(text: str, editor: Callable[[str], str]) -> str:
72
+ def repl(m: re.Match) -> str:
73
+ inner = m.group(1)
74
+ new_inner = editor(inner)
75
+ return f"$${new_inner}$$\n"
76
+
77
+ return _LATEX_BLOCK.sub(repl, text)
78
+
79
+ for cell in nb.cells:
80
+ if cell.cell_type == "markdown":
81
+ cell.source = edit_latex_blocks(cell.source, editor)
82
+
83
+ for cell in nb.cells:
84
+ if cell.cell_type == "markdown":
85
+ for old_tag, new_tag in state.update_map.items():
86
+ cell.source = re.sub(
87
+ rf"\$\({re.escape(old_tag)}\)\$",
88
+ rf"$({new_tag})$",
89
+ cell.source,
90
+ )
91
+
92
+ return nb
93
+
94
+
95
+ def main():
96
+ args = parse_args()
97
+ nb_path: pathlib.Path = args.notebook
98
+
99
+ if not nb_path.exists():
100
+ print(f"Error: file not found: {nb_path}", file=sys.stderr)
101
+ sys.exit(1)
102
+
103
+ if nb_path.suffix != ".ipynb":
104
+ print("Error: input file must be .ipynb", file=sys.stderr)
105
+ sys.exit(1)
106
+
107
+ nb = nbformat.read(nb_path, as_version=4)
108
+ updated_nb = update_nb(nb)
109
+ nbformat.write(updated_nb, nb_path)
110
+
111
+ print(f"Overwritten: {nb_path}")
@@ -0,0 +1,27 @@
1
+ Metadata-Version: 2.4
2
+ Name: tagrefsorter
3
+ Version: 0.1.0
4
+ Summary: CLI tool to normalize LaTeX \tag numbering in Jupyter Notebook files
5
+ Author-email: SuzuSys <sigmatics@outlook.jp>
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: nbformat>=5.0
10
+
11
+ # tagrefsorter
12
+
13
+ A CLI tool to normalize LaTeX `\tag{}` numbering in Jupyter Notebook (`.ipynb`) files.
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ pip install tagrefsorter
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ ```bash
24
+ tagrefsorter path/to/notebook.ipynb
25
+ ```
26
+
27
+ The notebook is overwritten in place.
@@ -0,0 +1,7 @@
1
+ tagrefsorter/__init__.py,sha256=QTYqXqSTHFRkM9TEgpDFcHvwLbvqHDqvqfQ9EiXkcAM,23
2
+ tagrefsorter/cli.py,sha256=dj_XHBj683C_Sd_zkMGwdfI_u2T32P2600U_DkpURkw,3357
3
+ tagrefsorter-0.1.0.dist-info/METADATA,sha256=86qjMLziXoOmOyZrzMhOocJb-FjKcbOSiVD8IhsTbF4,568
4
+ tagrefsorter-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
5
+ tagrefsorter-0.1.0.dist-info/entry_points.txt,sha256=DNXFQ_v9pd34EnVN1y4TvOfXzSQwt1-GzQ9LZvhjrzs,55
6
+ tagrefsorter-0.1.0.dist-info/top_level.txt,sha256=oiecSoVt5S0bYHETiHJ49zEcRCVtwSkP2_XRGdjr2p0,13
7
+ tagrefsorter-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ tagrefsorter = tagrefsorter.cli:main
@@ -0,0 +1 @@
1
+ tagrefsorter