waylan: Awesome! I'll have to take a closer look when I have some time.
djeebus: Just found an issue: plugins aren't getting normalized properly. Should have it fixed soon too.
djeebus: All ready now!
waylan: The py33 tests are being handled in #1453. Guess we can move on with this issue by updating the docs.
diff --git a/docs/about/release-notes.md b/docs/about/release-notes.md
index e5a2abaf..9ae74fb1 100644
--- a/docs/about/release-notes.md
+++ b/docs/about/release-notes.md
@@ -25,6 +25,25 @@ The current and past members of the MkDocs team.
 
 ### Major Additions to Development Version
 
+#### Path Based Settings are Relative to Configuration File (#543)
+
+Previously any relative paths in the various configuration options were
+resolved relative to the current working directory. They are now resolved
+relative to the configuration file. As the documentation has always encouraged
+running the various MkDocs commands from the directory that contains the
+configuration file (project root), this change will not affect most users.
+However, it will make it much easier to implement automated builds or otherwise
+run commands from a location other than the project root.
+
+Simply use the `-f/--config-file` option and point it at the configuration file:
+
+```sh
+mkdocs build --config-file /path/to/my/config/file.yml
+```
+
+As previously, if no file is specified, MkDocs looks for a file named
+`mkdocs.yml` in the current working directory.
+
 #### Refactor Search Plugin
 
 The search plugin has been completely refactored to include support for the
diff --git a/docs/user-guide/configuration.md b/docs/user-guide/configuration.md
index 333b7e67..8cac62f9 100644
--- a/docs/user-guide/configuration.md
+++ b/docs/user-guide/configuration.md
@@ -215,9 +215,10 @@ If a set of key/value pairs, the following nested keys can be defined:
 
     #### custom_dir:
 
-    A directory to custom a theme. This can either be a relative directory, in
-    which case it is resolved relative to the directory containing your
-    configuration file, or it can be an absolute directory path.
+    A directory containing a custom theme. This can either be a relative
+    directory, in which case it is resolved relative to the directory containing
+    your configuration file, or it can be an absolute directory path from the
+    root of your local file system.
 
     See [styling your docs][theme_dir] for details if you would like to tweak an
     existing theme.
@@ -240,19 +241,19 @@ If a set of key/value pairs, the following nested keys can be defined:
 
 ### docs_dir
 
-Lets you set the directory containing the documentation source markdown files.
-This can either be a relative directory, in which case it is resolved relative
-to the directory containing your configuration file, or it can be an absolute
-directory path from the root of your local file system.
+The directory containing the documentation source markdown files. This can
+either be a relative directory, in which case it is resolved relative to the
+directory containing your configuration file, or it can be an absolute directory
+path from the root of your local file system.
 
 **default**: `'docs'`
 
 ### site_dir
 
-Lets you set the directory where the output HTML and other files are created.
-This can either be a relative directory, in which case it is resolved relative
-to the directory containing your configuration file, or it can be an absolute
-directory path from the root of your local file system.
+The directory where the output HTML and other files are created. This can either
+be a relative directory, in which case it is resolved relative to the directory
+containing your configuration file, or it can be an absolute directory path from
+the root of your local file system.
 
 **default**: `'site'`
 
diff --git a/docs/user-guide/custom-themes.md b/docs/user-guide/custom-themes.md
index 2f0021dd..18f72b28 100644
--- a/docs/user-guide/custom-themes.md
+++ b/docs/user-guide/custom-themes.md
@@ -20,41 +20,48 @@ and their usage.
 ## Creating a custom theme
 
 The bare minimum required for a custom theme is a `main.html` [Jinja2 template]
-file. This should be placed in a directory which will be the `custom_dir` and it
-should be created next to the `mkdocs.yml` configuration file. Within
-`mkdocs.yml`, specify the theme `custom_dir` option and set it to the name of
-the directory containing `main.html`. For example, given this example project
-layout:
-
-    mkdocs.yml
-    docs/
-        index.md
-        about.md
-    custom_theme/
-        main.html
-        ...
-
-You would include the following settings in `mkdocs.yml` to use the custom theme
+file which is placed in a directory that is *not* a child of the [docs_dir].
+Within `mkdocs.yml`, set the theme.[custom_dir] option to the path of the
+directory containing `main.html`. The path should be relative to the
+configuration file. For example, given this example project layout:
+
+```no-highlight
+mkdocs.yml
+docs/
+    index.md
+    about.md
+custom_theme/
+    main.html
+    ...
+```
+
+... you would include the following settings in `mkdocs.yml` to use the custom theme
 directory:
 
-    theme:
-        name: null
-        custom_dir: 'custom_theme'
+```yaml
+theme:
+    name: null
+    custom_dir: 'custom_theme/'
+```
 
 !!! Note
 
-    Generally, when building your own custom theme, the theme `name`
-    configuration setting would be set to `null`. However, if used in
-    combination with the `custom_dir` configuration value a custom theme can be
-    used to replace only specific parts of a built-in theme. For example, with
-    the above layout and if you set `name: "mkdocs"` then the `main.html` file
-    in the `custom_dir` would replace that in the theme but otherwise the
-    `mkdocs` theme would remain the same. This is useful if you want to make
+    Generally, when building your own custom theme, the theme.[name]
+    configuration setting would be set to `null`. However, if the
+    theme.[custom_dir] configuration value is used in combination with an
+    existing theme, the theme.[custom_dir] can be used to replace only specific
+    parts of a built-in theme. For example, with the above layout and if you set
+    `name: "mkdocs"` then the `main.html` file in the theme.[custom_dir] would
+    replace the file of the same name in the `mkdocs` theme but otherwise the
+    `mkdocs` theme would remain unchanged. This is useful if you want to make
     small adjustments to an existing theme.
 
     For more specific information, see [styling your docs].
 
 [styling your docs]: ./styling-your-docs.md#using-the-theme-custom_dir
