mkdocs-include-markdown-plugin 7.1.1__tar.gz → 7.1.3__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.
Files changed (15) hide show
  1. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/LICENSE +1 -1
  2. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/PKG-INFO +24 -5
  3. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/README.md +20 -2
  4. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/pyproject.toml +4 -4
  5. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/cache.py +35 -17
  6. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/config.py +1 -0
  7. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/directive.py +1 -2
  8. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/event.py +14 -17
  9. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/plugin.py +6 -9
  10. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/process.py +56 -5
  11. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/.gitignore +0 -0
  12. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/__init__.py +0 -0
  13. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/files_watcher.py +0 -0
  14. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/logger.py +0 -0
  15. {mkdocs_include_markdown_plugin-7.1.1 → mkdocs_include_markdown_plugin-7.1.3}/src/mkdocs_include_markdown_plugin/py.typed +0 -0
@@ -187,7 +187,7 @@
187
187
  same "printed page" as the copyright notice for easier
188
188
  identification within third-party archives.
189
189
 
190
- Copyright 2017-2024 Joe Rickerby and contributors
190
+ Copyright 2017-2025 Joe Rickerby and contributors
191
191
 
192
192
  Licensed under the Apache License, Version 2.0 (the "License");
193
193
  you may not use this file except in compliance with the License.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: mkdocs-include-markdown-plugin
3
- Version: 7.1.1
3
+ Version: 7.1.3
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,7 +9,8 @@ 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: Apache-2.0
12
+ License-Expression: Apache-2.0
13
+ License-File: LICENSE
13
14
  Keywords: includer,markdown,mkdocs,plugin
14
15
  Classifier: Development Status :: 5 - Production/Stable
15
16
  Classifier: Environment :: Console
@@ -135,8 +136,9 @@ plugins:
135
136
  cache: 600
136
137
  ```
137
138
 
138
- In order to use this feature, the dependency [platformdirs] must be installed.
139
- You can include it in the installation of the plugin adding the `cache` extra:
139
+ In order to use this feature, the dependency [platformdirs] must be installed
140
+ or the setting [`cache_dir`](#cache_dir) must be defined. You can include
141
+ [platformdirs] in the installation of the plugin adding the `cache` extra:
140
142
 
141
143
  ```txt
142
144
  # requirements.txt
@@ -145,6 +147,23 @@ mkdocs-include-markdown-plugin[cache]
145
147
 
146
148
  <!-- mdpo-disable-next-line -->
147
149
 
