22Preprocessor that transforms markdown cells: Insert numbering in from of heading
33"""
44
5- import re
6-
75from nbconvert .preprocessors .base import Preprocessor
86
7+ try : # for Mistune >= 3.0
8+ import mistune
9+ from mistune .core import BlockState
10+ from mistune .renderers .markdown import MarkdownRenderer
11+
12+ MISTUNE_V3 = True
13+ except ImportError : # for Mistune >= 2.0
14+ MISTUNE_V3 = False
15+
16+ WRONG_MISTUNE_VERSION_ERROR = "Error: NumberedHeadingsPreprocessor requires mistune >= 3"
17+
918
1019class NumberedHeadingsPreprocessor (Preprocessor ):
1120 """Pre-processor that will rewrite markdown headings to include numberings."""
1221
1322 def __init__ (self , * args , ** kwargs ):
1423 """Init"""
1524 super ().__init__ (* args , ** kwargs )
25+ if not MISTUNE_V3 :
26+ raise Exception (WRONG_MISTUNE_VERSION_ERROR )
27+ self .md_parser = mistune .create_markdown (renderer = None )
28+ self .md_renderer = MarkdownRenderer ()
1629 self .current_numbering = [0 ]
1730
1831 def format_numbering (self ):
@@ -29,23 +42,24 @@ def _inc_current_numbering(self, level):
2942 self .current_numbering = self .current_numbering [:level ]
3043 self .current_numbering [level - 1 ] += 1
3144
32- def _transform_markdown_line (self , line , resources ):
33- """Rewrites one markdown line, if needed"""
34- if m := re .match (r"^(?P<level>#+) (?P<heading>.*)" , line ):
35- level = len (m .group ("level" ))
36- self ._inc_current_numbering (level )
37- old_heading = m .group ("heading" ).strip ()
38- new_heading = self .format_numbering () + " " + old_heading
39- return "#" * level + " " + new_heading
40-
41- return line
42-
4345 def preprocess_cell (self , cell , resources , index ):
4446 """Rewrites all the headings in the cell if it is markdown"""
45- if cell ["cell_type" ] == "markdown" :
46- cell ["source" ] = "\n " .join (
47- self ._transform_markdown_line (line , resources )
48- for line in cell ["source" ].splitlines ()
49- )
50-
51- return cell , resources
47+ if cell ["cell_type" ] != "markdown" :
48+ return cell , resources
49+ try :
50+ md_ast = self .md_parser (cell ["source" ])
51+ assert not isinstance (md_ast , str ) # type guard ; str is not returned by ast parser
52+ for element in md_ast :
53+ if element ["type" ] == "heading" :
54+ level = element ["attrs" ]["level" ]
55+ self ._inc_current_numbering (level )
56+ if len (element ["children" ]) > 0 :
57+ child = element ["children" ][0 ]
58+ if child ["type" ] == "text" :
59+ child ["raw" ] = self .format_numbering () + " " + child ["raw" ]
60+ new_source = self .md_renderer (md_ast , BlockState ())
61+ cell ["source" ] = new_source
62+ return cell , resources
63+ except Exception :
64+ self .log .warning ("Failed processing cell headings" , exc_info = True )
65+ return cell , resources
0 commit comments