+[custom_dir]: ./configuration.md#custom_dir
+[name]: ./configuration.md#name
+[docs_dir]:./configuration.md#docs_dir
 
 ## Basic theme
 
diff --git a/docs/user-guide/styling-your-docs.md b/docs/user-guide/styling-your-docs.md
index 4837e8f6..c97abf99 100644
--- a/docs/user-guide/styling-your-docs.md
+++ b/docs/user-guide/styling-your-docs.md
@@ -138,7 +138,7 @@ And then point your `mkdocs.yml` configuration file at the new directory:
 ```yaml
 theme:
     name: mkdocs
-    custom_dir: custom_theme
+    custom_dir: custom_theme/
 ```
 
 To override the 404 error page ("file not found"), add a new template file named
diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py
index afcec0a9..60f9b587 100644
--- a/mkdocs/config/base.py
+++ b/mkdocs/config/base.py
@@ -21,13 +21,14 @@ class Config(utils.UserDict):
     for running validation on the structure and contents.
     """
 
-    def __init__(self, schema):
+    def __init__(self, schema, config_file_path=None):
         """
         The schema is a Python dict which maps the config name to a validator.
         """
 
         self._schema = schema
         self._schema_keys = set(dict(schema).keys())
+        self.config_file_path = config_file_path
         self.data = {}
 
         self.user_configs = []
@@ -172,7 +173,7 @@ def load_config(config_file=None, **kwargs):
 
     # Initialise the config with the default schema .
     from mkdocs import config
-    cfg = Config(schema=config.DEFAULT_SCHEMA)
+    cfg = Config(schema=config.DEFAULT_SCHEMA, config_file_path=options['config_file_path'])
     # First load the config file
     cfg.load_file(config_file)
     # Then load the options to overwrite anything in the config.
diff --git a/mkdocs/config/config_options.py b/mkdocs/config/config_options.py
index 9a7eb0b6..30da2a2a 100644
--- a/mkdocs/config/config_options.py
+++ b/mkdocs/config/config_options.py
@@ -293,6 +293,24 @@ class FilesystemObject(Type):
         super(FilesystemObject, self).__init__(type_=utils.string_types, **kwargs)
         self.exists = exists
 
+    def pre_validation(self, config, key_name):
+        value = config[key_name]
+
+        if not value:
+            return
+
+        if os.path.isabs(value):
+            return
+
+        if config.config_file_path is None:
+            # Unable to determine absolute path of the config file; fall back
+            # to trusting the relative path
+            return
+
+        config_dir = os.path.dirname(config.config_file_path)
+        value = os.path.join(config_dir, value)
+        config[key_name] = value
+
     def run_validation(self, value):
         value = super(FilesystemObject, self).run_validation(value)
         if self.exists and not self.existence_test(value):
@@ -311,9 +329,11 @@ class Dir(FilesystemObject):
     name = 'directory'
 
     def post_validation(self, config, key_name):
+        if config.config_file_path is None:
+            return
 
         # Validate that the dir is not the parent dir of the config file.
-        if os.path.dirname(config['config_file_path']) == config[key_name]:
+        if os.path.dirname(config.config_file_path) == config[key_name]:
             raise ValidationError(
                 ("The '{0}' should not be the parent directory of the config "
                  "file. Use a child directory instead so that the config file "
@@ -430,7 +450,12 @@ class Theme(BaseConfigOption):
 
         # Ensure custom_dir is an absolute path
         if 'custom_dir' in theme_config and not os.path.isabs(theme_config['custom_dir']):
-            theme_config['custom_dir'] = os.path.abspath(theme_config['custom_dir'])
+            config_dir = os.path.dirname(config.config_file_path)
+            theme_config['custom_dir'] = os.path.join(config_dir, theme_config['custom_dir'])
+
+        if 'custom_dir' in theme_config and not os.path.isdir(theme_config['custom_dir']):
+            raise ValidationError("The path set in {name}.custom_dir ('{path}') does not exist.".
+                                  format(path=theme_config['custom_dir'], name=self.name))
 
         config[key_name] = theme.Theme(**theme_config)
 
@@ -621,6 +646,10 @@ class Plugins(OptionallyRequired):
     def __init__(self, **kwargs):
         super(Plugins, self).__init__(**kwargs)
         self.installed_plugins = plugins.get_plugins()
+        self.config_file_path = None
+
+    def pre_validation(self, config, key_name):
+        self.config_file_path = config.config_file_path
 
     def run_validation(self, value):
         if not isinstance(value, (list, tuple)):
@@ -635,11 +664,15 @@ class Plugins(OptionallyRequired):
                 if not isinstance(cfg, dict):
                     raise ValidationError('Invalid config options for '
                                           'the "{0}" plugin.'.format(name))
-                plgins[name] = self.load_plugin(name, cfg)
-            elif isinstance(item, utils.string_types):
-                plgins[item] = self.load_plugin(item, {})
+                item = name
             else:
+                cfg = {}
+
+            if not isinstance(item, utils.string_types):
                 raise ValidationError('Invalid Plugins configuration')
+
+            plgins[item] = self.load_plugin(item, cfg)
+
         return plgins
 
     def load_plugin(self, name, config):
@@ -654,7 +687,7 @@ class Plugins(OptionallyRequired):
                 plugins.BasePlugin.__name__))
 
         plugin = Plugin()
-        errors, warnings = plugin.load_config(config)
+        errors, warnings = plugin.load_config(config, self.config_file_path)
         self.warnings.extend(warnings)
         errors_message = '\n'.join(
             "Plugin value: '{}'. Error: {}".format(x, y)
diff --git a/mkdocs/plugins.py b/mkdocs/plugins.py
index 69d99ed8..de4e837d 100644
--- a/mkdocs/plugins.py
+++ b/mkdocs/plugins.py
@@ -42,10 +42,10 @@ class BasePlugin(object):
     config_scheme = ()
     config = {}
 
-    def load_config(self, options):
+    def load_config(self, options, config_file_path=None):
         """ Load config from a dict of options. Returns a tuple of (errors, warnings)."""
 
-        self.config = Config(schema=self.config_scheme)
+        self.config = Config(schema=self.config_scheme, config_file_path=config_file_path)
         self.config.load_dict(options)
 
         return self.config.validate()
diff --git a/tox.ini b/tox.ini
index 52d0d7e0..bee768b2 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,6 +1,6 @@
 [tox]
 envlist =
-    py{27,34,35,36}-{unittests,integration,min-req},
+    py{27,34,35,36,py,py3}-{unittests,integration,min-req},
     flake8, markdown-lint, linkchecker, jshint, csslint
 
 [testenv]
@@ -9,6 +9,7 @@ deps=
     py{27,34,35,36,py,py3}-{unittests,integration}: -rrequirements/project.txt
     py{27,34,35,36,py,py3}-min-req: -rrequirements/project-min.txt
     py{27,34,35,36,py,py3}-{unittests,min-req}: -rrequirements/test.txt
+    py{27,py}-{unittests,min-req}: backports.tempfile
 commands=
     {envpython} --version
     py{27,34,35,36,py,py3}-{unittests,min-req}: {envbindir}/nosetests --with-coverage --cover-package mkdocs mkdocs

Paths are relative to you, not the config file
You can do `mkdocs build --config-file=path/to/mkdocs.yml` but any configuration values in that file are relative to your current working directory.

This seems wrong to me, but it's only something I've ran into when trying to automate MkDocs builds (for integration testing). I'm not sure if we want to change this, or what people really expect. The common use case is that you are in the same directory as the config file anyway, so that doen't matter.

Something like this should be changed or at least decided for a 1.0 release.

Thoughts?

diff --git a/mkdocs/tests/build_tests.py b/mkdocs/tests/build_tests.py
index ac5f5116..32b86206 100644
--- a/mkdocs/tests/build_tests.py
+++ b/mkdocs/tests/build_tests.py
@@ -3,8 +3,6 @@
 
 from __future__ import unicode_literals
 import os
-import shutil
-import tempfile
 import unittest
 import mock
 import io
@@ -15,6 +13,11 @@ except ImportError:
     # In Py3 use builtin zip function
     pass
 
+try:
+    # py>=3.2
+    from tempfile import TemporaryDirectory
+except ImportError:
+    from backports.tempfile import TemporaryDirectory
 
 from mkdocs import nav
 from mkdocs.commands import build
@@ -319,9 +322,7 @@ class BuildTests(unittest.TestCase):
         self.assertEqual(page.content.strip(), '<p>foo</p>')
 
     def test_copying_media(self):
-        docs_dir = tempfile.mkdtemp()
-        site_dir = tempfile.mkdtemp()
-        try:
+        with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir:
             # Create a non-empty markdown file, image, html file, dot file and dot directory.
             f = open(os.path.join(docs_dir, 'index.md'), 'w')
             f.write(dedent("""