150
+ #### `cache_dir`
151
+
152
+ Directory where cached HTTP requests will be stored. If set, [platformdirs] is not
153
+ needed to be installed to use [`cache`](#cache).
154
+
155
+ ```yaml
156
+ plugins:
157
+ - include-markdown:
158
+ cache: 600
159
+ cache_dir: ./mkdocs-include-markdown-cache
160
+ ```
161
+
162
+ A _.gitignore_ file will be added to the cache directory if not exists to avoid
163
+ committing the cache files.
164
+
165
+ <!-- mdpo-disable-next-line -->
166
+
148
167
  #### `directives`
149
168
 
150
169
  Customize the names of the directives.
@@ -101,8 +101,9 @@ plugins:
101
101
  cache: 600
102
102
  ```
103
103
 
104
- In order to use this feature, the dependency [platformdirs] must be installed.
105
- You can include it in the installation of the plugin adding the `cache` extra:
104
+ In order to use this feature, the dependency [platformdirs] must be installed
105
+ or the setting [`cache_dir`](#cache_dir) must be defined. You can include
106
+ [platformdirs] in the installation of the plugin adding the `cache` extra:
106
107
 
107
108
  ```txt
108
109
  # requirements.txt
@@ -111,6 +112,23 @@ mkdocs-include-markdown-plugin[cache]
111
112
 
112
113
  <!-- mdpo-disable-next-line -->
113
114
 
115
+ #### `cache_dir`
116
+
117
+ Directory where cached HTTP requests will be stored. If set, [platformdirs] is not
118
+ needed to be installed to use [`cache`](#cache).
119
+
120
+ ```yaml
121
+ plugins:
122
+ - include-markdown:
123
+ cache: 600
124
+ cache_dir: ./mkdocs-include-markdown-cache
125
+ ```
126
+
127
+ A _.gitignore_ file will be added to the cache directory if not exists to avoid
128
+ committing the cache files.
129
+
130
+ <!-- mdpo-disable-next-line -->
131
+
114
132
  #### `directives`
115
133
 
116
134
  Customize the names of the directives.
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "mkdocs-include-markdown-plugin"
3
- version = "7.1.1"
3
+ version = "7.1.3"
4
4
  description = "Mkdocs Markdown includer plugin."
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -104,9 +104,9 @@ targets = [{ file = "pyproject.toml" }]
104
104
  [tool.project-config]
105
105
  cache = "2 days"
106
106
  style = [
107
- "gh://mondeja/project-config-styles@v5.4/base/pre-commit/md2po2md.json5",
108
- "gh://mondeja/project-config-styles@v5.4/python/base.json5",
109
- "gh://mondeja/project-config-styles@v5.4/python/mypy.json5",
107
+ "gh://mondeja/project-config-styles@v5.5/base/pre-commit/md2po2md.json5",
108
+ "gh://mondeja/project-config-styles@v5.5/python/base.json5",
109
+ "gh://mondeja/project-config-styles@v5.5/python/mypy.json5",
110
110
  ]
111
111
 
112
112
  [tool.coverage.run]
@@ -9,9 +9,6 @@ import time
9
9
  from importlib.util import find_spec
10
10
 
11
11
 
12
- CACHE_AVAILABLE = find_spec('platformdirs') is not None
13
-
14
-
15
12
  class Cache:
16
13
  """Cache for arbitrary content, one file per entry."""
17
14
 
@@ -46,7 +43,7 @@ class Cache:
46
43
  is_file = stat.S_ISREG(os.stat(fpath).st_mode)
47
44
  except (FileNotFoundError, OSError): # pragma: no cover
48
45
  return None
49
- if is_file:
46
+ if is_file: # pragma: no branch
50
47
  creation_time = self.get_creation_time_from_fpath(fpath)
51
48
  if time.time() < creation_time + self.expiration_seconds:
52
49
  return self.read_file(fpath, encoding=encoding)
@@ -64,31 +61,52 @@ class Cache:
64
61
  def clean(self) -> None:
65
62
  """Clean expired entries from the cache."""
66
63
  for fname in os.listdir(self.cache_dir):
64
+ if fname == '.gitignore':
65
+ continue
67
66
  fpath = os.path.join(self.cache_dir, fname)
68
67
  creation_time = self.get_creation_time_from_fpath(fpath)
69
68
  if time.time() > creation_time + self.expiration_seconds:
70
69
  os.remove(fpath)
71
70
 
72
71
 
73
- def get_cache_directory() -> str | None:
74
- """Get the cache directory."""
75
- if not CACHE_AVAILABLE:
72
+ def get_cache_directory(cache_dir: str) -> str | None:
73
+ """Get cache directory."""
74
+ if cache_dir:
75
+ return cache_dir
76
+
77
+ if not is_platformdirs_installed():
76
78
  return None
77
79
 
78
80
  try:
79
81
  from platformdirs import user_data_dir
80
- except ImportError:
82
+ except ImportError: # pragma: no cover
83
+ return None
84
+ else:
85
+ return user_data_dir('mkdocs-include-markdown-plugin')
86
+
87
+
88
+ def initialize_cache(expiration_seconds: int, cache_dir: str) -> Cache | None:
89
+ """Initialize a cache instance."""
90
+ cache_directory = get_cache_directory(cache_dir)
91
+
92
+ if cache_directory is None:
81
93
  return None
82
94
 
83
- cache_dir = user_data_dir('mkdocs-include-markdown-plugin')
84
- os.makedirs(cache_dir, exist_ok=True)
95
+ os.makedirs(cache_directory, exist_ok=True)
85
96
 
86
- return cache_dir
97
+ # Add a `.gitignore` file to prevent the cache directory from being
98
+ # included in the repository. This is needed because the cache directory
99
+ # can be configured as a relative path with `cache_dir` setting.
100
+ gitignore = os.path.join(cache_directory, '.gitignore')
101
+ if not os.path.exists(gitignore):
102
+ with open(gitignore, 'wb') as f:
103
+ f.write(b'*\n')
87
104
 
105
+ cache = Cache(cache_directory, expiration_seconds)
106
+ cache.clean()
107
+ return cache
88
108
 
89
- def initialize_cache(expiration_seconds: int) -> Cache | None:
90
- """Initialize a cache instance."""
91
- cache_dir = get_cache_directory()
92
- return None if cache_dir is None else Cache(
93
- cache_dir, expiration_seconds,
94
- )
109
+
110
+ def is_platformdirs_installed() -> bool:
111
+ """Check if `platformdirs` package is installed without importing it."""
112
+ return find_spec('platformdirs') is not None
@@ -24,6 +24,7 @@ class PluginConfig(Config): # noqa: D101
24
24
  end = Optional(MkType(str))
25
25
  exclude = ListOfItems(MkType(str), default=[])
26
26
  cache = MkType(int, default=0)
27
+ cache_dir = MkType(str, default='')
27
28
  recursive = MkType(bool, default=True)
28
29
  directives = MkType(
29
30
  dict,
@@ -306,7 +306,6 @@ def resolve_file_paths_to_exclude(
306
306
  docs_dir: str,
307
307
  ) -> list[str]:
308
308
  """Resolve the file paths to exclude for a directive."""
309
- root_dir = None
310
309
  if process.is_absolute_path(exclude_string):
311
310
  return glob.glob(exclude_string, flags=GLOB_FLAGS)
312
311
 
@@ -330,7 +329,7 @@ def resolve_file_paths_to_exclude(
330
329
  )
331
330
  ]
332
331
 
333
- return glob.glob(
332
+ return glob.glob( # pragma: no cover
334
333
  exclude_string,
335
334
  flags=GLOB_FLAGS,
336
335
  root_dir=docs_dir,
@@ -83,21 +83,20 @@ def get_file_content( # noqa: PLR0913, PLR0915
83
83
  http_cache: Cache | None = None,
84
84
  ) -> str:
85
85
  """Return the content of the file to include."""
86
- settings_ignore_paths = []
87
- if settings.exclude is not None:
88
- for path in glob.glob(
89
- [
90
- os.path.join(docs_dir, fp)
91
- if not os.path.isabs(fp)
92
- else fp for fp in settings.exclude
93
- ],
94
- flags=GLOB_FLAGS,
95
- root_dir=docs_dir,
96
- ):
97
- if path not in settings_ignore_paths:
98
- settings_ignore_paths.append(path)
86
+ if settings.exclude:
87
+ settings_ignore_paths = list(glob.glob(
88
+ [
89
+ os.path.join(docs_dir, fp)
90
+ if not os.path.isabs(fp)
91
+ else fp for fp in settings.exclude
92
+ ],
93
+ flags=GLOB_FLAGS,
94
+ root_dir=docs_dir,
95
+ ))
99
96
  if page_src_path in settings_ignore_paths:
100
97
  return markdown
98
+ else:
99
+ settings_ignore_paths = []
101
100
 
102
101
  new_found_include_contents: list[tuple[str, str]] = []
103
102
  new_found_include_markdown_contents: list[tuple[str, str]] = []
@@ -150,8 +149,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
150
149
  for path in resolve_file_paths_to_exclude(
151
150
  exclude_string, page_src_path, docs_dir,
152
151
  ):
153
- if path not in ignore_paths:
154
- ignore_paths.append(path)
152
+ ignore_paths.append(path)
155
153
 
156
154
  file_paths_to_include, is_url = resolve_file_paths_to_include(
157
155
  filename,
@@ -361,8 +359,7 @@ def get_file_content( # noqa: PLR0913, PLR0915
361
359
  for path in resolve_file_paths_to_exclude(
362
360
  exclude_string, page_src_path, docs_dir,
363
361
  ):
364
- if path not in ignore_paths:
365
- ignore_paths.append(path)
362
+ ignore_paths.append(path)
366
363
 
367
364
  file_paths_to_include, is_url = resolve_file_paths_to_include(
368
365
  filename,
@@ -31,22 +31,19 @@ class IncludeMarkdownPlugin(BasePlugin[PluginConfig]):
31
31
 
32
32
  def on_config(self, config: MkDocsConfig) -> MkDocsConfig:
33
33
  if self.config.cache > 0:
34
- cache = initialize_cache(self.config.cache)
34
+ cache = initialize_cache(self.config.cache, self.config.cache_dir)
35
35
  if cache is None:
36
36
  raise PluginError(
37
- 'The "platformdirs" package is required to use the'
38
- ' "cache" option. Install'
39
- ' mkdocs-include-markdown-plugin with the "cache"'
40
- ' extra to install it.',
37
+ 'Either `cache_dir` global setting must be configured or'
38
+ ' `platformdirs` package is required to use the'
39
+ ' `cache` option. Install mkdocs-include-markdown-plugin'
40
+ " with the 'cache' extra to install `platformdirs`.",
41
41
  )
42
- cache.clean()
43
42
  self._cache = cache
44
43
 
45
44
  if '__default' not in self.config.directives: # pragma: no cover
46
45
  for directive in self.config.directives:
47
- if directive not in {
48
- 'include', 'include-markdown',
49
- }:
46
+ if directive not in ('include', 'include-markdown'):
50
47
  raise PluginError(
51
48
  f"Invalid directive name '{directive}' at 'directives'"
52
49
  ' global setting. Valid values are "include" and'
@@ -112,6 +112,49 @@ MARKDOWN_LINK_DEFINITION_REGEX = re.compile(
112
112
  flags=re.VERBOSE | re.MULTILINE,
113
113
  )
114
114
 
115
+ # Matched html image and source definition.
116
+ # e.g. <img src="path/to/image.png" alt="alt-text">
117
+ # e.g. <source src="path/to/image.png" alt="alt-text">
118
+ MARKDOWN_HTML_IMAGE_REGEX = re.compile(
119
+ r'''
120
+ <(?:img|source) # img or source
121
+ (?:\s+ # More than one whitespace
122
+ (?!src=) # Not src=
123
+ [\w-]+ # attribute name
124
+ (?:\s*=\s*)? # arbitrary whitespace (optional)
125
+ (?:
126
+ "[^"]*" # Quoted value (double quote)
127
+ |
128
+ '[^']*' # Quoted value (single quote)
129
+ )?
130
+ )* # Other attributes are repeated 0 or more times
131
+ \s+ # More than one whitespace
132
+ src=["'](\S+?)["'] # src = $1 (double quote or single quote)
133
+ ''',
134
+ flags=re.VERBOSE | re.MULTILINE,
135
+ )
136
+
137
+ # Matched html anchor definition.
138
+ # e.g. <a href="https://example.com">example</a>
139
+ MARKDOWN_HTML_ANCHOR_DEFINITION_REGEX = re.compile(
140
+ r'''
141
+ <a
142
+ (?:\s+ # More than one whitespace
143
+ (?!href=) # Not href=
144
+ [\w-]+ # attribute name
145
+ (?:\s*=\s*)? # arbitrary whitespace (optional)
146
+ (?:
147
+ "[^"]*" # Quoted value (double quote)
148
+ |
149
+ '[^']*' # Quoted value (single quote)
150
+ )?
151
+ )* # Other attributes are repeated 0 or more times
152
+ \s+ # More than one whitespace
153
+ href=["'](\S+?)["']# href = $1 (double quote or single quote)
154
+ ''',
155
+ flags=re.VERBOSE | re.MULTILINE,
156
+ )
157
+
115
158
 
116
159
  def transform_p_by_p_skipping_codeblocks( # noqa: PLR0912, PLR0915
117
160
  markdown: str,
@@ -244,10 +287,10 @@ def rewrite_relative_urls(
244
287
  ``source_path`` will still work when inserted into a file at
245
288
  ``destination_path``.
246
289
  """
247
- def rewrite_url(url: str) -> str:
248
- from urllib.parse import urlparse, urlunparse
290
+ from urllib.parse import urlparse, urlunparse
249
291
 
250
- if is_relative_path(url):
292
+ def rewrite_url(url: str) -> str:
293
+ if is_url(url):
251
294
  return url
252
295
 
253
296
  scheme, netloc, path, params, query, fragment = urlparse(url)
@@ -299,10 +342,18 @@ def rewrite_relative_urls(
299
342
  found_href_url_group_index_3,
300
343
  paragraph,
301
344
  )
302
- return MARKDOWN_LINK_DEFINITION_REGEX.sub(
345
+ paragraph = MARKDOWN_LINK_DEFINITION_REGEX.sub(
303
346
  functools.partial(found_href, url_group_index=2),
304
347
  paragraph,
305
348
  )
349
+ paragraph = MARKDOWN_HTML_IMAGE_REGEX.sub(
350
+ functools.partial(found_href, url_group_index=1),
351
+ paragraph,
352
+ )
353
+ return MARKDOWN_HTML_ANCHOR_DEFINITION_REGEX.sub(
354
+ functools.partial(found_href, url_group_index=1),
355
+ paragraph,
356
+ )
306
357
 
307
358
  return transform_p_by_p_skipping_codeblocks(
308
359
  markdown,
@@ -425,7 +476,7 @@ def increase_headings_offset(markdown: str, offset: int = 0) -> str:
425
476
 
426
477
  def rstrip_trailing_newlines(content: str) -> str:
427
478
  """Removes trailing newlines from a string."""
428
- while content.endswith('\n') or content.endswith('\r'):
479
+ while content.endswith(('\n', '\r')):
429
480
  content = content.rstrip('\r\n')
430
481
  return content
431
482