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)
Beispiel #2
0
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)
Beispiel #3
0
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
Beispiel #4
0
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
Beispiel #5
0
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)
Beispiel #6
0
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
Beispiel #7
0
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
Beispiel #8
0
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