@@ -351,14 +352,9 @@ class BuildTests(unittest.TestCase):
             self.assertTrue(os.path.isfile(os.path.join(site_dir, 'example.html')))
             self.assertFalse(os.path.isfile(os.path.join(site_dir, '.hidden')))
             self.assertFalse(os.path.isfile(os.path.join(site_dir, '.git/hidden')))
-        finally:
-            shutil.rmtree(docs_dir)
-            shutil.rmtree(site_dir)
 
     def test_copy_theme_files(self):
-        docs_dir = tempfile.mkdtemp()
-        site_dir = tempfile.mkdtemp()
-        try:
+        with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir:
             # Create a non-empty markdown file.
             f = open(os.path.join(docs_dir, 'index.md'), 'w')
             f.write(dedent("""
@@ -383,9 +379,6 @@ class BuildTests(unittest.TestCase):
             self.assertFalse(os.path.isfile(os.path.join(site_dir, 'base.html')))
             self.assertFalse(os.path.isfile(os.path.join(site_dir, 'content.html')))
             self.assertFalse(os.path.isfile(os.path.join(site_dir, 'nav.html')))
-        finally:
-            shutil.rmtree(docs_dir)
-            shutil.rmtree(site_dir)
 
     def test_strict_mode_valid(self):
         pages = [
@@ -467,9 +460,7 @@ class BuildTests(unittest.TestCase):
         self.assertEqual(context['config']['extra']['a'], 1)
 
     def test_BOM(self):
-        docs_dir = tempfile.mkdtemp()
-        site_dir = tempfile.mkdtemp()
-        try:
+        with TemporaryDirectory() as docs_dir, TemporaryDirectory() as site_dir:
             # Create an UTF-8 Encoded file with BOM (as Micorsoft editors do). See #1186.
             f = io.open(os.path.join(docs_dir, 'index.md'), 'w', encoding='utf-8-sig')
             f.write('# An UTF-8 encoded file with a BOM')
@@ -490,7 +481,3 @@ class BuildTests(unittest.TestCase):
             self.assertTrue(
                 '<h1 id="an-utf-8-encoded-file-with-a-bom">An UTF-8 encoded file with a BOM</h1>' in output
             )
-
-        finally:
-            shutil.rmtree(docs_dir)
-            shutil.rmtree(site_dir)
diff --git a/mkdocs/tests/config/base_tests.py b/mkdocs/tests/config/base_tests.py
index ce56cd93..5118279f 100644
--- a/mkdocs/tests/config/base_tests.py
+++ b/mkdocs/tests/config/base_tests.py
@@ -3,6 +3,12 @@ import os
 import tempfile
 import unittest
 
+try:
+    # py>=3.2
+    from tempfile import TemporaryDirectory
+except ImportError:
+    from backports.tempfile import TemporaryDirectory
+
 from mkdocs import exceptions
 from mkdocs.config import base, defaults
 from mkdocs.config.config_options import BaseConfigOption
@@ -42,7 +48,9 @@ class ConfigBaseTests(unittest.TestCase):
         Allows users to specify a config other than the default `mkdocs.yml`.
         """
 
-        config_file = tempfile.NamedTemporaryFile('w', delete=False)
+        temp_dir = TemporaryDirectory()
+        config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w')
+        os.mkdir(os.path.join(temp_dir.name, 'docs'))
         try:
             config_file.write("site_name: MkDocs Test\n")
             config_file.flush()
@@ -64,7 +72,12 @@ class ConfigBaseTests(unittest.TestCase):
         `load_config` can accept an open file descriptor.
         """
 
-        config_file = tempfile.NamedTemporaryFile('r+', delete=False)
+        temp_dir = TemporaryDirectory()
+        temp_path = temp_dir.name
+        config_fname = os.path.join(temp_path, 'mkdocs.yml')
+
+        config_file = open(config_fname, 'w+')
+        os.mkdir(os.path.join(temp_path, 'docs'))
         try:
             config_file.write("site_name: MkDocs Test\n")
             config_file.flush()
@@ -75,7 +88,7 @@ class ConfigBaseTests(unittest.TestCase):
             # load_config will always close the file
             self.assertTrue(config_file.closed)
         finally:
-            os.remove(config_file.name)
+            temp_dir.cleanup()
 
     def test_load_from_closed_file(self):
         """
@@ -83,7 +96,10 @@ class ConfigBaseTests(unittest.TestCase):
         Ensure `load_config` reloads the closed file.
         """
 
-        config_file = tempfile.NamedTemporaryFile('w', delete=False)
+        temp_dir = TemporaryDirectory()
+        config_file = open(os.path.join(temp_dir.name, 'mkdocs.yml'), 'w')
+        os.mkdir(os.path.join(temp_dir.name, 'docs'))
+
         try:
             config_file.write("site_name: MkDocs Test\n")
             config_file.flush()
@@ -93,7 +109,7 @@ class ConfigBaseTests(unittest.TestCase):
             self.assertTrue(isinstance(cfg, base.Config))
             self.assertEqual(cfg['site_name'], 'MkDocs Test')
         finally:
-            os.remove(config_file.name)
+            temp_dir.cleanup()
 
     def test_load_from_deleted_file(self):
         """
@@ -234,3 +250,28 @@ class ConfigBaseTests(unittest.TestCase):
             ('invalid_option', 'run_validation warning'),
             ('invalid_option', 'post_validation warning'),
         ])
