Repository: mondeja/mkdocs-include-markdown-plugin
Pull request #171 (2023-08-04 02:07:24)

Fix error getting content from cache


Patch:
diff --git a/examples/http-cache/docs/README.md b/examples/http-cache/docs/README.md
index 69ff55d..7e8b565 100644
--- a/examples/http-cache/docs/README.md
+++ b/examples/http-cache/docs/README.md
@@ -1,14 +1,20 @@
 # Header
 
-You need to install `platformdirs` to run this example.
+:warning: You need to install `platformdirs` to run this example.
 
 {%
   include-markdown "https://raw.githubusercontent.com/mondeja/mkdocs-include-markdown-plugin/master/examples/basic/docs/included.md"
 %}
 
+## mkdocs.yml
+
+```
 {%
-  include-markdown "https://raw.githubusercontent.com/mondeja/mkdocs-include-markdown-plugin/master/examples/basic/mkdocs.yml"
+  include "https://raw.githubusercontent.com/mondeja/mkdocs-include-markdown-plugin/master/examples/basic/mkdocs.yml"
 %}
+```
+
+## From cache
 
 {%
   include-markdown "https://raw.githubusercontent.com/mondeja/mkdocs-include-markdown-plugin/master/examples/basic/docs/included.md"
diff --git a/pyproject.toml b/pyproject.toml
index 6bd1c58..73afe6e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -111,7 +111,6 @@ style = [
 
 [tool.coverage.run]
 source = ["src"]
-omit = ["src/mkdocs_include_markdown_plugin/plugin.py"]
 plugins = ["covdefaults"]
 parallel = true
 data_file = ".coverage/.coverage"
diff --git a/src/mkdocs_include_markdown_plugin/cache.py b/src/mkdocs_include_markdown_plugin/cache.py
index ecbf826..558c92f 100644
--- a/src/mkdocs_include_markdown_plugin/cache.py
+++ b/src/mkdocs_include_markdown_plugin/cache.py
@@ -44,7 +44,7 @@ class Cache:
         fpath = os.path.join(self.cache_dir, key)
         if os.path.isfile(fpath):
             creation_time = self.get_creation_time_from_fpath(fpath)
-            if time.time() > creation_time + self.expiration_seconds:
+            if time.time() < creation_time + self.expiration_seconds:
                 return self.read_file(fpath)
             else:
                 os.remove(fpath)
diff --git a/src/mkdocs_include_markdown_plugin/directive.py b/src/mkdocs_include_markdown_plugin/directive.py
index 1c28a0e..6519437 100644
--- a/src/mkdocs_include_markdown_plugin/directive.py
+++ b/src/mkdocs_include_markdown_plugin/directive.py
@@ -60,9 +60,9 @@ TRUE_FALSE_BOOL_STR = {
 }
 
 
-def bool_arg(arg: str) -> re.Pattern[str]:
+def arg(arg: str) -> re.Pattern[str]:
     """Return a compiled regexp to match a boolean argument."""
-    return re.compile(rf'{arg}=(\w+)')
+    return re.compile(rf'{arg}=([-\w]*)')
 
 
 def str_arg(arg: str) -> re.Pattern[str]:
@@ -80,14 +80,14 @@ ARGUMENT_REGEXES = {
     'encoding': str_arg('encoding'),
 
     # bool
-    'comments': bool_arg('comments'),
-    'preserve-includer-indent': bool_arg('preserve-includer-indent'),
-    'dedent': bool_arg('dedent'),
-    'trailing-newlines': bool_arg('trailing-newlines'),
-    'rewrite-relative-urls': bool_arg('rewrite-relative-urls'),
+    'comments': arg('comments'),
+    'preserve-includer-indent': arg('preserve-includer-indent'),
+    'dedent': arg('dedent'),
+    'trailing-newlines': arg('trailing-newlines'),
+    'rewrite-relative-urls': arg('rewrite-relative-urls'),
 
     # int
-    'heading-offset': re.compile(r'heading-offset=(-?\d+)'),
+    'heading-offset': arg('heading-offset'),
 }
 
 
diff --git a/src/mkdocs_include_markdown_plugin/event.py b/src/mkdocs_include_markdown_plugin/event.py
index f02e731..624f1c5 100644
--- a/src/mkdocs_include_markdown_plugin/event.py
+++ b/src/mkdocs_include_markdown_plugin/event.py
@@ -89,9 +89,9 @@ def get_file_content(
 
         exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
         if exclude_match is None:
-            if defaults['exclude'] is None:
+            if defaults['exclude'] is None:  # pragma: no branch
                 ignore_paths: list[str] = []
-            else:
+            else:  # pragma: no cover
                 ignore_paths = glob.glob(defaults['exclude'])
         else:
             exclude_string = parse_string_argument(exclude_match)
@@ -121,7 +121,7 @@ def get_file_content(
         else:
             file_paths_to_include = process.filter_paths(
                 glob.iglob(file_path_glob, recursive=True),
-                ignore_paths=ignore_paths,
+                ignore_paths,
             )
 
         if not file_paths_to_include:
@@ -209,7 +209,7 @@ def get_file_content(
             else:
                 new_text_to_include = process.read_file(file_path, encoding)
 
-            if start is not None or end is not None:
+            if start or end:
                 new_text_to_include, *expected_not_found = (
                     process.filter_inclusions(
                         start,
@@ -307,9 +307,9 @@ def get_file_content(
 
         exclude_match = ARGUMENT_REGEXES['exclude'].search(arguments_string)
         if exclude_match is None:
-            if defaults['exclude'] is None:
+            if defaults['exclude'] is None:  # pragma: no branch
                 ignore_paths: list[str] = []
-            else:
+            else:  # pragma: no cover
                 ignore_paths = glob.glob(defaults['exclude'])
         else:
             exclude_string = parse_string_argument(exclude_match)
@@ -340,7 +340,7 @@ def get_file_content(
         else:
             file_paths_to_include = process.filter_paths(
                 glob.iglob(file_path_glob, recursive=True),
-                ignore_paths=ignore_paths,
+                ignore_paths,
             )
 
         if not file_paths_to_include:
@@ -432,16 +432,27 @@ def get_file_content(
             arguments_string,
         )
         if offset_match:
+            offset = offset_match.group(1)
+            if offset == '':
+                lineno = lineno_from_content_start(
+                    markdown,
+                    directive_match_start,
+                )
+                raise BuildError(
+                    "Invalid empty 'heading-offset' argument in"
+                    " 'include-markdown' directive at"
+                    f' {os.path.relpath(page_src_path, docs_dir)}:{lineno}',
+                )
             try:
-                offset = int(offset_match.group(1))
+                offset = int(offset)
             except ValueError:
                 lineno = lineno_from_content_start(
                     markdown,
                     directive_match_start,
                 )
                 raise BuildError(
-                    "Invalid 'heading-offset' argument in 'include-markdown'"
-                    ' directive at '
+                    f"Invalid 'heading-offset' argument \"{offset}\" in"
+                    " 'include-markdown' directive at "
                     f'{os.path.relpath(page_src_path, docs_dir)}:{lineno}',
                 )
         else:
@@ -468,7 +479,7 @@ def get_file_content(
             else:
                 new_text_to_include = process.read_file(file_path, encoding)
 
-            if start is not None or end is not None:
+            if start or end:
                 new_text_to_include, *expected_not_found = (
                     process.filter_inclusions(
                         start,
diff --git a/src/mkdocs_include_markdown_plugin/plugin.py b/src/mkdocs_include_markdown_plugin/plugin.py
index fb80a41..7f9a2ff 100644
--- a/src/mkdocs_include_markdown_plugin/plugin.py
+++ b/src/mkdocs_include_markdown_plugin/plugin.py
@@ -58,7 +58,7 @@ class IncludeMarkdownPlugin(BasePlugin):
 
         return config
 
-    def _watch_included_files(self) -> None:
+    def _watch_included_files(self) -> None:  # pragma: no cover
         global FILES_WATCHER, SERVER
         SERVER = cast(LiveReloadServer, SERVER)
         FILES_WATCHER = cast(FilesWatcher, FILES_WATCHER)
@@ -83,7 +83,7 @@ class IncludeMarkdownPlugin(BasePlugin):
             config: MkDocsConfig,
             files: Files,
     ) -> str:
-        if SERVER is not None:
+        if SERVER is not None:  # pragma: no cover
             self._watch_included_files()
         return html
 
@@ -94,7 +94,7 @@ class IncludeMarkdownPlugin(BasePlugin):
             builder: Callable[[Any], Any],
     ) -> None:
         global SERVER
-        if SERVER is None:
+        if SERVER is None:  # pragma: no cover
             SERVER = server
             self._watch_included_files()
 
diff --git a/src/mkdocs_include_markdown_plugin/process.py b/src/mkdocs_include_markdown_plugin/process.py
index c4d8f02..f70ecdf 100644
--- a/src/mkdocs_include_markdown_plugin/process.py
+++ b/src/mkdocs_include_markdown_plugin/process.py
@@ -324,7 +324,7 @@ def filter_inclusions(
                 expected_start_not_found = True
         text_to_include = new_text_to_include
 
-    elif new_end is not None:
+    elif new_end is not None:  # pragma: no branch
         end = interpret_escapes(new_end)
         if end in text_to_include:
             text_to_include = text_to_include.split(
@@ -381,7 +381,7 @@ def rstrip_trailing_newlines(content: str) -> str:
 
 def filter_paths(
         filepaths: Iterator[str],
-        ignore_paths: list[str] | None = None,
+        ignore_paths: list[str],
 ) -> list[str]:
     """Filters a list of paths removing those defined in other list of paths.
 
@@ -400,9 +400,6 @@ def filter_paths(
     Returns:
         list: Non filtered paths ordered alphabetically.
     """
-    if ignore_paths is None:
-        ignore_paths = []
-
     response = []
     for filepath in filepaths:
         # ignore by filepath


Test patch:
diff --git a/tests/test_unit/test_arguments.py b/tests/test_unit/test_arguments.py
index ab44e86..151b97e 100644
--- a/tests/test_unit/test_arguments.py
+++ b/tests/test_unit/test_arguments.py
@@ -201,6 +201,50 @@ def test_empty_encoding_argument(directive, page, tmp_path, caplog):
     )
 
 
+@pytest.mark.parametrize(
+    ('argument_value', 'exception_message'),
+    (
+        pytest.param(
+            'invalidoption', (
+                "Invalid 'heading-offset' argument \"invalidoption\" in"
+                " 'include-markdown' directive at includer.md:1"
+            ),
+            id='invalidoption',
+        ),
+        pytest.param(
+            '', (
+                "Invalid empty 'heading-offset' argument in"
+                " 'include-markdown' directive at includer.md:1"
+            ),
+            id='empty',
+        ),
+    ),
+)
+def test_invalid_heading_offset_arguments(
+    argument_value,
+    exception_message,
+    page,
+    tmp_path,
+    caplog,
+):
+    page_to_include_filepath = tmp_path / 'included.md'
+    page_to_include_filepath.write_text('# Content to include')
+
+    with pytest.raises(BuildError) as exc:
+        on_page_markdown(
+            f'''{{%
+  include-markdown "{page_to_include_filepath}"
+  comments=false
+  heading-offset={argument_value}
+%}}''',
+            page(tmp_path / 'includer.md'),
+            tmp_path,
+        )
+
+    assert len(caplog.records) == 0
+    assert str(exc.value) == exception_message
+
+
 class TestFilename:
     double_quoted_filenames = [
         'inc"luded.md', 'inc"lude"d.md', 'included.md"', '"included.md',
diff --git a/tests/test_unit/test_cache.py b/tests/test_unit/test_cache.py
index 6ae21a5..f578ad3 100644
--- a/tests/test_unit/test_cache.py
+++ b/tests/test_unit/test_cache.py
@@ -16,6 +16,12 @@ def test_cache_read_file(tmp_path):
     )
 
 
+def test_cache_expiration_on_get(tmp_path):
+    cache = Cache(tmp_path)
+    cache.set_('foo', f'{time.time() - 600*10}\nbar')
+    assert cache.get_('foo') is None
+
+
 def test_cache_clean(tmp_path):
     now_ts = int(time.time())
 
diff --git a/tests/test_unit/test_include_markdown.py b/tests/test_unit/test_include_markdown.py
index 2bacf6e..513233e 100644
--- a/tests/test_unit/test_include_markdown.py
+++ b/tests/test_unit/test_include_markdown.py
@@ -400,7 +400,7 @@ Some text
             id='dedent=true,preserve-includer-indent=true',
         ),
 
-        # Markdown heading offset 1
+        # Markdown heading offsets
         pytest.param(
             '''# Header
 
@@ -423,7 +423,6 @@ Example data
             [],
             id='heading-offset=1',
         ),
-        # Markdown heading offset 2
         pytest.param(
             '''# Header
 
@@ -494,30 +493,6 @@ Example data
             id='heading-offset=0',
         ),
 
-        # Markdown heading offset string
-        pytest.param(
-            '''# Header
-
-{%
-  include-markdown "{filepath}"
-  heading-offset=true
-%}
-''',
-            '''# This should be a first level heading.
-
-Example data''',
-            '''# Header
-
-<!-- BEGIN INCLUDE {filepath} -->
-# This should be a first level heading.
-
-Example data
-<!-- END INCLUDE -->
-''',
-            [],
-            id='heading-offset=<str>',
-        ),
-
         # Markdown heading negative offset
         pytest.param(
             '''# Header
@@ -622,10 +597,7 @@ After include.
         pytest.param(
             '''# Header
 
-{%
-  include-markdown "{filepath}"
-  heading-offset=true
-%}
+{% include-markdown "{filepath}" %}
 ''',
             '''Тест інклуде
 азъ
@@ -664,7 +636,7 @@ vɛвѣди
             id='russian-characters',
         ),
 
-        # right strip unix trailing newlines
+        # Right strip unix trailing newlines
         pytest.param(
             '''1. List item number 1
 1. {% include-markdown "{filepath}" comments=false trailing-newlines=false %}
@@ -679,7 +651,7 @@ vɛвѣди
             id='rstrip-unix-trailing-newlines',
         ),
 
-        # right strip windows trailing nwlines
+        # Right strip windows trailing newlines
         pytest.param(
             '''1. List item number 1
 1. {%
@@ -698,7 +670,7 @@ vɛвѣди
             id='rstrip-windows-trailing-newlines',
         ),
 
-        # right strip trailing newlines keeping comments
+        # Right strip trailing newlines keeping comments
         pytest.param(
             '''1. List item number 1
 1. {% include-markdown "{filepath}" trailing-newlines=false %}
diff --git a/tests/test_unit/test_process.py b/tests/test_unit/test_process.py
index 96bde28..a85113c 100644
--- a/tests/test_unit/test_process.py
+++ b/tests/test_unit/test_process.py
@@ -2,8 +2,10 @@
 
 import pytest
 
+from mkdocs_include_markdown_plugin.cache import Cache
 from mkdocs_include_markdown_plugin.process import (
     increase_headings_offset,
+    read_url,
     rewrite_relative_urls,
 )
 
@@ -149,6 +151,30 @@ Some text after
 ''',
             id='cpp-likelink-indented-codeblock-windows-newlines',
         ),
+        pytest.param(
+            '''```
+[link](CHANGELOG.md)
+```
+''',
+            'README',
+            'docs/nav.md',
+            '''```
+[link](CHANGELOG.md)
+```
+''',
+            id='exclude-fenced-code-blocks',
+        ),
+        pytest.param(
+            ' ' * 4 + '''
+    [link](CHANGELOG.md)
+''' + ' ' * 4 + '\n',
+            'README',
+            'docs/nav.md',
+            ' ' * 4 + '''
+    [link](CHANGELOG.md)
+''' + ' ' * 4 + '\n',
+            id='exclude-indented-code-blocks',
+        ),
     ),
 )
 def test_rewrite_relative_urls(
@@ -323,3 +349,28 @@ def test_dont_increase_heading_offset_inside_fenced_codeblocks(
     expected_result,
 ):
     assert increase_headings_offset(markdown, offset=offset) == expected_result
+
+
+def test_read_url_cached_content(tmp_path):
+    url = (
+        'https://raw.githubusercontent.com/mondeja/'
+        'mkdocs-include-markdown-plugin/master/README.md'
+    )
+    cache_dir = tmp_path.as_posix()
+    cached_file_name = Cache.generate_unique_key_from_url(url)
+    cached_file_path = tmp_path / cached_file_name
+    if cached_file_path.exists():
+        cached_file_path.unlink()
+
+    cache = Cache(cache_dir, 600)
+    content = read_url(url, cache)
+    assert cached_file_path.exists()
+
+    cached_content = cached_file_path.read_text(
+        encoding='utf-8',
+    ).split('\n', 1)[1]
+    assert content == cached_content
+
+    assert cache.get_(url) == cached_content
+    assert cache.get_(url) == read_url(url, cache)
+    cached_file_path.unlink()