Coverage for src/midgy/front_matter.py: 90%
103 statements
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-02 14:04 -0800
« prev ^ index » next coverage.py v6.5.0, created at 2022-12-02 14:04 -0800
1from enum import Enum
2from re import compile
4__all__ = ("load",)
5SHEBANG = compile("^#!(?P<interpreter>\S+)\s+(?P<command>\S*)")
8FM = Enum("FM", {"yaml": "-", "toml": "+"})
11def strip_and_classifiy(x):
12 x = x.strip()
13 return FM(x[0]), "".join(x.splitlines(True)[1:-1])
16def load(x):
17 """load front matter including the delimiters.
19 --- and +++ reference yaml and toml front matter respectively."""
20 kind, x = strip_and_classifiy(x)
21 return {FM.toml: load_toml, FM.yaml: load_yaml}[kind](x)
24def load_toml(x):
25 from tomli import loads
27 return loads(x)
30def load_yaml(x):
31 return _get_yaml_loader()(x)
34def _get_yaml_loader():
35 loader = getattr(_get_yaml_loader, "loader", None)
36 if loader:
37 return loader
38 try:
39 from ruamel.yaml import safe_load as load
40 except ModuleNotFoundError:
41 try:
42 from yaml import safe_load as load
43 except ModuleNotFoundError:
44 from json import loads as load
45 _get_yaml_loader.loader = load
46 return load
49def _shebang_lexer(state, startLine, endLine, silent):
50 start = state.bMarks[startLine] + state.tShift[startLine]
51 maximum = state.eMarks[startLine]
53 # our front matter allows for indents and can occur at positions
54 # other than 0
55 # this should filter out non-front matter
56 if state.tokens:
57 return False
59 m = SHEBANG.match(state.src[start:maximum])
60 if not m:
61 return False
63 parent = state.parentType
64 line_max = state.lineMax
66 # this will prevent lazy continuations from ever going past our end marker
67 state.lineMax = startLine
69 token = state.push("shebang", "", 0)
70 token.hidden = True
71 token.content = state.getLines(startLine, startLine + 1, 0, True)
72 token.block = True
74 state.parentType = parent
75 state.lineMax = line_max
76 state.line = startLine + 1
77 token.map = [startLine, state.line]
79 return True
82def _front_matter_lexer(state, startLine, endLine, silent):
83 auto_closed = False
84 start = state.bMarks[startLine] + state.tShift[startLine]
85 maximum = state.eMarks[startLine]
86 src_len = len(state.src)
87 if state.tokens:
88 if len(state.tokens) == 1:
89 if state.tokens[0].type != "shebang":
90 return False
92 # our front matter allows for indents and can occur at positions
93 # other than 0
94 # this should filter out non-front matter
96 if state.sCount[startLine]:
97 return False
99 if state.tokens:
100 if len(state.tokens) > 1:
101 return False
102 if state.tokens[-1].type != "shebang":
103 return False
105 markup = None
106 if state.srcCharCode[start] == ord("-"):
107 markup = "-"
108 elif state.srcCharCode[start] == ord("+"):
109 markup = "+"
110 else:
111 return False
113 if state.srcCharCode[start + 1 : maximum] != tuple(map(ord, (markup, markup))):
114 return False
116 # Search for the end of the block
117 nextLine = startLine
119 while True:
120 nextLine += 1
121 if nextLine >= endLine:
122 return False
124 start = state.bMarks[nextLine] + state.tShift[nextLine]
125 maximum = state.eMarks[nextLine]
127 if start < maximum and state.sCount[nextLine] < state.blkIndent:
128 break
130 if ord(markup) != state.srcCharCode[start]:
131 continue
133 if state.sCount[nextLine] - state.blkIndent >= 4:
134 continue
136 if state.srcCharCode[start + 1 : maximum] == tuple(map(ord, (markup, markup))):
137 auto_closed = True
138 nextLine += 1
139 break
141 parent = state.parentType
142 line_max = state.lineMax
143 state.parentType = "container"
145 # this will prevent lazy continuations from ever going past our end marker
146 state.lineMax = nextLine
148 token = state.push("front_matter", "", 0)
149 token.hidden = True
150 token.markup = markup
151 token.content = state.getLines(startLine, nextLine, 0, True)
152 token.block = True
154 state.parentType = parent
155 state.lineMax = line_max
156 state.line = nextLine
157 token.map = [startLine, state.line]
159 return True