-
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 df53b2b
Showing
4 changed files
with
140 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,50 @@ | ||
""" | ||
Preprocessor that transforms markdown cells: Insert numbering in from of heading | ||
""" | ||
|
||
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): | ||
"""Init""" | ||
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): | ||
"""Increase internal counter keeping track of numberings""" | ||
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): | ||
"""Rewrites one markdown line, if needed""" | ||
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() |