+
+    def test_load_from_file_with_relative_paths(self):
+        """
+        When explicitly setting a config file, paths should be relative to the
+        config file, not the working directory.
+        """
+
+        config_dir = TemporaryDirectory()
+        config_fname = os.path.join(config_dir.name, 'mkdocs.yml')
+        docs_dir = os.path.join(config_dir.name, 'src')
+        os.mkdir(docs_dir)
+
+        config_file = open(config_fname, 'w')
+
+        try:
+            config_file.write("docs_dir: src\nsite_name: MkDocs Test\n")
+            config_file.flush()
+            config_file.close()
+
+            cfg = base.load_config(config_file=config_file)
+            self.assertTrue(isinstance(cfg, base.Config))
+            self.assertEqual(cfg['site_name'], 'MkDocs Test')
+            self.assertEqual(cfg['docs_dir'], docs_dir)
+        finally:
+            config_dir.cleanup()
diff --git a/mkdocs/tests/config/config_options_tests.py b/mkdocs/tests/config/config_options_tests.py
index 17c4141b..56b7a724 100644
--- a/mkdocs/tests/config/config_options_tests.py
+++ b/mkdocs/tests/config/config_options_tests.py
@@ -6,6 +6,7 @@ import unittest
 import mkdocs
 from mkdocs import utils
 from mkdocs.config import config_options
+from mkdocs.config.base import Config
 
 
 class OptionallyRequiredTest(unittest.TestCase):
@@ -272,18 +273,21 @@ class DirTest(unittest.TestCase):
                           option.validate, [])
 
     def test_doc_dir_is_config_dir(self):
+        cfg = Config(
+            [('docs_dir', config_options.Dir())],
+            config_file_path=os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
+        )
 
         test_config = {
-            'config_file_path': os.path.join(os.path.abspath('.'), 'mkdocs.yml'),
             'docs_dir': '.'
         }
 
-        docs_dir = config_options.Dir()
+        cfg.load_dict(test_config)
 
