瀏覽代碼

Load sphinx config in a separate process

This loads the Sphinx config in a different process to deal with
module-reload issues. These can occur when the `conf.py` file imports a
module from the local git repo to set the version and release of the
current docs.

Before, all `conf.py` files were read in the same interpreter process,
so the module would only be loaded once. This is undesired if the module
is different for each version to build docs for. For example, changes in
the `__version__` variable would not be picked up and it might lead to
errors when `conf.py` and module go out of sync.

The alternative to this method would be to iterate over `sys.modules`
and reset/reload them manually, but that would probably require
additional configuration by the user and looks even hackier than this
implementation.
Jan Holthuis 4 年之前
父節點
當前提交
07032d6a2d
共有 1 個文件被更改,包括 74 次插入21 次删除
  1. 74 21
      sphinx_multiversion/main.py

+ 74 - 21
sphinx_multiversion/main.py

@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 import itertools
 import argparse
+import multiprocessing
+import contextlib
 import json
 import logging
 import os
@@ -19,6 +21,75 @@ from . import sphinx
 from . import git
 
 
[email protected]
+def working_dir(path):
+    prev_cwd = os.getcwd()
+    os.chdir(path)
+    try:
+        yield
+    finally:
+        os.chdir(prev_cwd)
+
+
+def load_sphinx_config_worker(q, confpath, confoverrides, add_defaults):
+    try:
+        with working_dir(confpath):
+            current_config = sphinx_config.Config.read(
+                confpath, confoverrides,
+            )
+
+        if add_defaults:
+            current_config.add(
+                "smv_tag_whitelist", sphinx.DEFAULT_TAG_WHITELIST, "html", str
+            )
+            current_config.add(
+                "smv_branch_whitelist",
+                sphinx.DEFAULT_TAG_WHITELIST,
+                "html",
+                str,
+            )
+            current_config.add(
+                "smv_remote_whitelist",
+                sphinx.DEFAULT_REMOTE_WHITELIST,
+                "html",
+                str,
+            )
+            current_config.add(
+                "smv_released_pattern",
+                sphinx.DEFAULT_RELEASED_PATTERN,
+                "html",
+                str,
+            )
+            current_config.add(
+                "smv_outputdir_format",
+                sphinx.DEFAULT_OUTPUTDIR_FORMAT,
+                "html",
+                str,
+            )
+            current_config.add("smv_prefer_remote_refs", False, "html", bool)
+        current_config.pre_init_values()
+        current_config.init_values()
+    except Exception as err:
+        q.put(err)
+        return
+
+    q.put(current_config)
+
+
+def load_sphinx_config(confpath, confoverrides, add_defaults=False):
+    q = multiprocessing.Queue()
+    proc = multiprocessing.Process(
+        target=load_sphinx_config_worker,
+        args=(q, confpath, confoverrides, add_defaults),
+    )
+    proc.start()
+    proc.join()
+    result = q.get_nowait()
+    if isinstance(result, Exception):
+        raise result
+    return result
+
+
 def main(argv=None):
     if not argv:
         argv = sys.argv[1:]
@@ -77,23 +148,9 @@ def main(argv=None):
         confoverrides[key] = value
 
     # Parse config
-    config = sphinx_config.Config.read(confdir_absolute, confoverrides)
-    config.add("smv_tag_whitelist", sphinx.DEFAULT_TAG_WHITELIST, "html", str)
-    config.add(
-        "smv_branch_whitelist", sphinx.DEFAULT_TAG_WHITELIST, "html", str
+    config = load_sphinx_config(
+        confdir_absolute, confoverrides, add_defaults=True
     )
-    config.add(
-        "smv_remote_whitelist", sphinx.DEFAULT_REMOTE_WHITELIST, "html", str
-    )
-    config.add(
-        "smv_released_pattern", sphinx.DEFAULT_RELEASED_PATTERN, "html", str
-    )
-    config.add(
-        "smv_outputdir_format", sphinx.DEFAULT_OUTPUTDIR_FORMAT, "html", str
-    )
-    config.add("smv_prefer_remote_refs", False, "html", bool)
-    config.pre_init_values()
-    config.init_values()
 
     # Get relative paths to root of git repository
     gitroot = pathlib.Path(".").resolve()
@@ -141,9 +198,7 @@ def main(argv=None):
             # Find config
             confpath = os.path.join(repopath, confdir)
             try:
-                current_config = sphinx_config.Config.read(
-                    confpath, confoverrides,
-                )
+                current_config = load_sphinx_config(confpath, confoverrides)
             except (OSError, sphinx_config.ConfigError):
                 logger.error(
                     "Failed load config for %s from %s",
@@ -151,8 +206,6 @@ def main(argv=None):
                     confpath,
                 )
                 continue
-            current_config.pre_init_values()
-            current_config.init_values()
 
             # Ensure that there are not duplicate output dirs
             outputdir = config.smv_outputdir_format.format(