mkdocs-simple-plugin 3.1.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.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.3
2
2
  Name: mkdocs-simple-plugin
3
- Version: 3.1.0
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
@@ -22,7 +22,7 @@ Classifier: Programming Language :: Python :: 3.11
22
22
  Requires-Python: >=3
23
23
  Requires-Dist: click>=7.1
24
24
  Requires-Dist: markupsafe>=2.1.1
25
- Requires-Dist: mkdocs>=1.4.0
25
+ Requires-Dist: mkdocs>=1.6.0
26
26
  Requires-Dist: pyyaml>=6.0
27
27
  Description-Content-Type: text/markdown
28
28
 
@@ -85,7 +85,7 @@ def write_config(config_file, config):
85
85
  """Write configuration file."""
86
86
  if os.path.dirname(config_file):
87
87
  os.makedirs(os.path.dirname(config_file), exist_ok=True)
88
- with open(config_file, 'w+') as file:
88
+ with open(config_file, 'w+', encoding="utf-8") as file:
89
89
  try:
90
90
  yaml.dump(
91
91
  data=config,
@@ -105,7 +105,7 @@ def setup_config(config_file="mkdocs.yml"):
105
105
  # from the folder name.
106
106
  write_config(config_file, config)
107
107
  # Open the config file to verify settings.
108
- with open(config_file, 'r') as stream:
108
+ with open(config_file, 'r', encoding="utf-8") as stream:
109
109
  try:
110
110
  local_config = yaml.load(stream, yaml.Loader)
111
111
  if local_config:
@@ -90,14 +90,16 @@ mkdocs serve
90
90
  import os
91
91
  import tempfile
92
92
  import time
93
- import yaml
94
-
93
+ from typing import Callable, Literal
95
94
 
96
- from mkdocs.structure.files import Files, File
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
 
@@ -309,11 +311,16 @@ class SimplePlugin(BasePlugin):
309
311
  self.dirty = False
310
312
  self.last_build_time = None
311
313
 
312
- def on_startup(self, *, command, dirty: bool) -> None:
314
+ def on_startup(self,
315
+ *,
316
+ command: Literal['build',
317
+ 'gh-deploy',
318
+ 'serve'],
319
+ dirty: bool):
313
320
  """Configure the plugin on startup."""
314
321
  self.dirty = dirty
315
322
 
316
- def on_config(self, config, **kwargs):
323
+ def on_config(self, config: MkDocsConfig):
317
324
  """Update configuration to use a temporary build directory."""
318
325
  # Save the config for documentation
319
326
  default_config = dict((name, config_option.default)
@@ -359,7 +366,8 @@ class SimplePlugin(BasePlugin):
359
366
  os.path.abspath(config['docs_dir']))
360
367
  return config
361
368
 
362
- def on_files(self, files: Files, *, config):
369
+ def on_files(self, files: Files, /, *,
370
+ config: MkDocsConfig):
363
371
  """Update files based on plugin settings."""
364
372
  # Configure simple
365
373
  simple = Simple(**self.config)
@@ -372,16 +380,11 @@ class SimplePlugin(BasePlugin):
372
380
 
373
381
  if not self.config["merge_docs_dir"]:
374
382
  # If not merging, remove files that are from the docs dir
375
- # pylint: disable=protected-access
376
- for file in files._files[:]:
377
- if file.abs_src_path.startswith(
378
- 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):
379
386
  files.remove(file)
380
387
 
381
- dedupe_files = {}
382
- for file in files:
383
- dedupe_files[file.abs_dest_path] = file
384
-
385
388
  for path in self.paths:
386
389
  file = File(
387
390
  src_dir=os.path.abspath(path.output_root),
@@ -389,13 +392,13 @@ class SimplePlugin(BasePlugin):
389
392
  dest_dir=config.site_dir,
390
393
  use_directory_urls=config["use_directory_urls"]
391
394
  )
392
- if file.abs_dest_path in dedupe_files:
393
- if file.abs_dest_path in files:
394
- files.remove(dedupe_files[file.abs_dest_path])
395
+ if file.src_uri in files.src_uris:
396
+ files.remove(file)
395
397
  files.append(file)
396
398
  return files
397
399
 
398
- def on_serve(self, server, *, config, builder):
400
+ def on_serve(self, server: LiveReloadServer, /, *, config: MkDocsConfig,
401
+ builder: Callable):
399
402
  """Add files to watch server."""
400
403
  # don't watch the build directory
401
404
  # pylint: disable=protected-access
@@ -1,4 +1,5 @@
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
 
@@ -7,14 +8,7 @@ from dataclasses import dataclass
7
8
  from mkdocs import utils
8
9
 
9
10
 
10
- def get_line(line: str) -> str:
11
- """Returns line with EOL."""
12
- if not line:
13
- return None
14
- return line if line.endswith("\n") else line + '\n'
15
-
16
-
17
- def get_match(pattern: re.Pattern, line: str) -> re.Match:
11
+ def _get_match(pattern: re.Pattern, line: str) -> re.Match:
18
12
  """Returns the match for the given pattern."""
19
13
  if not pattern:
20
14
  return None
@@ -149,15 +143,15 @@ class ExtractionPattern:
149
143
  """Process input parameters."""
150
144
  setup_inline = InlineParams()
151
145
 
152
- file_match = get_match(setup_inline.filename_pattern, line)
146
+ file_match = _get_match(setup_inline.filename_pattern, line)
153
147
  if file_match and file_match.lastindex:
154
148
  setup_inline.filename = file_match[file_match.lastindex]
155
149
 
156
- trim_match = get_match(setup_inline.trim_pattern, line)
150
+ trim_match = _get_match(setup_inline.trim_pattern, line)
157
151
  if trim_match and trim_match.lastindex:
158
152
  setup_inline.trim = int(trim_match[trim_match.lastindex])
159
153
 
160
- content_match = get_match(setup_inline.content_pattern, line)
154
+ content_match = _get_match(setup_inline.content_pattern, line)
161
155
  if content_match and content_match.lastindex:
162
156
  regex_pattern = content_match[content_match.lastindex]
163
157
  setup_inline.content = re.compile(regex_pattern)
@@ -167,7 +161,7 @@ class ExtractionPattern:
167
161
  # 1. default from extraction pattern settings
168
162
  # 2. default from inline params
169
163
  self.stop = self._stop_default
170
- stop_match = get_match(setup_inline.stop_pattern, line)
164
+ stop_match = _get_match(setup_inline.stop_pattern, line)
171
165
  if stop_match and stop_match.lastindex:
172
166
  regex_pattern = stop_match[stop_match.lastindex]
173
167
  self.stop = re.compile(regex_pattern)
@@ -185,7 +179,7 @@ class ExtractionPattern:
185
179
  line = line[self.inline.trim:]
186
180
  # Process inline content regex
187
181
  if self.inline.content:
188
- match_object = get_match(self.inline.content, line)
182
+ match_object = _get_match(self.inline.content, line)
189
183
  if match_object.lastindex:
190
184
  return match_object[match_object.lastindex]
191
185
  # Preform replace operations
@@ -227,126 +221,132 @@ class LazyFile:
227
221
  return os.path.join(self.file_directory, self.file_name)
228
222
 
229
223
  def write(self, arg: str) -> None:
230
- """Create and write the file, only if not empty."""
231
- if not arg:
224
+ """Create and write a string line to the file, iff not none."""
225
+ if arg is None:
232
226
  return
233
227
  if self.file_object is None:
234
228
  filename = os.path.join(self.file_directory, self.file_name)
235
229
  os.makedirs(self.file_directory, exist_ok=True)
236
230
  self.file_object = open(filename, 'w+')
237
- self.file_object.write(arg)
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))
238
237
 
