-
Notifications
You must be signed in to change notification settings - Fork 571
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add NumberedHeadingsPreprocessor
- Loading branch information
Mathias Millet
committed
Oct 19, 2024
1 parent
e159962
commit 4e870ae
Showing
4 changed files
with
133 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import re | ||
|
||
from nbconvert.preprocessors.base import Preprocessor | ||
|
||
|
||
class NumberedHeadingsPreprocessor(Preprocessor): | ||
"""Pre-processor that will rewrite markdown headings to include numberings.""" | ||
|
||
def __init__(self, *args, **kwargs): | ||
super().__init__(*args, **kwargs) | ||
self.current_numbering = [0] | ||
|
||
def format_numbering(self): | ||
"""Return a string representation of the current numbering""" | ||
return ".".join(str(n) for n in self.current_numbering) | ||
|
||
def inc_current_numbering(self, level): | ||
if level > len(self.current_numbering): | ||
self.current_numbering = self.current_numbering + [0] * ( | ||
level - len(self.current_numbering) | ||
) | ||
elif level < len(self.current_numbering): | ||
self.current_numbering = self.current_numbering[:level] | ||
self.current_numbering[level - 1] += 1 | ||
|
||
def transform_markdown_line(self, line, resources): | ||
if m := re.match(r"^(?P<level>#+) (?P<heading>.*)", line): | ||
level = len(m.group("level")) | ||
self.inc_current_numbering(level) | ||
old_heading = m.group("heading").strip() | ||
new_heading = self.format_numbering() + " " + old_heading | ||
return "#" * level + " " + new_heading | ||
|
||
return line | ||
|
||
def preprocess_cell(self, cell, resources, index): | ||
if cell["cell_type"] == "markdown": | ||
cell["source"] = "\n".join( | ||
self.transform_markdown_line(line, resources) | ||
for line in cell["source"].splitlines() | ||
) | ||
|
||
return cell, resources |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
""" | ||
Module with tests for the Numbered Headings preprocessor. | ||
""" | ||
|
||
from nbformat import v4 as nbformat | ||
|
||
from nbconvert.preprocessors.numbered_headings import NumberedHeadingsPreprocessor | ||
|
||
from .base import PreprocessorTestsBase | ||
|
||
MARKDOWN_1 = """ | ||
# Heading 1 | ||
## Sub-heading | ||
some content | ||
""" | ||
|
||
MARKDOWN_1_POST = """ | ||
# 1 Heading 1 | ||
## 1.1 Sub-heading | ||
some content | ||
""" | ||
|
||
|
||
MARKDOWN_2 = """ | ||
## Second sub-heading | ||
# Another main heading | ||
## Sub-heading | ||
some more content | ||
### Third heading | ||
""" | ||
|
||
MARKDOWN_2_POST = """ | ||
## 1.2 Second sub-heading | ||
# 2 Another main heading | ||
## 2.1 Sub-heading | ||
some more content | ||
### 2.1.1 Third heading | ||
""" | ||
|
||
|
||
class TestNumberedHeadings(PreprocessorTestsBase): | ||
def build_notebook(self): | ||
cells = [ | ||
nbformat.new_code_cell(source="$ e $", execution_count=1), | ||
nbformat.new_markdown_cell(source=MARKDOWN_1), | ||
nbformat.new_code_cell(source="$ e $", execution_count=1), | ||
nbformat.new_markdown_cell(source=MARKDOWN_2), | ||
] | ||
|
||
return nbformat.new_notebook(cells=cells) | ||
|
||
def build_preprocessor(self): | ||
"""Make an instance of a preprocessor""" | ||
preprocessor = NumberedHeadingsPreprocessor() | ||
preprocessor.enabled = True | ||
return preprocessor | ||
|
||
def test_constructor(self): | ||
"""Can a ClearOutputPreprocessor be constructed?""" | ||
self.build_preprocessor() | ||
|
||
def test_output(self): | ||
"""Test the output of the NumberedHeadingsPreprocessor""" | ||
nb = self.build_notebook() | ||
res = self.build_resources() | ||
preprocessor = self.build_preprocessor() | ||
nb, res = preprocessor(nb, res) | ||
print(nb.cells[1].source) | ||
assert nb.cells[1].source.strip() == MARKDOWN_1_POST.strip() | ||
assert nb.cells[3].source.strip() == MARKDOWN_2_POST.strip() |