Repository: mkdocs/mkdocs

Issue:
Wrong example for `!relative` option
The Configuration page lists an example using `pymdownx.snippets` for the `!relative` YAML tag.

The issue with this example is, that the `base_path` option of snippets is expecting a list and not a single String, which breaks the configuration itself.
In fact, the relative option cannot be used by snippets at all it seems.

This error in the documentation should be fixed, as it will otherwise give people the idea, that it can be used with snippets like that...
Or IF it can be used, there should be more clarification on this.

Below is a collection of stacktraces thrown by MkDocs when using `!relative` in different setups:

```yaml
markdown_extensions:
  # ...
  - pymdownx.snippets:
      base_path: !relative
```

<details><summary>Stacktrace</summary>
<p>

```
ERROR   -  Config value 'markdown_extensions': Failed to load extension 'pymdownx.snippets'.
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\mkdocs\config\config_options.py", line 1021, in run_validation
    md.registerExtensions((ext,), self.configdata)
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\markdown\core.py", line 115, in registerExtensions
    ext.extendMarkdown(self)
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\pymdownx\snippets.py", line 402, in extendMarkdown
    snippet = SnippetPreprocessor(config, md)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\pymdownx\snippets.py", line 85, in __init__
    self.base_path = [os.path.abspath(b) for b in base]
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'RelativeDirPlaceholder' object is not iterable
Aborted with 1 configuration errors!
ERROR   -  [21:51:48] An error happened during the rebuild. The server will appear stuck until build errors are resolved.
```

</p>
</details>

```yaml
markdown_extensions:
  # ...
  - pymdownx.snippets:
      base_path:
        - !relative
```

<details><summary>Stacktrace</summary>
<p>

```
ERROR   -  Config value 'markdown_extensions': Failed to load extension 'pymdownx.snippets'.
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\mkdocs\utils\yaml.py", line 52, in __fspath__
    return os.path.join(self.value(), self.suffix)
                        ^^^^^^^^^^^^
  File "C:\Users\Andreas\AppData\Local\Programs\Python\Python311\Lib\site-packages\mkdocs\utils\yaml.py", line 98, in value
    raise exceptions.ConfigurationError(
ConfigurationError: The current file is not set for the '!relative' tag. It cannot be used in this context; the intended usage is within `markdown_extensions`.
Aborted with 1 configuration errors!
ERROR   -  [21:52:36] An error happened during the rebuild. The server will appear stuck until build errors are resolved.
```

</p>
</details> 


Discussion:
Yes that is true. There's currently a problem with both examples. I actually ran into it just yesterday.

The 1st example has a bug that is fixed in https://github.com/facelessuser/pymdown-extensions/pull/2206 but not released yet. But then it will run into the same bug as the 2nd example.

The 2nd example runs into a validation bug. Back when I was developing this, this line wasn't there yet: https://github.com/facelessuser/pymdown-extensions/blame/f4a66a6f06aa8e6f72f25fe6c579781f12e5636e/pymdownx/snippets.py#L85
Because this line is executed at load time and because mkdocs tries to validate the extension at load time, this fails.
https://github.com/mkdocs/mkdocs/blob/8909ab3f6dcd0556a8266a8cd6980f89810d1855/mkdocs/config/config_options.py#L1020

Note that with the 2nd example only `!relative` is broken, `!relative $config_dir` still works.
> Note that with the 2nd example only `!relative` is broken, `!relative $config_dir` still works.

Okay. Sadly, this wouldn't be of use for me personally, as the `!relative` option would be most useful for me for translatable docs as I could have the snippet in the same relative position to the docs page instead of having X variants in the same folder...

Guess I'll have to wait for pymdown extensions to update.
There seems to still be some issues with this option, even after 10.4 was released, which should fix it.

I mention this here, because I saw oprypin contributing the possible fix for this to the extension, so maybe he has further input on this.
Info on the issue, including currend markdown_extensions settings, is found here: https://github.com/facelessuser/pymdown-extensions/discussions/2236
Yes I was saying there are 2 problems and only 1 is fixed.

Resolving patch:
diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py
--- a/mkdocs/config/config_options.py
+++ b/mkdocs/config/config_options.py
@@ -10,6 +10,7 @@
 import types
 import warnings
 from collections import Counter, UserString
+from types import SimpleNamespace
 from typing import (
     Any,
     Callable,
@@ -993,6 +994,12 @@ def validate_ext_cfg(self, ext: object, cfg: object) -> None:
             raise ValidationError(f"Invalid config options for Markdown Extension '{ext}'.")
         self.configdata[ext] = cfg
 
+    def pre_validation(self, config, key_name):
+        # To appease validation in case it involves the `!relative` tag.
+        config._current_page = current_page = SimpleNamespace()  # type: ignore[attr-defined]
+        current_page.file = SimpleNamespace()
+        current_page.file.src_path = ''
+
     def run_validation(self, value: object) -> list[str]:
         self.configdata: dict[str, dict] = {}
         if not isinstance(value, (list, tuple, dict)):
@@ -1037,6 +1044,7 @@ def run_validation(self, value: object) -> list[str]:
         return extensions
 
     def post_validation(self, config: Config, key_name: str):
+        config._current_page = None  # type: ignore[attr-defined]
         config[self.configkey] = self.configdata
 
 
diff --git a/mkdocs/utils/yaml.py b/mkdocs/utils/yaml.py
--- a/mkdocs/utils/yaml.py
+++ b/mkdocs/utils/yaml.py
@@ -97,12 +97,13 @@ def __init__(self, config: MkDocsConfig, suffix: str = ''):
         super().__init__(config, suffix)
 
     def value(self) -> str:
-        if self.config._current_page is None:
+        current_page = self.config._current_page
+        if current_page is None:
             raise exceptions.ConfigurationError(
                 "The current file is not set for the '!relative' tag. "
                 "It cannot be used in this context; the intended usage is within `markdown_extensions`."
             )
-        return os.path.dirname(self.config._current_page.file.abs_src_path)
+        return os.path.dirname(os.path.join(self.config.docs_dir, current_page.file.src_path))
 
 
 def get_yaml_loader(loader=yaml.Loader, config: MkDocsConfig | None = None):


Test patch:
diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py
--- a/mkdocs/tests/build_tests.py
+++ b/mkdocs/tests/build_tests.py
@@ -814,7 +814,7 @@ def run(self, lines: list[str]) -> list[str]:
 
 class _TestExtension(markdown.extensions.Extension):
     def __init__(self, base_path: str) -> None:
-        self.base_path = base_path
+        self.base_path = str(base_path)
 
     def extendMarkdown(self, md: markdown.Markdown) -> None:
         md.preprocessors.register(_TestPreprocessor(self.base_path), "mkdocs_test", priority=32)