We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Be more defensive in how we insert our mkdocs media
This logic can be executed multiple times on [some builds](https://readthedocs.org/projects/phpspreadsheet/builds/8340376/) (see the `mkdocs.yml`):

https://github.com/rtfd/readthedocs.org/blob/ed8dd29a68d009fc08929fabf7155883482619c1/readthedocs/doc_builder/backends/mkdocs.py#L143-L152

If we have an issue reseting the branch/tag of the docs, we will end up inserting our media files twice, which causes them to be executed twice.

## Solution

This code should check if one of the files we insert is already in the list:

```
if 'readthedocs-data.js' not in user_config.get('extra_javascript'):
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `readthedocs/doc_builder/backends/mkdocs.py`
Content:
```
1 # -*- coding: utf-8 -*-
2 
3 """
4 MkDocs_ backend for building docs.
5 
6 .. _MkDocs: http://www.mkdocs.org/
7 """
8 import json
9 import logging
10 import os
11 
12 import yaml
13 from django.conf import settings
14 from django.template import loader as template_loader
15 
16 from readthedocs.doc_builder.base import BaseBuilder
17 from readthedocs.doc_builder.exceptions import MkDocsYAMLParseError
18 from readthedocs.projects.models import Feature
19 
20 
21 log = logging.getLogger(__name__)
22 
23 
24 def get_absolute_static_url():
25     """
26     Get the fully qualified static URL from settings.
27 
28     Mkdocs needs a full domain because it tries to link to local files.
29     """
30     static_url = settings.STATIC_URL
31 
32     if not static_url.startswith('http'):
33         domain = getattr(settings, 'PRODUCTION_DOMAIN')
34         static_url = 'http://{}{}'.format(domain, static_url)
35 
36     return static_url
37 
38 
39 class BaseMkdocs(BaseBuilder):
40 
41     """Mkdocs builder."""
42 
43     # The default theme for mkdocs is the 'mkdocs' theme
44     DEFAULT_THEME_NAME = 'mkdocs'
45 
46     def __init__(self, *args, **kwargs):
47         super().__init__(*args, **kwargs)
48         self.old_artifact_path = os.path.join(
49             self.version.project.checkout_path(self.version.slug),
50             self.build_dir,
51         )
52         self.root_path = self.version.project.checkout_path(self.version.slug)
53         self.yaml_file = self.get_yaml_config()
54 
55         # README: historically, the default theme was ``readthedocs`` but in
56         # https://github.com/rtfd/readthedocs.org/pull/4556 we change it to
57         # ``mkdocs`` to maintain the same behavior in Read the Docs than
58         # building locally. Although, we can't apply this into the Corporate
59         # site. To keep the same default theme there, we created a Feature flag
60         # for these project that were building with MkDocs in the Corporate
61         # site.
62         if self.project.has_feature(Feature.MKDOCS_THEME_RTD):
63             self.DEFAULT_THEME_NAME = 'readthedocs'
64             log.warning(
65                 'Project using readthedocs theme as default for MkDocs: slug=%s',
66                 self.project.slug,
67             )
68         else:
69             self.DEFAULT_THEME_NAME = 'mkdocs'
70 
71     def get_yaml_config(self):
72         """Find the ``mkdocs.yml`` file in the project root."""
73         mkdoc_path = self.config.mkdocs.configuration
74         if not mkdoc_path:
75             mkdoc_path = os.path.join(
76                 self.project.checkout_path(self.version.slug),
77                 'mkdocs.yml',
78             )
79             if not os.path.exists(mkdoc_path):
80                 return None
81         return mkdoc_path
82 
83     def load_yaml_config(self):
84         """
85         Load a YAML config.
86 
87         :raises: ``MkDocsYAMLParseError`` if failed due to syntax errors.
88         """
89         try:
90             return yaml.safe_load(open(self.yaml_file, 'r'),)
91         except IOError:
92             return {
93                 'site_name': self.version.project.name,
94             }
95         except yaml.YAMLError as exc:
96             note = ''
97             if hasattr(exc, 'problem_mark'):
98                 mark = exc.problem_mark
99                 note = ' (line %d, column %d)' % (
100                     mark.line + 1,
101                     mark.column + 1,
102                 )
103             raise MkDocsYAMLParseError(
104                 'Your mkdocs.yml could not be loaded, '
105                 'possibly due to a syntax error{note}'.format(note=note),
106             )
107 
108     def append_conf(self, **__):
109         """
110         Set mkdocs config values.
111 
112         :raises: ``MkDocsYAMLParseError`` if failed due to known type errors
113                  (i.e. expecting a list and a string is found).
114         """
115         if not self.yaml_file:
116             self.yaml_file = os.path.join(self.root_path, 'mkdocs.yml')
117 
118         user_config = self.load_yaml_config()
119 
120         # Handle custom docs dirs
121         user_docs_dir = user_config.get('docs_dir')
122         if not isinstance(user_docs_dir, (type(None), str)):
123             raise MkDocsYAMLParseError(
124                 MkDocsYAMLParseError.INVALID_DOCS_DIR_CONFIG,
125             )
126 
127         docs_dir = self.docs_dir(docs_dir=user_docs_dir)
128         self.create_index(extension='md')
129         user_config['docs_dir'] = docs_dir
130 
131         # Set mkdocs config values
132         static_url = get_absolute_static_url()
133 
134         for config in ('extra_css', 'extra_javascript'):
135             user_value = user_config.get(config, [])
136             if not isinstance(user_value, list):
137                 raise MkDocsYAMLParseError(
138                     MkDocsYAMLParseError.INVALID_EXTRA_CONFIG.format(
139                         config=config,
140                     ),
141                 )
142 
143         user_config.setdefault('extra_javascript', []).extend([
144             'readthedocs-data.js',
145             '%score/js/readthedocs-doc-embed.js' % static_url,
146             '%sjavascript/readthedocs-analytics.js' % static_url,
147         ])
148         user_config.setdefault('extra_css', []).extend([
149             '%scss/badge_only.css' % static_url,
150             '%scss/readthedocs-doc-embed.css' % static_url,
151         ])
152 
153         # The docs path is relative to the location
154         # of the mkdocs configuration file.
155         docs_path = os.path.join(
156             os.path.dirname(self.yaml_file),
157             docs_dir,
158         )
159 
160         # RTD javascript writing
161         rtd_data = self.generate_rtd_data(
162             docs_dir=os.path.relpath(docs_path, self.root_path),
163             mkdocs_config=user_config,
164         )
165         with open(os.path.join(docs_path, 'readthedocs-data.js'), 'w') as f:
166             f.write(rtd_data)
167 
168         # Use Read the Docs' analytics setup rather than mkdocs'
169         # This supports using RTD's privacy improvements around analytics
170         user_config['google_analytics'] = None
171 
172         # README: make MkDocs to use ``readthedocs`` theme as default if the
173         # user didn't specify a specific theme manually
174         if self.project.has_feature(Feature.MKDOCS_THEME_RTD):
175             if 'theme' not in user_config:
176                 # mkdocs<0.17 syntax
177                 user_config['theme'] = self.DEFAULT_THEME_NAME
178 
179         # Write the modified mkdocs configuration
180         yaml.safe_dump(
181             user_config,
182             open(self.yaml_file, 'w'),
183         )
184 
185         # Write the mkdocs.yml to the build logs
186         self.run(
187             'cat',
188             os.path.relpath(self.yaml_file, self.root_path),
189             cwd=self.root_path,
190         )
191 
192     def generate_rtd_data(self, docs_dir, mkdocs_config):
193         """Generate template properties and render readthedocs-data.js."""
194         # Use the analytics code from mkdocs.yml
195         # if it isn't set already by Read the Docs,
196         analytics_code = self.version.project.analytics_code
197         if not analytics_code and mkdocs_config.get('google_analytics'):
198             # http://www.mkdocs.org/user-guide/configuration/#google_analytics
199             analytics_code = mkdocs_config['google_analytics'][0]
200 
201         # Will be available in the JavaScript as READTHEDOCS_DATA.
202         readthedocs_data = {
203             'project': self.version.project.slug,
204             'version': self.version.slug,
205             'language': self.version.project.language,
206             'programming_language': self.version.project.programming_language,
207             'page': None,
208             'theme': self.get_theme_name(mkdocs_config),
209             'builder': 'mkdocs',
210             'docroot': docs_dir,
211             'source_suffix': '.md',
212             'api_host': getattr(
213                 settings,
214                 'PUBLIC_API_URL',
215                 'https://readthedocs.org',
216             ),
217             'ad_free': not self.project.show_advertising,
218             'commit': self.version.project.vcs_repo(self.version.slug).commit,
219             'global_analytics_code': getattr(
220                 settings,
221                 'GLOBAL_ANALYTICS_CODE',
222                 'UA-17997319-1',
223             ),
224             'user_analytics_code': analytics_code,
225         }
226         data_json = json.dumps(readthedocs_data, indent=4)
227         data_ctx = {
228             'data_json': data_json,
229             'current_version': readthedocs_data['version'],
230             'slug': readthedocs_data['project'],
231             'html_theme': readthedocs_data['theme'],
232             'pagename': None,
233         }
234         tmpl = template_loader.get_template('doc_builder/data.js.tmpl')
235         return tmpl.render(data_ctx)
236 
237     def build(self):
238         checkout_path = self.project.checkout_path(self.version.slug)
239         build_command = [
240             'python',
241             self.python_env.venv_bin(filename='mkdocs'),
242             self.builder,
243             '--clean',
244             '--site-dir',
245             self.build_dir,
246             '--config-file',
247             self.yaml_file,
248         ]
249         if self.config.mkdocs.fail_on_warning:
250             build_command.append('--strict')
251         cmd_ret = self.run(
252             *build_command, cwd=checkout_path,
253             bin_path=self.python_env.venv_bin()
254         )
255         return cmd_ret.successful
256 
257     def get_theme_name(self, mkdocs_config):
258         """
259         Get the theme configuration in the mkdocs_config.
260 
261         In v0.17.0, the theme configuration switched
262         from two separate configs (both optional) to a nested directive.
263 
264         :see: http://www.mkdocs.org/about/release-notes/#theme-customization-1164
265         :returns: the name of the theme RTD will use
266         """
267         theme_setting = mkdocs_config.get('theme')
268         if isinstance(theme_setting, dict):
269             # Full nested theme config (the new configuration)
270             return theme_setting.get('name') or self.DEFAULT_THEME_NAME
271 
272         if theme_setting:
273             # A string which is the name of the theme
274             return theme_setting
275 
276         theme_dir = mkdocs_config.get('theme_dir')
277         if theme_dir:
278             # Use the name of the directory in this project's custom theme directory
279             return theme_dir.rstrip('/').split('/')[-1]
280 
281         return self.DEFAULT_THEME_NAME
282 
283 
284 class MkdocsHTML(BaseMkdocs):
285     type = 'mkdocs'
286     builder = 'build'
287     build_dir = '_build/html'
288 
289 
290 class MkdocsJSON(BaseMkdocs):
291     type = 'mkdocs_json'
292     builder = 'json'
293     build_dir = '_build/json'
294
```
--- 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/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py
--- a/readthedocs/doc_builder/backends/mkdocs.py
+++ b/readthedocs/doc_builder/backends/mkdocs.py
@@ -140,15 +140,25 @@
                     ),
                 )
 
-        user_config.setdefault('extra_javascript', []).extend([
+        extra_javascript_list = [
             'readthedocs-data.js',
             '%score/js/readthedocs-doc-embed.js' % static_url,
             '%sjavascript/readthedocs-analytics.js' % static_url,
-        ])
-        user_config.setdefault('extra_css', []).extend([
+        ]
+        extra_css_list = [
             '%scss/badge_only.css' % static_url,
             '%scss/readthedocs-doc-embed.css' % static_url,
-        ])
+        ]
+
+        # Only add static file if the files are not already in the list
+        user_config.setdefault('extra_javascript', []).extend(
+            [js for js in extra_javascript_list if js not in user_config.get(
+                'extra_javascript')]
+        )
+        user_config.setdefault('extra_css', []).extend(
+            [css for css in extra_css_list if css not in user_config.get(
+                'extra_css')]
+        )
 
         # The docs path is relative to the location
         # of the mkdocs configuration file.

{"golden_diff": "diff --git a/readthedocs/doc_builder/backends/mkdocs.py b/readthedocs/doc_builder/backends/mkdocs.py\n--- a/readthedocs/doc_builder/backends/mkdocs.py\n+++ b/readthedocs/doc_builder/backends/mkdocs.py\n@@ -140,15 +140,25 @@\n                     ),\n                 )\n \n-        user_config.setdefault('extra_javascript', []).extend([\n+        extra_javascript_list = [\n             'readthedocs-data.js',\n             '%score/js/readthedocs-doc-embed.js' % static_url,\n             '%sjavascript/readthedocs-analytics.js' % static_url,\n-        ])\n-        user_config.setdefault('extra_css', []).extend([\n+        ]\n+        extra_css_list = [\n             '%scss/badge_only.css' % static_url,\n             '%scss/readthedocs-doc-embed.css' % static_url,\n-        ])\n+        ]\n+\n+        # Only add static file if the files are not already in the list\n+        user_config.setdefault('extra_javascript', []).extend(\n+            [js for js in extra_javascript_list if js not in user_config.get(\n+                'extra_javascript')]\n+        )\n+        user_config.setdefault('extra_css', []).extend(\n+            [css for css in extra_css_list if css not in user_config.get(\n+                'extra_css')]\n+        )\n \n         # The docs path is relative to the location\n         # of the mkdocs configuration file.\n", "issue": "Be more defensive in how we insert our mkdocs media\nThis logic can be executed multiple times on [some builds](https://readthedocs.org/projects/phpspreadsheet/builds/8340376/) (see the `mkdocs.yml`):\r\n\r\nhttps://github.com/rtfd/readthedocs.org/blob/ed8dd29a68d009fc08929fabf7155883482619c1/readthedocs/doc_builder/backends/mkdocs.py#L143-L152\r\n\r\nIf we have an issue reseting the branch/tag of the docs, we will end up inserting our media files twice, which causes them to be executed twice.\r\n\r\n## Solution\r\n\r\nThis code should check if one of the files we insert is already in the list:\r\n\r\n```\r\nif 'readthedocs-data.js' not in user_config.get('extra_javascript'):\r\n```\n", "before_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nMkDocs_ backend for building docs.\n\n.. _MkDocs: http://www.mkdocs.org/\n\"\"\"\nimport json\nimport logging\nimport os\n\nimport yaml\nfrom django.conf import settings\nfrom django.template import loader as template_loader\n\nfrom readthedocs.doc_builder.base import BaseBuilder\nfrom readthedocs.doc_builder.exceptions import MkDocsYAMLParseError\nfrom readthedocs.projects.models import Feature\n\n\nlog = logging.getLogger(__name__)\n\n\ndef get_absolute_static_url():\n    \"\"\"\n    Get the fully qualified static URL from settings.\n\n    Mkdocs needs a full domain because it tries to link to local files.\n    \"\"\"\n    static_url = settings.STATIC_URL\n\n    if not static_url.startswith('http'):\n        domain = getattr(settings, 'PRODUCTION_DOMAIN')\n        static_url = 'http://{}{}'.format(domain, static_url)\n\n    return static_url\n\n\nclass BaseMkdocs(BaseBuilder):\n\n    \"\"\"Mkdocs builder.\"\"\"\n\n    # The default theme for mkdocs is the 'mkdocs' theme\n    DEFAULT_THEME_NAME = 'mkdocs'\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.old_artifact_path = os.path.join(\n            self.version.project.checkout_path(self.version.slug),\n            self.build_dir,\n        )\n        self.root_path = self.version.project.checkout_path(self.version.slug)\n        self.yaml_file = self.get_yaml_config()\n\n        # README: historically, the default theme was ``readthedocs`` but in\n        # https://github.com/rtfd/readthedocs.org/pull/4556 we change it to\n        # ``mkdocs`` to maintain the same behavior in Read the Docs than\n        # building locally. Although, we can't apply this into the Corporate\n        # site. To keep the same default theme there, we created a Feature flag\n        # for these project that were building with MkDocs in the Corporate\n        # site.\n        if self.project.has_feature(Feature.MKDOCS_THEME_RTD):\n            self.DEFAULT_THEME_NAME = 'readthedocs'\n            log.warning(\n                'Project using readthedocs theme as default for MkDocs: slug=%s',\n                self.project.slug,\n            )\n        else:\n            self.DEFAULT_THEME_NAME = 'mkdocs'\n\n    def get_yaml_config(self):\n        \"\"\"Find the ``mkdocs.yml`` file in the project root.\"\"\"\n        mkdoc_path = self.config.mkdocs.configuration\n        if not mkdoc_path:\n            mkdoc_path = os.path.join(\n                self.project.checkout_path(self.version.slug),\n                'mkdocs.yml',\n            )\n            if not os.path.exists(mkdoc_path):\n                return None\n        return mkdoc_path\n\n    def load_yaml_config(self):\n        \"\"\"\n        Load a YAML config.\n\n        :raises: ``MkDocsYAMLParseError`` if failed due to syntax errors.\n        \"\"\"\n        try:\n            return yaml.safe_load(open(self.yaml_file, 'r'),)\n        except IOError:\n            return {\n                'site_name': self.version.project.name,\n            }\n        except yaml.YAMLError as exc:\n            note = ''\n            if hasattr(exc, 'problem_mark'):\n                mark = exc.problem_mark\n                note = ' (line %d, column %d)' % (\n                    mark.line + 1,\n                    mark.column + 1,\n                )\n            raise MkDocsYAMLParseError(\n                'Your mkdocs.yml could not be loaded, '\n                'possibly due to a syntax error{note}'.format(note=note),\n            )\n\n    def append_conf(self, **__):\n        \"\"\"\n        Set mkdocs config values.\n\n        :raises: ``MkDocsYAMLParseError`` if failed due to known type errors\n                 (i.e. expecting a list and a string is found).\n        \"\"\"\n        if not self.yaml_file:\n            self.yaml_file = os.path.join(self.root_path, 'mkdocs.yml')\n\n        user_config = self.load_yaml_config()\n\n        # Handle custom docs dirs\n        user_docs_dir = user_config.get('docs_dir')\n        if not isinstance(user_docs_dir, (type(None), str)):\n            raise MkDocsYAMLParseError(\n                MkDocsYAMLParseError.INVALID_DOCS_DIR_CONFIG,\n            )\n\n        docs_dir = self.docs_dir(docs_dir=user_docs_dir)\n        self.create_index(extension='md')\n        user_config['docs_dir'] = docs_dir\n\n        # Set mkdocs config values\n        static_url = get_absolute_static_url()\n\n        for config in ('extra_css', 'extra_javascript'):\n            user_value = user_config.get(config, [])\n            if not isinstance(user_value, list):\n                raise MkDocsYAMLParseError(\n                    MkDocsYAMLParseError.INVALID_EXTRA_CONFIG.format(\n                        config=config,\n                    ),\n                )\n\n        user_config.setdefault('extra_javascript', []).extend([\n            'readthedocs-data.js',\n            '%score/js/readthedocs-doc-embed.js' % static_url,\n            '%sjavascript/readthedocs-analytics.js' % static_url,\n        ])\n        user_config.setdefault('extra_css', []).extend([\n            '%scss/badge_only.css' % static_url,\n            '%scss/readthedocs-doc-embed.css' % static_url,\n        ])\n\n        # The docs path is relative to the location\n        # of the mkdocs configuration file.\n        docs_path = os.path.join(\n            os.path.dirname(self.yaml_file),\n            docs_dir,\n        )\n\n        # RTD javascript writing\n        rtd_data = self.generate_rtd_data(\n            docs_dir=os.path.relpath(docs_path, self.root_path),\n            mkdocs_config=user_config,\n        )\n        with open(os.path.join(docs_path, 'readthedocs-data.js'), 'w') as f:\n            f.write(rtd_data)\n\n        # Use Read the Docs' analytics setup rather than mkdocs'\n        # This supports using RTD's privacy improvements around analytics\n        user_config['google_analytics'] = None\n\n        # README: make MkDocs to use ``readthedocs`` theme as default if the\n        # user didn't specify a specific theme manually\n        if self.project.has_feature(Feature.MKDOCS_THEME_RTD):\n            if 'theme' not in user_config:\n                # mkdocs<0.17 syntax\n                user_config['theme'] = self.DEFAULT_THEME_NAME\n\n        # Write the modified mkdocs configuration\n        yaml.safe_dump(\n            user_config,\n            open(self.yaml_file, 'w'),\n        )\n\n        # Write the mkdocs.yml to the build logs\n        self.run(\n            'cat',\n            os.path.relpath(self.yaml_file, self.root_path),\n            cwd=self.root_path,\n        )\n\n    def generate_rtd_data(self, docs_dir, mkdocs_config):\n        \"\"\"Generate template properties and render readthedocs-data.js.\"\"\"\n        # Use the analytics code from mkdocs.yml\n        # if it isn't set already by Read the Docs,\n        analytics_code = self.version.project.analytics_code\n        if not analytics_code and mkdocs_config.get('google_analytics'):\n            # http://www.mkdocs.org/user-guide/configuration/#google_analytics\n            analytics_code = mkdocs_config['google_analytics'][0]\n\n        # Will be available in the JavaScript as READTHEDOCS_DATA.\n        readthedocs_data = {\n            'project': self.version.project.slug,\n            'version': self.version.slug,\n            'language': self.version.project.language,\n            'programming_language': self.version.project.programming_language,\n            'page': None,\n            'theme': self.get_theme_name(mkdocs_config),\n            'builder': 'mkdocs',\n            'docroot': docs_dir,\n            'source_suffix': '.md',\n            'api_host': getattr(\n                settings,\n                'PUBLIC_API_URL',\n                'https://readthedocs.org',\n            ),\n            'ad_free': not self.project.show_advertising,\n            'commit': self.version.project.vcs_repo(self.version.slug).commit,\n            'global_analytics_code': getattr(\n                settings,\n                'GLOBAL_ANALYTICS_CODE',\n                'UA-17997319-1',\n            ),\n            'user_analytics_code': analytics_code,\n        }\n        data_json = json.dumps(readthedocs_data, indent=4)\n        data_ctx = {\n            'data_json': data_json,\n            'current_version': readthedocs_data['version'],\n            'slug': readthedocs_data['project'],\n            'html_theme': readthedocs_data['theme'],\n            'pagename': None,\n        }\n        tmpl = template_loader.get_template('doc_builder/data.js.tmpl')\n        return tmpl.render(data_ctx)\n\n    def build(self):\n        checkout_path = self.project.checkout_path(self.version.slug)\n        build_command = [\n            'python',\n            self.python_env.venv_bin(filename='mkdocs'),\n            self.builder,\n            '--clean',\n            '--site-dir',\n            self.build_dir,\n            '--config-file',\n            self.yaml_file,\n        ]\n        if self.config.mkdocs.fail_on_warning:\n            build_command.append('--strict')\n        cmd_ret = self.run(\n            *build_command, cwd=checkout_path,\n            bin_path=self.python_env.venv_bin()\n        )\n        return cmd_ret.successful\n\n    def get_theme_name(self, mkdocs_config):\n        \"\"\"\n        Get the theme configuration in the mkdocs_config.\n\n        In v0.17.0, the theme configuration switched\n        from two separate configs (both optional) to a nested directive.\n\n        :see: http://www.mkdocs.org/about/release-notes/#theme-customization-1164\n        :returns: the name of the theme RTD will use\n        \"\"\"\n        theme_setting = mkdocs_config.get('theme')\n        if isinstance(theme_setting, dict):\n            # Full nested theme config (the new configuration)\n            return theme_setting.get('name') or self.DEFAULT_THEME_NAME\n\n        if theme_setting:\n            # A string which is the name of the theme\n            return theme_setting\n\n        theme_dir = mkdocs_config.get('theme_dir')\n        if theme_dir:\n            # Use the name of the directory in this project's custom theme directory\n            return theme_dir.rstrip('/').split('/')[-1]\n\n        return self.DEFAULT_THEME_NAME\n\n\nclass MkdocsHTML(BaseMkdocs):\n    type = 'mkdocs'\n    builder = 'build'\n    build_dir = '_build/html'\n\n\nclass MkdocsJSON(BaseMkdocs):\n    type = 'mkdocs_json'\n    builder = 'json'\n    build_dir = '_build/json'\n", "path": "readthedocs/doc_builder/backends/mkdocs.py"}], "after_files": [{"content": "# -*- coding: utf-8 -*-\n\n\"\"\"\nMkDocs_ backend for building docs.\n\n.. _MkDocs: http://www.mkdocs.org/\n\"\"\"\nimport json\nimport logging\nimport os\n\nimport yaml\nfrom django.conf import settings\nfrom django.template import loader as template_loader\n\nfrom readthedocs.doc_builder.base import BaseBuilder\nfrom readthedocs.doc_builder.exceptions import MkDocsYAMLParseError\nfrom readthedocs.projects.models import Feature\n\n\nlog = logging.getLogger(__name__)\n\n\ndef get_absolute_static_url():\n    \"\"\"\n    Get the fully qualified static URL from settings.\n\n    Mkdocs needs a full domain because it tries to link to local files.\n    \"\"\"\n    static_url = settings.STATIC_URL\n\n    if not static_url.startswith('http'):\n        domain = getattr(settings, 'PRODUCTION_DOMAIN')\n        static_url = 'http://{}{}'.format(domain, static_url)\n\n    return static_url\n\n\nclass BaseMkdocs(BaseBuilder):\n\n    \"\"\"Mkdocs builder.\"\"\"\n\n    # The default theme for mkdocs is the 'mkdocs' theme\n    DEFAULT_THEME_NAME = 'mkdocs'\n\n    def __init__(self, *args, **kwargs):\n        super().__init__(*args, **kwargs)\n        self.old_artifact_path = os.path.join(\n            self.version.project.checkout_path(self.version.slug),\n            self.build_dir,\n        )\n        self.root_path = self.version.project.checkout_path(self.version.slug)\n        self.yaml_file = self.get_yaml_config()\n\n        # README: historically, the default theme was ``readthedocs`` but in\n        # https://github.com/rtfd/readthedocs.org/pull/4556 we change it to\n        # ``mkdocs`` to maintain the same behavior in Read the Docs than\n        # building locally. Although, we can't apply this into the Corporate\n        # site. To keep the same default theme there, we created a Feature flag\n        # for these project that were building with MkDocs in the Corporate\n        # site.\n        if self.project.has_feature(Feature.MKDOCS_THEME_RTD):\n            self.DEFAULT_THEME_NAME = 'readthedocs'\n            log.warning(\n                'Project using readthedocs theme as default for MkDocs: slug=%s',\n                self.project.slug,\n            )\n        else:\n            self.DEFAULT_THEME_NAME = 'mkdocs'\n\n    def get_yaml_config(self):\n        \"\"\"Find the ``mkdocs.yml`` file in the project root.\"\"\"\n        mkdoc_path = self.config.mkdocs.configuration\n        if not mkdoc_path:\n            mkdoc_path = os.path.join(\n                self.project.checkout_path(self.version.slug),\n                'mkdocs.yml',\n            )\n            if not os.path.exists(mkdoc_path):\n                return None\n        return mkdoc_path\n\n    def load_yaml_config(self):\n        \"\"\"\n        Load a YAML config.\n\n        :raises: ``MkDocsYAMLParseError`` if failed due to syntax errors.\n        \"\"\"\n        try:\n            return yaml.safe_load(open(self.yaml_file, 'r'),)\n        except IOError:\n            return {\n                'site_name': self.version.project.name,\n            }\n        except yaml.YAMLError as exc:\n            note = ''\n            if hasattr(exc, 'problem_mark'):\n                mark = exc.problem_mark\n                note = ' (line %d, column %d)' % (\n                    mark.line + 1,\n                    mark.column + 1,\n                )\n            raise MkDocsYAMLParseError(\n                'Your mkdocs.yml could not be loaded, '\n                'possibly due to a syntax error{note}'.format(note=note),\n            )\n\n    def append_conf(self, **__):\n        \"\"\"\n        Set mkdocs config values.\n\n        :raises: ``MkDocsYAMLParseError`` if failed due to known type errors\n                 (i.e. expecting a list and a string is found).\n        \"\"\"\n        if not self.yaml_file:\n            self.yaml_file = os.path.join(self.root_path, 'mkdocs.yml')\n\n        user_config = self.load_yaml_config()\n\n        # Handle custom docs dirs\n        user_docs_dir = user_config.get('docs_dir')\n        if not isinstance(user_docs_dir, (type(None), str)):\n            raise MkDocsYAMLParseError(\n                MkDocsYAMLParseError.INVALID_DOCS_DIR_CONFIG,\n            )\n\n        docs_dir = self.docs_dir(docs_dir=user_docs_dir)\n        self.create_index(extension='md')\n        user_config['docs_dir'] = docs_dir\n\n        # Set mkdocs config values\n        static_url = get_absolute_static_url()\n\n        for config in ('extra_css', 'extra_javascript'):\n            user_value = user_config.get(config, [])\n            if not isinstance(user_value, list):\n                raise MkDocsYAMLParseError(\n                    MkDocsYAMLParseError.INVALID_EXTRA_CONFIG.format(\n                        config=config,\n                    ),\n                )\n\n        extra_javascript_list = [\n            'readthedocs-data.js',\n            '%score/js/readthedocs-doc-embed.js' % static_url,\n            '%sjavascript/readthedocs-analytics.js' % static_url,\n        ]\n        extra_css_list = [\n            '%scss/badge_only.css' % static_url,\n            '%scss/readthedocs-doc-embed.css' % static_url,\n        ]\n\n        # Only add static file if the files are not already in the list\n        user_config.setdefault('extra_javascript', []).extend(\n            [js for js in extra_javascript_list if js not in user_config.get(\n                'extra_javascript')]\n        )\n        user_config.setdefault('extra_css', []).extend(\n            [css for css in extra_css_list if css not in user_config.get(\n                'extra_css')]\n        )\n\n        # The docs path is relative to the location\n        # of the mkdocs configuration file.\n        docs_path = os.path.join(\n            os.path.dirname(self.yaml_file),\n            docs_dir,\n        )\n\n        # RTD javascript writing\n        rtd_data = self.generate_rtd_data(\n            docs_dir=os.path.relpath(docs_path, self.root_path),\n            mkdocs_config=user_config,\n        )\n        with open(os.path.join(docs_path, 'readthedocs-data.js'), 'w') as f:\n            f.write(rtd_data)\n\n        # Use Read the Docs' analytics setup rather than mkdocs'\n        # This supports using RTD's privacy improvements around analytics\n        user_config['google_analytics'] = None\n\n        # README: make MkDocs to use ``readthedocs`` theme as default if the\n        # user didn't specify a specific theme manually\n        if self.project.has_feature(Feature.MKDOCS_THEME_RTD):\n            if 'theme' not in user_config:\n                # mkdocs<0.17 syntax\n                user_config['theme'] = self.DEFAULT_THEME_NAME\n\n        # Write the modified mkdocs configuration\n        yaml.safe_dump(\n            user_config,\n            open(self.yaml_file, 'w'),\n        )\n\n        # Write the mkdocs.yml to the build logs\n        self.run(\n            'cat',\n            os.path.relpath(self.yaml_file, self.root_path),\n            cwd=self.root_path,\n        )\n\n    def generate_rtd_data(self, docs_dir, mkdocs_config):\n        \"\"\"Generate template properties and render readthedocs-data.js.\"\"\"\n        # Use the analytics code from mkdocs.yml\n        # if it isn't set already by Read the Docs,\n        analytics_code = self.version.project.analytics_code\n        if not analytics_code and mkdocs_config.get('google_analytics'):\n            # http://www.mkdocs.org/user-guide/configuration/#google_analytics\n            analytics_code = mkdocs_config['google_analytics'][0]\n\n        # Will be available in the JavaScript as READTHEDOCS_DATA.\n        readthedocs_data = {\n            'project': self.version.project.slug,\n            'version': self.version.slug,\n            'language': self.version.project.language,\n            'programming_language': self.version.project.programming_language,\n            'page': None,\n            'theme': self.get_theme_name(mkdocs_config),\n            'builder': 'mkdocs',\n            'docroot': docs_dir,\n            'source_suffix': '.md',\n            'api_host': getattr(\n                settings,\n                'PUBLIC_API_URL',\n                'https://readthedocs.org',\n            ),\n            'ad_free': not self.project.show_advertising,\n            'commit': self.version.project.vcs_repo(self.version.slug).commit,\n            'global_analytics_code': getattr(\n                settings,\n                'GLOBAL_ANALYTICS_CODE',\n                'UA-17997319-1',\n            ),\n            'user_analytics_code': analytics_code,\n        }\n        data_json = json.dumps(readthedocs_data, indent=4)\n        data_ctx = {\n            'data_json': data_json,\n            'current_version': readthedocs_data['version'],\n            'slug': readthedocs_data['project'],\n            'html_theme': readthedocs_data['theme'],\n            'pagename': None,\n        }\n        tmpl = template_loader.get_template('doc_builder/data.js.tmpl')\n        return tmpl.render(data_ctx)\n\n    def build(self):\n        checkout_path = self.project.checkout_path(self.version.slug)\n        build_command = [\n            'python',\n            self.python_env.venv_bin(filename='mkdocs'),\n            self.builder,\n            '--clean',\n            '--site-dir',\n            self.build_dir,\n            '--config-file',\n            self.yaml_file,\n        ]\n        if self.config.mkdocs.fail_on_warning:\n            build_command.append('--strict')\n        cmd_ret = self.run(\n            *build_command, cwd=checkout_path,\n            bin_path=self.python_env.venv_bin()\n        )\n        return cmd_ret.successful\n\n    def get_theme_name(self, mkdocs_config):\n        \"\"\"\n        Get the theme configuration in the mkdocs_config.\n\n        In v0.17.0, the theme configuration switched\n        from two separate configs (both optional) to a nested directive.\n\n        :see: http://www.mkdocs.org/about/release-notes/#theme-customization-1164\n        :returns: the name of the theme RTD will use\n        \"\"\"\n        theme_setting = mkdocs_config.get('theme')\n        if isinstance(theme_setting, dict):\n            # Full nested theme config (the new configuration)\n            return theme_setting.get('name') or self.DEFAULT_THEME_NAME\n\n        if theme_setting:\n            # A string which is the name of the theme\n            return theme_setting\n\n        theme_dir = mkdocs_config.get('theme_dir')\n        if theme_dir:\n            # Use the name of the directory in this project's custom theme directory\n            return theme_dir.rstrip('/').split('/')[-1]\n\n        return self.DEFAULT_THEME_NAME\n\n\nclass MkdocsHTML(BaseMkdocs):\n    type = 'mkdocs'\n    builder = 'build'\n    build_dir = '_build/html'\n\n\nclass MkdocsJSON(BaseMkdocs):\n    type = 'mkdocs_json'\n    builder = 'json'\n    build_dir = '_build/json'\n", "path": "readthedocs/doc_builder/backends/mkdocs.py"}]}