-        test_config['docs_dir'] = docs_dir.validate(test_config['docs_dir'])
+        fails, warns = cfg.validate()
 
-        self.assertRaises(config_options.ValidationError,
-                          docs_dir.post_validation, test_config, 'docs_dir')
+        self.assertEqual(len(fails), 1)
+        self.assertEqual(len(warns), 0)
 
 
 class SiteDirTest(unittest.TestCase):
@@ -293,12 +297,23 @@ class SiteDirTest(unittest.TestCase):
         site_dir = config_options.SiteDir()
         docs_dir = config_options.Dir()
 
-        config['config_file_path'] = os.path.join(os.path.abspath('..'), 'mkdocs.yml')
+        fname = os.path.join(os.path.abspath('..'), 'mkdocs.yml')
 
         config['docs_dir'] = docs_dir.validate(config['docs_dir'])
         config['site_dir'] = site_dir.validate(config['site_dir'])
-        site_dir.post_validation(config, 'site_dir')
-        return True  # No errors were raised
+
+        schema = [
+            ('site_dir', site_dir),
+            ('docs_dir', docs_dir),
+        ]
+        cfg = Config(schema, fname)
+        cfg.load_dict(config)
+        failed, warned = cfg.validate()
+
+        if failed:
+            raise config_options.ValidationError(failed)
+
+        return True
 
     def test_doc_dir_in_site_dir(self):
 
diff --git a/mkdocs/tests/config/config_tests.py b/mkdocs/tests/config/config_tests.py
index ebfdba48..d956bf3b 100644
--- a/mkdocs/tests/config/config_tests.py
+++ b/mkdocs/tests/config/config_tests.py
@@ -3,10 +3,16 @@
 
 from __future__ import unicode_literals
 import os
-import shutil
 import tempfile
 import unittest
 
+try:
+    # py>=3.2
+    from tempfile import TemporaryDirectory
+except ImportError:
+    from backports.tempfile import TemporaryDirectory
+
+
 import mkdocs
 from mkdocs import config
 from mkdocs import utils
