mkdocs-include-markdown-plugin 7.0.0__tar.gz → 7.1.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/.gitignore +1 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/PKG-INFO +16 -3
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/README.md +14 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/pyproject.toml +1 -1
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/cache.py +24 -19
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/config.py +8 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/directive.py +80 -52
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/event.py +46 -32
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/plugin.py +13 -19
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/process.py +67 -40
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/LICENSE +0 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/__init__.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/files_watcher.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/logger.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.0 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mkdocs-include-markdown-plugin
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.1.1
|
|
4
4
|
Summary: Mkdocs Markdown includer plugin.
|
|
5
5
|
Project-URL: Source, https://github.com/mondeja/mkdocs-include-markdown-plugin
|
|
6
6
|
Project-URL: Documentation, https://github.com/mondeja/mkdocs-include-markdown-plugin#documentation
|
|
@@ -9,8 +9,7 @@ Project-URL: Changelog, https://github.com/mondeja/mkdocs-include-markdown-plugi
|
|
|
9
9
|
Author: Joe Rickerby
|
|
10
10
|
Author-email: Álvaro Mondéjar Rubio <mondejar1994@gmail.com>
|
|
11
11
|
Maintainer-email: Álvaro Mondéjar Rubio <mondejar1994@gmail.com>
|
|
12
|
-
License
|
|
13
|
-
License-File: LICENSE
|
|
12
|
+
License: Apache-2.0
|
|
14
13
|
Keywords: includer,markdown,mkdocs,plugin
|
|
15
14
|
Classifier: Development Status :: 5 - Production/Stable
|
|
16
15
|
Classifier: Environment :: Console
|
|
@@ -144,6 +143,20 @@ You can include it in the installation of the plugin adding the `cache` extra:
|
|
|
144
143
|
mkdocs-include-markdown-plugin[cache]
|
|
145
144
|
```
|
|
146
145
|
|
|
146
|
+
<!-- mdpo-disable-next-line -->
|
|
147
|
+
|
|
148
|
+
#### `directives`
|
|
149
|
+
|
|
150
|
+
Customize the names of the directives.
|
|
151
|
+
|
|
152
|
+
```yaml
|
|
153
|
+
plugins:
|
|
154
|
+
- include-markdown:
|
|
155
|
+
directives:
|
|
156
|
+
include-markdown: include-md
|
|
157
|
+
include: replace
|
|
158
|
+
```
|
|
159
|
+
|
|
147
160
|
### Reference
|
|
148
161
|
|
|
149
162
|
This plugin provides two directives, one to include Markdown files and another
|
|
@@ -109,6 +109,20 @@ You can include it in the installation of the plugin adding the `cache` extra:
|
|
|
109
109
|
mkdocs-include-markdown-plugin[cache]
|
|
110
110
|
```
|
|
111
111
|
|
|
112
|
+
<!-- mdpo-disable-next-line -->
|
|
113
|
+
|
|
114
|
+
#### `directives`
|
|
115
|
+
|
|
116
|
+
Customize the names of the directives.
|
|
117
|
+
|
|
118
|
+
```yaml
|
|
119
|
+
plugins:
|
|
120
|
+
- include-markdown:
|
|
121
|
+
directives:
|
|
122
|
+
include-markdown: include-md
|
|
123
|
+
include: replace
|
|
124
|
+
```
|
|
125
|
+
|
|
112
126
|
### Reference
|
|
113
127
|
|
|
114
128
|
This plugin provides two directives, one to include Markdown files and another
|
|
@@ -2,18 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import base64
|
|
6
5
|
import hashlib
|
|
7
6
|
import os
|
|
7
|
+
import stat
|
|
8
8
|
import time
|
|
9
|
+
from importlib.util import find_spec
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
from platformdirs import user_data_dir
|
|
13
|
-
except ImportError: # pragma: no cover
|
|
14
|
-
CACHE_AVAILABLE = False
|
|
15
|
-
else:
|
|
16
|
-
CACHE_AVAILABLE = True
|
|
12
|
+
CACHE_AVAILABLE = find_spec('platformdirs') is not None
|
|
17
13
|
|
|
18
14
|
|
|
19
15
|
class Cache:
|
|
@@ -29,24 +25,28 @@ class Cache:
|
|
|
29
25
|
|
|
30
26
|
def get_creation_time_from_fpath(self, fpath: str) -> int:
|
|
31
27
|
"""Get creation time of an entry in the cache given its path."""
|
|
32
|
-
with open(fpath,
|
|
28
|
+
with open(fpath, 'rb') as f:
|
|
33
29
|
return int(f.readline())
|
|
34
30
|
|
|
35
31
|
@classmethod
|
|
36
32
|
def generate_unique_key_from_url(cls, url: str) -> str:
|
|
37
33
|
"""Generate a unique key from an URL."""
|
|
38
|
-
return
|
|
39
|
-
hashlib.sha3_512(url.encode()).digest(),
|
|
40
|
-
).decode('utf-8')
|
|
34
|
+
return hashlib.blake2b(url.encode(), digest_size=16).digest().hex()
|
|
41
35
|
|
|
42
36
|
def read_file(self, fpath: str, encoding: str = 'utf-8') -> str: # noqa: D102
|
|
43
|
-
|
|
44
|
-
|
|
37
|
+
f = open(fpath, encoding=encoding) # noqa: SIM115
|
|
38
|
+
content = f.read().split('\n', 1)[1]
|
|
39
|
+
f.close()
|
|
40
|
+
return content
|
|
45
41
|
|
|
46
42
|
def get_(self, url: str, encoding: str = 'utf-8') -> str | None: # noqa: D102
|
|
47
43
|
key = self.generate_unique_key_from_url(url)
|
|
48
44
|
fpath = os.path.join(self.cache_dir, key)
|
|
49
|
-
|
|
45
|
+
try:
|
|
46
|
+
is_file = stat.S_ISREG(os.stat(fpath).st_mode)
|
|
47
|
+
except (FileNotFoundError, OSError): # pragma: no cover
|
|
48
|
+
return None
|
|
49
|
+
if is_file:
|
|
50
50
|
creation_time = self.get_creation_time_from_fpath(fpath)
|
|
51
51
|
if time.time() < creation_time + self.expiration_seconds:
|
|
52
52
|
return self.read_file(fpath, encoding=encoding)
|
|
@@ -56,9 +56,10 @@ class Cache:
|
|
|
56
56
|
def set_(self, url: str, value: str, encoding: str = 'utf-8') -> None: # noqa: D102
|
|
57
57
|
key = self.generate_unique_key_from_url(url)
|
|
58
58
|
fpath = os.path.join(self.cache_dir, key)
|
|
59
|
-
with open(fpath, '
|
|
60
|
-
f
|
|
61
|
-
|
|
59
|
+
with open(fpath, 'wb') as fp:
|
|
60
|
+
now = f'{int(time.time())}\n'
|
|
61
|
+
fp.write(now.encode(encoding))
|
|
62
|
+
fp.write(value.encode(encoding))
|
|
62
63
|
|
|
63
64
|
def clean(self) -> None:
|
|
64
65
|
"""Clean expired entries from the cache."""
|
|
@@ -74,9 +75,13 @@ def get_cache_directory() -> str | None:
|
|
|
74
75
|
if not CACHE_AVAILABLE:
|
|
75
76
|
return None
|
|
76
77
|
|
|
78
|
+
try:
|
|
79
|
+
from platformdirs import user_data_dir
|
|
80
|
+
except ImportError:
|
|
81
|
+
return None
|
|
82
|
+
|
|
77
83
|
cache_dir = user_data_dir('mkdocs-include-markdown-plugin')
|
|
78
|
-
|
|
79
|
-
os.makedirs(cache_dir)
|
|
84
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
80
85
|
|
|
81
86
|
return cache_dir
|
|
82
87
|
|
|
@@ -25,3 +25,11 @@ class PluginConfig(Config): # noqa: D101
|
|
|
25
25
|
exclude = ListOfItems(MkType(str), default=[])
|
|
26
26
|
cache = MkType(int, default=0)
|
|
27
27
|
recursive = MkType(bool, default=True)
|
|
28
|
+
directives = MkType(
|
|
29
|
+
dict,
|
|
30
|
+
default={
|
|
31
|
+
'__default': '',
|
|
32
|
+
'include': 'include',
|
|
33
|
+
'include-markdown': 'include-markdown',
|
|
34
|
+
},
|
|
35
|
+
)
|
|
@@ -2,8 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import functools
|
|
5
6
|
import os
|
|
6
7
|
import re
|
|
8
|
+
import stat
|
|
7
9
|
import string
|
|
8
10
|
from dataclasses import dataclass
|
|
9
11
|
from typing import TYPE_CHECKING
|
|
@@ -21,8 +23,8 @@ class DirectiveBoolArgument: # noqa: D101
|
|
|
21
23
|
regex: re.Pattern[str]
|
|
22
24
|
|
|
23
25
|
|
|
24
|
-
if TYPE_CHECKING:
|
|
25
|
-
from typing import Literal, TypedDict
|
|
26
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
27
|
+
from typing import Callable, Literal, TypedDict
|
|
26
28
|
|
|
27
29
|
DirectiveBoolArgumentsDict = dict[str, DirectiveBoolArgument]
|
|
28
30
|
|
|
@@ -73,11 +75,13 @@ TRUE_FALSE_BOOL_STR = {
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
|
|
78
|
+
@functools.lru_cache
|
|
76
79
|
def arg(arg: str) -> re.Pattern[str]:
|
|
77
80
|
"""Return a compiled regexp to match a boolean argument."""
|
|
78
81
|
return re.compile(rf'{arg}=([{RE_ESCAPED_PUNCTUATION}\w]*)')
|
|
79
82
|
|
|
80
83
|
|
|
84
|
+
@functools.lru_cache
|
|
81
85
|
def str_arg(arg: str) -> re.Pattern[str]:
|
|
82
86
|
"""Return a compiled regexp to match a string argument."""
|
|
83
87
|
return re.compile(
|
|
@@ -87,21 +91,23 @@ def str_arg(arg: str) -> re.Pattern[str]:
|
|
|
87
91
|
|
|
88
92
|
|
|
89
93
|
ARGUMENT_REGEXES = {
|
|
90
|
-
'start': str_arg
|
|
91
|
-
'end': str_arg
|
|
92
|
-
'exclude': str_arg
|
|
93
|
-
'encoding': str_arg
|
|
94
|
+
'start': functools.partial(str_arg, 'start'),
|
|
95
|
+
'end': functools.partial(str_arg, 'end'),
|
|
96
|
+
'exclude': functools.partial(str_arg, 'exclude'),
|
|
97
|
+
'encoding': functools.partial(str_arg, 'encoding'),
|
|
94
98
|
|
|
95
99
|
# bool
|
|
96
|
-
'comments': arg
|
|
97
|
-
'preserve-includer-indent':
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
'
|
|
101
|
-
'
|
|
100
|
+
'comments': functools.partial(arg, 'comments'),
|
|
101
|
+
'preserve-includer-indent': functools.partial(
|
|
102
|
+
arg, 'preserve-includer-indent',
|
|
103
|
+
),
|
|
104
|
+
'dedent': functools.partial(arg, 'dedent'),
|
|
105
|
+
'trailing-newlines': functools.partial(arg, 'trailing-newlines'),
|
|
106
|
+
'rewrite-relative-urls': functools.partial(arg, 'rewrite-relative-urls'),
|
|
107
|
+
'recursive': functools.partial(arg, 'recursive'),
|
|
102
108
|
|
|
103
109
|
# int
|
|
104
|
-
'heading-offset': arg
|
|
110
|
+
'heading-offset': functools.partial(arg, 'heading-offset'),
|
|
105
111
|
}
|
|
106
112
|
|
|
107
113
|
INCLUDE_DIRECTIVE_ARGS = {
|
|
@@ -110,26 +116,29 @@ INCLUDE_DIRECTIVE_ARGS = {
|
|
|
110
116
|
)
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
|
|
119
|
+
WARN_INVALID_DIRECTIVE_ARGS_REGEX = re.compile(
|
|
120
|
+
rf'[\w-]*=[{RE_ESCAPED_PUNCTUATION}\w]*',
|
|
121
|
+
)
|
|
114
122
|
|
|
115
123
|
|
|
116
124
|
def warn_invalid_directive_arguments(
|
|
117
125
|
arguments_string: str,
|
|
118
|
-
directive_lineno: int,
|
|
126
|
+
directive_lineno: Callable[[], int],
|
|
119
127
|
directive: Literal['include', 'include-markdown'],
|
|
120
128
|
page_src_path: str | None,
|
|
121
129
|
docs_dir: str,
|
|
122
130
|
) -> None:
|
|
123
131
|
"""Warns about the invalid arguments passed to a directive."""
|
|
124
|
-
arg_re = re.compile(rf'[\w-]*=[{RE_ESCAPED_PUNCTUATION}\w]*')
|
|
125
132
|
valid_args = (
|
|
126
133
|
INCLUDE_DIRECTIVE_ARGS if directive == 'include'
|
|
127
|
-
else
|
|
134
|
+
else set(ARGUMENT_REGEXES)
|
|
128
135
|
)
|
|
129
|
-
for arg_value in
|
|
136
|
+
for arg_value in WARN_INVALID_DIRECTIVE_ARGS_REGEX.findall(
|
|
137
|
+
arguments_string,
|
|
138
|
+
):
|
|
130
139
|
if arg_value.split('=', 1)[0] not in valid_args:
|
|
131
140
|
location = process.file_lineno_message(
|
|
132
|
-
page_src_path, docs_dir, directive_lineno,
|
|
141
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
133
142
|
)
|
|
134
143
|
logger.warning(
|
|
135
144
|
f"Invalid argument '{arg_value}' in"
|
|
@@ -166,7 +175,7 @@ def parse_string_argument(match: re.Match[str]) -> str | None:
|
|
|
166
175
|
|
|
167
176
|
|
|
168
177
|
def create_include_tag(
|
|
169
|
-
opening_tag: str, closing_tag: str, tag: str
|
|
178
|
+
opening_tag: str, closing_tag: str, tag: str,
|
|
170
179
|
) -> re.Pattern[str]:
|
|
171
180
|
"""Create a regex pattern to match an inclusion tag directive.
|
|
172
181
|
|
|
@@ -174,9 +183,9 @@ def create_include_tag(
|
|
|
174
183
|
INCLUDE_TAG_RE by the effective tag.
|
|
175
184
|
"""
|
|
176
185
|
return re.compile(
|
|
177
|
-
INCLUDE_TAG_RE.replace(' include', f' {tag}').replace(
|
|
178
|
-
'$OPENING_TAG', re.escape(opening_tag),
|
|
179
|
-
).replace('$CLOSING_TAG', re.escape(closing_tag)),
|
|
186
|
+
INCLUDE_TAG_RE.replace(' include', f' {tag}', 1).replace(
|
|
187
|
+
'$OPENING_TAG', re.escape(opening_tag), 1,
|
|
188
|
+
).replace('$CLOSING_TAG', re.escape(closing_tag), 1),
|
|
180
189
|
flags=re.VERBOSE | re.DOTALL,
|
|
181
190
|
)
|
|
182
191
|
|
|
@@ -193,7 +202,7 @@ def parse_bool_options(
|
|
|
193
202
|
for option_name in option_names:
|
|
194
203
|
bool_options[option_name] = DirectiveBoolArgument(
|
|
195
204
|
value=defaults[option_name], # type: ignore
|
|
196
|
-
regex=ARGUMENT_REGEXES[option_name],
|
|
205
|
+
regex=ARGUMENT_REGEXES[option_name](),
|
|
197
206
|
)
|
|
198
207
|
|
|
199
208
|
for arg_name, arg in bool_options.items():
|
|
@@ -209,7 +218,7 @@ def parse_bool_options(
|
|
|
209
218
|
return bool_options, invalid_args
|
|
210
219
|
|
|
211
220
|
|
|
212
|
-
def resolve_file_paths_to_include(
|
|
221
|
+
def resolve_file_paths_to_include( # noqa: PLR0912
|
|
213
222
|
include_string: str,
|
|
214
223
|
includer_page_src_path: str | None,
|
|
215
224
|
docs_dir: str,
|
|
@@ -220,22 +229,29 @@ def resolve_file_paths_to_include(
|
|
|
220
229
|
return [include_string], True
|
|
221
230
|
|
|
222
231
|
if process.is_absolute_path(include_string):
|
|
223
|
-
if os.name == 'nt': # pragma:
|
|
232
|
+
if os.name == 'nt': # pragma: no cover
|
|
224
233
|
# Windows
|
|
225
234
|
fpath = os.path.normpath(include_string)
|
|
226
|
-
|
|
235
|
+
try:
|
|
236
|
+
is_file = stat.S_ISREG(os.stat(fpath).st_mode)
|
|
237
|
+
except (FileNotFoundError, OSError):
|
|
238
|
+
is_file = False
|
|
239
|
+
if not is_file:
|
|
227
240
|
return [], False
|
|
228
241
|
|
|
229
242
|
return process.filter_paths(
|
|
230
243
|
[fpath], ignore_paths,
|
|
231
244
|
), False
|
|
245
|
+
|
|
246
|
+
try:
|
|
247
|
+
is_file = stat.S_ISREG(os.stat(include_string).st_mode)
|
|
248
|
+
except (FileNotFoundError, OSError):
|
|
249
|
+
is_file = False
|
|
232
250
|
return process.filter_paths(
|
|
233
|
-
glob.iglob(
|
|
234
|
-
|
|
235
|
-
flags=GLOB_FLAGS,
|
|
251
|
+
[include_string] if is_file else glob.iglob(
|
|
252
|
+
include_string, flags=GLOB_FLAGS,
|
|
236
253
|
),
|
|
237
|
-
ignore_paths,
|
|
238
|
-
), False
|
|
254
|
+
ignore_paths), False
|
|
239
255
|
|
|
240
256
|
if process.is_relative_path(include_string):
|
|
241
257
|
if includer_page_src_path is None: # pragma: no cover
|
|
@@ -247,29 +263,41 @@ def resolve_file_paths_to_include(
|
|
|
247
263
|
root_dir = os.path.abspath(
|
|
248
264
|
os.path.dirname(includer_page_src_path),
|
|
249
265
|
)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
), False
|
|
260
|
-
|
|
261
|
-
# relative to docs_dir
|
|
262
|
-
return process.filter_paths(
|
|
263
|
-
(
|
|
264
|
-
os.path.normpath(os.path.join(docs_dir, fp))
|
|
266
|
+
paths = []
|
|
267
|
+
include_path = os.path.join(root_dir, include_string)
|
|
268
|
+
try:
|
|
269
|
+
is_file = stat.S_ISREG(os.stat(include_path).st_mode)
|
|
270
|
+
except (FileNotFoundError, OSError):
|
|
271
|
+
is_file = False
|
|
272
|
+
if is_file:
|
|
273
|
+
paths.append(include_path)
|
|
274
|
+
else:
|
|
265
275
|
for fp in glob.iglob(
|
|
266
276
|
include_string,
|
|
267
277
|
flags=GLOB_FLAGS,
|
|
268
|
-
root_dir=
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
ignore_paths,
|
|
272
|
-
|
|
278
|
+
root_dir=root_dir,
|
|
279
|
+
):
|
|
280
|
+
paths.append(os.path.join(root_dir, fp))
|
|
281
|
+
return process.filter_paths(paths, ignore_paths), False
|
|
282
|
+
|
|
283
|
+
# relative to docs_dir
|
|
284
|
+
paths = []
|
|
285
|
+
root_dir = docs_dir
|
|
286
|
+
include_path = os.path.join(root_dir, include_string)
|
|
287
|
+
try:
|
|
288
|
+
is_file = stat.S_ISREG(os.stat(include_path).st_mode)
|
|
289
|
+
except (FileNotFoundError, OSError):
|
|
290
|
+
is_file = False
|
|
291
|
+
if is_file:
|
|
292
|
+
paths.append(include_path)
|
|
293
|
+
else:
|
|
294
|
+
for fp in glob.iglob(
|
|
295
|
+
include_string,
|
|
296
|
+
flags=GLOB_FLAGS,
|
|
297
|
+
root_dir=root_dir,
|
|
298
|
+
):
|
|
299
|
+
paths.append(os.path.join(root_dir, fp))
|
|
300
|
+
return process.filter_paths(paths, ignore_paths), False
|
|
273
301
|
|
|
274
302
|
|
|
275
303
|
def resolve_file_paths_to_exclude(
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import functools
|
|
5
6
|
import html
|
|
6
7
|
import os
|
|
7
8
|
import re
|
|
@@ -17,6 +18,7 @@ from mkdocs_include_markdown_plugin.cache import Cache
|
|
|
17
18
|
from mkdocs_include_markdown_plugin.directive import (
|
|
18
19
|
ARGUMENT_REGEXES,
|
|
19
20
|
GLOB_FLAGS,
|
|
21
|
+
create_include_tag,
|
|
20
22
|
parse_bool_options,
|
|
21
23
|
parse_filename_argument,
|
|
22
24
|
parse_string_argument,
|
|
@@ -28,7 +30,7 @@ from mkdocs_include_markdown_plugin.files_watcher import FilesWatcher
|
|
|
28
30
|
from mkdocs_include_markdown_plugin.logger import logger
|
|
29
31
|
|
|
30
32
|
|
|
31
|
-
if TYPE_CHECKING:
|
|
33
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
32
34
|
from typing import Literal, TypedDict
|
|
33
35
|
|
|
34
36
|
from mkdocs.structure.pages import Page
|
|
@@ -104,7 +106,8 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
104
106
|
match: re.Match[str],
|
|
105
107
|
) -> str:
|
|
106
108
|
directive_match_start = match.start()
|
|
107
|
-
directive_lineno =
|
|
109
|
+
directive_lineno = functools.partial(
|
|
110
|
+
process.lineno_from_content_start,
|
|
108
111
|
markdown,
|
|
109
112
|
directive_match_start,
|
|
110
113
|
)
|
|
@@ -114,7 +117,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
114
117
|
filename, raw_filename = parse_filename_argument(match)
|
|
115
118
|
if filename is None:
|
|
116
119
|
location = process.file_lineno_message(
|
|
117
|
-
page_src_path, docs_dir, directive_lineno,
|
|
120
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
118
121
|
)
|
|
119
122
|
raise PluginError(
|
|
120
123
|
"Found no path passed including with 'include'"
|
|
@@ -131,13 +134,13 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
131
134
|
docs_dir,
|
|
132
135
|
)
|
|
133
136
|
|
|
134
|
-
exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
|
|
137
|
+
exclude_match = ARGUMENT_REGEXES['exclude']().search(arguments_string)
|
|
135
138
|
ignore_paths = [*settings_ignore_paths]
|
|
136
139
|
if exclude_match is not None:
|
|
137
140
|
exclude_string = parse_string_argument(exclude_match)
|
|
138
141
|
if exclude_string is None:
|
|
139
142
|
location = process.file_lineno_message(
|
|
140
|
-
page_src_path, docs_dir, directive_lineno,
|
|
143
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
141
144
|
)
|
|
142
145
|
raise PluginError(
|
|
143
146
|
"Invalid empty 'exclude' argument in 'include'"
|
|
@@ -159,7 +162,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
159
162
|
|
|
160
163
|
if not file_paths_to_include:
|
|
161
164
|
location = process.file_lineno_message(
|
|
162
|
-
page_src_path, docs_dir, directive_lineno,
|
|
165
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
163
166
|
)
|
|
164
167
|
raise PluginError(
|
|
165
168
|
f"No files found including '{raw_filename}' at {location}",
|
|
@@ -176,7 +179,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
176
179
|
)
|
|
177
180
|
if invalid_bool_args:
|
|
178
181
|
location = process.file_lineno_message(
|
|
179
|
-
page_src_path, docs_dir, directive_lineno,
|
|
182
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
180
183
|
)
|
|
181
184
|
raise PluginError(
|
|
182
185
|
f"Invalid value for '{invalid_bool_args[0]}' argument of"
|
|
@@ -184,12 +187,12 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
184
187
|
f' Possible values are true or false.',
|
|
185
188
|
)
|
|
186
189
|
|
|
187
|
-
start_match = ARGUMENT_REGEXES['start'].search(arguments_string)
|
|
190
|
+
start_match = ARGUMENT_REGEXES['start']().search(arguments_string)
|
|
188
191
|
if start_match:
|
|
189
192
|
start = parse_string_argument(start_match)
|
|
190
193
|
if start is None:
|
|
191
194
|
location = process.file_lineno_message(
|
|
192
|
-
page_src_path, docs_dir, directive_lineno,
|
|
195
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
193
196
|
)
|
|
194
197
|
raise PluginError(
|
|
195
198
|
"Invalid empty 'start' argument in 'include' directive at"
|
|
@@ -198,12 +201,12 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
198
201
|
else:
|
|
199
202
|
start = defaults['start']
|
|
200
203
|
|
|
201
|
-
end_match = ARGUMENT_REGEXES['end'].search(arguments_string)
|
|
204
|
+
end_match = ARGUMENT_REGEXES['end']().search(arguments_string)
|
|
202
205
|
if end_match:
|
|
203
206
|
end = parse_string_argument(end_match)
|
|
204
207
|
if end is None:
|
|
205
208
|
location = process.file_lineno_message(
|
|
206
|
-
page_src_path, docs_dir, directive_lineno,
|
|
209
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
207
210
|
)
|
|
208
211
|
raise PluginError(
|
|
209
212
|
"Invalid empty 'end' argument in 'include' directive at"
|
|
@@ -212,12 +215,13 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
212
215
|
else:
|
|
213
216
|
end = defaults['end']
|
|
214
217
|
|
|
215
|
-
encoding_match = ARGUMENT_REGEXES['encoding'].search(
|
|
218
|
+
encoding_match = ARGUMENT_REGEXES['encoding']().search(
|
|
219
|
+
arguments_string)
|
|
216
220
|
if encoding_match:
|
|
217
221
|
encoding = parse_string_argument(encoding_match)
|
|
218
222
|
if encoding is None:
|
|
219
223
|
location = process.file_lineno_message(
|
|
220
|
-
page_src_path, docs_dir, directive_lineno,
|
|
224
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
221
225
|
)
|
|
222
226
|
raise PluginError(
|
|
223
227
|
"Invalid empty 'encoding' argument in 'include'"
|
|
@@ -294,7 +298,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
294
298
|
])
|
|
295
299
|
plural_suffix = 's' if len(file_paths_to_include) > 1 else ''
|
|
296
300
|
location = process.file_lineno_message(
|
|
297
|
-
page_src_path, docs_dir, directive_lineno,
|
|
301
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
298
302
|
)
|
|
299
303
|
logger.warning(
|
|
300
304
|
f"Delimiter {delimiter_name} '{delimiter_value}'"
|
|
@@ -313,7 +317,8 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
313
317
|
match: re.Match[str],
|
|
314
318
|
) -> str:
|
|
315
319
|
directive_match_start = match.start()
|
|
316
|
-
directive_lineno =
|
|
320
|
+
directive_lineno = functools.partial(
|
|
321
|
+
process.lineno_from_content_start,
|
|
317
322
|
markdown,
|
|
318
323
|
directive_match_start,
|
|
319
324
|
)
|
|
@@ -324,7 +329,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
324
329
|
filename, raw_filename = parse_filename_argument(match)
|
|
325
330
|
if filename is None:
|
|
326
331
|
location = process.file_lineno_message(
|
|
327
|
-
page_src_path, docs_dir, directive_lineno,
|
|
332
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
328
333
|
)
|
|
329
334
|
raise PluginError(
|
|
330
335
|
"Found no path passed including with 'include-markdown'"
|
|
@@ -341,13 +346,13 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
341
346
|
docs_dir,
|
|
342
347
|
)
|
|
343
348
|
|
|
344
|
-
exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
|
|
349
|
+
exclude_match = ARGUMENT_REGEXES['exclude']().search(arguments_string)
|
|
345
350
|
ignore_paths = [*settings_ignore_paths]
|
|
346
351
|
if exclude_match is not None:
|
|
347
352
|
exclude_string = parse_string_argument(exclude_match)
|
|
348
353
|
if exclude_string is None:
|
|
349
354
|
location = process.file_lineno_message(
|
|
350
|
-
page_src_path, docs_dir, directive_lineno,
|
|
355
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
351
356
|
)
|
|
352
357
|
raise PluginError(
|
|
353
358
|
"Invalid empty 'exclude' argument in 'include-markdown'"
|
|
@@ -368,7 +373,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
368
373
|
|
|
369
374
|
if not file_paths_to_include:
|
|
370
375
|
location = process.file_lineno_message(
|
|
371
|
-
page_src_path, docs_dir, directive_lineno,
|
|
376
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
372
377
|
)
|
|
373
378
|
raise PluginError(
|
|
374
379
|
f"No files found including '{raw_filename}' at {location}",
|
|
@@ -388,7 +393,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
388
393
|
)
|
|
389
394
|
if invalid_bool_args:
|
|
390
395
|
location = process.file_lineno_message(
|
|
391
|
-
page_src_path, docs_dir, directive_lineno,
|
|
396
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
392
397
|
)
|
|
393
398
|
raise PluginError(
|
|
394
399
|
f"Invalid value for '{invalid_bool_args[0]}' argument of"
|
|
@@ -397,12 +402,12 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
397
402
|
)
|
|
398
403
|
|
|
399
404
|
# start and end arguments
|
|
400
|
-
start_match = ARGUMENT_REGEXES['start'].search(arguments_string)
|
|
405
|
+
start_match = ARGUMENT_REGEXES['start']().search(arguments_string)
|
|
401
406
|
if start_match:
|
|
402
407
|
start = parse_string_argument(start_match)
|
|
403
408
|
if start is None:
|
|
404
409
|
location = process.file_lineno_message(
|
|
405
|
-
page_src_path, docs_dir, directive_lineno,
|
|
410
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
406
411
|
)
|
|
407
412
|
raise PluginError(
|
|
408
413
|
"Invalid empty 'start' argument in 'include-markdown'"
|
|
@@ -411,12 +416,12 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
411
416
|
else:
|
|
412
417
|
start = defaults['start']
|
|
413
418
|
|
|
414
|
-
end_match = ARGUMENT_REGEXES['end'].search(arguments_string)
|
|
419
|
+
end_match = ARGUMENT_REGEXES['end']().search(arguments_string)
|
|
415
420
|
if end_match:
|
|
416
421
|
end = parse_string_argument(end_match)
|
|
417
422
|
if end is None:
|
|
418
423
|
location = process.file_lineno_message(
|
|
419
|
-
page_src_path, docs_dir, directive_lineno,
|
|
424
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
420
425
|
)
|
|
421
426
|
raise PluginError(
|
|
422
427
|
"Invalid empty 'end' argument in 'include-markdown'"
|
|
@@ -425,12 +430,13 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
425
430
|
else:
|
|
426
431
|
end = defaults['end']
|
|
427
432
|
|
|
428
|
-
encoding_match = ARGUMENT_REGEXES['encoding'].search(
|
|
433
|
+
encoding_match = ARGUMENT_REGEXES['encoding']().search(
|
|
434
|
+
arguments_string)
|
|
429
435
|
if encoding_match:
|
|
430
436
|
encoding = parse_string_argument(encoding_match)
|
|
431
437
|
if encoding is None:
|
|
432
438
|
location = process.file_lineno_message(
|
|
433
|
-
page_src_path, docs_dir, directive_lineno,
|
|
439
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
434
440
|
)
|
|
435
441
|
raise PluginError(
|
|
436
442
|
"Invalid empty 'encoding' argument in 'include-markdown'"
|
|
@@ -440,14 +446,14 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
440
446
|
encoding = defaults['encoding']
|
|
441
447
|
|
|
442
448
|
# heading offset
|
|
443
|
-
offset_match = ARGUMENT_REGEXES['heading-offset'].search(
|
|
449
|
+
offset_match = ARGUMENT_REGEXES['heading-offset']().search(
|
|
444
450
|
arguments_string,
|
|
445
451
|
)
|
|
446
452
|
if offset_match:
|
|
447
453
|
offset_raw_value = offset_match[1]
|
|
448
454
|
if offset_raw_value == '':
|
|
449
455
|
location = process.file_lineno_message(
|
|
450
|
-
page_src_path, docs_dir, directive_lineno,
|
|
456
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
451
457
|
)
|
|
452
458
|
raise PluginError(
|
|
453
459
|
"Invalid empty 'heading-offset' argument in"
|
|
@@ -457,7 +463,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
457
463
|
offset = int(offset_raw_value)
|
|
458
464
|
except ValueError:
|
|
459
465
|
location = process.file_lineno_message(
|
|
460
|
-
page_src_path, docs_dir, directive_lineno,
|
|
466
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
461
467
|
)
|
|
462
468
|
raise PluginError(
|
|
463
469
|
f"Invalid 'heading-offset' argument \"{offset_raw_value}\""
|
|
@@ -579,7 +585,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
|
|
|
579
585
|
])
|
|
580
586
|
plural_suffix = 's' if len(file_paths_to_include) > 1 else ''
|
|
581
587
|
location = process.file_lineno_message(
|
|
582
|
-
page_src_path, docs_dir, directive_lineno,
|
|
588
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
583
589
|
)
|
|
584
590
|
logger.warning(
|
|
585
591
|
f"Delimiter {delimiter_name} '{delimiter_value}' of"
|
|
@@ -630,8 +636,16 @@ def on_page_markdown(
|
|
|
630
636
|
page.file.abs_src_path,
|
|
631
637
|
docs_dir,
|
|
632
638
|
{
|
|
633
|
-
'include':
|
|
634
|
-
|
|
639
|
+
'include': create_include_tag(
|
|
640
|
+
config.opening_tag,
|
|
641
|
+
config.closing_tag,
|
|
642
|
+
config.directives.get('include', 'include'),
|
|
643
|
+
),
|
|
644
|
+
'include-markdown': create_include_tag(
|
|
645
|
+
config.opening_tag,
|
|
646
|
+
config.closing_tag,
|
|
647
|
+
config.directives.get('include-markdown', 'include-markdown'),
|
|
648
|
+
),
|
|
635
649
|
},
|
|
636
650
|
{
|
|
637
651
|
'encoding': config.encoding,
|
|
@@ -7,20 +7,18 @@ from functools import cached_property
|
|
|
7
7
|
from typing import TYPE_CHECKING
|
|
8
8
|
|
|
9
9
|
from mkdocs.exceptions import PluginError
|
|
10
|
-
from mkdocs.livereload import LiveReloadServer
|
|
11
10
|
from mkdocs.plugins import BasePlugin, event_priority
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
if TYPE_CHECKING:
|
|
15
|
-
import re
|
|
13
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
16
14
|
|
|
17
15
|
from mkdocs.config.defaults import MkDocsConfig
|
|
16
|
+
from mkdocs.livereload import LiveReloadServer
|
|
18
17
|
from mkdocs.structure.files import Files
|
|
19
18
|
from mkdocs.structure.pages import Page
|
|
20
19
|
|
|
21
20
|
from mkdocs_include_markdown_plugin.cache import Cache, initialize_cache
|
|
22
21
|
from mkdocs_include_markdown_plugin.config import PluginConfig
|
|
23
|
-
from mkdocs_include_markdown_plugin.directive import create_include_tag
|
|
24
22
|
from mkdocs_include_markdown_plugin.event import (
|
|
25
23
|
on_page_markdown as _on_page_markdown,
|
|
26
24
|
)
|
|
@@ -44,22 +42,18 @@ class IncludeMarkdownPlugin(BasePlugin[PluginConfig]):
|
|
|
44
42
|
cache.clean()
|
|
45
43
|
self._cache = cache
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
45
|
+
if '__default' not in self.config.directives: # pragma: no cover
|
|
46
|
+
for directive in self.config.directives:
|
|
47
|
+
if directive not in {
|
|
48
|
+
'include', 'include-markdown',
|
|
49
|
+
}:
|
|
50
|
+
raise PluginError(
|
|
51
|
+
f"Invalid directive name '{directive}' at 'directives'"
|
|
52
|
+
' global setting. Valid values are "include" and'
|
|
53
|
+
' "include-markdown".',
|
|
54
|
+
)
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
def _include_markdown_tag(self) -> re.Pattern[str]:
|
|
58
|
-
return create_include_tag(
|
|
59
|
-
self.config.opening_tag,
|
|
60
|
-
self.config.closing_tag,
|
|
61
|
-
tag='include-markdown',
|
|
62
|
-
)
|
|
56
|
+
return config
|
|
63
57
|
|
|
64
58
|
@cached_property
|
|
65
59
|
def _files_watcher(self) -> FilesWatcher:
|
|
@@ -6,13 +6,12 @@ import functools
|
|
|
6
6
|
import io
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
|
+
import stat
|
|
9
10
|
from collections.abc import Callable, Iterator
|
|
10
|
-
from typing import TYPE_CHECKING
|
|
11
|
-
from urllib.parse import urlparse, urlunparse
|
|
12
|
-
from urllib.request import Request, urlopen
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
13
12
|
|
|
14
13
|
|
|
15
|
-
if TYPE_CHECKING:
|
|
14
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
16
15
|
from typing import Any
|
|
17
16
|
|
|
18
17
|
from mkdocs_include_markdown_plugin.cache import Cache
|
|
@@ -51,9 +50,9 @@ MARKDOWN_LINK_REGEX = re.compile( # noqa: DUO138
|
|
|
51
50
|
)
|
|
52
51
|
\]
|
|
53
52
|
\( # literal paren
|
|
54
|
-
|
|
53
|
+
\s*
|
|
55
54
|
<?(.*?)>? # href = $3
|
|
56
|
-
|
|
55
|
+
\s*
|
|
57
56
|
( # $4
|
|
58
57
|
(['"]) # quote char = $5
|
|
59
58
|
(.*?) # Title = $6
|
|
@@ -145,10 +144,7 @@ def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
|
|
|
145
144
|
for line in io.StringIO(markdown):
|
|
146
145
|
if not _current_fcodeblock_delimiter:
|
|
147
146
|
lstripped_line = line.lstrip()
|
|
148
|
-
if (
|
|
149
|
-
lstripped_line.startswith('```')
|
|
150
|
-
or lstripped_line.startswith('~~~')
|
|
151
|
-
):
|
|
147
|
+
if lstripped_line.startswith(('```', '~~~')):
|
|
152
148
|
_current_fcodeblock_delimiter = lstripped_line[:3]
|
|
153
149
|
process_current_paragraph()
|
|
154
150
|
current_paragraph = ''
|
|
@@ -226,10 +222,7 @@ def transform_line_by_line_skipping_codeblocks(
|
|
|
226
222
|
for line in io.StringIO(markdown):
|
|
227
223
|
if not _current_fcodeblock_delimiter:
|
|
228
224
|
lstripped_line = line.lstrip()
|
|
229
|
-
if (
|
|
230
|
-
lstripped_line.startswith('```')
|
|
231
|
-
or lstripped_line.startswith('~~~')
|
|
232
|
-
):
|
|
225
|
+
if lstripped_line.startswith(('```', '~~~')):
|
|
233
226
|
_current_fcodeblock_delimiter = lstripped_line[:3]
|
|
234
227
|
else:
|
|
235
228
|
line = func(line) # noqa: PLW2901
|
|
@@ -252,28 +245,34 @@ def rewrite_relative_urls(
|
|
|
252
245
|
``destination_path``.
|
|
253
246
|
"""
|
|
254
247
|
def rewrite_url(url: str) -> str:
|
|
248
|
+
from urllib.parse import urlparse, urlunparse
|
|
249
|
+
|
|
250
|
+
if is_relative_path(url):
|
|
251
|
+
return url
|
|
252
|
+
|
|
255
253
|
scheme, netloc, path, params, query, fragment = urlparse(url)
|
|
256
254
|
|
|
257
255
|
# absolute or mail
|
|
258
256
|
if path.startswith('/') or scheme == 'mailto':
|
|
259
257
|
return url
|
|
260
258
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
path = os.path.relpath(
|
|
259
|
+
new_path = os.path.relpath(
|
|
264
260
|
os.path.join(os.path.dirname(source_path), path),
|
|
265
261
|
os.path.dirname(destination_path),
|
|
266
262
|
)
|
|
267
263
|
|
|
268
264
|
# ensure forward slashes are used, on Windows
|
|
269
|
-
|
|
265
|
+
new_path = new_path.replace('\\', '/').replace('//', '/')
|
|
270
266
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
267
|
+
try:
|
|
268
|
+
if path[-1] == '/':
|
|
269
|
+
# the above operation removes a trailing slash,
|
|
270
|
+
# so add it back if it was present in the input
|
|
271
|
+
new_path += '/'
|
|
272
|
+
except IndexError: # pragma: no cover
|
|
273
|
+
pass
|
|
275
274
|
|
|
276
|
-
return urlunparse((scheme, netloc,
|
|
275
|
+
return urlunparse((scheme, netloc, new_path, params, query, fragment))
|
|
277
276
|
|
|
278
277
|
def found_href(m: re.Match[str], url_group_index: int = -1) -> str:
|
|
279
278
|
match_start, match_end = m.span(0)
|
|
@@ -359,9 +358,10 @@ def filter_inclusions( # noqa: PLR0912
|
|
|
359
358
|
if end not in text_to_include:
|
|
360
359
|
expected_end_not_found = True
|
|
361
360
|
|
|
361
|
+
start_split = text_to_include.split(start)
|
|
362
362
|
text_parts = (
|
|
363
|
-
|
|
364
|
-
if
|
|
363
|
+
start_split[1:]
|
|
364
|
+
if len(start_split) > 1 else [text_to_include]
|
|
365
365
|
)
|
|
366
366
|
|
|
367
367
|
for start_text in text_parts:
|
|
@@ -382,25 +382,39 @@ def _transform_negative_offset_func_factory(
|
|
|
382
382
|
offset: int,
|
|
383
383
|
) -> Callable[[str], str]:
|
|
384
384
|
heading_prefix = '#' * abs(offset)
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
385
|
+
|
|
386
|
+
def transform(line: str) -> str:
|
|
387
|
+
try:
|
|
388
|
+
if line[0] != '#':
|
|
389
|
+
return line
|
|
390
|
+
except IndexError: # pragma: no cover
|
|
391
|
+
return line
|
|
392
|
+
if line.startswith(heading_prefix):
|
|
393
|
+
return heading_prefix + line.lstrip('#')
|
|
394
|
+
return '#' + line.lstrip('#')
|
|
395
|
+
|
|
396
|
+
return transform
|
|
390
397
|
|
|
391
398
|
|
|
392
399
|
def _transform_positive_offset_func_factory(
|
|
393
400
|
offset: int,
|
|
394
401
|
) -> Callable[[str], str]:
|
|
395
402
|
heading_prefix = '#' * offset
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
403
|
+
|
|
404
|
+
def transform(line: str) -> str:
|
|
405
|
+
try:
|
|
406
|
+
prefix = line[0]
|
|
407
|
+
except IndexError: # pragma: no cover
|
|
408
|
+
return line
|
|
409
|
+
else:
|
|
410
|
+
return heading_prefix + line if prefix == '#' else line
|
|
411
|
+
|
|
412
|
+
return transform
|
|
399
413
|
|
|
400
414
|
|
|
401
415
|
def increase_headings_offset(markdown: str, offset: int = 0) -> str:
|
|
402
416
|
"""Increases the headings depth of a snippet of Makdown content."""
|
|
403
|
-
if not offset:
|
|
417
|
+
if not offset: # pragma: no cover
|
|
404
418
|
return markdown
|
|
405
419
|
return transform_line_by_line_skipping_codeblocks(
|
|
406
420
|
markdown,
|
|
@@ -444,18 +458,27 @@ def filter_paths(
|
|
|
444
458
|
continue
|
|
445
459
|
|
|
446
460
|
# ignore by dirpath (relative or absolute)
|
|
447
|
-
|
|
461
|
+
fp_split = filepath.split(os.sep)
|
|
462
|
+
fp_split.pop()
|
|
463
|
+
if (os.sep).join(fp_split) in ignore_paths:
|
|
448
464
|
continue
|
|
449
465
|
|
|
450
466
|
# ignore if is a directory
|
|
451
|
-
|
|
452
|
-
|
|
467
|
+
try:
|
|
468
|
+
if not stat.S_ISDIR(os.stat(filepath).st_mode):
|
|
469
|
+
response.append(filepath)
|
|
470
|
+
except (FileNotFoundError, OSError): # pragma: no cover
|
|
471
|
+
continue
|
|
453
472
|
response.sort()
|
|
454
473
|
return response
|
|
455
474
|
|
|
456
475
|
|
|
457
476
|
def is_url(string: str) -> bool:
|
|
458
477
|
"""Determine if a string is an URL."""
|
|
478
|
+
if ':' not in string: # fast path
|
|
479
|
+
return False
|
|
480
|
+
from urllib.parse import urlparse
|
|
481
|
+
|
|
459
482
|
try:
|
|
460
483
|
result = urlparse(string)
|
|
461
484
|
return all([result.scheme, result.netloc])
|
|
@@ -470,13 +493,15 @@ def is_relative_path(string: str) -> bool:
|
|
|
470
493
|
|
|
471
494
|
def is_absolute_path(string: str) -> bool:
|
|
472
495
|
"""Check if a string looks like an absolute path."""
|
|
473
|
-
return
|
|
496
|
+
return string.startswith((os.sep, '/'))
|
|
474
497
|
|
|
475
498
|
|
|
476
499
|
def read_file(file_path: str, encoding: str) -> str:
|
|
477
500
|
"""Read a file and return its content."""
|
|
478
|
-
|
|
479
|
-
|
|
501
|
+
f = open(file_path, encoding=encoding) # noqa: SIM115
|
|
502
|
+
content = f.read()
|
|
503
|
+
f.close()
|
|
504
|
+
return content
|
|
480
505
|
|
|
481
506
|
|
|
482
507
|
def read_url(
|
|
@@ -485,6 +510,8 @@ def read_url(
|
|
|
485
510
|
encoding: str = 'utf-8',
|
|
486
511
|
) -> Any:
|
|
487
512
|
"""Read an HTTP location and return its content."""
|
|
513
|
+
from urllib.request import Request, urlopen
|
|
514
|
+
|
|
488
515
|
if http_cache is not None:
|
|
489
516
|
cached_content = http_cache.get_(url, encoding)
|
|
490
517
|
if cached_content is not None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|