239
238
  def close(self) -> str:
240
239
  """Finish the file."""
241
240
  if self.file_object is not None:
242
- file = os.path.join(self.file_directory, self.file_name)
243
- utils.log.debug(" ... extracted %s", file)
241
+ file_path = os.path.join(self.file_directory, self.file_name)
242
+ utils.log.debug(" ... extracted %s", file_path)
244
243
  self.file_object.close()
245
244
  self.file_object = None
246
- return file
245
+ return file_path
247
246
  return None
248
247
 
249
248
 
250
249
  class StreamExtract:
251
- """Extract documentation portions of files to an output stream."""
250
+ """Extract files to an output stream.
251
+
252
+ Optionally filter using a list of ExtractionPatterns.
253
+ """
252
254
 
253
255
  def __init__(
254
256
  self,
255
- input_stream: LazyFile,
257
+ input_stream: TextIOWrapper,
256
258
  output_stream: LazyFile,
257
259
  terminate: re.Pattern = None,
258
260
  patterns: ExtractionPattern = None,
259
261
  **kwargs):
260
262
  """Initialize StreamExtract with input and output streams."""
261
263
  self.input_stream = input_stream
262
- self.default_stream = output_stream
263
264
  self.output_stream = output_stream
264
265
  self.terminate = terminate
