We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Allow mkdocs.yaml config file (.yaml extension)
Even though `.yml` is commonly used, `.yaml` is actually preferred:

https://yaml.org/faq.html

> Is there an official extension for YAML files?
>
> Please use ".yaml" when possible.

It would therefore be great if you would allow `mkdocs.yaml`.

Somewhat related t #2164

Allow mkdocs.yaml when '--config' is not passed
We wanted to give our colleagues to possibility to build docs automatically when they put a `mkdocs.yml` in the docs folder of their repository. But it happened often, that they used `.yaml` instead the `.yml`.

When this PR is accepted, it will look first look for `mkdocs.yml` when `--config` is not provided. If this file is not present, it will try to find `mkdocs.yaml`.
When both are not present, it behaves like before.
When both are present, it behaves like before.
If only the `.yaml` version it present, this one will be used as the config file.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `mkdocs/config/base.py`
Content:
```
1 import logging
2 import os
3 import sys
4 from yaml import YAMLError
5 from collections import UserDict
6 from contextlib import contextmanager
7 
8 from mkdocs import exceptions
9 from mkdocs import utils
10 
11 
12 log = logging.getLogger('mkdocs.config')
13 
14 
15 class ValidationError(Exception):
16     """Raised during the validation process of the config on errors."""
17 
18 
19 class Config(UserDict):
20     """
21     MkDocs Configuration dict
22 
23     This is a fairly simple extension of a standard dictionary. It adds methods
24     for running validation on the structure and contents.
25     """
26 
27     def __init__(self, schema, config_file_path=None):
28         """
29         The schema is a Python dict which maps the config name to a validator.
30         """
31 
32         self._schema = schema
33         self._schema_keys = set(dict(schema).keys())
34         # Ensure config_file_path is a Unicode string
35         if config_file_path is not None and not isinstance(config_file_path, str):
36             try:
37                 # Assume config_file_path is encoded with the file system encoding.
38                 config_file_path = config_file_path.decode(encoding=sys.getfilesystemencoding())
39             except UnicodeDecodeError:
40                 raise ValidationError("config_file_path is not a Unicode string.")
41         self.config_file_path = config_file_path
42         self.data = {}
43 
44         self.user_configs = []
45         self.set_defaults()
46 
47     def set_defaults(self):
48         """
49         Set the base config by going through each validator and getting the
50         default if it has one.
51         """
52 
53         for key, config_option in self._schema:
54             self[key] = config_option.default
55 
56     def _validate(self):
57 
58         failed, warnings = [], []
59 
60         for key, config_option in self._schema:
61             try:
62                 value = self.get(key)
63                 self[key] = config_option.validate(value)
64                 warnings.extend([(key, w) for w in config_option.warnings])
65                 config_option.reset_warnings()
66             except ValidationError as e:
67                 failed.append((key, e))
68 
69         for key in (set(self.keys()) - self._schema_keys):
70             warnings.append((
71                 key, f"Unrecognised configuration name: {key}"
72             ))
73 
74         return failed, warnings
75 
76     def _pre_validate(self):
77 
78         failed, warnings = [], []
79 
80         for key, config_option in self._schema:
81             try:
82                 config_option.pre_validation(self, key_name=key)
83                 warnings.extend([(key, w) for w in config_option.warnings])
84                 config_option.reset_warnings()
85             except ValidationError as e:
86                 failed.append((key, e))
87 
88         return failed, warnings
89 
90     def _post_validate(self):
91 
92         failed, warnings = [], []
93 
94         for key, config_option in self._schema:
95             try:
96                 config_option.post_validation(self, key_name=key)
97                 warnings.extend([(key, w) for w in config_option.warnings])
98                 config_option.reset_warnings()
99             except ValidationError as e:
100                 failed.append((key, e))
101 
102         return failed, warnings
103 
104     def validate(self):
105 
106         failed, warnings = self._pre_validate()
107 
108         run_failed, run_warnings = self._validate()
109 
110         failed.extend(run_failed)
111         warnings.extend(run_warnings)
112 
113         # Only run the post validation steps if there are no failures, warnings
114         # are okay.
115         if len(failed) == 0:
116             post_failed, post_warnings = self._post_validate()
117             failed.extend(post_failed)
118             warnings.extend(post_warnings)
119 
120         return failed, warnings
121 
122     def load_dict(self, patch):
123         """ Load config options from a dictionary. """
124 
125         if not isinstance(patch, dict):
126             raise exceptions.ConfigurationError(
127                 "The configuration is invalid. The expected type was a key "
128                 "value mapping (a python dict) but we got an object of type: "
129                 "{}".format(type(patch)))
130 
131         self.user_configs.append(patch)
132         self.data.update(patch)
133 
134     def load_file(self, config_file):
135         """ Load config options from the open file descriptor of a YAML file. """
136         try:
137             return self.load_dict(utils.yaml_load(config_file))
138         except YAMLError as e:
139             # MkDocs knows and understands ConfigurationErrors
140             raise exceptions.ConfigurationError(
141                 f"MkDocs encountered an error parsing the configuration file: {e}"
142             )
143 
144 
145 @contextmanager
146 def _open_config_file(config_file):
147     """
148     A context manager which yields an open file descriptor ready to be read.
149 
150     Accepts a filename as a string, an open or closed file descriptor, or None.
151     When None, it defaults to `mkdocs.yml` in the CWD. If a closed file descriptor
152     is received, a new file descriptor is opened for the same file.
153 
154     The file descriptor is automaticaly closed when the context manager block is existed.
155     """
156 
157     # Default to the standard config filename.
158     if config_file is None:
159         config_file = os.path.abspath('mkdocs.yml')
160 
161     # If closed file descriptor, get file path to reopen later.
162     if hasattr(config_file, 'closed') and config_file.closed:
163         config_file = config_file.name
164 
165     log.debug(f"Loading configuration file: {config_file}")
166 
167     # If it is a string, we can assume it is a path and attempt to open it.
168     if isinstance(config_file, str):
169         if os.path.exists(config_file):
170             config_file = open(config_file, 'rb')
171         else:
172             raise exceptions.ConfigurationError(
173                 f"Config file '{config_file}' does not exist.")
174 
175     # Ensure file descriptor is at begining
176     config_file.seek(0)
177     try:
178         yield config_file
179     finally:
180         if hasattr(config_file, 'close'):
181             config_file.close()
182 
183 
184 def load_config(config_file=None, **kwargs):
185     """
186     Load the configuration for a given file object or name
187 
188     The config_file can either be a file object, string or None. If it is None
189     the default `mkdocs.yml` filename will loaded.
190 
191     Extra kwargs are passed to the configuration to replace any default values
192     unless they themselves are None.
193     """
194     options = kwargs.copy()
195 
196     # Filter None values from the options. This usually happens with optional
197     # parameters from Click.
198     for key, value in options.copy().items():
199         if value is None:
200             options.pop(key)
201 
202     with _open_config_file(config_file) as fd:
203         options['config_file_path'] = getattr(fd, 'name', '')
204 
205         # Initialise the config with the default schema.
206         from mkdocs.config.defaults import get_schema
207         cfg = Config(schema=get_schema(), config_file_path=options['config_file_path'])
208         # load the config file
209         cfg.load_file(fd)
210 
211     # Then load the options to overwrite anything in the config.
212     cfg.load_dict(options)
213 
214     errors, warnings = cfg.validate()
215 
216     for config_name, warning in warnings:
217         log.warning(f"Config value: '{config_name}'. Warning: {warning}")
218 
219     for config_name, error in errors:
220         log.error(f"Config value: '{config_name}'. Error: {error}")
221 
222     for key, value in cfg.items():
223         log.debug(f"Config value: '{key}' = {value!r}")
224 
225     if len(errors) > 0:
226         raise exceptions.Abort(
227             "Aborted with {} Configuration Errors!".format(len(errors))
228         )
229     elif cfg['strict'] and len(warnings) > 0:
230         raise exceptions.Abort(
231             "Aborted with {} Configuration Warnings in 'strict' mode!".format(len(warnings))
232         )
233 
234     return cfg
235
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py
--- a/mkdocs/config/base.py
+++ b/mkdocs/config/base.py
@@ -156,24 +156,34 @@
 
     # Default to the standard config filename.
     if config_file is None:
-        config_file = os.path.abspath('mkdocs.yml')
-
-    # If closed file descriptor, get file path to reopen later.
-    if hasattr(config_file, 'closed') and config_file.closed:
-        config_file = config_file.name
-
-    log.debug(f"Loading configuration file: {config_file}")
-
+        paths_to_try = ['mkdocs.yml', 'mkdocs.yaml']
     # If it is a string, we can assume it is a path and attempt to open it.
-    if isinstance(config_file, str):
-        if os.path.exists(config_file):
-            config_file = open(config_file, 'rb')
+    elif isinstance(config_file, str):
+        paths_to_try = [config_file]
+    # If closed file descriptor, get file path to reopen later.
+    elif getattr(config_file, 'closed', False):
+        paths_to_try = [config_file.name]
+    else:
+        paths_to_try = None
+
+    if paths_to_try:
+        # config_file is not a file descriptor, so open it as a path.
+        for path in paths_to_try:
+            path = os.path.abspath(path)
+            log.debug(f"Loading configuration file: {path}")
+            try:
+                config_file = open(path, 'rb')
+                break
+            except FileNotFoundError:
+                continue
         else:
             raise exceptions.ConfigurationError(
-                f"Config file '{config_file}' does not exist.")
+                f"Config file '{paths_to_try[0]}' does not exist.")
+    else:
+        log.debug(f"Loading configuration file: {config_file}")
+        # Ensure file descriptor is at begining
+        config_file.seek(0)
 
-    # Ensure file descriptor is at begining
-    config_file.seek(0)
     try:
         yield config_file
     finally:

{"golden_diff": "diff --git a/mkdocs/config/base.py b/mkdocs/config/base.py\n--- a/mkdocs/config/base.py\n+++ b/mkdocs/config/base.py\n@@ -156,24 +156,34 @@\n \n     # Default to the standard config filename.\n     if config_file is None:\n-        config_file = os.path.abspath('mkdocs.yml')\n-\n-    # If closed file descriptor, get file path to reopen later.\n-    if hasattr(config_file, 'closed') and config_file.closed:\n-        config_file = config_file.name\n-\n-    log.debug(f\"Loading configuration file: {config_file}\")\n-\n+        paths_to_try = ['mkdocs.yml', 'mkdocs.yaml']\n     # If it is a string, we can assume it is a path and attempt to open it.\n-    if isinstance(config_file, str):\n-        if os.path.exists(config_file):\n-            config_file = open(config_file, 'rb')\n+    elif isinstance(config_file, str):\n+        paths_to_try = [config_file]\n+    # If closed file descriptor, get file path to reopen later.\n+    elif getattr(config_file, 'closed', False):\n+        paths_to_try = [config_file.name]\n+    else:\n+        paths_to_try = None\n+\n+    if paths_to_try:\n+        # config_file is not a file descriptor, so open it as a path.\n+        for path in paths_to_try:\n+            path = os.path.abspath(path)\n+            log.debug(f\"Loading configuration file: {path}\")\n+            try:\n+                config_file = open(path, 'rb')\n+                break\n+            except FileNotFoundError:\n+                continue\n         else:\n             raise exceptions.ConfigurationError(\n-                f\"Config file '{config_file}' does not exist.\")\n+                f\"Config file '{paths_to_try[0]}' does not exist.\")\n+    else:\n+        log.debug(f\"Loading configuration file: {config_file}\")\n+        # Ensure file descriptor is at begining\n+        config_file.seek(0)\n \n-    # Ensure file descriptor is at begining\n-    config_file.seek(0)\n     try:\n         yield config_file\n     finally:\n", "issue": "Allow mkdocs.yaml config file (.yaml extension)\nEven though `.yml` is commonly used, `.yaml` is actually preferred:\r\n\r\nhttps://yaml.org/faq.html\r\n\r\n> Is there an official extension for YAML files?\r\n>\r\n> Please use \".yaml\" when possible.\r\n\r\nIt would therefore be great if you would allow `mkdocs.yaml`.\r\n\r\nSomewhat related t #2164\r\n\nAllow mkdocs.yaml when '--config' is not passed\nWe wanted to give our colleagues to possibility to build docs automatically when they put a `mkdocs.yml` in the docs folder of their repository. But it happened often, that they used `.yaml` instead the `.yml`.\r\n\r\nWhen this PR is accepted, it will look first look for `mkdocs.yml` when `--config` is not provided. If this file is not present, it will try to find `mkdocs.yaml`.\r\nWhen both are not present, it behaves like before.\r\nWhen both are present, it behaves like before.\r\nIf only the `.yaml` version it present, this one will be used as the config file.\n", "before_files": [{"content": "import logging\nimport os\nimport sys\nfrom yaml import YAMLError\nfrom collections import UserDict\nfrom contextlib import contextmanager\n\nfrom mkdocs import exceptions\nfrom mkdocs import utils\n\n\nlog = logging.getLogger('mkdocs.config')\n\n\nclass ValidationError(Exception):\n    \"\"\"Raised during the validation process of the config on errors.\"\"\"\n\n\nclass Config(UserDict):\n    \"\"\"\n    MkDocs Configuration dict\n\n    This is a fairly simple extension of a standard dictionary. It adds methods\n    for running validation on the structure and contents.\n    \"\"\"\n\n    def __init__(self, schema, config_file_path=None):\n        \"\"\"\n        The schema is a Python dict which maps the config name to a validator.\n        \"\"\"\n\n        self._schema = schema\n        self._schema_keys = set(dict(schema).keys())\n        # Ensure config_file_path is a Unicode string\n        if config_file_path is not None and not isinstance(config_file_path, str):\n            try:\n                # Assume config_file_path is encoded with the file system encoding.\n                config_file_path = config_file_path.decode(encoding=sys.getfilesystemencoding())\n            except UnicodeDecodeError:\n                raise ValidationError(\"config_file_path is not a Unicode string.\")\n        self.config_file_path = config_file_path\n        self.data = {}\n\n        self.user_configs = []\n        self.set_defaults()\n\n    def set_defaults(self):\n        \"\"\"\n        Set the base config by going through each validator and getting the\n        default if it has one.\n        \"\"\"\n\n        for key, config_option in self._schema:\n            self[key] = config_option.default\n\n    def _validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                value = self.get(key)\n                self[key] = config_option.validate(value)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        for key in (set(self.keys()) - self._schema_keys):\n            warnings.append((\n                key, f\"Unrecognised configuration name: {key}\"\n            ))\n\n        return failed, warnings\n\n    def _pre_validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                config_option.pre_validation(self, key_name=key)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        return failed, warnings\n\n    def _post_validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                config_option.post_validation(self, key_name=key)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        return failed, warnings\n\n    def validate(self):\n\n        failed, warnings = self._pre_validate()\n\n        run_failed, run_warnings = self._validate()\n\n        failed.extend(run_failed)\n        warnings.extend(run_warnings)\n\n        # Only run the post validation steps if there are no failures, warnings\n        # are okay.\n        if len(failed) == 0:\n            post_failed, post_warnings = self._post_validate()\n            failed.extend(post_failed)\n            warnings.extend(post_warnings)\n\n        return failed, warnings\n\n    def load_dict(self, patch):\n        \"\"\" Load config options from a dictionary. \"\"\"\n\n        if not isinstance(patch, dict):\n            raise exceptions.ConfigurationError(\n                \"The configuration is invalid. The expected type was a key \"\n                \"value mapping (a python dict) but we got an object of type: \"\n                \"{}\".format(type(patch)))\n\n        self.user_configs.append(patch)\n        self.data.update(patch)\n\n    def load_file(self, config_file):\n        \"\"\" Load config options from the open file descriptor of a YAML file. \"\"\"\n        try:\n            return self.load_dict(utils.yaml_load(config_file))\n        except YAMLError as e:\n            # MkDocs knows and understands ConfigurationErrors\n            raise exceptions.ConfigurationError(\n                f\"MkDocs encountered an error parsing the configuration file: {e}\"\n            )\n\n\n@contextmanager\ndef _open_config_file(config_file):\n    \"\"\"\n    A context manager which yields an open file descriptor ready to be read.\n\n    Accepts a filename as a string, an open or closed file descriptor, or None.\n    When None, it defaults to `mkdocs.yml` in the CWD. If a closed file descriptor\n    is received, a new file descriptor is opened for the same file.\n\n    The file descriptor is automaticaly closed when the context manager block is existed.\n    \"\"\"\n\n    # Default to the standard config filename.\n    if config_file is None:\n        config_file = os.path.abspath('mkdocs.yml')\n\n    # If closed file descriptor, get file path to reopen later.\n    if hasattr(config_file, 'closed') and config_file.closed:\n        config_file = config_file.name\n\n    log.debug(f\"Loading configuration file: {config_file}\")\n\n    # If it is a string, we can assume it is a path and attempt to open it.\n    if isinstance(config_file, str):\n        if os.path.exists(config_file):\n            config_file = open(config_file, 'rb')\n        else:\n            raise exceptions.ConfigurationError(\n                f\"Config file '{config_file}' does not exist.\")\n\n    # Ensure file descriptor is at begining\n    config_file.seek(0)\n    try:\n        yield config_file\n    finally:\n        if hasattr(config_file, 'close'):\n            config_file.close()\n\n\ndef load_config(config_file=None, **kwargs):\n    \"\"\"\n    Load the configuration for a given file object or name\n\n    The config_file can either be a file object, string or None. If it is None\n    the default `mkdocs.yml` filename will loaded.\n\n    Extra kwargs are passed to the configuration to replace any default values\n    unless they themselves are None.\n    \"\"\"\n    options = kwargs.copy()\n\n    # Filter None values from the options. This usually happens with optional\n    # parameters from Click.\n    for key, value in options.copy().items():\n        if value is None:\n            options.pop(key)\n\n    with _open_config_file(config_file) as fd:\n        options['config_file_path'] = getattr(fd, 'name', '')\n\n        # Initialise the config with the default schema.\n        from mkdocs.config.defaults import get_schema\n        cfg = Config(schema=get_schema(), config_file_path=options['config_file_path'])\n        # load the config file\n        cfg.load_file(fd)\n\n    # Then load the options to overwrite anything in the config.\n    cfg.load_dict(options)\n\n    errors, warnings = cfg.validate()\n\n    for config_name, warning in warnings:\n        log.warning(f\"Config value: '{config_name}'. Warning: {warning}\")\n\n    for config_name, error in errors:\n        log.error(f\"Config value: '{config_name}'. Error: {error}\")\n\n    for key, value in cfg.items():\n        log.debug(f\"Config value: '{key}' = {value!r}\")\n\n    if len(errors) > 0:\n        raise exceptions.Abort(\n            \"Aborted with {} Configuration Errors!\".format(len(errors))\n        )\n    elif cfg['strict'] and len(warnings) > 0:\n        raise exceptions.Abort(\n            \"Aborted with {} Configuration Warnings in 'strict' mode!\".format(len(warnings))\n        )\n\n    return cfg\n", "path": "mkdocs/config/base.py"}], "after_files": [{"content": "import logging\nimport os\nimport sys\nfrom yaml import YAMLError\nfrom collections import UserDict\nfrom contextlib import contextmanager\n\nfrom mkdocs import exceptions\nfrom mkdocs import utils\n\n\nlog = logging.getLogger('mkdocs.config')\n\n\nclass ValidationError(Exception):\n    \"\"\"Raised during the validation process of the config on errors.\"\"\"\n\n\nclass Config(UserDict):\n    \"\"\"\n    MkDocs Configuration dict\n\n    This is a fairly simple extension of a standard dictionary. It adds methods\n    for running validation on the structure and contents.\n    \"\"\"\n\n    def __init__(self, schema, config_file_path=None):\n        \"\"\"\n        The schema is a Python dict which maps the config name to a validator.\n        \"\"\"\n\n        self._schema = schema\n        self._schema_keys = set(dict(schema).keys())\n        # Ensure config_file_path is a Unicode string\n        if config_file_path is not None and not isinstance(config_file_path, str):\n            try:\n                # Assume config_file_path is encoded with the file system encoding.\n                config_file_path = config_file_path.decode(encoding=sys.getfilesystemencoding())\n            except UnicodeDecodeError:\n                raise ValidationError(\"config_file_path is not a Unicode string.\")\n        self.config_file_path = config_file_path\n        self.data = {}\n\n        self.user_configs = []\n        self.set_defaults()\n\n    def set_defaults(self):\n        \"\"\"\n        Set the base config by going through each validator and getting the\n        default if it has one.\n        \"\"\"\n\n        for key, config_option in self._schema:\n            self[key] = config_option.default\n\n    def _validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                value = self.get(key)\n                self[key] = config_option.validate(value)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        for key in (set(self.keys()) - self._schema_keys):\n            warnings.append((\n                key, f\"Unrecognised configuration name: {key}\"\n            ))\n\n        return failed, warnings\n\n    def _pre_validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                config_option.pre_validation(self, key_name=key)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        return failed, warnings\n\n    def _post_validate(self):\n\n        failed, warnings = [], []\n\n        for key, config_option in self._schema:\n            try:\n                config_option.post_validation(self, key_name=key)\n                warnings.extend([(key, w) for w in config_option.warnings])\n                config_option.reset_warnings()\n            except ValidationError as e:\n                failed.append((key, e))\n\n        return failed, warnings\n\n    def validate(self):\n\n        failed, warnings = self._pre_validate()\n\n        run_failed, run_warnings = self._validate()\n\n        failed.extend(run_failed)\n        warnings.extend(run_warnings)\n\n        # Only run the post validation steps if there are no failures, warnings\n        # are okay.\n        if len(failed) == 0:\n            post_failed, post_warnings = self._post_validate()\n            failed.extend(post_failed)\n            warnings.extend(post_warnings)\n\n        return failed, warnings\n\n    def load_dict(self, patch):\n        \"\"\" Load config options from a dictionary. \"\"\"\n\n        if not isinstance(patch, dict):\n            raise exceptions.ConfigurationError(\n                \"The configuration is invalid. The expected type was a key \"\n                \"value mapping (a python dict) but we got an object of type: \"\n                \"{}\".format(type(patch)))\n\n        self.user_configs.append(patch)\n        self.data.update(patch)\n\n    def load_file(self, config_file):\n        \"\"\" Load config options from the open file descriptor of a YAML file. \"\"\"\n        try:\n            return self.load_dict(utils.yaml_load(config_file))\n        except YAMLError as e:\n            # MkDocs knows and understands ConfigurationErrors\n            raise exceptions.ConfigurationError(\n                f\"MkDocs encountered an error parsing the configuration file: {e}\"\n            )\n\n\n@contextmanager\ndef _open_config_file(config_file):\n    \"\"\"\n    A context manager which yields an open file descriptor ready to be read.\n\n    Accepts a filename as a string, an open or closed file descriptor, or None.\n    When None, it defaults to `mkdocs.yml` in the CWD. If a closed file descriptor\n    is received, a new file descriptor is opened for the same file.\n\n    The file descriptor is automaticaly closed when the context manager block is existed.\n    \"\"\"\n\n    # Default to the standard config filename.\n    if config_file is None:\n        paths_to_try = ['mkdocs.yml', 'mkdocs.yaml']\n    # If it is a string, we can assume it is a path and attempt to open it.\n    elif isinstance(config_file, str):\n        paths_to_try = [config_file]\n    # If closed file descriptor, get file path to reopen later.\n    elif getattr(config_file, 'closed', False):\n        paths_to_try = [config_file.name]\n    else:\n        paths_to_try = None\n\n    if paths_to_try:\n        # config_file is not a file descriptor, so open it as a path.\n        for path in paths_to_try:\n            path = os.path.abspath(path)\n            log.debug(f\"Loading configuration file: {path}\")\n            try:\n                config_file = open(path, 'rb')\n                break\n            except FileNotFoundError:\n                continue\n        else:\n            raise exceptions.ConfigurationError(\n                f\"Config file '{paths_to_try[0]}' does not exist.\")\n    else:\n        log.debug(f\"Loading configuration file: {config_file}\")\n        # Ensure file descriptor is at begining\n        config_file.seek(0)\n\n    try:\n        yield config_file\n    finally:\n        if hasattr(config_file, 'close'):\n            config_file.close()\n\n\ndef load_config(config_file=None, **kwargs):\n    \"\"\"\n    Load the configuration for a given file object or name\n\n    The config_file can either be a file object, string or None. If it is None\n    the default `mkdocs.yml` filename will loaded.\n\n    Extra kwargs are passed to the configuration to replace any default values\n    unless they themselves are None.\n    \"\"\"\n    options = kwargs.copy()\n\n    # Filter None values from the options. This usually happens with optional\n    # parameters from Click.\n    for key, value in options.copy().items():\n        if value is None:\n            options.pop(key)\n\n    with _open_config_file(config_file) as fd:\n        options['config_file_path'] = getattr(fd, 'name', '')\n\n        # Initialise the config with the default schema.\n        from mkdocs.config.defaults import get_schema\n        cfg = Config(schema=get_schema(), config_file_path=options['config_file_path'])\n        # load the config file\n        cfg.load_file(fd)\n\n    # Then load the options to overwrite anything in the config.\n    cfg.load_dict(options)\n\n    errors, warnings = cfg.validate()\n\n    for config_name, warning in warnings:\n        log.warning(f\"Config value: '{config_name}'. Warning: {warning}\")\n\n    for config_name, error in errors:\n        log.error(f\"Config value: '{config_name}'. Error: {error}\")\n\n    for key, value in cfg.items():\n        log.debug(f\"Config value: '{key}' = {value!r}\")\n\n    if len(errors) > 0:\n        raise exceptions.Abort(\n            \"Aborted with {} Configuration Errors!\".format(len(errors))\n        )\n    elif cfg['strict'] and len(warnings) > 0:\n        raise exceptions.Abort(\n            \"Aborted with {} Configuration Warnings in 'strict' mode!\".format(len(warnings))\n        )\n\n    return cfg\n", "path": "mkdocs/config/base.py"}]}