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.
tagrefsorter/__init__.py
ADDED
|
@@ -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 @@
|
|
|
1
|
+
tagrefsorter
|