def run(self): # type: () -> None if not color_terminal(): nocolor() if not self.verbose: # type: ignore status_stream = StringIO() else: status_stream = sys.stdout # type: ignore confoverrides = {} # type: Dict[str, Any] if self.project: confoverrides['project'] = self.project if self.version: confoverrides['version'] = self.version if self.release: confoverrides['release'] = self.release if self.today: confoverrides['today'] = self.today if self.copyright: confoverrides['copyright'] = self.copyright if self.nitpicky: confoverrides['nitpicky'] = self.nitpicky for builder, builder_target_dir in self.builder_target_dirs: app = None try: confdir = self.config_dir or self.source_dir with patch_docutils(confdir), docutils_namespace(): app = Sphinx(self.source_dir, self.config_dir, builder_target_dir, self.doctree_dir, builder, confoverrides, status_stream, freshenv=self.fresh_env, warningiserror=self.warning_is_error) app.build(force_all=self.all_files) if app.statuscode: raise DistutilsExecError( 'caused by %s builder.' % app.builder.name) except Exception as exc: handle_exception(app, self, exc, sys.stderr) if not self.pdb: raise SystemExit(1) if not self.link_index: continue src = app.config.master_doc + app.builder.out_suffix # type: ignore dst = app.builder.get_outfilename('index') # type: ignore os.symlink(src, dst)
def handle_exception(app, args, exception, stderr=sys.stderr): # type: (Sphinx, Any, Union[Exception, KeyboardInterrupt], IO) -> None warnings.warn('sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.', RemovedInSphinx30Warning, stacklevel=2) build.handle_exception(app, args, exception, stderr)
def build_sphinx( sourcedir, outputdir, confdir=None, path_config=None, noconfig=False, confoverrides=None, extra_extensions=None, htmloverrides=None, latexoverrides=None, doctreedir=None, filenames=None, force_all=False, quiet=False, really_quiet=False, nitpicky=False, builder="html", freshenv=False, warningiserror=False, tags=None, verbosity=0, jobs=None, keep_going=False, ): """Sphinx build "main" command-line entry. This is a slightly modified version of https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/cmd/build.py#L198. Extra parameters ---------------- extra_extensions : list | None A list of extra extensions to load into Sphinx. This must be done before Sphinx is initialized otherwise the extensions aren't properly initialized. """ if confoverrides is None: confoverrides = {} if latexoverrides is None: latexoverrides = {} ####################### # Configuration updates # Start with the default Sphinx config sphinx_config = DEFAULT_CONFIG.copy() # Update with the *default* config.yml default_yaml_config = yaml.safe_load(PATH_YAML_DEFAULT.read_text()) new_config = yaml_to_sphinx(default_yaml_config) _recursive_update(sphinx_config, new_config) # Update with the given config file, if it exists if path_config: path_config = Path(path_config) yaml_config = yaml.safe_load(path_config.read_text()) # Check for manual Sphinx over-rides which we'll apply later to take precedence sphinx_overrides = yaml_config.get("sphinx", {}).get("config") if sphinx_overrides: confoverrides.update(sphinx_overrides) # Some latex-specific changes we need to make if we're building latex if builder == "latex": # First update the overrides with the latex config latexoverrides.update(yaml_config.get("latex", {})) # If we have a document title and no explicit latex title, use the doc title if "title" in yaml_config.keys(): latex_documents = latexoverrides.get("latex_documents", {}) if "title" not in latex_documents: latex_documents["title"] = yaml_config["title"] latexoverrides["latex_documents"] = latex_documents new_config = yaml_to_sphinx(yaml_config) _recursive_update(sphinx_config, new_config) # Manual configuration overrides from the CLI _recursive_update(sphinx_config, confoverrides) # HTML-specific configuration from the CLI if htmloverrides is None: htmloverrides = {} for key, val in htmloverrides.items(): sphinx_config["html_context.%s" % key] = val # #LaTeX-specific configuration # TODO: if this is included we should ignore latex_documents # if latexoverrides is None: # latexoverrides = {} # for key, val in latexoverrides.items(): # config[key] = val # Add the folder `_static` if it exists if Path(sourcedir).joinpath("_static").is_dir(): paths_static = sphinx_config.get("html_static_path", []) paths_static.append("_static") sphinx_config["html_static_path"] = paths_static # Flags from the CLI # Raise more warnings if nitpicky: sphinx_config["nitpicky"] = True ################################## # Preparing Sphinx build arguments # Configuration directory if noconfig: confdir = None elif not confdir: confdir = sourcedir # Doctrees directory if not doctreedir: doctreedir = Path(outputdir).parent.joinpath(".doctrees") if jobs is None: jobs = 1 # Manually re-building files in filenames if filenames is None: filenames = [] missing_files = [] for filename in filenames: if not op.isfile(filename): missing_files.append(filename) if missing_files: raise ValueError("cannot find files %r" % missing_files) if force_all and filenames: raise ValueError("cannot combine -a option and filenames") # Debug args (hack to get this to pass through properly) def debug_args(): pass debug_args.pdb = False debug_args.verbosity = False debug_args.traceback = False # Logging behavior status = sys.stdout warning = sys.stderr error = sys.stderr if quiet: status = None if really_quiet: status = warning = None ################### # Build with Sphinx app = None # In case we fail, this allows us to handle the exception try: # This patch is what Sphinx does, so we copy it blindly... with patch_docutils(confdir), docutils_namespace(): app = Sphinx( srcdir=sourcedir, confdir=confdir, outdir=outputdir, doctreedir=doctreedir, buildername=builder, confoverrides=sphinx_config, status=status, warning=warning, freshenv=freshenv, warningiserror=warningiserror, tags=tags, verbosity=verbosity, parallel=jobs, keep_going=keep_going, ) # Apply Latex Overrides for latex_documents if ( latexoverrides is not None and "latex_documents" in latexoverrides.keys() ): from .pdf import update_latex_documents latex_documents = update_latex_documents( app.config.latex_documents[0], latexoverrides ) app.config.latex_documents = [latex_documents] app.build(force_all, filenames) # Write an index.html file in the root to redirect to the first page path_index = outputdir.joinpath("index.html") if sphinx_config["globaltoc_path"]: path_toc = Path(sphinx_config["globaltoc_path"]) if not path_toc.exists(): raise ValueError( ( "You gave a Configuration file path" f"that doesn't exist: {path_toc}" ) ) if path_toc.suffix not in [".yml", ".yaml"]: raise ValueError( "You gave a Configuration file path" f"that is not a YAML file: {path_toc}" ) else: path_toc = None if not path_index.exists() and path_toc: toc = yaml.safe_load(path_toc.read_text()) if isinstance(toc, dict): first_page = toc["file"] else: first_page = toc[0]["file"] first_page = first_page.split(".")[0] + ".html" with open(path_index, "w") as ff: ff.write(REDIRECT_TEXT.format(first_page=first_page)) return app.statuscode except (Exception, KeyboardInterrupt) as exc: handle_exception(app, debug_args, exc, error) return exc
def build_sphinx( sourcedir, outputdir, confdir=None, noconfig=False, confoverrides=None, extra_extensions=None, htmloverrides=None, latexoverrides=None, doctreedir=None, filenames=None, force_all=False, quiet=False, really_quiet=False, nitpicky=False, builder="html", freshenv=False, warningiserror=False, tags=None, verbosity=0, jobs=None, keep_going=False, ): """Sphinx build "main" command-line entry. This is a slightly modified version of https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/cmd/build.py#L198. Extra parameters ---------------- extra_extensions : list | None A list of extra extensions to load into Sphinx. This must be done before Sphinx is initialized otherwise the extensions aren't properly initialized. """ # Manual configuration overrides if confoverrides is None: confoverrides = {} config = DEFAULT_CONFIG.copy() config.update(confoverrides) if extra_extensions: if not isinstance(extra_extensions, list): extra_extensions = [extra_extensions] for ext in extra_extensions: config["extensions"].append(ext) # HTML-specific configuration if htmloverrides is None: htmloverrides = {} for key, val in htmloverrides.items(): config["html_context.%s" % key] = val # #LaTeX-specific configuration # TODO: if this is included we should ignore latex_documents # if latexoverrides is None: # latexoverrides = {} # for key, val in latexoverrides.items(): # config[key] = val # Configuration directory if noconfig: confdir = None elif not confdir: confdir = sourcedir # Doctrees directory if not doctreedir: doctreedir = Path(outputdir).parent.joinpath(".doctrees") if jobs is None: jobs = 1 # Manually re-building files in filenames if filenames is None: filenames = [] missing_files = [] for filename in filenames: if not op.isfile(filename): missing_files.append(filename) if missing_files: raise ValueError("cannot find files %r" % missing_files) if force_all and filenames: raise ValueError("cannot combine -a option and filenames") # Debug args (hack to get this to pass through properly) def debug_args(): pass debug_args.pdb = False debug_args.verbosity = False debug_args.traceback = False # Logging behavior status = sys.stdout warning = sys.stderr error = sys.stderr if quiet: status = None if really_quiet: status = warning = None # Raise more warnings if nitpicky: config["nitpicky"] = True app = None # In case we fail, this allows us to handle the exception try: with patch_docutils(confdir), docutils_namespace(): app = Sphinx( sourcedir, confdir, outputdir, doctreedir, builder, config, status, warning, freshenv, warningiserror, tags, verbosity, jobs, keep_going, ) # Apply Latex Overrides for latex_documents if (latexoverrides is not None and "latex_documents" in latexoverrides.keys()): from .pdf import update_latex_documents latex_documents = update_latex_documents( app.config.latex_documents[0], latexoverrides) app.config.latex_documents = [latex_documents] app.build(force_all, filenames) # Write an index.html file in the root to redirect to the first page path_index = outputdir.joinpath("index.html") if config["globaltoc_path"]: path_toc = Path(config["globaltoc_path"]) if not path_toc.exists(): raise ValueError(("You gave a Configuration file path" f"that doesn't exist: {path_toc}")) if path_toc.suffix not in [".yml", ".yaml"]: raise ValueError("You gave a Configuration file path" f"that is not a YAML file: {path_toc}") else: path_toc = None if not path_index.exists() and path_toc: toc = yaml.safe_load(path_toc.read_text()) if isinstance(toc, dict): first_page = toc["file"] else: first_page = toc[0]["file"] first_page = first_page.split(".")[0] + ".html" with open(path_index, "w") as ff: ff.write(REDIRECT_TEXT.format(first_page=first_page)) return app.statuscode except (Exception, KeyboardInterrupt) as exc: handle_exception(app, debug_args, exc, error) return exc
def handle_exception(app, args, exception, stderr=sys.stderr): # type: (Sphinx, Any, Union[Exception, KeyboardInterrupt], IO) -> None warnings.warn( 'sphinx.cmdline module is deprecated. Use sphinx.cmd.build instead.', RemovedInSphinx30Warning) build.handle_exception(app, args, exception, stderr)
def build_sphinx( sourcedir, outputdir, confdir=None, noconfig=False, confoverrides=None, htmloverrides=None, doctreedir=None, filenames=None, force_all=False, quiet=False, really_quiet=False, nitpicky=False, builder="html", freshenv=False, warningiserror=False, tags=None, verbosity=0, jobs=None, keep_going=False, ): """Sphinx build "main" command-line entry. This is a slightly modified version of https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/cmd/build.py#L198. """ # Manual configuration overrides if confoverrides is None: confoverrides = {} config = DEFAULT_CONFIG.copy() config.update(confoverrides) # HTML-specific configuration if htmloverrides is None: htmloverrides = {} for key, val in htmloverrides.items(): config["html_context.%s" % key] = val # Configuration directory if noconfig: confdir = None elif not confdir: confdir = sourcedir # Doctrees directory if not doctreedir: doctreedir = op.join(outputdir, ".doctrees") if jobs is None: jobs = 1 # Manually re-building files in filenames if filenames is None: filenames = [] missing_files = [] for filename in filenames: if not op.isfile(filename): missing_files.append(filename) if missing_files: raise ValueError("cannot find files %r" % missing_files) if force_all and filenames: raise ValueError("cannot combine -a option and filenames") # Debug args (hack to get this to pass through properly) def debug_args(): pass debug_args.pdb = False debug_args.verbosity = False debug_args.traceback = False # Logging behavior status = sys.stdout warning = sys.stderr error = sys.stderr if quiet: status = None if really_quiet: status = warning = None # Error on warnings if nitpicky: config["nitpicky"] = True app = None # In case we fail, this allows us to handle the exception try: with patch_docutils(confdir), docutils_namespace(): app = Sphinx( sourcedir, confdir, outputdir, doctreedir, builder, config, status, warning, freshenv, warningiserror, tags, verbosity, jobs, keep_going, ) app.build(force_all, filenames) # Write an index.html file in the root to redirect to the first page path_index = outputdir.joinpath("index.html") path_toc = Path(config["globaltoc_path"]) if not path_index.exists() and path_toc.exists(): toc = yaml.safe_load(path_toc.read_text()) first_page = toc[0]["file"].split(".")[0] + ".html" with open(path_index, "w") as ff: ff.write(REDIRECT_TEXT.format(first_page=first_page)) return app.statuscode except (Exception, KeyboardInterrupt) as exc: handle_exception(app, debug_args, exc, error) return 2
def build_sphinx( sourcedir, outputdir, confdir=None, path_config=None, noconfig=False, confoverrides=None, doctreedir=None, filenames=None, force_all=False, quiet=False, really_quiet=False, builder="html", freshenv=False, warningiserror=False, tags=None, verbosity=0, jobs=None, keep_going=False, ) -> Union[int, Exception]: """Sphinx build "main" command-line entry. This is a slightly modified version of https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/cmd/build.py#L198. """ ####################### # Configuration creation sphinx_config, config_meta = get_final_config( user_yaml=Path(path_config) if path_config else None, cli_config=confoverrides or {}, sourcedir=Path(sourcedir), ) ################################## # Preparing Sphinx build arguments # Configuration directory if noconfig: confdir = None elif not confdir: confdir = sourcedir # Doctrees directory if not doctreedir: doctreedir = Path(outputdir).parent.joinpath(".doctrees") if jobs is None: jobs = 1 # Manually re-building files in filenames if filenames is None: filenames = [] missing_files = [] for filename in filenames: if not op.isfile(filename): missing_files.append(filename) if missing_files: raise IOError("cannot find files %r" % missing_files) if force_all and filenames: raise ValueError("cannot combine -a option and filenames") # Debug args (hack to get this to pass through properly) def debug_args(): pass debug_args.pdb = False debug_args.verbosity = False debug_args.traceback = False # Logging behavior status = sys.stdout warning = sys.stderr error = sys.stderr if quiet: status = None if really_quiet: status = warning = None ################### # Build with Sphinx app = None # In case we fail, this allows us to handle the exception try: # These patches temporarily override docutils global variables, # such as the dictionaries of directives, roles and nodes # NOTE: this action is not thread-safe and not suitable for asynchronous use! with patch_docutils(confdir), docutils_namespace(): app = Sphinx( srcdir=sourcedir, confdir=confdir, outdir=outputdir, doctreedir=doctreedir, buildername=builder, confoverrides=sphinx_config, status=status, warning=warning, freshenv=freshenv, warningiserror=warningiserror, tags=tags, verbosity=verbosity, parallel=jobs, keep_going=keep_going, ) app.srcdir = Path(app.srcdir).as_posix() app.outdir = Path(app.outdir).as_posix() app.confdir = Path(app.confdir).as_posix() app.doctreedir = Path(app.doctreedir).as_posix() # We have to apply this update after the sphinx initialisation, # since default_latex_documents is dynamically generated # see sphinx/builders/latex/__init__.py:default_latex_documents # TODO what if the user has specifically set latex_documents? default_latex_document = app.config.latex_documents[0] new_latex_document = update_latex_document( default_latex_document, config_meta["latex_doc_overrides"]) app.config.latex_documents = [new_latex_document] app.build(force_all, filenames) # Write an index.html file in the root to redirect to the first page path_index = outputdir.joinpath("index.html") if sphinx_config["globaltoc_path"]: path_toc = Path(sphinx_config["globaltoc_path"]) if not path_toc.exists(): raise IOError(("You gave a Configuration file path" f"that doesn't exist: {path_toc}")) if path_toc.suffix not in [".yml", ".yaml"]: raise IOError("You gave a Configuration file path" f"that is not a YAML file: {path_toc}") else: path_toc = None if not path_index.exists() and path_toc: toc = yaml.safe_load(path_toc.read_text(encoding="utf8")) if isinstance(toc, dict): first_page = toc["file"] else: first_page = toc[0]["file"] first_page = first_page.split(".")[0] + ".html" with open(path_index, "w", encoding="utf8") as ff: ff.write(REDIRECT_TEXT.format(first_page=first_page)) return app.statuscode except (Exception, KeyboardInterrupt) as exc: handle_exception(app, debug_args, exc, error) return exc
def build_sphinx( sourcedir, outputdir, *, use_external_toc=True, confdir=None, path_config=None, noconfig=False, confoverrides=None, doctreedir=None, filenames=None, force_all=False, quiet=False, really_quiet=False, builder="html", freshenv=False, warningiserror=False, tags=None, verbosity=0, jobs=None, keep_going=False, ) -> Union[int, Exception]: """Sphinx build "main" command-line entry. This is a slightly modified version of https://github.com/sphinx-doc/sphinx/blob/3.x/sphinx/cmd/build.py#L198. """ ####################### # Configuration creation sphinx_config, config_meta = get_final_config( user_yaml=Path(path_config) if path_config else None, cli_config=confoverrides or {}, sourcedir=Path(sourcedir), use_external_toc=use_external_toc, ) ################################## # Preparing Sphinx build arguments # Configuration directory if noconfig: confdir = None elif not confdir: confdir = sourcedir # Doctrees directory if not doctreedir: doctreedir = Path(outputdir).parent.joinpath(".doctrees") if jobs is None: jobs = 1 # Manually re-building files in filenames if filenames is None: filenames = [] missing_files = [] for filename in filenames: if not op.isfile(filename): missing_files.append(filename) if missing_files: raise IOError("cannot find files %r" % missing_files) if force_all and filenames: raise ValueError("cannot combine -a option and filenames") # Debug args (hack to get this to pass through properly) def debug_args(): pass debug_args.pdb = False debug_args.verbosity = False debug_args.traceback = False # Logging behavior status = sys.stdout warning = sys.stderr error = sys.stderr if quiet: status = None if really_quiet: status = warning = None ################### # Build with Sphinx app = None # In case we fail, this allows us to handle the exception try: # These patches temporarily override docutils global variables, # such as the dictionaries of directives, roles and nodes # NOTE: this action is not thread-safe and not suitable for asynchronous use! with patch_docutils(confdir), docutils_namespace(): app = Sphinx( srcdir=sourcedir, confdir=confdir, outdir=outputdir, doctreedir=doctreedir, buildername=builder, confoverrides=sphinx_config, status=status, warning=warning, freshenv=freshenv, warningiserror=warningiserror, tags=tags, verbosity=verbosity, parallel=jobs, keep_going=keep_going, ) app.srcdir = Path(app.srcdir).as_posix() app.outdir = Path(app.outdir).as_posix() app.confdir = Path(app.confdir).as_posix() app.doctreedir = Path(app.doctreedir).as_posix() # We have to apply this update after the sphinx initialisation, # since default_latex_documents is dynamically generated # see sphinx/builders/latex/__init__.py:default_latex_documents new_latex_documents = update_latex_documents( app.config.latex_documents, config_meta["latex_doc_overrides"]) app.config.latex_documents = new_latex_documents # setting up sphinx-multitoc-numbering if app.config["use_multitoc_numbering"]: # if sphinx-external-toc is used if "external_toc_path" in app.config: import yaml site_map = app.config.external_site_map site_map_str = yaml.dump(site_map.as_json()) # only if there is atleast one numbered: true in the toc file if "numbered: true" in site_map_str: app.setup_extension("sphinx_multitoc_numbering") else: app.setup_extension("sphinx_multitoc_numbering") # Build latex_doc tuples based on --individualpages option request if config_meta["latex_individualpages"]: from .pdf import autobuild_singlepage_latexdocs # Ask Builder to read the source files to fetch titles and documents app.builder.read() latex_documents = autobuild_singlepage_latexdocs(app) app.config.latex_documents = latex_documents app.build(force_all, filenames) return app.statuscode except (Exception, KeyboardInterrupt) as exc: handle_exception(app, debug_args, exc, error) return exc