265
266
  self.patterns = patterns
266
- self.wrote_something = False
267
- self.output_files = []
268
- self.streams = {
267
+
268
+ self._default_stream = output_stream
269
+ self._output_files = []
270
+ self._streams = {
269
271
  output_stream.file_name: output_stream
270
272
  }
271
273
 
272
- def transcribe(self, text: str) -> None:
273
- """Write some text and record if something was written."""
274
- self.output_stream.write(text)
275
- if text:
276
- self.wrote_something = True
277
-
278
- def try_extract_match(
274
+ def _try_extract_match(
279
275
  self,
280
276
  match_object: re.Match,
281
277
  emit_last: bool = True) -> bool:
282
- """Extract match into output.
278
+ """Extracts line iff there's a match.
283
279
 
284
- If _match_object_ is not false-y, returns true.
285
- If extract flag is true, emits the last group of the match if any.
280
+ Returns:
281
+ True iff match_object exists.
286
282
  """
287
283
  if not match_object:
288
284
  return False
289
285
  if match_object.lastindex and emit_last:
290
- self.transcribe(get_line(match_object[match_object.lastindex]))
286
+ self.output_stream.write(match_object[match_object.lastindex])
291
287
  return True
292
288
 
293
289
  def close(self) -> list:
294
- """Returns true if something was written"""
290
+ """Close the file and return a list of filenames written to."""
295
291
  file = self.output_stream.close()
296
- if file and self.wrote_something:
297
- self.output_files.append(file)
298
- return self.output_files
292
+ if file:
293
+ self._output_files.append(file)
294
+ return self._output_files
299
295
 
300
- def set_output_file(self, filename: str) -> None:
301
- """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."""
302
298
  output_stream = self.output_stream
303
299
  if filename:
304
300
  # If we've opened this file before, re-use its stream.
305
- if filename in self.streams:
306
- return self.set_output_stream(self.streams[filename])
301
+ if filename in self._streams:
302
+ return self.set_output_stream(self._streams[filename])
307
303
  # Otherwise, make a new one and save it to the list.
308
304
  output_stream = LazyFile(
309
305
  self.output_stream.file_directory, filename)
310
- self.streams[filename] = output_stream
311
- self.set_output_stream(output_stream)
306
+ self._streams[filename] = output_stream
307
+ return self.set_output_stream(output_stream)
312
308
 
313
- def set_output_stream(self, stream: LazyFile) -> None:
314
- """Set the output stream."""
309
+ def set_output_stream(self, stream: LazyFile) -> LazyFile:
310
+ """Set the current output stream and return the stream."""
315
311
  if self.output_stream != stream:
316
312
  self.close()
317
313
  self.output_stream = stream
314
+ return self.output_stream
318
315
 
319
316
  def extract(self, **kwargs) -> list:
320
317
  """Extract from file with semiliterate configuration.
321
318
 
322
- Invoke this method to perform the extraction. Returns true if
323
- any text is actually extracted, false otherwise.
319
+ Invoke this method to perform the extraction.
320
+
321
+ Returns:
322
+ A list of files extracted.
324
323
  """
325
324
  active_pattern = None if self.patterns else ExtractionPattern()
326
- for pattern in self.patterns:
325
+ patterns = self.patterns if self.patterns else []
326
+ for pattern in patterns:
327
327
  if not pattern.start:
328
328
  active_pattern = pattern
329
329
 
330
330
  for line in self.input_stream:
331
331
  # Check terminate, regardless of state:
332
- if self.try_extract_match(
333
- get_match(self.terminate, line), active_pattern):
332
+ if self._try_extract_match(
333
+ _get_match(self.terminate, line), active_pattern):
334
334
  return self.close()
335
335
  # Change state if flagged to do so:
336
336
  if active_pattern is None:
337
- for pattern in self.patterns:
338
- start = get_match(pattern.start, line)
337
+ for pattern in patterns:
338
+ start = _get_match(pattern.start, line)
339
339
  if start:
340
340
  active_pattern = pattern
341
341
  active_pattern.setup(line)
342
342
  self.set_output_file(active_pattern.get_filename())
343
- self.try_extract_match(start)
343
+ self._try_extract_match(start)
344
344
  break
345
345
  continue
346
346
  # We are extracting. See if we should stop:
347
- if self.try_extract_match(get_match(active_pattern.stop, line)):
347
+ if self._try_extract_match(_get_match(active_pattern.stop, line)):
348
348
  active_pattern = None
349
- self.set_output_stream(self.default_stream)
349
+ self.set_output_stream(self._default_stream)
350
350
  continue
351
351
  # Extract all other lines in the normal way:
352
352
  self.extract_line(line, active_pattern)
@@ -354,8 +354,8 @@ class StreamExtract:
354
354
 
355
355
  def extract_line(self, line: str, extraction_pattern: re.Pattern) -> None:
356
356
  """Copy line to the output stream, applying specified replacements."""
357
- line = get_line(extraction_pattern.replace_line(line))
358
- self.transcribe(line)
357
+ line = extraction_pattern.replace_line(line)
358
+ self.output_stream.write(line)
359
359
 
360
360
 
361
361
  class Semiliterate:
@@ -410,18 +410,25 @@ class Semiliterate:
410
410
  self.file_filter = re.compile(pattern)
411
411
  self.destination = destination
412
412
  self.terminate = (terminate is not None) and re.compile(terminate)
413
- self.patterns = []
413
+ self.extractions = []
414
414
  if not extract:
415
415
  extract = []
416
416
  if isinstance(extract, dict):
417
417
  # if there is only one extraction pattern, allow it to be a single
418
418
  # dict entry
419
419
  extract = [extract]
420
- for pattern in extract:
421
- self.patterns.append(ExtractionPattern(**pattern))
420
+ for extract_params in extract:
421
+ self.extractions.append(ExtractionPattern(**extract_params))
422
422
 
423
423
  def filename_match(self, name: str) -> str:
424
- """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
+ """
425
432
  name_match = self.file_filter.search(name)
426
433
  if name_match:
427
434
  new_name = os.path.splitext(name)[0] + '.md'
@@ -438,7 +445,12 @@ class Semiliterate:
438
445
  **kwargs) -> list:
439
446
  """Try to extract documentation from file with name.
440
447
 
441
- Returns True if extraction was successful.
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.
442
454
  """
443
455
  to_file = self.filename_match(from_file)
444
456
  if not to_file:
@@ -452,11 +464,11 @@ class Semiliterate:
452
464
  input_stream=original_file,
453
465
  output_stream=LazyFile(destination_directory, to_file),
454
466
  terminate=self.terminate,
455
- patterns=self.patterns,
467
+ patterns=self.extractions,
456
468
  **kwargs)
457
469
  return extraction.extract()
458
470
  except (UnicodeDecodeError) as error:
459
- utils.log.info("mkdocs-simple-plugin: Skipped %s", from_file_path)
471
+ utils.log.debug("mkdocs-simple-plugin: Skipped %s", from_file_path)
460
472
  utils.log.debug(
461
473
  "mkdocs-simple-plugin: Error details: %s", str(error))
462
474
  except (OSError, IOError) as error:
@@ -100,7 +100,7 @@ class Simple():
100
100
  mkdocsignore = os.path.join(base_path, ".mkdocsignore")
101
101
  if os.path.exists(mkdocsignore):
102
102
  ignore_list = []
103
- with open(mkdocsignore, "r") as txt_file:
103
+ with open(mkdocsignore, mode="r", encoding="utf-8") as txt_file:
104
104
  ignore_list = txt_file.read().splitlines()
105
105
  # Remove all comment lines
106
106
  ignore_list = [x for x in ignore_list if not x.startswith('#')]
@@ -255,7 +255,7 @@ class Simple():
255
255
  if not self.is_doc_file(os.path.join(from_dir, name)):
256
256
  return []
257
257
 
258
- if (do_copy):
258
+ if do_copy:
259
259
  destination = os.path.join(to_dir, name)
260
260
  os.makedirs(to_dir, exist_ok=True)
261
261
  copy(original, destination)
@@ -34,7 +34,7 @@ classifiers = [
34
34
  dependencies = [
35
35
  "click>=7.1",
36
36
  "MarkupSafe>=2.1.1",
37
- "mkdocs>=1.4.0",
37
+ "mkdocs>=1.6.0",
38
38
  "PyYAML>=6.0",
39
39
  ]
40
40