mkdocs-simple-plugin 3.0.0__tar.gz → 3.2.0__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_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/PKG-INFO +3 -4
- mkdocs_simple_plugin-3.2.0/mkdocs_simple_plugin/README.md +55 -0
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/generator.py +2 -3
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/plugin.py +93 -43
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/semiliterate.py +149 -124
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/simple.py +50 -35
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/pyproject.toml +2 -3
- mkdocs_simple_plugin-3.0.0/mkdocs_simple_plugin/README.md +0 -42
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/.gitignore +0 -0
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/LICENSE +0 -0
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/README.md +0 -0
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/.pages +0 -0
- {mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: mkdocs-simple-plugin
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.2.0
|
|
4
4
|
Summary: Plugin for adding simple wiki site creation from markdown files interspersed within your code with MkDocs.
|
|
5
5
|
Project-URL: Documentation, http://www.althack.dev/mkdocs-simple-plugin
|
|
6
6
|
Project-URL: Homepage, http://www.althack.dev/mkdocs-simple-plugin
|
|
@@ -15,7 +15,6 @@ Classifier: Intended Audience :: Developers
|
|
|
15
15
|
Classifier: Intended Audience :: Information Technology
|
|
16
16
|
Classifier: Programming Language :: Python
|
|
17
17
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.7
|
|
19
18
|
Classifier: Programming Language :: Python :: 3.8
|
|
20
19
|
Classifier: Programming Language :: Python :: 3.9
|
|
21
20
|
Classifier: Programming Language :: Python :: 3.10
|
|
@@ -23,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.11
|
|
|
23
22
|
Requires-Python: >=3
|
|
24
23
|
Requires-Dist: click>=7.1
|
|
25
24
|
Requires-Dist: markupsafe>=2.1.1
|
|
26
|
-
Requires-Dist: mkdocs>=1.
|
|
25
|
+
Requires-Dist: mkdocs>=1.6.0
|
|
27
26
|
Requires-Dist: pyyaml>=6.0
|
|
28
27
|
Description-Content-Type: text/markdown
|
|
29
28
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Package Guide
|
|
2
|
+
|
|
3
|
+
## Prerequisites
|
|
4
|
+
|
|
5
|
+
{% include "setup.snippet" %}
|
|
6
|
+
|
|
7
|
+
## Building
|
|
8
|
+
|
|
9
|
+
{% include "build.snippet" %}
|
|
10
|
+
|
|
11
|
+
## Testing
|
|
12
|
+
|
|
13
|
+
{% include "tests/linters.snippet" %}
|
|
14
|
+
|
|
15
|
+
{% include "tests/unit_tests.snippet" %}
|
|
16
|
+
|
|
17
|
+
{% include "tests/integration_tests.snippet" %}
|
|
18
|
+
|
|
19
|
+
{% include "tests/local_tests.snippet" %}
|
|
20
|
+
|
|
21
|
+
## VSCode
|
|
22
|
+
|
|
23
|
+
This package includes a preconfigured Visual Studio Code (VSCode) workspace and development container, making it easier to get started with developing your plugin.
|
|
24
|
+
|
|
25
|
+
To get started with developing your plugin in VSCode, follow these steps:
|
|
26
|
+
|
|
27
|
+
1. **Open the project in VSCode** Open VSCode and select the "Open Folder" option from the File menu. Navigate to the location where you've saved the project and select the root folder of the project.
|
|
28
|
+
|
|
29
|
+
2. **Connect to the development container** VSCode will automatically detect the presence of the development container and prompt you to connect to it. Follow the on-screen instructions to connect to the container.
|
|
30
|
+
|
|
31
|
+
3. **Run the build task** To build the plugin, you can use the preconfigured build task in VSCode. This task is defined in the `build.sh` file and can be run by using the "Run Build Task" option from the Terminal menu or by using the Ctrl + Shift + B shortcut.
|
|
32
|
+
|
|
33
|
+
4. **Debug the plugin** You can use the VSCode debugger to inspect the code and debug your plugin. The debugger is configured in the launch.json file and can be started by using the "Start Debugging" option from the Debug menu or by using the F5 key.
|
|
34
|
+
|
|
35
|
+
For more information on how to use VSCode and Docker for development, please refer to [how I develop with VSCode and Docker](https://allisonthackston.com/articles/docker-development.html) and [how I use VSCode tasks](https://allisonthackston.com/articles/vscode-tasks.html).
|
|
36
|
+
|
|
37
|
+
## Packaging
|
|
38
|
+
|
|
39
|
+
The project uses Hatch to build and package the plugin [](https://github.com/pypa/hatch)
|
|
40
|
+
|
|
41
|
+
Hatch is a Python packaging tool that helps simplify the process of building and distributing Python packages. It automates many manual steps, such as creating a setup script, creating a distribution package, and uploading the package to a repository, allowing developers to focus on writing code. Hatch is flexible and customizable, with options for specifying dependencies, including additional files, and setting up test suites, and is actively maintained with a growing community of users and contributors.
|
|
42
|
+
|
|
43
|
+
### Build the package
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
hatch build
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Publish the package
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
hatch publish
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Please note that you may need to set up the appropriate credentials for the repository before you can publish the package. If you encounter any issues with publishing the package, please refer to the [Hatch documentation](https://hatch.pypa.io/latest/) for guidance.
|
|
@@ -68,7 +68,6 @@ def default_config():
|
|
|
68
68
|
# Set the config variables via environment if exist
|
|
69
69
|
maybe_set_string("site_name")
|
|
70
70
|
maybe_set_string("site_url")
|
|
71
|
-
maybe_set_string("site_dir")
|
|
72
71
|
maybe_set_string("repo_url")
|
|
73
72
|
maybe_set_dict("theme", "name")
|
|
74
73
|
return config
|
|
@@ -86,7 +85,7 @@ def write_config(config_file, config):
|
|
|
86
85
|
"""Write configuration file."""
|
|
87
86
|
if os.path.dirname(config_file):
|
|
88
87
|
os.makedirs(os.path.dirname(config_file), exist_ok=True)
|
|
89
|
-
with open(config_file, 'w+') as file:
|
|
88
|
+
with open(config_file, 'w+', encoding="utf-8") as file:
|
|
90
89
|
try:
|
|
91
90
|
yaml.dump(
|
|
92
91
|
data=config,
|
|
@@ -106,7 +105,7 @@ def setup_config(config_file="mkdocs.yml"):
|
|
|
106
105
|
# from the folder name.
|
|
107
106
|
write_config(config_file, config)
|
|
108
107
|
# Open the config file to verify settings.
|
|
109
|
-
with open(config_file, 'r') as stream:
|
|
108
|
+
with open(config_file, 'r', encoding="utf-8") as stream:
|
|
110
109
|
try:
|
|
111
110
|
local_config = yaml.load(stream, yaml.Loader)
|
|
112
111
|
if local_config:
|
|
@@ -90,14 +90,16 @@ mkdocs serve
|
|
|
90
90
|
import os
|
|
91
91
|
import tempfile
|
|
92
92
|
import time
|
|
93
|
-
import
|
|
94
|
-
|
|
93
|
+
from typing import Callable, Literal
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
from mkdocs.plugins import BasePlugin
|
|
98
|
-
from mkdocs.config import config_options
|
|
95
|
+
import yaml
|
|
99
96
|
from mkdocs import config as mkdocs_config
|
|
100
97
|
from mkdocs import utils
|
|
98
|
+
from mkdocs.config import config_options
|
|
99
|
+
from mkdocs.config.defaults import MkDocsConfig
|
|
100
|
+
from mkdocs.livereload import LiveReloadServer
|
|
101
|
+
from mkdocs.plugins import BasePlugin
|
|
102
|
+
from mkdocs.structure.files import File, Files
|
|
101
103
|
|
|
102
104
|
from mkdocs_simple_plugin.simple import Simple
|
|
103
105
|
|
|
@@ -107,22 +109,48 @@ class SimplePlugin(BasePlugin):
|
|
|
107
109
|
|
|
108
110
|
# md file=config_scheme.snippet
|
|
109
111
|
config_scheme = (
|
|
110
|
-
# ### include_folders
|
|
112
|
+
# ### include_folders (renamed)
|
|
113
|
+
#
|
|
114
|
+
# Renamed [folders](#folders)
|
|
115
|
+
('include_folders', config_options.Deprecated(
|
|
116
|
+
moved_to="folders", removed=False)),
|
|
117
|
+
#
|
|
118
|
+
# ### folders
|
|
111
119
|
#
|
|
112
120
|
# Directories whose name matches a glob pattern in this list will be
|
|
113
121
|
# searched for documentation
|
|
114
|
-
('
|
|
122
|
+
('folders', config_options.Type(list, default=['*'])),
|
|
115
123
|
#
|
|
116
|
-
# ### ignore_folders
|
|
124
|
+
# ### ignore_folders (renamed)
|
|
125
|
+
#
|
|
126
|
+
# Renamed [ignore](#ignore)
|
|
127
|
+
('ignore_folders', config_options.Deprecated(
|
|
128
|
+
moved_to="ignore", removed=False)),
|
|
129
|
+
#
|
|
130
|
+
# ### ignore
|
|
117
131
|
#
|
|
118
132
|
# Directories whose name matches a glob pattern in this list will NOT be
|
|
119
133
|
# searched for documentation.
|
|
120
|
-
('
|
|
134
|
+
('ignore', config_options.Type(
|
|
135
|
+
list,
|
|
136
|
+
default=[
|
|
137
|
+
"venv",
|
|
138
|
+
".cache/**",
|
|
139
|
+
".devcontainer/**",
|
|
140
|
+
".github/**",
|
|
141
|
+
".vscode/**",
|
|
142
|
+
"**/__pycache__/**",
|
|
143
|
+
".git/**",
|
|
144
|
+
"*.egg-info",
|
|
145
|
+
])),
|
|
121
146
|
#
|
|
122
|
-
# ### ignore_hidden
|
|
147
|
+
# ### ignore_hidden (deprecated)
|
|
123
148
|
#
|
|
124
149
|
# Hidden directories will not be searched if this is true.
|
|
125
|
-
('ignore_hidden', config_options.
|
|
150
|
+
('ignore_hidden', config_options.Deprecated(
|
|
151
|
+
moved_to=None,
|
|
152
|
+
message="Common ignore files have been added to 'ignore' instead",
|
|
153
|
+
removed=False)),
|
|
126
154
|
#
|
|
127
155
|
# ### merge_docs_dir
|
|
128
156
|
#
|
|
@@ -132,17 +160,35 @@ class SimplePlugin(BasePlugin):
|
|
|
132
160
|
# the result.
|
|
133
161
|
('merge_docs_dir', config_options.Type(bool, default=True)),
|
|
134
162
|
#
|
|
135
|
-
# ### build_docs_dir
|
|
163
|
+
# ### build_docs_dir (renamed)
|
|
164
|
+
#
|
|
165
|
+
# Renamed [build_dir](#build_dir)
|
|
166
|
+
('build_docs_dir', config_options.Deprecated(
|
|
167
|
+
moved_to="build_dir", removed=False)),
|
|
168
|
+
#
|
|
169
|
+
# ### build_dir
|
|
136
170
|
#
|
|
137
171
|
# If set, the directory where docs will be collated to be build.
|
|
138
172
|
# Otherwise, the build docs directory will be a temporary directory.
|
|
139
|
-
('
|
|
173
|
+
('build_dir', config_options.Type(str, default='')),
|
|
174
|
+
#
|
|
175
|
+
# #### copy
|
|
140
176
|
#
|
|
141
|
-
#
|
|
177
|
+
# If set, docs will be copied to the build_docs_dir.
|
|
178
|
+
# Otherwise, files will be used in place.
|
|
179
|
+
('copy', config_options.Type(bool, default=False)),
|
|
180
|
+
#
|
|
181
|
+
# ### include_extensions (renamed)
|
|
182
|
+
#
|
|
183
|
+
# Renamed [include](#include)
|
|
184
|
+
('include_extensions', config_options.Deprecated(
|
|
185
|
+
moved_to="include", message="", removed=False)),
|
|
186
|
+
#
|
|
187
|
+
# ### include
|
|
142
188
|
#
|
|
143
189
|
# Any file in the searched directories whose name contains a string in
|
|
144
190
|
# this list will simply be copied to the generated documentation.
|
|
145
|
-
('
|
|
191
|
+
('include',
|
|
146
192
|
config_options.Type(
|
|
147
193
|
list,
|
|
148
194
|
default=[
|
|
@@ -260,16 +306,21 @@ class SimplePlugin(BasePlugin):
|
|
|
260
306
|
# PY2 returns a byte string by default. The Unicode prefix ensures a
|
|
261
307
|
# Unicode string is returned. And it makes MkDocs temp dirs easier to
|
|
262
308
|
# identify.
|
|
263
|
-
self.
|
|
309
|
+
self.tmp_build_dir = tempfile.mkdtemp(prefix="mkdocs_simple_")
|
|
264
310
|
self.paths = None
|
|
265
311
|
self.dirty = False
|
|
266
312
|
self.last_build_time = None
|
|
267
313
|
|
|
268
|
-
def on_startup(self,
|
|
314
|
+
def on_startup(self,
|
|
315
|
+
*,
|
|
316
|
+
command: Literal['build',
|
|
317
|
+
'gh-deploy',
|
|
318
|
+
'serve'],
|
|
319
|
+
dirty: bool):
|
|
269
320
|
"""Configure the plugin on startup."""
|
|
270
321
|
self.dirty = dirty
|
|
271
322
|
|
|
272
|
-
def on_config(self, config
|
|
323
|
+
def on_config(self, config: MkDocsConfig):
|
|
273
324
|
"""Update configuration to use a temporary build directory."""
|
|
274
325
|
# Save the config for documentation
|
|
275
326
|
default_config = dict((name, config_option.default)
|
|
@@ -285,57 +336,55 @@ class SimplePlugin(BasePlugin):
|
|
|
285
336
|
config_site_dir = get_config_site_dir(config.config_file_path)
|
|
286
337
|
|
|
287
338
|
# Set the build docs dir to tmp location if not set by user
|
|
288
|
-
if not self.config['
|
|
289
|
-
self.config['
|
|
339
|
+
if not self.config['build_dir'] and self.config['merge_docs_dir']:
|
|
340
|
+
self.config['build_dir'] = config['docs_dir']
|
|
290
341
|
else:
|
|
291
|
-
self.config['
|
|
342
|
+
self.config['build_dir'] = self.tmp_build_dir
|
|
292
343
|
|
|
293
344
|
utils.log.info(
|
|
294
|
-
"mkdocs-simple-plugin:
|
|
295
|
-
self.config['
|
|
345
|
+
"mkdocs-simple-plugin: build_dir: %s",
|
|
346
|
+
self.config['build_dir'])
|
|
296
347
|
|
|
297
348
|
# Create build directory
|
|
298
|
-
os.makedirs(self.config['
|
|
349
|
+
os.makedirs(self.config['build_dir'], exist_ok=True)
|
|
299
350
|
# Save original docs directory location
|
|
300
351
|
self.orig_docs_dir = config['docs_dir']
|
|
301
352
|
# Update the docs_dir with our temporary one if not merging
|
|
302
353
|
if not self.config['merge_docs_dir']:
|
|
303
|
-
config['docs_dir'] = self.config['
|
|
354
|
+
config['docs_dir'] = self.config['build_dir']
|
|
304
355
|
# Add all markdown extensions to include list
|
|
305
|
-
self.config['
|
|
306
|
-
self.config['
|
|
356
|
+
self.config['include'] = list(utils.markdown_extensions) + \
|
|
357
|
+
self.config['include']
|
|
307
358
|
|
|
308
359
|
# Always ignore the output paths
|
|
309
360
|
self.config["ignore_paths"] = [
|
|
310
361
|
os.path.abspath(config_site_dir),
|
|
311
362
|
os.path.abspath(config['site_dir']),
|
|
312
|
-
os.path.abspath(self.config['
|
|
363
|
+
os.path.abspath(self.config['build_dir'])]
|
|
313
364
|
if self.config['merge_docs_dir']:
|
|
314
365
|
self.config["ignore_paths"].append(
|
|
315
366
|
os.path.abspath(config['docs_dir']))
|
|
316
367
|
return config
|
|
317
368
|
|
|
318
|
-
def on_files(self, files: Files, *,
|
|
369
|
+
def on_files(self, files: Files, /, *,
|
|
370
|
+
config: MkDocsConfig):
|
|
319
371
|
"""Update files based on plugin settings."""
|
|
320
372
|
# Configure simple
|
|
321
373
|
simple = Simple(**self.config)
|
|
322
374
|
|
|
323
375
|
# Save paths to add to watch if serving
|
|
324
|
-
|
|
376
|
+
do_copy = self.config["copy"]
|
|
377
|
+
self.paths = simple.build_docs(
|
|
378
|
+
self.dirty, self.last_build_time, do_copy)
|
|
325
379
|
self.last_build_time = time.time()
|
|
326
380
|
|
|
327
381
|
if not self.config["merge_docs_dir"]:
|
|
328
382
|
# If not merging, remove files that are from the docs dir
|
|
329
|
-
|
|
330
|
-
for file in files.
|
|
331
|
-
if file.abs_src_path.startswith(
|
|
332
|
-
os.path.abspath(config['docs_dir'])):
|
|
383
|
+
abs_docs_dir = os.path.abspath(config['docs_dir'])
|
|
384
|
+
for _, file in files.src_uris.items():
|
|
385
|
+
if file.abs_src_path.startswith(abs_docs_dir):
|
|
333
386
|
files.remove(file)
|
|
334
387
|
|
|
335
|
-
dedupe_files = {}
|
|
336
|
-
for file in files:
|
|
337
|
-
dedupe_files[file.abs_dest_path] = file
|
|
338
|
-
|
|
339
388
|
for path in self.paths:
|
|
340
389
|
file = File(
|
|
341
390
|
src_dir=os.path.abspath(path.output_root),
|
|
@@ -343,17 +392,18 @@ class SimplePlugin(BasePlugin):
|
|
|
343
392
|
dest_dir=config.site_dir,
|
|
344
393
|
use_directory_urls=config["use_directory_urls"]
|
|
345
394
|
)
|
|
346
|
-
if file.
|
|
347
|
-
files.remove(
|
|
395
|
+
if file.src_uri in files.src_uris:
|
|
396
|
+
files.remove(file)
|
|
348
397
|
files.append(file)
|
|
349
398
|
return files
|
|
350
399
|
|
|
351
|
-
def on_serve(self, server, *, config,
|
|
400
|
+
def on_serve(self, server: LiveReloadServer, /, *, config: MkDocsConfig,
|
|
401
|
+
builder: Callable):
|
|
352
402
|
"""Add files to watch server."""
|
|
353
403
|
# don't watch the build directory
|
|
354
404
|
# pylint: disable=protected-access
|
|
355
|
-
if self.config["
|
|
356
|
-
server.unwatch(self.config["
|
|
405
|
+
if self.config["build_dir"] in server._watched_paths:
|
|
406
|
+
server.unwatch(self.config["build_dir"])
|
|
357
407
|
|
|
358
408
|
# watch all the doc files
|
|
359
409
|
for path in self.paths:
|
{mkdocs_simple_plugin-3.0.0 → mkdocs_simple_plugin-3.2.0}/mkdocs_simple_plugin/semiliterate.py
RENAMED
|
@@ -1,24 +1,72 @@
|
|
|
1
1
|
"""Semiliterate module handles document extraction from source files."""
|
|
2
|
+
from io import TextIOWrapper
|
|
2
3
|
import os
|
|
3
4
|
import re
|
|
4
5
|
|
|
5
|
-
from
|
|
6
|
-
|
|
6
|
+
from dataclasses import dataclass
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
"""Returns line with EOL."""
|
|
10
|
-
if not line:
|
|
11
|
-
return None
|
|
12
|
-
return line if line.endswith("\n") else line + '\n'
|
|
8
|
+
from mkdocs import utils
|
|
13
9
|
|
|
14
10
|
|
|
15
|
-
def
|
|
11
|
+
def _get_match(pattern: re.Pattern, line: str) -> re.Match:
|
|
16
12
|
"""Returns the match for the given pattern."""
|
|
17
13
|
if not pattern:
|
|
18
14
|
return None
|
|
19
15
|
return pattern.search(line)
|
|
20
16
|
|
|
21
17
|
|
|
18
|
+
@dataclass
|
|
19
|
+
class InlineParams:
|
|
20
|
+
"""Inline parameters for extraction."""
|
|
21
|
+
# md file=inline_params.snippet
|
|
22
|
+
# These parameters should be on the same line as the start block.
|
|
23
|
+
#
|
|
24
|
+
# For example:
|
|
25
|
+
#
|
|
26
|
+
# ```
|
|
27
|
+
# /**md file="new_name.md" trim=2 content="^\s*\/\/\s?(.*)$"
|
|
28
|
+
# ```
|
|
29
|
+
#
|
|
30
|
+
# #### Set output file name
|
|
31
|
+
#
|
|
32
|
+
# Filename is relative to the folder of the file being processed.
|
|
33
|
+
#
|
|
34
|
+
# ```
|
|
35
|
+
# file=<name>
|
|
36
|
+
# ```
|
|
37
|
+
filename_pattern: re.Pattern = re.compile(r"file=[\"']?(\w+.\w+)[\"']?\b")
|
|
38
|
+
filename: str = None
|
|
39
|
+
#
|
|
40
|
+
# #### Trim the front of a line
|
|
41
|
+
#
|
|
42
|
+
# Useful for removing leading spaces.
|
|
43
|
+
#
|
|
44
|
+
# ```
|
|
45
|
+
# trim=#
|
|
46
|
+
# ```
|
|
47
|
+
trim_pattern: re.Pattern = re.compile(r"trim=[\"']?(\d+)[\"']?\b")
|
|
48
|
+
trim: int = 0
|
|
49
|
+
# #### Capture content
|
|
50
|
+
#
|
|
51
|
+
# Regex expression to capture content, otherwise all lines are captured.
|
|
52
|
+
#
|
|
53
|
+
# ```
|
|
54
|
+
# content=<regex>
|
|
55
|
+
# ```
|
|
56
|
+
content_pattern: re.Pattern = re.compile(r"content=[\"']?([^\"']*)[\"']?")
|
|
57
|
+
content: str = None
|
|
58
|
+
#
|
|
59
|
+
# #### Stop capture
|
|
60
|
+
#
|
|
61
|
+
# Regex expression to indicate capture should stop.
|
|
62
|
+
#
|
|
63
|
+
# ```
|
|
64
|
+
# stop=<regex>
|
|
65
|
+
# ```
|
|
66
|
+
stop_pattern: re.Pattern = re.compile(r"stop=[\"']?([^\"']*)[\"']?")
|
|
67
|
+
# /md
|
|
68
|
+
|
|
69
|
+
|
|
22
70
|
class ExtractionPattern:
|
|
23
71
|
"""An ExtractionPattern for a file."""
|
|
24
72
|
# md file="ExtractionPattern.snippet"
|
|
@@ -89,90 +137,49 @@ class ExtractionPattern:
|
|
|
89
137
|
else:
|
|
90
138
|
self.replace.append((re.compile(item[0]), item[1]))
|
|
91
139
|
|
|
92
|
-
|
|
93
|
-
# These parameters should be on the same line as the start block.
|
|
94
|
-
#
|
|
95
|
-
# For example:
|
|
96
|
-
#
|
|
97
|
-
# ```
|
|
98
|
-
# /**md file="new_name.md" trim=2 content="^\s*\/\/\s?(.*)$"
|
|
99
|
-
# ```
|
|
100
|
-
#
|
|
101
|
-
# #### Set output file name
|
|
102
|
-
#
|
|
103
|
-
# Filename is relative to the folder of the file being processed.
|
|
104
|
-
#
|
|
105
|
-
# ```
|
|
106
|
-
# file=<name>
|
|
107
|
-
# ```
|
|
108
|
-
self._filename_pattern = re.compile(r"file=[\"']?(\w+.\w+)[\"']?\b")
|
|
109
|
-
self._filename = None
|
|
110
|
-
#
|
|
111
|
-
# #### Trim the front of a line
|
|
112
|
-
#
|
|
113
|
-
# Useful for removing leading spaces.
|
|
114
|
-
#
|
|
115
|
-
# ```
|
|
116
|
-
# trim=#
|
|
117
|
-
# ```
|
|
118
|
-
self._trim_pattern = re.compile(r"trim=[\"']?(\d+)[\"']?\b")
|
|
119
|
-
self._trim = 0
|
|
120
|
-
# #### Capture content
|
|
121
|
-
#
|
|
122
|
-
# Regex expression to capture content, otherwise all lines are captured.
|
|
123
|
-
#
|
|
124
|
-
# ```
|
|
125
|
-
# content=<regex>
|
|
126
|
-
# ```
|
|
127
|
-
self._content_pattern = re.compile(r"content=[\"']?([^\"']*)[\"']?")
|
|
128
|
-
self._content = None
|
|
129
|
-
#
|
|
130
|
-
# #### Stop capture
|
|
131
|
-
#
|
|
132
|
-
# Regex expression to indicate capture should stop.
|
|
133
|
-
#
|
|
134
|
-
# ```
|
|
135
|
-
# stop=<regex>
|
|
136
|
-
# ```
|
|
137
|
-
self._stop_pattern = re.compile(r"stop=[\"']?([^\"']*)[\"']?")
|
|
138
|
-
# /md
|
|
140
|
+
self.inline = InlineParams()
|
|
139
141
|
|
|
140
142
|
def setup(self, line: str) -> None:
|
|
141
143
|
"""Process input parameters."""
|
|
142
|
-
|
|
143
|
-
|
|
144
|
+
setup_inline = InlineParams()
|
|
145
|
+
|
|
146
|
+
file_match = _get_match(setup_inline.filename_pattern, line)
|
|
144
147
|
if file_match and file_match.lastindex:
|
|
145
|
-
|
|
148
|
+
setup_inline.filename = file_match[file_match.lastindex]
|
|
146
149
|
|
|
147
|
-
|
|
148
|
-
trim_match = get_match(self._trim_pattern, line)
|
|
150
|
+
trim_match = _get_match(setup_inline.trim_pattern, line)
|
|
149
151
|
if trim_match and trim_match.lastindex:
|
|
150
|
-
|
|
152
|
+
setup_inline.trim = int(trim_match[trim_match.lastindex])
|
|
151
153
|
|
|
152
|
-
|
|
153
|
-
content_match = get_match(self._content_pattern, line)
|
|
154
|
+
content_match = _get_match(setup_inline.content_pattern, line)
|
|
154
155
|
if content_match and content_match.lastindex:
|
|
155
156
|
regex_pattern = content_match[content_match.lastindex]
|
|
156
|
-
|
|
157
|
+
setup_inline.content = re.compile(regex_pattern)
|
|
157
158
|
|
|
159
|
+
# choose which stop option to use.
|
|
160
|
+
# Order by;
|
|
161
|
+
# 1. default from extraction pattern settings
|
|
162
|
+
# 2. default from inline params
|
|
158
163
|
self.stop = self._stop_default
|
|
159
|
-
stop_match =
|
|
164
|
+
stop_match = _get_match(setup_inline.stop_pattern, line)
|
|
160
165
|
if stop_match and stop_match.lastindex:
|
|
161
166
|
regex_pattern = stop_match[stop_match.lastindex]
|
|
162
167
|
self.stop = re.compile(regex_pattern)
|
|
163
168
|
|
|
169
|
+
self.inline = setup_inline
|
|
170
|
+
|
|
164
171
|
def get_filename(self) -> str:
|
|
165
172
|
"""Returns the filename if defined in start arguments."""
|
|
166
|
-
return self.
|
|
173
|
+
return self.inline.filename
|
|
167
174
|
|
|
168
175
|
def replace_line(self, line: str) -> str:
|
|
169
176
|
"""Apply the specified replacements to the line and return it."""
|
|
170
177
|
# Process trimming
|
|
171
|
-
if self.
|
|
172
|
-
line = line[self.
|
|
178
|
+
if self.inline.trim:
|
|
179
|
+
line = line[self.inline.trim:]
|
|
173
180
|
# Process inline content regex
|
|
174
|
-
if self.
|
|
175
|
-
match_object =
|
|
181
|
+
if self.inline.content:
|
|
182
|
+
match_object = _get_match(self.inline.content, line)
|
|
176
183
|
if match_object.lastindex:
|
|
177
184
|
return match_object[match_object.lastindex]
|
|
178
185
|
# Preform replace operations
|
|
@@ -214,126 +221,132 @@ class LazyFile:
|
|
|
214
221
|
return os.path.join(self.file_directory, self.file_name)
|
|
215
222
|
|
|
216
223
|
def write(self, arg: str) -> None:
|
|
217
|
-
"""Create and write the file,
|
|
218
|
-
if
|
|
224
|
+
"""Create and write a string line to the file, iff not none."""
|
|
225
|
+
if arg is None:
|
|
219
226
|
return
|
|
220
227
|
if self.file_object is None:
|
|
221
228
|
filename = os.path.join(self.file_directory, self.file_name)
|
|
222
229
|
os.makedirs(self.file_directory, exist_ok=True)
|
|
223
230
|
self.file_object = open(filename, 'w+')
|
|
224
|
-
|
|
231
|
+
|
|
232
|
+
def get_line(line: str) -> str:
|
|
233
|
+
"""Returns line with EOL."""
|
|
234
|
+
return line if line.endswith("\n") else line + '\n'
|
|
235
|
+
|
|
236
|
+
self.file_object.write(get_line(arg))
|
|
225
237
|
|
|
226
238
|
def close(self) -> str:
|
|
227
239
|
"""Finish the file."""
|
|
228
240
|
if self.file_object is not None:
|
|
229
|
-
|
|
230
|
-
utils.log.debug(" ... extracted %s",
|
|
241
|
+
file_path = os.path.join(self.file_directory, self.file_name)
|
|
242
|
+
utils.log.debug(" ... extracted %s", file_path)
|
|
231
243
|
self.file_object.close()
|
|
232
244
|
self.file_object = None
|
|
233
|
-
return
|
|
245
|
+
return file_path
|
|
234
246
|
return None
|
|
235
247
|
|
|
236
248
|
|
|
237
249
|
class StreamExtract:
|
|
238
|
-
"""Extract
|
|
250
|
+
"""Extract files to an output stream.
|
|
251
|
+
|
|
252
|
+
Optionally filter using a list of ExtractionPatterns.
|
|
253
|
+
"""
|
|
239
254
|
|
|
240
255
|
def __init__(
|
|
241
256
|
self,
|
|
242
|
-
input_stream:
|
|
257
|
+
input_stream: TextIOWrapper,
|
|
243
258
|
output_stream: LazyFile,
|
|
244
259
|
terminate: re.Pattern = None,
|
|
245
260
|
patterns: ExtractionPattern = None,
|
|
246
261
|
**kwargs):
|
|
247
262
|
"""Initialize StreamExtract with input and output streams."""
|
|
248
263
|
self.input_stream = input_stream
|
|
249
|
-
self.default_stream = output_stream
|
|
250
264
|
self.output_stream = output_stream
|
|
251
265
|
self.terminate = terminate
|
|
252
266
|
self.patterns = patterns
|
|
253
|
-
|
|
254
|
-
self.
|
|
255
|
-
self.
|
|
267
|
+
|
|
268
|
+
self._default_stream = output_stream
|
|
269
|
+
self._output_files = []
|
|
270
|
+
self._streams = {
|
|
256
271
|
output_stream.file_name: output_stream
|
|
257
272
|
}
|
|
258
273
|
|
|
259
|
-
def
|
|
260
|
-
"""Write some text and record if something was written."""
|
|
261
|
-
self.output_stream.write(text)
|
|
262
|
-
if text:
|
|
263
|
-
self.wrote_something = True
|
|
264
|
-
|
|
265
|
-
def try_extract_match(
|
|
274
|
+
def _try_extract_match(
|
|
266
275
|
self,
|
|
267
276
|
match_object: re.Match,
|
|
268
277
|
emit_last: bool = True) -> bool:
|
|
269
|
-
"""
|
|
278
|
+
"""Extracts line iff there's a match.
|
|
270
279
|
|
|
271
|
-
|
|
272
|
-
|
|
280
|
+
Returns:
|
|
281
|
+
True iff match_object exists.
|
|
273
282
|
"""
|
|
274
283
|
if not match_object:
|
|
275
284
|
return False
|
|
276
285
|
if match_object.lastindex and emit_last:
|
|
277
|
-
self.
|
|
286
|
+
self.output_stream.write(match_object[match_object.lastindex])
|
|
278
287
|
return True
|
|
279
288
|
|
|
280
289
|
def close(self) -> list:
|
|
281
|
-
"""
|
|
290
|
+
"""Close the file and return a list of filenames written to."""
|
|
282
291
|
file = self.output_stream.close()
|
|
283
|
-
if file
|
|
284
|
-
self.
|
|
285
|
-
return self.
|
|
292
|
+
if file:
|
|
293
|
+
self._output_files.append(file)
|
|
294
|
+
return self._output_files
|
|
286
295
|
|
|
287
|
-
def set_output_file(self, filename: str) ->
|
|
288
|
-
"""Set output stream from filename."""
|
|
296
|
+
def set_output_file(self, filename: str) -> LazyFile:
|
|
297
|
+
"""Set the current output stream from filename and return the stream."""
|
|
289
298
|
output_stream = self.output_stream
|
|
290
299
|
if filename:
|
|
291
300
|
# If we've opened this file before, re-use its stream.
|
|
292
|
-
if filename in self.
|
|
293
|
-
return self.set_output_stream(self.
|
|
301
|
+
if filename in self._streams:
|
|
302
|
+
return self.set_output_stream(self._streams[filename])
|
|
294
303
|
# Otherwise, make a new one and save it to the list.
|
|
295
304
|
output_stream = LazyFile(
|
|
296
305
|
self.output_stream.file_directory, filename)
|
|
297
|
-
self.
|
|
298
|
-
self.set_output_stream(output_stream)
|
|
306
|
+
self._streams[filename] = output_stream
|
|
307
|
+
return self.set_output_stream(output_stream)
|
|
299
308
|
|
|
300
|
-
def set_output_stream(self, stream: LazyFile) ->
|
|
301
|
-
"""Set the output stream."""
|
|
309
|
+
def set_output_stream(self, stream: LazyFile) -> LazyFile:
|
|
310
|
+
"""Set the current output stream and return the stream."""
|
|
302
311
|
if self.output_stream != stream:
|
|
303
312
|
self.close()
|
|
304
313
|
self.output_stream = stream
|
|
314
|
+
return self.output_stream
|
|
305
315
|
|
|
306
316
|
def extract(self, **kwargs) -> list:
|
|
307
317
|
"""Extract from file with semiliterate configuration.
|
|
308
318
|
|
|
309
|
-
Invoke this method to perform the extraction.
|
|
310
|
-
|
|
319
|
+
Invoke this method to perform the extraction.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
A list of files extracted.
|
|
311
323
|
"""
|
|
312
324
|
active_pattern = None if self.patterns else ExtractionPattern()
|
|
313
|
-
|
|
325
|
+
patterns = self.patterns if self.patterns else []
|
|
326
|
+
for pattern in patterns:
|
|
314
327
|
if not pattern.start:
|
|
315
328
|
active_pattern = pattern
|
|
316
329
|
|
|
317
330
|
for line in self.input_stream:
|
|
318
331
|
# Check terminate, regardless of state:
|
|
319
|
-
if self.
|
|
320
|
-
|
|
332
|
+
if self._try_extract_match(
|
|
333
|
+
_get_match(self.terminate, line), active_pattern):
|
|
321
334
|
return self.close()
|
|
322
335
|
# Change state if flagged to do so:
|
|
323
336
|
if active_pattern is None:
|
|
324
|
-
for pattern in
|
|
325
|
-
start =
|
|
337
|
+
for pattern in patterns:
|
|
338
|
+
start = _get_match(pattern.start, line)
|
|
326
339
|
if start:
|
|
327
340
|
active_pattern = pattern
|
|
328
341
|
active_pattern.setup(line)
|
|
329
342
|
self.set_output_file(active_pattern.get_filename())
|
|
330
|
-
self.
|
|
343
|
+
self._try_extract_match(start)
|
|
331
344
|
break
|
|
332
345
|
continue
|
|
333
346
|
# We are extracting. See if we should stop:
|
|
334
|
-
if self.
|
|
347
|
+
if self._try_extract_match(_get_match(active_pattern.stop, line)):
|
|
335
348
|
active_pattern = None
|
|
336
|
-
self.set_output_stream(self.
|
|
349
|
+
self.set_output_stream(self._default_stream)
|
|
337
350
|
continue
|
|
338
351
|
# Extract all other lines in the normal way:
|
|
339
352
|
self.extract_line(line, active_pattern)
|
|
@@ -341,8 +354,8 @@ class StreamExtract:
|
|
|
341
354
|
|
|
342
355
|
def extract_line(self, line: str, extraction_pattern: re.Pattern) -> None:
|
|
343
356
|
"""Copy line to the output stream, applying specified replacements."""
|
|
344
|
-
line =
|
|
345
|
-
self.
|
|
357
|
+
line = extraction_pattern.replace_line(line)
|
|
358
|
+
self.output_stream.write(line)
|
|
346
359
|
|
|
347
360
|
|
|
348
361
|
class Semiliterate:
|
|
@@ -397,18 +410,25 @@ class Semiliterate:
|
|
|
397
410
|
self.file_filter = re.compile(pattern)
|
|
398
411
|
self.destination = destination
|
|
399
412
|
self.terminate = (terminate is not None) and re.compile(terminate)
|
|
400
|
-
self.
|
|
413
|
+
self.extractions = []
|
|
401
414
|
if not extract:
|
|
402
415
|
extract = []
|
|
403
416
|
if isinstance(extract, dict):
|
|
404
417
|
# if there is only one extraction pattern, allow it to be a single
|
|
405
418
|
# dict entry
|
|
406
419
|
extract = [extract]
|
|
407
|
-
for
|
|
408
|
-
self.
|
|
420
|
+
for extract_params in extract:
|
|
421
|
+
self.extractions.append(ExtractionPattern(**extract_params))
|
|
409
422
|
|
|
410
423
|
def filename_match(self, name: str) -> str:
|
|
411
|
-
"""Get the filename for the match, otherwise return None.
|
|
424
|
+
"""Get the filename for the match, otherwise return None.
|
|
425
|
+
|
|
426
|
+
Args:
|
|
427
|
+
name (str): The name to match with the pattern filter
|
|
428
|
+
|
|
429
|
+
Returns:
|
|
430
|
+
The output filename for 'name' or None
|
|
431
|
+
"""
|
|
412
432
|
name_match = self.file_filter.search(name)
|
|
413
433
|
if name_match:
|
|
414
434
|
new_name = os.path.splitext(name)[0] + '.md'
|
|
@@ -425,7 +445,12 @@ class Semiliterate:
|
|
|
425
445
|
**kwargs) -> list:
|
|
426
446
|
"""Try to extract documentation from file with name.
|
|
427
447
|
|
|
428
|
-
|
|
448
|
+
Args:
|
|
449
|
+
from_directory (str): The source directory
|
|
450
|
+
from_file (str): The source filename within directory
|
|
451
|
+
destination_directory (str): The destination directory
|
|
452
|
+
|
|
453
|
+
Returns a list of extracted files.
|
|
429
454
|
"""
|
|
430
455
|
to_file = self.filename_match(from_file)
|
|
431
456
|
if not to_file:
|
|
@@ -439,11 +464,11 @@ class Semiliterate:
|
|
|
439
464
|
input_stream=original_file,
|
|
440
465
|
output_stream=LazyFile(destination_directory, to_file),
|
|
441
466
|
terminate=self.terminate,
|
|
442
|
-
patterns=self.
|
|
467
|
+
patterns=self.extractions,
|
|
443
468
|
**kwargs)
|
|
444
469
|
return extraction.extract()
|
|
445
470
|
except (UnicodeDecodeError) as error:
|
|
446
|
-
utils.log.
|
|
471
|
+
utils.log.debug("mkdocs-simple-plugin: Skipped %s", from_file_path)
|
|
447
472
|
utils.log.debug(
|
|
448
473
|
"mkdocs-simple-plugin: Error details: %s", str(error))
|
|
449
474
|
except (OSError, IOError) as error:
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"""Simple module handles document extraction from source files."""
|
|
2
|
-
import os
|
|
3
2
|
import fnmatch
|
|
4
|
-
import
|
|
3
|
+
import os
|
|
5
4
|
import pathlib
|
|
5
|
+
import stat
|
|
6
6
|
|
|
7
7
|
from shutil import copy2 as copy
|
|
8
8
|
from dataclasses import dataclass
|
|
@@ -26,10 +26,10 @@ class Simple():
|
|
|
26
26
|
# pylint: disable=too-many-instance-attributes
|
|
27
27
|
def __init__(
|
|
28
28
|
self,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
build_dir: str,
|
|
30
|
+
folders: list,
|
|
31
|
+
include: list,
|
|
32
|
+
ignore: list,
|
|
33
33
|
ignore_hidden: bool,
|
|
34
34
|
ignore_paths: list,
|
|
35
35
|
semiliterate: list,
|
|
@@ -37,23 +37,22 @@ class Simple():
|
|
|
37
37
|
"""Initialize module instance with settings.
|
|
38
38
|
|
|
39
39
|
Args:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
ignore_folders (list): Glob of paths to exclude
|
|
40
|
+
build_dir (str): Output directory for processed files
|
|
41
|
+
folders (list): Glob of folders to search for files
|
|
42
|
+
include (list): Glob of filenames to copy directly to output
|
|
43
|
+
ignore (list): Glob of paths to exclude
|
|
45
44
|
ignore_hidden (bool): Whether to ignore hidden files for processing
|
|
46
45
|
ignore_paths (list): Absolute filepaths to exclude
|
|
47
46
|
semiliterate (list): Settings for processing file content in
|
|
48
47
|
Semiliterate
|
|
49
48
|
|
|
50
49
|
"""
|
|
51
|
-
self.build_dir =
|
|
52
|
-
self.
|
|
53
|
-
self.
|
|
54
|
-
self.ignore_glob = set(
|
|
55
|
-
self.ignore_hidden = ignore_hidden
|
|
56
|
-
self.hidden_prefix = set([".", "__"])
|
|
50
|
+
self.build_dir = build_dir
|
|
51
|
+
self.folders = set(folders)
|
|
52
|
+
self.doc_glob = set(include)
|
|
53
|
+
self.ignore_glob = set(ignore)
|
|
54
|
+
self.ignore_hidden = ignore_hidden # to be deprecated
|
|
55
|
+
self.hidden_prefix = set([".", "__"]) # to be deprecated
|
|
57
56
|
self.ignore_paths = set(ignore_paths)
|
|
58
57
|
self.semiliterate = []
|
|
59
58
|
for item in semiliterate:
|
|
@@ -64,7 +63,7 @@ class Simple():
|
|
|
64
63
|
files = []
|
|
65
64
|
# Get all of the entries that match the include pattern.
|
|
66
65
|
entries = []
|
|
67
|
-
for pattern in self.
|
|
66
|
+
for pattern in self.folders:
|
|
68
67
|
entries.extend(pathlib.Path().glob(pattern))
|
|
69
68
|
# Ignore any entries that match the ignore pattern
|
|
70
69
|
entries[:] = [
|
|
@@ -101,7 +100,7 @@ class Simple():
|
|
|
101
100
|
mkdocsignore = os.path.join(base_path, ".mkdocsignore")
|
|
102
101
|
if os.path.exists(mkdocsignore):
|
|
103
102
|
ignore_list = []
|
|
104
|
-
with open(mkdocsignore, "r") as txt_file:
|
|
103
|
+
with open(mkdocsignore, mode="r", encoding="utf-8") as txt_file:
|
|
105
104
|
ignore_list = txt_file.read().splitlines()
|
|
106
105
|
# Remove all comment lines
|
|
107
106
|
ignore_list = [x for x in ignore_list if not x.startswith('#')]
|
|
@@ -115,12 +114,12 @@ class Simple():
|
|
|
115
114
|
return True
|
|
116
115
|
return False
|
|
117
116
|
|
|
118
|
-
def
|
|
119
|
-
"""Check if file
|
|
117
|
+
def is_doc_file(self, name: str) -> bool:
|
|
118
|
+
"""Check if file is a desired doc file."""
|
|
120
119
|
def match_pattern(name, pattern):
|
|
121
120
|
return fnmatch.fnmatch(name, pattern) or pattern in name
|
|
122
121
|
|
|
123
|
-
return any(match_pattern(name, pattern) for pattern in self.
|
|
122
|
+
return any(match_pattern(name, pattern) for pattern in self.doc_glob)
|
|
124
123
|
|
|
125
124
|
def should_extract_file(self, name: str):
|
|
126
125
|
"""Check if file should be extracted."""
|
|
@@ -142,13 +141,18 @@ class Simple():
|
|
|
142
141
|
return any(name.startswith(pattern)
|
|
143
142
|
for pattern in self.hidden_prefix)
|
|
144
143
|
return any(hidden_prefix(part) for part in parts)
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
# Check if file is text based
|
|
145
|
+
try:
|
|
146
|
+
with open(name, 'r', encoding='utf-8') as f:
|
|
147
|
+
_ = f.read()
|
|
148
|
+
except UnicodeDecodeError:
|
|
149
|
+
return False
|
|
150
|
+
|
|
151
|
+
# Check if file is hidden and should ignore
|
|
147
152
|
if self.ignore_hidden:
|
|
148
153
|
is_hidden = has_hidden_prefix(name) or has_hidden_attribute(name)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
return extract
|
|
154
|
+
return not is_hidden
|
|
155
|
+
return True
|
|
152
156
|
|
|
153
157
|
def merge_docs(self, from_dir, dirty=False):
|
|
154
158
|
"""Merge docs directory"""
|
|
@@ -173,7 +177,11 @@ class Simple():
|
|
|
173
177
|
source_file, destination_file)
|
|
174
178
|
self.ignore_paths.add(from_dir)
|
|
175
179
|
|
|
176
|
-
def build_docs(
|
|
180
|
+
def build_docs(
|
|
181
|
+
self,
|
|
182
|
+
dirty=False,
|
|
183
|
+
last_build_time=None,
|
|
184
|
+
do_copy=False) -> list:
|
|
177
185
|
"""Build the docs directory from workspace files."""
|
|
178
186
|
paths = []
|
|
179
187
|
files = self.get_files()
|
|
@@ -188,8 +196,9 @@ class Simple():
|
|
|
188
196
|
build_prefix = os.path.normpath(
|
|
189
197
|
os.path.join(self.build_dir, from_dir))
|
|
190
198
|
|
|
191
|
-
|
|
192
|
-
|
|
199
|
+
doc_paths = self.get_doc_file(
|
|
200
|
+
from_dir, name, build_prefix, do_copy)
|
|
201
|
+
if doc_paths:
|
|
193
202
|
paths.append(
|
|
194
203
|
SimplePath(
|
|
195
204
|
output_root=".",
|
|
@@ -231,17 +240,23 @@ class Simple():
|
|
|
231
240
|
|
|
232
241
|
return []
|
|
233
242
|
|
|
234
|
-
def
|
|
243
|
+
def get_doc_file(
|
|
244
|
+
self,
|
|
245
|
+
from_dir: str,
|
|
246
|
+
name: str,
|
|
247
|
+
to_dir: str,
|
|
248
|
+
do_copy: bool) -> list:
|
|
235
249
|
"""Copy file with the same name to a new directory.
|
|
236
250
|
|
|
237
251
|
Returns true if file copied.
|
|
238
252
|
"""
|
|
239
253
|
original = os.path.join(from_dir, name)
|
|
240
|
-
destination = os.path.join(to_dir, name)
|
|
241
254
|
|
|
242
|
-
if not self.
|
|
255
|
+
if not self.is_doc_file(os.path.join(from_dir, name)):
|
|
243
256
|
return []
|
|
244
257
|
|
|
245
|
-
|
|
246
|
-
|
|
258
|
+
if do_copy:
|
|
259
|
+
destination = os.path.join(to_dir, name)
|
|
260
|
+
os.makedirs(to_dir, exist_ok=True)
|
|
261
|
+
copy(original, destination)
|
|
247
262
|
return [original]
|
|
@@ -23,19 +23,18 @@ classifiers = [
|
|
|
23
23
|
"Intended Audience :: Information Technology",
|
|
24
24
|
"Programming Language :: Python",
|
|
25
25
|
"Programming Language :: Python :: 3 :: Only",
|
|
26
|
-
"Programming Language :: Python :: 3.7",
|
|
27
26
|
"Programming Language :: Python :: 3.8",
|
|
28
27
|
"Programming Language :: Python :: 3.9",
|
|
29
28
|
"Programming Language :: Python :: 3.10",
|
|
30
29
|
"Programming Language :: Python :: 3.11"
|
|
31
30
|
]
|
|
32
31
|
# md file="versions.snippet"
|
|
33
|
-
# _Python 3.x, 3.
|
|
32
|
+
# _Python 3.x, 3.8, 3.9, 3.10, 3.11 supported._
|
|
34
33
|
# /md
|
|
35
34
|
dependencies = [
|
|
36
35
|
"click>=7.1",
|
|
37
36
|
"MarkupSafe>=2.1.1",
|
|
38
|
-
"mkdocs>=1.
|
|
37
|
+
"mkdocs>=1.6.0",
|
|
39
38
|
"PyYAML>=6.0",
|
|
40
39
|
]
|
|
41
40
|
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
# Package Guide
|
|
2
|
-
|
|
3
|
-
## Prerequisites
|
|
4
|
-
|
|
5
|
-
{% include "setup.snippet" %}
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
## Building
|
|
9
|
-
|
|
10
|
-
{% include "build.snippet" %}
|
|
11
|
-
|
|
12
|
-
## Testing
|
|
13
|
-
|
|
14
|
-
{% include "tests/linters.snippet" %}
|
|
15
|
-
|
|
16
|
-
{% include "tests/unit_tests.snippet" %}
|
|
17
|
-
|
|
18
|
-
{% include "tests/integration_tests.snippet" %}
|
|
19
|
-
|
|
20
|
-
{% include "tests/local_tests.snippet" %}
|
|
21
|
-
|
|
22
|
-
## VSCode
|
|
23
|
-
|
|
24
|
-
Included in this package is a VSCode workspace and development container. See [how I develop with VSCode and Docker](https://allisonthackston.com/articles/docker-development.html) and [how I use VSCode tasks](https://allisonthackston.com/articles/vscode-tasks.html).
|
|
25
|
-
|
|
26
|
-
## Packaging
|
|
27
|
-
|
|
28
|
-
[](https://github.com/pypa/hatch)
|
|
29
|
-
|
|
30
|
-
The project uses Hatch to build and package the plugin
|
|
31
|
-
|
|
32
|
-
### Build the package
|
|
33
|
-
|
|
34
|
-
```bash
|
|
35
|
-
hatch build
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
### Publish the package
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
hatch publish
|
|
42
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|