Passed
Push — develop ( 3f2dd5...5b1b90 )
by Christophe
02:10
created

pandoc_codeblock_include._main.include()   A

Complexity

Conditions 4

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 7
nop 2
dl 0
loc 18
rs 10
c 0
b 0
f 0
1
#!/usr/bin/env python
2
3
"""
4
Pandoc filter for including file in code block.
5
"""
6
7
from collections.abc import Iterable
8
from typing import Any
9
10
from panflute import CodeBlock, Doc, Element, debug, run_filters
11
12
13
def parse_include(parsed: dict[str, Any], name: str, value: str) -> None:
14
    """
15
    Extract include information from attributes.
16
17
    Arguments
18
    ---------
19
    parsed
20
        A dictionnary of attributes
21
    name
22
        attribute name
23
    value
24
        attribute value
25
    """
26
    if name == "include":
27
        try:
28
            with open(value, encoding="utf-8") as stream:
29
                file_content = stream.readlines()
30
            parsed["content"] = file_content
31
            parsed["include"] = value
32
        except OSError:
33
            debug("[WARNING] pandoc-codeblock-include: " + value + " not found")
34
        except UnicodeDecodeError:
35
            debug(
36
                "[WARNING] pandoc-codeblock-include: file "
37
                + value
38
                + " is not encoded in utf-8"
39
            )
40
41
42
def parse_start_from(parsed: dict[str, Any], name: str, value: str) -> None:
43
    """
44
    Extract startFrom information from attributes.
45
46
    Arguments
47
    ---------
48
    parsed
49
        A dictionnary of attributes
50
    name
51
        attribute name
52
    value
53
        attribute value
54
    """
55
    if name == "startFrom":
56
        try:
57
            parsed["start_from"] = int(value) - 1
58
        except ValueError:
59
            debug(
60
                "[WARNING] pandoc-codeblock-include: "
61
                + value
62
                + " is not a correct integer"
63
            )
64
65
66
def parse_end_at(parsed: dict[str, Any], name: str, value: str) -> None:
67
    """
68
    Extract information from attributes.
69
70
    Arguments
71
    ---------
72
    parsed
73
        A dictionnary of attributes
74
    name
75
        attribute name
76
    value
77
        attribute value
78
    """
79
    if name == "endAt":
80
        try:
81
            parsed["end_at"] = int(value)
82
        except ValueError:
83
            debug(
84
                "[WARNING] pandoc-codeblock-include: "
85
                + value
86
                + " is not a correct integer"
87
            )
88
89
90
def parse_attributes(items: Iterable[tuple[str, str]]) -> dict[str, str]:
91
    """
92
    Extract usefull information from attributes.
93
94
    Arguments
95
    ---------
96
    items
97
        element attributes
98
99
    Returns
100
    -------
101
    dict[str, str]
102
        a mapping containing possible 'content', 'start_from' and 'end_at'
103
    """
104
    parsed: dict[str, Any] = {}
105
    for name, value in items:
106
        parse_include(parsed, name, value)
107
        parse_start_from(parsed, name, value)
108
        parse_end_at(parsed, name, value)
109
    return parsed
110
111
112
def inject_content(elem: Element, parsed: dict[str, Any]) -> None:
113
    """
114
    Inject parsed attributes into element content.
115
116
    Arguments
117
    ---------
118
    elem
119
        Pandoc element
120
    parsed
121
        dictionnary of attributes
122
    """
123
    start_from = parsed.get("start_from", 0)
124
    end_at = parsed.get("end_at", len(parsed["content"]))
125
    text = parsed["content"][start_from:end_at]
126
127
    # inject file content in element text
128
    try:
129
        elem.text = "".join(line.decode("utf8") for line in text)
130
    except AttributeError:
131
        elem.text = "".join(text)
132
    except UnicodeDecodeError:
133
        debug(
134
            "[WARNING] pandoc-codeblock-include: file "
135
            + parsed["include"]
136
            + " is not encoded in utf-8"
137
        )
138
139
140
def clear_latex_attributes(elem: Element) -> None:
141
    """
142
    Clear LaTeX attributes.
143
144
    Arguments
145
    ---------
146
    elem
147
        current element
148
    """
149
    # Clear the attributes else latex will get a problem with the listings
150
    for attribute in ("include", "endAt"):
151
        if attribute in elem.attributes:
152
            del elem.attributes[attribute]
153
154
155
def include(elem: Element, doc: Doc) -> None:
156
    """
157
    Transform CodeBlock element.
158
159
    Arguments
160
    ---------
161
    elem
162
        current element
163
    doc
164
        pandoc document
165
    """
166
    # Is it a CodeBlock?
167
    if isinstance(elem, CodeBlock):
168
        parsed = parse_attributes(elem.attributes.items())
169
        if "content" in parsed:
170
            inject_content(elem, parsed)
171
        if doc.format in ("latex", "beamer"):
172
            clear_latex_attributes(elem)
173
174
175
def main(doc: Doc | None = None) -> Doc:
176
    """
177
    Convert the pandoc document.
178
179
    Arguments
180
    ---------
181
    doc
182
        pandoc document
183
184
    Returns
185
    -------
186
    Doc
187
        The modified pandoc document.
188
    """
189
    return run_filters([include], doc=doc)
190
191
192
if __name__ == "__main__":
193
    main()
194