mkdocs-include-markdown-plugin 7.0.1__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.1 → mkdocs_include_markdown_plugin-7.1.1}/PKG-INFO +15 -1
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/README.md +14 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/pyproject.toml +1 -1
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/cache.py +18 -11
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/config.py +8 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/directive.py +47 -27
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/event.py +45 -31
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/plugin.py +12 -16
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/process.py +53 -22
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/.gitignore +0 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/LICENSE +0 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/__init__.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/files_watcher.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.1 → mkdocs_include_markdown_plugin-7.1.1}/src/mkdocs_include_markdown_plugin/logger.py +0 -0
- {mkdocs_include_markdown_plugin-7.0.1 → 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
|
|
@@ -143,6 +143,20 @@ You can include it in the installation of the plugin adding the `cache` extra:
|
|
|
143
143
|
mkdocs-include-markdown-plugin[cache]
|
|
144
144
|
```
|
|
145
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
|
+
|
|
146
160
|
### Reference
|
|
147
161
|
|
|
148
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
|
|
@@ -4,15 +4,12 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import hashlib
|
|
6
6
|
import os
|
|
7
|
+
import stat
|
|
7
8
|
import time
|
|
9
|
+
from importlib.util import find_spec
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
from platformdirs import user_data_dir
|
|
12
|
-
except ImportError: # pragma: no cover
|
|
13
|
-
CACHE_AVAILABLE = False
|
|
14
|
-
else:
|
|
15
|
-
CACHE_AVAILABLE = True
|
|
12
|
+
CACHE_AVAILABLE = find_spec('platformdirs') is not None
|
|
16
13
|
|
|
17
14
|
|
|
18
15
|
class Cache:
|
|
@@ -37,13 +34,19 @@ class Cache:
|
|
|
37
34
|
return hashlib.blake2b(url.encode(), digest_size=16).digest().hex()
|
|
38
35
|
|
|
39
36
|
def read_file(self, fpath: str, encoding: str = 'utf-8') -> str: # noqa: D102
|
|
40
|
-
|
|
41
|
-
|
|
37
|
+
f = open(fpath, encoding=encoding) # noqa: SIM115
|
|
38
|
+
content = f.read().split('\n', 1)[1]
|
|
39
|
+
f.close()
|
|
40
|
+
return content
|
|
42
41
|
|
|
43
42
|
def get_(self, url: str, encoding: str = 'utf-8') -> str | None: # noqa: D102
|
|
44
43
|
key = self.generate_unique_key_from_url(url)
|
|
45
44
|
fpath = os.path.join(self.cache_dir, key)
|
|
46
|
-
|
|
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:
|
|
47
50
|
creation_time = self.get_creation_time_from_fpath(fpath)
|
|
48
51
|
if time.time() < creation_time + self.expiration_seconds:
|
|
49
52
|
return self.read_file(fpath, encoding=encoding)
|
|
@@ -72,9 +75,13 @@ def get_cache_directory() -> str | None:
|
|
|
72
75
|
if not CACHE_AVAILABLE:
|
|
73
76
|
return None
|
|
74
77
|
|
|
78
|
+
try:
|
|
79
|
+
from platformdirs import user_data_dir
|
|
80
|
+
except ImportError:
|
|
81
|
+
return None
|
|
82
|
+
|
|
75
83
|
cache_dir = user_data_dir('mkdocs-include-markdown-plugin')
|
|
76
|
-
|
|
77
|
-
os.makedirs(cache_dir)
|
|
84
|
+
os.makedirs(cache_dir, exist_ok=True)
|
|
78
85
|
|
|
79
86
|
return cache_dir
|
|
80
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
|
|
@@ -22,7 +24,7 @@ class DirectiveBoolArgument: # noqa: D101
|
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
if TYPE_CHECKING: # pragma: no cover
|
|
25
|
-
from typing import Literal, TypedDict
|
|
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,8 +116,6 @@ INCLUDE_DIRECTIVE_ARGS = {
|
|
|
110
116
|
)
|
|
111
117
|
}
|
|
112
118
|
|
|
113
|
-
INCLUDE_MARKDOWN_DIRECTIVE_ARGS = set(ARGUMENT_REGEXES)
|
|
114
|
-
|
|
115
119
|
WARN_INVALID_DIRECTIVE_ARGS_REGEX = re.compile(
|
|
116
120
|
rf'[\w-]*=[{RE_ESCAPED_PUNCTUATION}\w]*',
|
|
117
121
|
)
|
|
@@ -119,7 +123,7 @@ WARN_INVALID_DIRECTIVE_ARGS_REGEX = re.compile(
|
|
|
119
123
|
|
|
120
124
|
def warn_invalid_directive_arguments(
|
|
121
125
|
arguments_string: str,
|
|
122
|
-
directive_lineno: int,
|
|
126
|
+
directive_lineno: Callable[[], int],
|
|
123
127
|
directive: Literal['include', 'include-markdown'],
|
|
124
128
|
page_src_path: str | None,
|
|
125
129
|
docs_dir: str,
|
|
@@ -127,15 +131,14 @@ def warn_invalid_directive_arguments(
|
|
|
127
131
|
"""Warns about the invalid arguments passed to a directive."""
|
|
128
132
|
valid_args = (
|
|
129
133
|
INCLUDE_DIRECTIVE_ARGS if directive == 'include'
|
|
130
|
-
else
|
|
134
|
+
else set(ARGUMENT_REGEXES)
|
|
131
135
|
)
|
|
132
|
-
for arg_value in
|
|
133
|
-
WARN_INVALID_DIRECTIVE_ARGS_REGEX,
|
|
136
|
+
for arg_value in WARN_INVALID_DIRECTIVE_ARGS_REGEX.findall(
|
|
134
137
|
arguments_string,
|
|
135
138
|
):
|
|
136
139
|
if arg_value.split('=', 1)[0] not in valid_args:
|
|
137
140
|
location = process.file_lineno_message(
|
|
138
|
-
page_src_path, docs_dir, directive_lineno,
|
|
141
|
+
page_src_path, docs_dir, directive_lineno(),
|
|
139
142
|
)
|
|
140
143
|
logger.warning(
|
|
141
144
|
f"Invalid argument '{arg_value}' in"
|
|
@@ -172,7 +175,7 @@ def parse_string_argument(match: re.Match[str]) -> str | None:
|
|
|
172
175
|
|
|
173
176
|
|
|
174
177
|
def create_include_tag(
|
|
175
|
-
opening_tag: str, closing_tag: str, tag: str
|
|
178
|
+
opening_tag: str, closing_tag: str, tag: str,
|
|
176
179
|
) -> re.Pattern[str]:
|
|
177
180
|
"""Create a regex pattern to match an inclusion tag directive.
|
|
178
181
|
|
|
@@ -199,7 +202,7 @@ def parse_bool_options(
|
|
|
199
202
|
for option_name in option_names:
|
|
200
203
|
bool_options[option_name] = DirectiveBoolArgument(
|
|
201
204
|
value=defaults[option_name], # type: ignore
|
|
202
|
-
regex=ARGUMENT_REGEXES[option_name],
|
|
205
|
+
regex=ARGUMENT_REGEXES[option_name](),
|
|
203
206
|
)
|
|
204
207
|
|
|
205
208
|
for arg_name, arg in bool_options.items():
|
|
@@ -226,19 +229,28 @@ def resolve_file_paths_to_include( # noqa: PLR0912
|
|
|
226
229
|
return [include_string], True
|
|
227
230
|
|
|
228
231
|
if process.is_absolute_path(include_string):
|
|
229
|
-
if os.name == 'nt': # pragma:
|
|
232
|
+
if os.name == 'nt': # pragma: no cover
|
|
230
233
|
# Windows
|
|
231
234
|
fpath = os.path.normpath(include_string)
|
|
232
|
-
|
|
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:
|
|
233
240
|
return [], False
|
|
234
241
|
|
|
235
242
|
return process.filter_paths(
|
|
236
243
|
[fpath], ignore_paths,
|
|
237
244
|
), False
|
|
238
245
|
|
|
246
|
+
try:
|
|
247
|
+
is_file = stat.S_ISREG(os.stat(include_string).st_mode)
|
|
248
|
+
except (FileNotFoundError, OSError):
|
|
249
|
+
is_file = False
|
|
239
250
|
return process.filter_paths(
|
|
240
|
-
[include_string] if
|
|
241
|
-
|
|
251
|
+
[include_string] if is_file else glob.iglob(
|
|
252
|
+
include_string, flags=GLOB_FLAGS,
|
|
253
|
+
),
|
|
242
254
|
ignore_paths), False
|
|
243
255
|
|
|
244
256
|
if process.is_relative_path(include_string):
|
|
@@ -253,7 +265,11 @@ def resolve_file_paths_to_include( # noqa: PLR0912
|
|
|
253
265
|
)
|
|
254
266
|
paths = []
|
|
255
267
|
include_path = os.path.join(root_dir, include_string)
|
|
256
|
-
|
|
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:
|
|
257
273
|
paths.append(include_path)
|
|
258
274
|
else:
|
|
259
275
|
for fp in glob.iglob(
|
|
@@ -268,7 +284,11 @@ def resolve_file_paths_to_include( # noqa: PLR0912
|
|
|
268
284
|
paths = []
|
|
269
285
|
root_dir = docs_dir
|
|
270
286
|
include_path = os.path.join(root_dir, include_string)
|
|
271
|
-
|
|
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:
|
|
272
292
|
paths.append(include_path)
|
|
273
293
|
else:
|
|
274
294
|
for fp in glob.iglob(
|
|
@@ -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,
|
|
@@ -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
13
|
if TYPE_CHECKING: # pragma: no cover
|
|
15
|
-
import re
|
|
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,20 +42,18 @@ class IncludeMarkdownPlugin(BasePlugin[PluginConfig]):
|
|
|
44
42
|
cache.clean()
|
|
45
43
|
self._cache = cache
|
|
46
44
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
+
)
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
return create_include_tag(
|
|
57
|
-
self.config.opening_tag,
|
|
58
|
-
self.config.closing_tag,
|
|
59
|
-
tag='include-markdown',
|
|
60
|
-
)
|
|
56
|
+
return config
|
|
61
57
|
|
|
62
58
|
@cached_property
|
|
63
59
|
def _files_watcher(self) -> FilesWatcher:
|
|
@@ -6,10 +6,9 @@ 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
14
|
if TYPE_CHECKING: # pragma: no cover
|
|
@@ -246,10 +245,15 @@ def rewrite_relative_urls(
|
|
|
246
245
|
``destination_path``.
|
|
247
246
|
"""
|
|
248
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
|
+
|
|
249
253
|
scheme, netloc, path, params, query, fragment = urlparse(url)
|
|
250
254
|
|
|
251
255
|
# absolute or mail
|
|
252
|
-
if
|
|
256
|
+
if path.startswith('/') or scheme == 'mailto':
|
|
253
257
|
return url
|
|
254
258
|
|
|
255
259
|
new_path = os.path.relpath(
|
|
@@ -260,10 +264,13 @@ def rewrite_relative_urls(
|
|
|
260
264
|
# ensure forward slashes are used, on Windows
|
|
261
265
|
new_path = new_path.replace('\\', '/').replace('//', '/')
|
|
262
266
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
|
267
274
|
|
|
268
275
|
return urlunparse((scheme, netloc, new_path, params, query, fragment))
|
|
269
276
|
|
|
@@ -351,9 +358,10 @@ def filter_inclusions( # noqa: PLR0912
|
|
|
351
358
|
if end not in text_to_include:
|
|
352
359
|
expected_end_not_found = True
|
|
353
360
|
|
|
361
|
+
start_split = text_to_include.split(start)
|
|
354
362
|
text_parts = (
|
|
355
|
-
|
|
356
|
-
if
|
|
363
|
+
start_split[1:]
|
|
364
|
+
if len(start_split) > 1 else [text_to_include]
|
|
357
365
|
)
|
|
358
366
|
|
|
359
367
|
for start_text in text_parts:
|
|
@@ -374,20 +382,34 @@ def _transform_negative_offset_func_factory(
|
|
|
374
382
|
offset: int,
|
|
375
383
|
) -> Callable[[str], str]:
|
|
376
384
|
heading_prefix = '#' * abs(offset)
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
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
|
|
382
397
|
|
|
383
398
|
|
|
384
399
|
def _transform_positive_offset_func_factory(
|
|
385
400
|
offset: int,
|
|
386
401
|
) -> Callable[[str], str]:
|
|
387
402
|
heading_prefix = '#' * offset
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
391
413
|
|
|
392
414
|
|
|
393
415
|
def increase_headings_offset(markdown: str, offset: int = 0) -> str:
|
|
@@ -442,8 +464,11 @@ def filter_paths(
|
|
|
442
464
|
continue
|
|
443
465
|
|
|
444
466
|
# ignore if is a directory
|
|
445
|
-
|
|
446
|
-
|
|
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
|
|
447
472
|
response.sort()
|
|
448
473
|
return response
|
|
449
474
|
|
|
@@ -452,6 +477,8 @@ def is_url(string: str) -> bool:
|
|
|
452
477
|
"""Determine if a string is an URL."""
|
|
453
478
|
if ':' not in string: # fast path
|
|
454
479
|
return False
|
|
480
|
+
from urllib.parse import urlparse
|
|
481
|
+
|
|
455
482
|
try:
|
|
456
483
|
result = urlparse(string)
|
|
457
484
|
return all([result.scheme, result.netloc])
|
|
@@ -471,8 +498,10 @@ def is_absolute_path(string: str) -> bool:
|
|
|
471
498
|
|
|
472
499
|
def read_file(file_path: str, encoding: str) -> str:
|
|
473
500
|
"""Read a file and return its content."""
|
|
474
|
-
|
|
475
|
-
|
|
501
|
+
f = open(file_path, encoding=encoding) # noqa: SIM115
|
|
502
|
+
content = f.read()
|
|
503
|
+
f.close()
|
|
504
|
+
return content
|
|
476
505
|
|
|
477
506
|
|
|
478
507
|
def read_url(
|
|
@@ -481,6 +510,8 @@ def read_url(
|
|
|
481
510
|
encoding: str = 'utf-8',
|
|
482
511
|
) -> Any:
|
|
483
512
|
"""Read an HTTP location and return its content."""
|
|
513
|
+
from urllib.request import Request, urlopen
|
|
514
|
+
|
|
484
515
|
if http_cache is not None:
|
|
485
516
|
cached_content = http_cache.get_(url, encoding)
|
|
486
517
|
if cached_content is not None:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|