@@ -81,8 +87,11 @@ class ConfigTests(unittest.TestCase):
         pages:
         - 'Introduction': 'index.md'
         """)
-        config_file = tempfile.NamedTemporaryFile('w', delete=False)
-        try:
+        with TemporaryDirectory() as temp_path:
+            os.mkdir(os.path.join(temp_path, 'docs'))
+            config_path = os.path.join(temp_path, 'mkdocs.yml')
+            config_file = open(config_path, 'w')
+
             config_file.write(ensure_utf(file_contents))
             config_file.flush()
             config_file.close()
@@ -90,93 +99,87 @@ class ConfigTests(unittest.TestCase):
             result = config.load_config(config_file=config_file.name)
             self.assertEqual(result['site_name'], expected_result['site_name'])
             self.assertEqual(result['pages'], expected_result['pages'])
-        finally:
-            os.remove(config_file.name)
 
     def test_theme(self):
-
-        mytheme = tempfile.mkdtemp()
-        custom = tempfile.mkdtemp()
-
-        configs = [
-            dict(),  # default theme
-            {"theme": "readthedocs"},  # builtin theme
-            {"theme_dir": mytheme},  # custom only
-            {"theme": "readthedocs", "theme_dir": custom},  # builtin and custom
-            {"theme": {'name': 'readthedocs'}},  # builtin as complex
-            {"theme": {'name': None, 'custom_dir': mytheme}},  # custom only as complex
-            {"theme": {'name': 'readthedocs', 'custom_dir': custom}},  # builtin and custom as complex
-            {  # user defined variables
-                'theme': {
-                    'name': 'mkdocs',
-                    'static_templates': ['foo.html'],
-                    'show_sidebar': False,
-                    'some_var': 'bar'
+        with TemporaryDirectory() as mytheme, TemporaryDirectory() as custom:
+            configs = [
+                dict(),  # default theme
+                {"theme": "readthedocs"},  # builtin theme
+                {"theme_dir": mytheme},  # custom only
+                {"theme": "readthedocs", "theme_dir": custom},  # builtin and custom
+                {"theme": {'name': 'readthedocs'}},  # builtin as complex
+                {"theme": {'name': None, 'custom_dir': mytheme}},  # custom only as complex
+                {"theme": {'name': 'readthedocs', 'custom_dir': custom}},  # builtin and custom as complex
+                {  # user defined variables
+                    'theme': {
+                        'name': 'mkdocs',
+                        'static_templates': ['foo.html'],
+                        'show_sidebar': False,
+                        'some_var': 'bar'
+                    }
                 }
-            }
-        ]
-
-        mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__))
-        mkdocs_templates_dir = os.path.join(mkdocs_dir, 'templates')
-        theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes'))
-
-        results = (
-            {
-                'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml'],
-                'vars': {'include_search_page': False, 'search_index_only': False}
-            }, {
-                'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml'],
-                'vars': {'include_search_page': True, 'search_index_only': False}
-            }, {
-                'dirs': [mytheme, mkdocs_templates_dir],
-                'static_templates': ['sitemap.xml'],
-                'vars': {}
-            }, {
-                'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml'],
-                'vars': {'include_search_page': True, 'search_index_only': False}
-            }, {
-                'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml'],
-                'vars': {'include_search_page': True, 'search_index_only': False}
-            }, {
-                'dirs': [mytheme, mkdocs_templates_dir],
-                'static_templates': ['sitemap.xml'],
-                'vars': {}
-            }, {
-                'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml'],
-                'vars': {'include_search_page': True, 'search_index_only': False}
-            }, {
-                'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
-                'static_templates': ['404.html', 'sitemap.xml', 'foo.html'],
-                'vars': {
-                    'show_sidebar': False,
-                    'some_var': 'bar',
-                    'include_search_page': False,
-                    'search_index_only': False
+            ]
+
+            mkdocs_dir = os.path.abspath(os.path.dirname(mkdocs.__file__))
+            mkdocs_templates_dir = os.path.join(mkdocs_dir, 'templates')
+            theme_dir = os.path.abspath(os.path.join(mkdocs_dir, 'themes'))
+
+            results = (
+                {
+                    'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml'],
+                    'vars': {'include_search_page': False, 'search_index_only': False}
+                }, {
+                    'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml'],
+                    'vars': {'include_search_page': True, 'search_index_only': False}
+                }, {
+                    'dirs': [mytheme, mkdocs_templates_dir],
+                    'static_templates': ['sitemap.xml'],
+                    'vars': {}
+                }, {
+                    'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml'],
+                    'vars': {'include_search_page': True, 'search_index_only': False}
+                }, {
+                    'dirs': [os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml'],
+                    'vars': {'include_search_page': True, 'search_index_only': False}
+                }, {
+                    'dirs': [mytheme, mkdocs_templates_dir],
+                    'static_templates': ['sitemap.xml'],
+                    'vars': {}
+                }, {
+                    'dirs': [custom, os.path.join(theme_dir, 'readthedocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml'],
+                    'vars': {'include_search_page': True, 'search_index_only': False}
+                }, {
+                    'dirs': [os.path.join(theme_dir, 'mkdocs'), mkdocs_templates_dir],
+                    'static_templates': ['404.html', 'sitemap.xml', 'foo.html'],
+                    'vars': {
+                        'show_sidebar': False,
+                        'some_var': 'bar',
+                        'include_search_page': False,
+                        'search_index_only': False
+                    }
                 }
-            }
-        )
+            )
 
-        for config_contents, result in zip(configs, results):
+            for config_contents, result in zip(configs, results):
 
-            c = config.Config(schema=(
-                ('theme', config_options.Theme(default='mkdocs')),
-                ('theme_dir', config_options.ThemeDir(exists=True)),
-            ))
-            c.load_dict(config_contents)
-            errors, warnings = c.validate()
-            self.assertEqual(len(errors), 0)
-            self.assertEqual(c['theme'].dirs, result['dirs'])
-            self.assertEqual(c['theme'].static_templates, set(result['static_templates']))
-            self.assertEqual(dict([(k, c['theme'][k]) for k in iter(c['theme'])]), result['vars'])
+                c = config.Config(schema=(
+                    ('theme', config_options.Theme(default='mkdocs')),
+                    ('theme_dir', config_options.ThemeDir(exists=True)),
+                ))
+                c.load_dict(config_contents)
+                errors, warnings = c.validate()
+                self.assertEqual(len(errors), 0)
+                self.assertEqual(c['theme'].dirs, result['dirs'])
+                self.assertEqual(c['theme'].static_templates, set(result['static_templates']))
+                self.assertEqual(dict([(k, c['theme'][k]) for k in iter(c['theme'])]), result['vars'])
 
     def test_default_pages(self):
-        tmp_dir = tempfile.mkdtemp()
-        try:
+        with TemporaryDirectory() as tmp_dir:
             open(os.path.join(tmp_dir, 'index.md'), 'w').close()
             open(os.path.join(tmp_dir, 'about.md'), 'w').close()
             conf = config.Config(schema=config.DEFAULT_SCHEMA)
@@ -187,12 +190,9 @@ class ConfigTests(unittest.TestCase):
             })
             conf.validate()
             self.assertEqual(['index.md', 'about.md'], conf['pages'])
-        finally:
-            shutil.rmtree(tmp_dir)
 
     def test_default_pages_nested(self):
-        tmp_dir = tempfile.mkdtemp()
-        try:
+        with TemporaryDirectory() as tmp_dir:
             open(os.path.join(tmp_dir, 'index.md'), 'w').close()
             open(os.path.join(tmp_dir, 'getting-started.md'), 'w').close()
             open(os.path.join(tmp_dir, 'about.md'), 'w').close()
@@ -228,8 +228,6 @@ class ConfigTests(unittest.TestCase):
                     os.path.join('subC', 'index.md')
                 ]}
             ], conf['pages'])
-        finally:
-            shutil.rmtree(tmp_dir)
 
     def test_doc_dir_in_site_dir(self):
 
diff --git a/mkdocs/tests/plugin_tests.py b/mkdocs/tests/plugin_tests.py
index 6fa4091a..ac2c3983 100644
--- a/mkdocs/tests/plugin_tests.py
+++ b/mkdocs/tests/plugin_tests.py
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
 
 import unittest
 import mock
+import os
 
 from mkdocs import plugins
 from mkdocs import utils
@@ -14,7 +15,8 @@ from mkdocs import config
 class DummyPlugin(plugins.BasePlugin):
     config_scheme = (
         ('foo', config.config_options.Type(utils.string_types, default='default foo')),
-        ('bar', config.config_options.Type(int, default=0))
+        ('bar', config.config_options.Type(int, default=0)),
+        ('dir', config.config_options.Dir(exists=False)),
     )
 
     def on_pre_page(self, content, **kwargs):
@@ -29,18 +31,27 @@ class DummyPlugin(plugins.BasePlugin):
 class TestPluginClass(unittest.TestCase):
 
     def test_valid_plugin_options(self):
+        test_dir = 'test'
 
         options = {
-            'foo': 'some value'
+            'foo': 'some value',
+            'dir': test_dir,
         }
 
+        cfg_fname = os.path.join('tmp', 'test', 'fname.yml')
+        cfg_fname = os.path.abspath(cfg_fname)
+
+        cfg_dirname = os.path.dirname(cfg_fname)
+        expected = os.path.join(cfg_dirname, test_dir)
+
         expected = {
             'foo': 'some value',
-            'bar': 0
+            'bar': 0,
+            'dir': expected,
         }
 
         plugin = DummyPlugin()
-        errors, warnings = plugin.load_config(options)
+        errors, warnings = plugin.load_config(options, config_file_path=cfg_fname)
         self.assertEqual(plugin.config, expected)
         self.assertEqual(errors, [])
         self.assertEqual(warnings, [])
@@ -132,7 +143,8 @@ class TestPluginConfig(unittest.TestCase):
         self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin)
         expected = {
             'foo': 'default foo',
-            'bar': 0
+            'bar': 0,
+            'dir': None,
         }
         self.assertEqual(cfg['plugins']['sample'].config, expected)
 
@@ -154,7 +166,8 @@ class TestPluginConfig(unittest.TestCase):
         self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin)
         expected = {
             'foo': 'foo value',
-            'bar': 42
+            'bar': 42,
+            'dir': None,
         }
         self.assertEqual(cfg['plugins']['sample'].config, expected)
 
@@ -194,7 +207,8 @@ class TestPluginConfig(unittest.TestCase):
         self.assertIsInstance(cfg['plugins']['sample'], plugins.BasePlugin)
         expected = {
             'foo': 'default foo',
-            'bar': 0
+            'bar': 0,
+            'dir': None,
         }
         self.assertEqual(cfg['plugins']['sample'].config, expected)
 

pip install -e .[dev]
parse_log_pytest
requirements.txt
pytest pytest-mock mock
apt-get update apt-get install -y gcc
3.9
requirements/project.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
click==8.1.8
exceptiongroup==1.2.2
importlib_metadata==8.6.1
iniconfig==2.1.0
Jinja2==3.1.6
livereload==2.7.1
Markdown==3.7
MarkupSafe==3.0.2
mdx-gh-links==0.4
-e git+https://github.com/mkdocs/mkdocs.git@d6449f90808b50979694a3e91b49b532f3bf785d#egg=mkdocs
mock==5.2.0
packaging==24.2
pluggy==1.5.0
pytest==8.3.5
pytest-mock==3.14.0
PyYAML==6.0.2
tomli==2.2.1
tornado==6.4.2
zipp==3.21.0

name: mkdocs
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=3.0.16=h5eee18b_0
  - pip=25.0=py39h06a4308_0
  - python=3.9.21=he870216_1
  - readline=8.2=h5eee18b_0
  - setuptools=75.8.0=py39h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - tzdata=2025a=h04d1e81_0
  - wheel=0.45.1=py39h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - click==8.1.8
      - exceptiongroup==1.2.2
      - importlib-metadata==8.6.1
      - iniconfig==2.1.0
      - jinja2==3.1.6
      - livereload==2.7.1
      - markdown==3.7
      - markupsafe==3.0.2
      - mdx-gh-links==0.4
      - mock==5.2.0
      - packaging==24.2
      - pluggy==1.5.0
      - pytest==8.3.5
      - pytest-mock==3.14.0
      - pyyaml==6.0.2
      - tomli==2.2.1
      - tornado==6.4.2
      - zipp==3.21.0
prefix: /opt/conda/envs/mkdocs

mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_file_with_relative_paths mkdocs/tests/config/config_options_tests.py::DirTest::test_doc_dir_is_config_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_common_prefix mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_doc_dir_in_site_dir mkdocs/tests/config/config_options_tests.py::SiteDirTest::test_site_dir_in_docs_dir mkdocs/tests/plugin_tests.py::TestPluginClass::test_invalid_plugin_options mkdocs/tests/plugin_tests.py::TestPluginClass::test_valid_plugin_options mkdocs/tests/plugin_tests.py::TestPluginCollection::test_event_returns_None mkdocs/tests/plugin_tests.py::TestPluginCollection::test_run_event_on_collection mkdocs/tests/plugin_tests.py::TestPluginCollection::test_run_event_twice_on_collection mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_none_with_default mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_with_options mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_without_options
mkdocs/tests/build_tests.py::BuildTests::test_BOM mkdocs/tests/build_tests.py::BuildTests::test_absolute_link mkdocs/tests/build_tests.py::BuildTests::test_anchor_only_link mkdocs/tests/build_tests.py::BuildTests::test_convert_internal_asbolute_media mkdocs/tests/build_tests.py::BuildTests::test_convert_internal_link mkdocs/tests/build_tests.py::BuildTests::test_convert_internal_link_differing_directory mkdocs/tests/build_tests.py::BuildTests::test_convert_internal_link_with_anchor mkdocs/tests/build_tests.py::BuildTests::test_convert_internal_media mkdocs/tests/build_tests.py::BuildTests::test_convert_markdown mkdocs/tests/build_tests.py::BuildTests::test_convert_multiple_internal_links mkdocs/tests/build_tests.py::BuildTests::test_copy_theme_files mkdocs/tests/build_tests.py::BuildTests::test_copying_media mkdocs/tests/build_tests.py::BuildTests::test_dont_convert_code_block_urls mkdocs/tests/build_tests.py::BuildTests::test_empty_document mkdocs/tests/build_tests.py::BuildTests::test_extension_config mkdocs/tests/build_tests.py::BuildTests::test_ignore_email_links mkdocs/tests/build_tests.py::BuildTests::test_ignore_external_link mkdocs/tests/build_tests.py::BuildTests::test_markdown_custom_extension mkdocs/tests/build_tests.py::BuildTests::test_markdown_duplicate_custom_extension mkdocs/tests/build_tests.py::BuildTests::test_markdown_fenced_code_extension mkdocs/tests/build_tests.py::BuildTests::test_markdown_table_extension mkdocs/tests/build_tests.py::BuildTests::test_not_use_directory_urls mkdocs/tests/build_tests.py::BuildTests::test_strict_mode_invalid mkdocs/tests/build_tests.py::BuildTests::test_strict_mode_valid
mkdocs/tests/build_tests.py::BuildTests::test_extra_context mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_closed_file mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_deleted_file mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_file mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_missing_file mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_from_open_file mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_load_missing_required mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_missing_required mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_post_validation_error mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_pre_and_run_validation_errors mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_pre_validation_error mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_run_and_post_validation_errors mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_run_validation_error mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_unrecognised_keys mkdocs/tests/config/base_tests.py::ConfigBaseTests::test_validation_warnings mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_empty mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_replace_default mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required mkdocs/tests/config/config_options_tests.py::OptionallyRequiredTest::test_required_no_default mkdocs/tests/config/config_options_tests.py::TypeTest::test_length mkdocs/tests/config/config_options_tests.py::TypeTest::test_multiple_types mkdocs/tests/config/config_options_tests.py::TypeTest::test_single_type mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_default_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_format mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_missing_port mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_port mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_invalid_address_type mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_named_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_valid_IPv6_address mkdocs/tests/config/config_options_tests.py::IpAddressTest::test_valid_address mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid mkdocs/tests/config/config_options_tests.py::URLTest::test_invalid_url mkdocs/tests/config/config_options_tests.py::URLTest::test_valid_url mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_bitbucket mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_custom mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_github mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_edit_uri_gitlab mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_bitbucket mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_custom mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_custom_and_empty_edit_uri mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_github mkdocs/tests/config/config_options_tests.py::RepoURLTest::test_repo_name_gitlab mkdocs/tests/config/config_options_tests.py::DirTest::test_file mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_attribute_error mkdocs/tests/config/config_options_tests.py::DirTest::test_incorrect_type_type_error mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir mkdocs/tests/config/config_options_tests.py::DirTest::test_missing_dir_but_required mkdocs/tests/config/config_options_tests.py::DirTest::test_valid_dir mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_complex_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_simple_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_as_string mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_config_missing_name mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_default mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_invalid_type mkdocs/tests/config/config_options_tests.py::ThemeTest::test_theme_name_is_none mkdocs/tests/config/config_options_tests.py::ThemeTest::test_uninstalled_theme_as_config mkdocs/tests/config/config_options_tests.py::ThemeTest::test_uninstalled_theme_as_string mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_config mkdocs/tests/config/config_options_tests.py::PagesTest::test_invalid_type mkdocs/tests/config/config_options_tests.py::PagesTest::test_old_format mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_dict mkdocs/tests/config/config_options_tests.py::PagesTest::test_provided_empty mkdocs/tests/config/config_options_tests.py::PrivateTest::test_defined mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_builtins_config mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_configkey mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_duplicates mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_config_option mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_invalid_dict_item mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_list_dicts mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_mixed_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_none mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_not_list mkdocs/tests/config/config_options_tests.py::MarkdownExtensionsTest::test_simple_list mkdocs/tests/config/config_tests.py::ConfigTests::test_config_option mkdocs/tests/config/config_tests.py::ConfigTests::test_default_pages mkdocs/tests/config/config_tests.py::ConfigTests::test_default_pages_nested mkdocs/tests/config/config_tests.py::ConfigTests::test_doc_dir_in_site_dir mkdocs/tests/config/config_tests.py::ConfigTests::test_empty_config mkdocs/tests/config/config_tests.py::ConfigTests::test_invalid_config mkdocs/tests/config/config_tests.py::ConfigTests::test_missing_config_file mkdocs/tests/config/config_tests.py::ConfigTests::test_missing_site_name mkdocs/tests/config/config_tests.py::ConfigTests::test_nonexistant_config mkdocs/tests/config/config_tests.py::ConfigTests::test_theme mkdocs/tests/plugin_tests.py::TestPluginCollection::test_run_undefined_event_on_collection mkdocs/tests/plugin_tests.py::TestPluginCollection::test_run_unknown_event_on_collection mkdocs/tests/plugin_tests.py::TestPluginCollection::test_set_multiple_plugins_on_collection mkdocs/tests/plugin_tests.py::TestPluginCollection::test_set_plugin_on_collection mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_empty_list_with_default mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_empty_list_with_empty_default mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_multivalue_dict mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_none_with_empty_default mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_not_list mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_not_string_or_dict mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_options_not_dict mkdocs/tests/plugin_tests.py::TestPluginConfig::test_plugin_config_uninstalled
docs/about/release-notes.md mkdocs/plugins.py docs/user-guide/custom-themes.md docs/user-guide/styling-your-docs.md docs/user-guide/configuration.md mkdocs/config/base.py tox.ini mkdocs/config/config_options.py
docs/about/release-notes.md mkdocs/plugins.py docs/user-guide/custom-themes.md docs/user-guide/styling-your-docs.md docs/user-guide/configuration.md mkdocs/config/base.py tox.ini mkdocs/config/config_options.py