Пример #1
0
    def __init__(self, dirname, filename, overrides, tags):
        # type: (unicode, unicode, Dict, Tags) -> None
        self.overrides = overrides
        self.values = Config.config_values.copy()
        config = {}  # type: Dict[unicode, Any]
        if dirname is not None:
            config_file = path.join(dirname, filename)
            config['__file__'] = config_file
            config['tags'] = tags
            with cd(dirname):
                # we promise to have the config dir as current dir while the
                # config file is executed
                try:
                    execfile_(filename, config)
                except SyntaxError as err:
                    raise ConfigError(CONFIG_SYNTAX_ERROR % err)
                except SystemExit:
                    raise ConfigError(CONFIG_EXIT_ERROR)

        self._raw_config = config
        # these two must be preinitialized because extensions can add their
        # own config values
        self.setup = config.get('setup', None)  # type: Callable

        if 'extensions' in overrides:
            if isinstance(overrides['extensions'], string_types):
                config['extensions'] = overrides.pop('extensions').split(',')
            else:
                config['extensions'] = overrides.pop('extensions')
        self.extensions = config.get('extensions', [])  # type: List[unicode]

        # correct values of copyright year that are not coherent with
        # the SOURCE_DATE_EPOCH environment variable (if set)
        # See https://reproducible-builds.org/specs/source-date-epoch/
        if getenv('SOURCE_DATE_EPOCH') is not None:
            for k in ('copyright', 'epub_copyright'):
                if k in config:
                    config[k] = copyright_year_re.sub(
                        '\g<1>%s' % format_date('%Y'),  # type: ignore  # NOQA
                        config[k])
Пример #2
0
def validate_config_values(app, config):
    # type: (Sphinx, Config) -> None
    for document in config.latex_documents:
        try:
            text_type(document[2])
        except UnicodeDecodeError:
            raise ConfigError(
                __('Invalid latex_documents.title found (might contain non-ASCII chars. '
                   'Please use u"..." notation instead): %r') % (document, ))

        try:
            text_type(document[3])
        except UnicodeDecodeError:
            raise ConfigError(
                __('Invalid latex_documents.author found (might contain non-ASCII chars. '
                   'Please use u"..." notation instead): %r') % (document, ))

    for key in list(config.latex_elements):
        if key not in DEFAULT_SETTINGS:
            msg = __("Unknown configure key: latex_elements[%r]. ignored.")
            logger.warning(msg % key)
            config.latex_elements.pop(key)
Пример #3
0
def validate_config_values(app):
    # type: (Sphinx) -> None
    if app.config.latex_toplevel_sectioning not in (None, 'part', 'chapter', 'section'):
        logger.warning('invalid latex_toplevel_sectioning, ignored: %s',
                       app.config.latex_toplevel_sectioning)
        app.config.latex_toplevel_sectioning = None  # type: ignore

    if 'footer' in app.config.latex_elements:
        if 'postamble' in app.config.latex_elements:
            logger.warning("latex_elements['footer'] conflicts with "
                           "latex_elements['postamble'], ignored.")
        else:
            warnings.warn("latex_elements['footer'] is deprecated. "
                          "Use latex_elements['preamble'] instead.",
                          RemovedInSphinx17Warning)
            app.config.latex_elements['postamble'] = app.config.latex_elements['footer']

    if app.config.latex_keep_old_macro_names:
        warnings.warn("latex_keep_old_macro_names is deprecated. "
                      "LaTeX markup since Sphinx 1.4.5 uses only prefixed macro names.",
                      RemovedInSphinx17Warning)

    for document in app.config.latex_documents:
        try:
            text_type(document[2])
        except UnicodeDecodeError:
            raise ConfigError(
                'Invalid latex_documents.title found (might contain non-ASCII chars. '
                'Please use u"..." notation instead): %r' % (document,)
            )

        try:
            text_type(document[3])
        except UnicodeDecodeError:
            raise ConfigError(
                'Invalid latex_documents.author found (might contain non-ASCII chars. '
                'Please use u"..." notation instead): %r' % (document,)
            )
Пример #4
0
def eval_config_file(filename, tags):
    # type: (str, Tags) -> Dict[str, Any]
    """Evaluate a config file."""
    namespace = {}  # type: Dict[str, Any]
    namespace['__file__'] = filename
    namespace['tags'] = tags

    with cd(path.dirname(filename)):
        # during executing config file, current dir is changed to ``confdir``.
        try:
            execfile_(filename, namespace)
        except SyntaxError as err:
            msg = __("There is a syntax error in your configuration file: %s\n")
            raise ConfigError(msg % err)
        except SystemExit:
            msg = __("The configuration file (or one of the modules it imports) "
                     "called sys.exit()")
            raise ConfigError(msg)
        except Exception:
            msg = __("There is a programmable error in your configuration file:\n\n%s")
            raise ConfigError(msg % traceback.format_exc())

    return namespace
Пример #5
0
    def init(self):
        super(DashBuilder, self).init()

        if self.app.config.dash_name is None:
            self.info('Using project name for `dash_name`')
            self.app.config.dash_name = self.app.config.project
        if self.app.config.dash_name.endswith('.docset'):
            self.app.config.dash_name = os.path.splitext(
                self.app.config.dash_name)[0]

        if self.app.config.dash_icon_file is not None and not self.app.config.dash_icon_file.endswith(
                '.png'):
            raise ConfigError('Please supply a PNG icon for `dash_icon_file`.')

        self.prepare_docset()
Пример #6
0
def on_builder_inited(app: Sphinx) -> None:
    """
    Called to build the API documentation HTML  files
    and inject our own intersphinx inventory object.
    """
    if app.builder.name != 'html':
        return

    rtd_version = 'latest'
    if os.environ.get('READTHEDOCS', '') == 'True':
        rtd_version = os.environ.get('READTHEDOCS_VERSION', 'latest')

    config = app.config
    if not config.pydoctor_args:
        raise ConfigError("Missing 'pydoctor_args'.")

    placeholders = {
        'outdir': app.outdir,
    }

    runs = config.pydoctor_args
    if not isinstance(runs, Mapping):
        # We have a single pydoctor call
        runs = {'main': runs}

    pydoctor_url_path = config.pydoctor_url_path
    if not isinstance(pydoctor_url_path, Mapping):
        pydoctor_url_path = {'main': pydoctor_url_path}

    for key, value in runs.items():
        arguments = _get_arguments(value, placeholders)

        options, _ = parse_args(arguments)
        output_path = pathlib.Path(options.htmloutput)
        temp_path = output_path.with_suffix('.pydoctor_temp')

        # Update intersphinx_mapping.
        url_path = pydoctor_url_path.get(key)
        if url_path:
            intersphinx_mapping = config.intersphinx_mapping
            url = url_path.format(**{'rtd_version': rtd_version})
            inv = (str(temp_path / 'objects.inv'), )
            intersphinx_mapping[f'{key}-api-docs'] = (None, (url, inv))

        # Build the API docs in temporary path.
        shutil.rmtree(temp_path, ignore_errors=True)
        _run_pydoctor(key, arguments)
        output_path.rename(temp_path)
Пример #7
0
def _copy_binder_reqs(app, binder_conf):
    """Copy Binder requirements files to a "binder" folder in the docs."""
    path_reqs = binder_conf.get('dependencies')
    for path in path_reqs:
        if not os.path.exists(os.path.join(app.srcdir, path)):
            raise ConfigError(
                "Couldn't find the Binder requirements file: {}, "
                "did you specify the path correctly?".format(path))

    binder_folder = os.path.join(app.outdir, 'binder')
    if not os.path.isdir(binder_folder):
        os.makedirs(binder_folder)

    # Copy over the requirements to the output directory
    for path in path_reqs:
        shutil.copy(os.path.join(app.srcdir, path), binder_folder)
Пример #8
0
def check_binder_conf(binder_conf):
    """Check to make sure that the Binder configuration is correct."""
    # Grab the configuration and return None if it's not configured
    binder_conf = {} if binder_conf is None else binder_conf
    if not isinstance(binder_conf, dict):
        raise ConfigError('`binder_conf` must be a dictionary or None.')
    if len(binder_conf) == 0:
        return binder_conf

    # Ensure all fields are populated
    req_values = ['binderhub_url', 'org', 'repo', 'branch', 'dependencies']
    optional_values = ['filepath_prefix', 'notebooks_dir', 'use_jupyter_lab']
    missing_values = []
    for val in req_values:
        if binder_conf.get(val) is None:
            missing_values.append(val)

    if len(missing_values) > 0:
        raise ConfigError('binder_conf is missing values for: {}'.format(
            missing_values))

    for key in binder_conf.keys():
        if key not in (req_values + optional_values):
            raise ConfigError("Unknown Binder config key: {}".format(key))

    # Ensure we have http in the URL
    if not any(binder_conf['binderhub_url'].startswith(ii)
               for ii in ['http://', 'https://']):
        raise ConfigError('did not supply a valid url, '
                          'gave binderhub_url: {}'
                          .format(binder_conf['binderhub_url']))

    # Ensure we have at least one dependency file
    # Need at least one of these three files
    required_reqs_files = ['requirements.txt', 'environment.yml', 'Dockerfile']
    path_reqs = binder_conf['dependencies']
    if isinstance(path_reqs, str):
        path_reqs = [path_reqs]
        binder_conf['dependencies'] = path_reqs
    elif not isinstance(path_reqs, (list, tuple)):
        raise ConfigError("`dependencies` value should be a list of strings. "
                          "Got type {}.".format(type(path_reqs)))

    binder_conf['notebooks_dir'] = binder_conf.get('notebooks_dir',
                                                   'notebooks')
    path_reqs_filenames = [os.path.basename(ii) for ii in path_reqs]
    if not any(ii in path_reqs_filenames for ii in required_reqs_files):
        raise ConfigError(
            'Did not find one of `requirements.txt` or `environment.yml` '
            'in the "dependencies" section of the binder configuration '
            'for sphinx-gallery. A path to at least one of these files '
            'must exist in your Binder dependencies.')
    return binder_conf
Пример #9
0
    def __init__(self, dirname, filename, overrides, tags):
        self.overrides = overrides
        self.values = Config.config_values.copy()
        config = {}
        if dirname is not None:
            config_file = path.join(dirname, filename)
            config['__file__'] = config_file
            config['tags'] = tags
            olddir = os.getcwd()
            try:
                # we promise to have the config dir as current dir while the
                # config file is executed
                os.chdir(dirname)
                # get config source -- 'b' is a no-op under 2.x, while 'U' is
                # ignored under 3.x (but 3.x compile() accepts \r\n newlines)
                f = open(filename, 'rbU')
                try:
                    source = f.read()
                finally:
                    f.close()
                try:
                    # compile to a code object, handle syntax errors
                    config_file_enc = config_file.encode(fs_encoding)
                    try:
                        code = compile(source, config_file_enc, 'exec')
                    except SyntaxError:
                        if convert_with_2to3:
                            # maybe the file uses 2.x syntax; try to refactor to
                            # 3.x syntax using 2to3
                            source = convert_with_2to3(config_file)
                            code = compile(source, config_file_enc, 'exec')
                        else:
                            raise
                    exec code in config
                except SyntaxError, err:
                    raise ConfigError(CONFIG_SYNTAX_ERROR % err)
            finally:
                os.chdir(olddir)

        self._raw_config = config
        # these two must be preinitialized because extensions can add their
        # own config values
        self.setup = config.get('setup', None)
        self.extensions = config.get('extensions', [])
Пример #10
0
    def validate_config(_, config):
        theme_config = getattr(config, "html_theme_options", {})

        # check nengo_logo config
        html_logo = getattr(config, "html_logo", "")
        nengo_logo = theme_config.get("nengo_logo", None)

        if html_logo and nengo_logo:
            warnings.warn("'html_logo' and 'nengo_logo' are both set; "
                          "'nengo_logo' will take precedence")
        elif html_logo:
            warnings.warn("Logo set using 'html_logo', consider using "
                          "'nengo_logo' instead")

        # check versioning config
        html_context = getattr(config, "html_context", {})
        releases = html_context.get("releases", "").split(",")
        building = html_context.get("building_version", "")

        if "latest" in releases:
            raise ConfigError(
                "nengo_sphinx_theme.ext.versions: 'latest' cannot be a "
                "release name (link to the most up-to-date version of the "
                "docs will be added automatically)")

        if building == "":
            warnings.warn(
                "'building_version' not set, versions will not be rendered")

        # check Google Analytics ID
        analytics_id = theme_config.get("analytics_id", None)
        if analytics_id is not None and not analytics_id.startswith("UA-"):
            warnings.warn("'analytics_id' looks strange. It should look like "
                          "'UA-000000-2'; got %r" % (analytics_id, ))

        # check Google Tag Manager container ID
        tagmanager_id = theme_config.get("tagmanager_id", None)
        if tagmanager_id is not None and not tagmanager_id.startswith("GTM-"):
            warnings.warn("'tagmanager_id' looks strange. It should look like "
                          "'GTM-XXXXXXX'; got %r" % (tagmanager_id, ))
Пример #11
0
    def __init__(self, dirname, filename, overrides, tags):
        self.overrides = overrides
        self.values = Config.config_values.copy()
        config = {}
        if dirname is not None:
            config_file = path.join(dirname, filename)
            config['__file__'] = config_file
            config['tags'] = tags
            olddir = os.getcwd()
            try:
                try:
                    os.chdir(dirname)
                    execfile(config['__file__'], config)
                except SyntaxError, err:
                    raise ConfigError('There is a syntax error in your '
                                      'configuration file: ' + str(err))
            finally:
                os.chdir(olddir)

        self._raw_config = config
        # these two must be preinitialized because extensions can add their
        # own config values
        self.setup = config.get('setup', None)
        self.extensions = config.get('extensions', [])
Пример #12
0
    def read(cls,
             confdir: str,
             overrides: Dict = None,
             tags: Tags = None) -> "Config":
        """Create a Config object from configuration file."""
        filename = path.join(confdir, CONFIG_FILENAME)
        if not path.isfile(filename):
            raise ConfigError(
                __("config directory doesn't contain a conf.py file (%s)") %
                confdir)
        namespace = eval_config_file(filename, tags)

        # Note: Old sphinx projects have been configured as "language = None" because
        #       sphinx-quickstart previously generated this by default.
        #       To keep compatibility, they should be fallback to 'en' for a while
        #       (This conversion should not be removed before 2025-01-01).
        if namespace.get("language", ...) is None:
            logger.warning(
                __("Invalid configuration value found: 'language = None'. "
                   "Update your configuration to a valid language code. "
                   "Falling back to 'en' (English)."))
            namespace["language"] = "en"

        return cls(namespace, overrides or {})
Пример #13
0
    def __init__(self,
                 srcdir,
                 confdir,
                 outdir,
                 doctreedir,
                 buildername,
                 confoverrides=None,
                 status=sys.stdout,
                 warning=sys.stderr,
                 freshenv=False,
                 warningiserror=False,
                 tags=None,
                 verbosity=0,
                 parallel=0,
                 keep_going=False):
        # type: (str, str, str, str, str, Dict, IO, IO, bool, bool, List[str], int, int, bool) -> None  # NOQA
        self.phase = BuildPhase.INITIALIZATION
        self.verbosity = verbosity
        self.extensions = {}  # type: Dict[str, Extension]
        self.builder = None  # type: Builder
        self.env = None  # type: BuildEnvironment
        self.project = None  # type: Project
        self.registry = SphinxComponentRegistry()
        self.html_themes = {}  # type: Dict[str, str]

        # validate provided directories
        self.srcdir = abspath(srcdir)
        self.outdir = abspath(outdir)
        self.doctreedir = abspath(doctreedir)
        self.confdir = confdir
        if self.confdir:  # confdir is optional
            self.confdir = abspath(self.confdir)
            if not path.isfile(path.join(self.confdir, 'conf.py')):
                raise ApplicationError(
                    __("config directory doesn't contain a "
                       "conf.py file (%s)") % confdir)

        if not path.isdir(self.srcdir):
            raise ApplicationError(
                __('Cannot find source directory (%s)') % self.srcdir)

        if self.srcdir == self.outdir:
            raise ApplicationError(
                __('Source directory and destination '
                   'directory cannot be identical'))

        self.parallel = parallel

        if status is None:
            self._status = StringIO()  # type: IO
            self.quiet = True
        else:
            self._status = status
            self.quiet = False

        if warning is None:
            self._warning = StringIO()  # type: IO
        else:
            self._warning = warning
        self._warncount = 0
        self.keep_going = warningiserror and keep_going
        if self.keep_going:
            self.warningiserror = False
        else:
            self.warningiserror = warningiserror
        logging.setup(self, self._status, self._warning)

        self.events = EventManager(self)

        # keep last few messages for traceback
        # This will be filled by sphinx.util.logging.LastMessagesWriter
        self.messagelog = deque(maxlen=10)  # type: deque

        # say hello to the world
        logger.info(bold(
            __('Running Sphinx v%s') % sphinx.__display_version__))

        # notice for parallel build on macOS and py38+
        if sys.version_info > (
                3, 8) and platform.system() == 'Darwin' and parallel > 1:
            logger.info(
                bold(
                    __("For security reason, parallel mode is disabled on macOS and "
                       "python3.8 and above.  For more details, please read "
                       "https://github.com/sphinx-doc/sphinx/issues/6803")))

        # status code for command-line application
        self.statuscode = 0

        # read config
        self.tags = Tags(tags)
        if self.confdir is None:
            self.config = Config({}, confoverrides or {})
        else:
            self.config = Config.read(self.confdir, confoverrides or {},
                                      self.tags)

        # initialize some limited config variables before initialize i18n and loading
        # extensions
        self.config.pre_init_values()

        # set up translation infrastructure
        self._init_i18n()

        # check the Sphinx version if requested
        if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
            raise VersionRequirementError(
                __('This project needs at least Sphinx v%s and therefore cannot '
                   'be built with this version.') % self.config.needs_sphinx)

        # set confdir to srcdir if -C given (!= no confdir); a few pieces
        # of code expect a confdir to be set
        if self.confdir is None:
            self.confdir = self.srcdir

        # load all built-in extension modules
        for extension in builtin_extensions:
            self.setup_extension(extension)

        # load all user-given extension modules
        for extension in self.config.extensions:
            self.setup_extension(extension)

        # preload builder module (before init config values)
        self.preload_builder(buildername)

        if not path.isdir(outdir):
            with progress_message(__('making output directory')):
                ensuredir(outdir)

        # the config file itself can be an extension
        if self.config.setup:
            prefix = __('while setting up extension %s:') % "conf.py"
            with prefixed_warnings(prefix):
                if callable(self.config.setup):
                    self.config.setup(self)
                else:
                    raise ConfigError(
                        __("'setup' as currently defined in conf.py isn't a Python callable. "
                           "Please modify its definition to make it a callable function. "
                           "This is needed for conf.py to behave as a Sphinx extension."
                           ))

        # now that we know all config values, collect them from conf.py
        self.config.init_values()
        self.events.emit('config-inited', self.config)

        # create the project
        self.project = Project(self.srcdir, self.config.source_suffix)
        # create the builder
        self.builder = self.create_builder(buildername)
        # set up the build environment
        self._init_env(freshenv)
        # set up the builder
        self._init_builder()
Пример #14
0
    def __init__(self,
                 srcdir,
                 confdir,
                 outdir,
                 doctreedir,
                 buildername,
                 confoverrides=None,
                 status=sys.stdout,
                 warning=sys.stderr,
                 freshenv=False,
                 warningiserror=False,
                 tags=None,
                 verbosity=0,
                 parallel=0):
        self.verbosity = verbosity
        self.next_listener_id = 0
        self._extensions = {}
        self._extension_metadata = {}
        self._additional_source_parsers = {}
        self._listeners = {}
        self._setting_up_extension = ['?']
        self.domains = {}
        self.buildername = buildername
        self.builderclasses = {}
        self.builder = None
        self.env = None
        self.enumerable_nodes = {}

        self.srcdir = srcdir
        self.confdir = confdir
        self.outdir = outdir
        self.doctreedir = doctreedir

        self.parallel = parallel

        if status is None:
            self._status = cStringIO()
            self.quiet = True
        else:
            self._status = status
            self.quiet = False

        if warning is None:
            self._warning = cStringIO()
        else:
            self._warning = warning
        self._warncount = 0
        self.warningiserror = warningiserror

        self._events = events.copy()
        self._translators = {}

        # keep last few messages for traceback
        self.messagelog = deque(maxlen=10)

        # say hello to the world
        self.info(bold('Running Sphinx v%s' % sphinx.__display_version__))

        # status code for command-line application
        self.statuscode = 0

        if not path.isdir(outdir):
            self.info('making output directory...')
            os.makedirs(outdir)

        # read config
        self.tags = Tags(tags)
        self.config = Config(confdir, CONFIG_FILENAME, confoverrides or {},
                             self.tags)
        self.config.check_unicode(self.warn)
        # defer checking types until i18n has been initialized

        # initialize some limited config variables before loading extensions
        self.config.pre_init_values(self.warn)

        # check the Sphinx version if requested
        if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
            raise VersionRequirementError(
                'This project needs at least Sphinx v%s and therefore cannot '
                'be built with this version.' % self.config.needs_sphinx)

        # force preload html_translator_class
        if self.config.html_translator_class:
            translator_class = self.import_object(
                self.config.html_translator_class,
                'html_translator_class setting')
            self.set_translator('html', translator_class)

        # set confdir to srcdir if -C given (!= no confdir); a few pieces
        # of code expect a confdir to be set
        if self.confdir is None:
            self.confdir = self.srcdir

        # load all built-in extension modules
        for extension in builtin_extensions:
            self.setup_extension(extension)

        # extension loading support for alabaster theme
        # self.config.html_theme is not set from conf.py at here
        # for now, sphinx always load a 'alabaster' extension.
        if 'alabaster' not in self.config.extensions:
            self.config.extensions.append('alabaster')

        # load all user-given extension modules
        for extension in self.config.extensions:
            self.setup_extension(extension)
        # the config file itself can be an extension
        if self.config.setup:
            self._setting_up_extension = ['conf.py']
            # py31 doesn't have 'callable' function for below check
            if hasattr(self.config.setup, '__call__'):
                self.config.setup(self)
            else:
                raise ConfigError(
                    "'setup' that is specified in the conf.py has not been " +
                    "callable. Please provide a callable `setup` function " +
                    "in order to behave as a sphinx extension conf.py itself.")

        # now that we know all config values, collect them from conf.py
        self.config.init_values(self.warn)

        # check extension versions if requested
        if self.config.needs_extensions:
            for extname, needs_ver in self.config.needs_extensions.items():
                if extname not in self._extensions:
                    self.warn(
                        'needs_extensions config value specifies a '
                        'version requirement for extension %s, but it is '
                        'not loaded' % extname)
                    continue
                has_ver = self._extension_metadata[extname]['version']
                if has_ver == 'unknown version' or needs_ver > has_ver:
                    raise VersionRequirementError(
                        'This project needs the extension %s at least in '
                        'version %s and therefore cannot be built with the '
                        'loaded version (%s).' % (extname, needs_ver, has_ver))

        # check primary_domain if requested
        if self.config.primary_domain and self.config.primary_domain not in self.domains:
            self.warn('primary_domain %r not found, ignored.' %
                      self.config.primary_domain)

        # set up translation infrastructure
        self._init_i18n()
        # check all configuration values for permissible types
        self.config.check_types(self.warn)
        # set up source_parsers
        self._init_source_parsers()
        # set up the build environment
        self._init_env(freshenv)
        # set up the builder
        self._init_builder(self.buildername)
        # set up the enumerable nodes
        self._init_enumerable_nodes()
Пример #15
0
    def __init__(self, srcdir, confdir, outdir, doctreedir, buildername,
                 confoverrides=None, status=sys.stdout, warning=sys.stderr,
                 freshenv=False, warningiserror=False, tags=None, verbosity=0,
                 parallel=0):
        # type: (unicode, unicode, unicode, unicode, unicode, Dict, IO, IO, bool, bool, List[unicode], int, int) -> None  # NOQA
        self.verbosity = verbosity
        self.extensions = {}                    # type: Dict[unicode, Extension]
        self._setting_up_extension = ['?']      # type: List[unicode]
        self.builder = None                     # type: Builder
        self.env = None                         # type: BuildEnvironment
        self.registry = SphinxComponentRegistry()
        self.enumerable_nodes = {}              # type: Dict[nodes.Node, Tuple[unicode, Callable]]  # NOQA
        self.post_transforms = []               # type: List[Transform]
        self.html_themes = {}                   # type: Dict[unicode, unicode]

        self.srcdir = srcdir
        self.confdir = confdir
        self.outdir = outdir
        self.doctreedir = doctreedir

        self.parallel = parallel

        if status is None:
            self._status = cStringIO()      # type: IO
            self.quiet = True
        else:
            self._status = status
            self.quiet = False

        if warning is None:
            self._warning = cStringIO()     # type: IO
        else:
            self._warning = warning
        self._warncount = 0
        self.warningiserror = warningiserror
        logging.setup(self, self._status, self._warning)

        self.events = EventManager()

        # keep last few messages for traceback
        # This will be filled by sphinx.util.logging.LastMessagesWriter
        self.messagelog = deque(maxlen=10)  # type: deque

        # say hello to the world
        logger.info(bold('Running Sphinx v%s' % sphinx.__display_version__))

        # status code for command-line application
        self.statuscode = 0

        if not path.isdir(outdir):
            logger.info('making output directory...')
            os.makedirs(outdir)

        # read config
        self.tags = Tags(tags)
        self.config = Config(confdir, CONFIG_FILENAME,
                             confoverrides or {}, self.tags)
        self.config.check_unicode()
        # defer checking types until i18n has been initialized

        # initialize some limited config variables before initialize i18n and loading
        # extensions
        self.config.pre_init_values()

        # set up translation infrastructure
        self._init_i18n()

        # check the Sphinx version if requested
        if self.config.needs_sphinx and self.config.needs_sphinx > sphinx.__display_version__:
            raise VersionRequirementError(
                _('This project needs at least Sphinx v%s and therefore cannot '
                  'be built with this version.') % self.config.needs_sphinx)

        # set confdir to srcdir if -C given (!= no confdir); a few pieces
        # of code expect a confdir to be set
        if self.confdir is None:
            self.confdir = self.srcdir

        # load all built-in extension modules
        for extension in builtin_extensions:
            self.setup_extension(extension)

        # load all user-given extension modules
        for extension in self.config.extensions:
            self.setup_extension(extension)

        # preload builder module (before init config values)
        self.preload_builder(buildername)

        # the config file itself can be an extension
        if self.config.setup:
            self._setting_up_extension = ['conf.py']
            # py31 doesn't have 'callable' function for below check
            if hasattr(self.config.setup, '__call__'):
                self.config.setup(self)
            else:
                raise ConfigError(
                    _("'setup' as currently defined in conf.py isn't a Python callable. "
                      "Please modify its definition to make it a callable function. This is "
                      "needed for conf.py to behave as a Sphinx extension.")
                )

        # now that we know all config values, collect them from conf.py
        self.config.init_values()

        # check extension versions if requested
        verify_required_extensions(self, self.config.needs_extensions)

        # check primary_domain if requested
        primary_domain = self.config.primary_domain
        if primary_domain and not self.registry.has_domain(primary_domain):
            logger.warning(_('primary_domain %r not found, ignored.'), primary_domain)

        # create the builder
        self.builder = self.create_builder(buildername)
        # check all configuration values for permissible types
        self.config.check_types()
        # set up source_parsers
        self._init_source_parsers()
        # set up the build environment
        self._init_env(freshenv)
        # set up the builder
        self._init_builder()
        # set up the enumerable nodes
        self._init_enumerable_nodes()
Пример #16
0
#
# All configuration values have a default; values that are commented out
# serve to show the default.

import os
import sys
import sphinx
from sphinx.errors import ConfigError

# Make Sphinx fail cleanly if using an old Python, rather than obscurely
# failing because some code in one of our extensions doesn't work there.
# In newer versions of Sphinx this will display nicely; in older versions
# Sphinx will also produce a Python backtrace but at least the information
# gets printed...
if sys.version_info < (3,5):
    raise ConfigError(
        "QEMU requires a Sphinx that uses Python 3.5 or better\n")

# The per-manual conf.py will set qemu_docdir for a single-manual build;
# otherwise set it here if this is an entire-manual-set build.
# This is always the absolute path of the docs/ directory in the source tree.
try:
    qemu_docdir
except NameError:
    qemu_docdir = os.path.abspath(".")

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use an absolute path starting from qemu_docdir.
#
sys.path.insert(0, os.path.join(qemu_docdir, "sphinx"))
Пример #17
0
def _complete_gallery_conf(sphinx_gallery_conf,
                           src_dir,
                           plot_gallery,
                           abort_on_example_error,
                           lang='python',
                           builder_name='html',
                           app=None):
    gallery_conf = copy.deepcopy(DEFAULT_GALLERY_CONF)
    gallery_conf.update(sphinx_gallery_conf)
    if sphinx_gallery_conf.get('find_mayavi_figures', False):
        logger.warning(
            "Deprecated image scraping variable `find_mayavi_figures`\n"
            "detected, use `image_scrapers` instead as:\n\n"
            "   image_scrapers=('matplotlib', 'mayavi')",
            type=DeprecationWarning)
        gallery_conf['image_scrapers'] += ('mayavi', )
    gallery_conf.update(plot_gallery=plot_gallery)
    gallery_conf.update(abort_on_example_error=abort_on_example_error)
    gallery_conf['src_dir'] = src_dir
    gallery_conf['app'] = app

    if gallery_conf.get("mod_example_dir", False):
        backreferences_warning = """\n========
        Sphinx-Gallery found the configuration key 'mod_example_dir'. This
        is deprecated, and you should now use the key 'backreferences_dir'
        instead. Support for 'mod_example_dir' will be removed in a subsequent
        version of Sphinx-Gallery. For more details, see the backreferences
        documentation:

        https://sphinx-gallery.github.io/configuration.html#references-to-examples"""  # noqa: E501
        gallery_conf['backreferences_dir'] = gallery_conf['mod_example_dir']
        logger.warning(backreferences_warning, type=DeprecationWarning)

    # Check capture_repr
    capture_repr = gallery_conf['capture_repr']
    supported_reprs = ['__repr__', '__str__', '_repr_html_']
    if isinstance(capture_repr, tuple):
        for rep in capture_repr:
            if rep not in supported_reprs:
                raise ConfigError("All entries in 'capture_repr' must be one "
                                  "of %s, got: %s" % (supported_reprs, rep))
    else:
        raise ConfigError("'capture_repr' must be a tuple, got: %s" %
                          (type(capture_repr), ))
    # Check ignore_repr_types
    if not isinstance(gallery_conf['ignore_repr_types'], str):
        raise ConfigError("'ignore_repr_types' must be a string, got: %s" %
                          (type(gallery_conf['ignore_repr_types']), ))

    # deal with show_memory
    gallery_conf['memory_base'] = 0.
    if gallery_conf['show_memory']:
        if not callable(gallery_conf['show_memory']):  # True-like
            try:
                from memory_profiler import memory_usage  # noqa
            except ImportError:
                logger.warning("Please install 'memory_profiler' to enable "
                               "peak memory measurements.")
                gallery_conf['show_memory'] = False
            else:

                def call_memory(func):
                    mem, out = memory_usage(func,
                                            max_usage=True,
                                            retval=True,
                                            multiprocess=True)
                    try:
                        mem = mem[0]  # old MP always returned a list
                    except TypeError:  # 'float' object is not subscriptable
                        pass
                    return mem, out

                gallery_conf['call_memory'] = call_memory
                gallery_conf['memory_base'] = _get_memory_base(gallery_conf)
        else:
            gallery_conf['call_memory'] = gallery_conf['show_memory']
    if not gallery_conf['show_memory']:  # can be set to False above

        def call_memory(func):
            return 0., func()

        gallery_conf['call_memory'] = call_memory
    assert callable(gallery_conf['call_memory'])

    # deal with scrapers
    scrapers = gallery_conf['image_scrapers']
    if not isinstance(scrapers, (tuple, list)):
        scrapers = [scrapers]
    scrapers = list(scrapers)
    for si, scraper in enumerate(scrapers):
        if isinstance(scraper, str):
            if scraper in _scraper_dict:
                scraper = _scraper_dict[scraper]
            else:
                orig_scraper = scraper
                try:
                    scraper = import_module(scraper)
                    scraper = getattr(scraper, '_get_sg_image_scraper')
                    scraper = scraper()
                except Exception as exp:
                    raise ConfigError('Unknown image scraper %r, got:\n%s' %
                                      (orig_scraper, exp))
            scrapers[si] = scraper
        if not callable(scraper):
            raise ConfigError('Scraper %r was not callable' % (scraper, ))
    gallery_conf['image_scrapers'] = tuple(scrapers)
    del scrapers
    # Here we try to set up matplotlib but don't raise an error,
    # we will raise an error later when we actually try to use it
    # (if we do so) in scrapers.py.
    # In principle we could look to see if there is a matplotlib scraper
    # in our scrapers list, but this would be backward incompatible with
    # anyone using or relying on our Agg-setting behavior (e.g., for some
    # custom matplotlib SVG scraper as in our docs).
    # Eventually we can make this a config var like matplotlib_agg or something
    # if people need us not to set it to Agg.
    try:
        _import_matplotlib()
    except (ImportError, ValueError):
        pass

    # compress_images
    compress_images = gallery_conf['compress_images']
    if isinstance(compress_images, str):
        compress_images = [compress_images]
    elif not isinstance(compress_images, (tuple, list)):
        raise ConfigError('compress_images must be a tuple, list, or str, '
                          'got %s' % (type(compress_images), ))
    compress_images = list(compress_images)
    allowed_values = ('images', 'thumbnails')
    pops = list()
    for ki, kind in enumerate(compress_images):
        if kind not in allowed_values:
            if kind.startswith('-'):
                pops.append(ki)
                continue
            raise ConfigError('All entries in compress_images must be one of '
                              '%s or a command-line switch starting with "-", '
                              'got %r' % (allowed_values, kind))
    compress_images_args = [compress_images.pop(p) for p in pops[::-1]]
    if len(compress_images) and not _has_optipng():
        logger.warning(
            'optipng binaries not found, PNG %s will not be optimized' %
            (' and '.join(compress_images), ))
        compress_images = ()
    gallery_conf['compress_images'] = compress_images
    gallery_conf['compress_images_args'] = compress_images_args

    # deal with resetters
    resetters = gallery_conf['reset_modules']
    if not isinstance(resetters, (tuple, list)):
        resetters = [resetters]
    resetters = list(resetters)
    for ri, resetter in enumerate(resetters):
        if isinstance(resetter, str):
            if resetter not in _reset_dict:
                raise ConfigError('Unknown module resetter named %r' %
                                  (resetter, ))
            resetters[ri] = _reset_dict[resetter]
        elif not callable(resetter):
            raise ConfigError('Module resetter %r was not callable' %
                              (resetter, ))
    gallery_conf['reset_modules'] = tuple(resetters)

    lang = lang if lang in ('python', 'python3', 'default') else 'python'
    gallery_conf['lang'] = lang
    del resetters

    # Ensure the first cell text is a string if we have it
    first_cell = gallery_conf.get("first_notebook_cell")
    if (not isinstance(first_cell, str)) and (first_cell is not None):
        raise ConfigError("The 'first_notebook_cell' parameter must be type "
                          "str or None, found type %s" % type(first_cell))
    # Ensure the last cell text is a string if we have it
    last_cell = gallery_conf.get("last_notebook_cell")
    if (not isinstance(last_cell, str)) and (last_cell is not None):
        raise ConfigError("The 'last_notebook_cell' parameter must be type str"
                          " or None, found type %s" % type(last_cell))
    # Check pypandoc
    pypandoc = gallery_conf['pypandoc']
    if not isinstance(pypandoc, (dict, bool)):
        raise ConfigError("'pypandoc' parameter must be of type bool or dict,"
                          "got: %s." % type(pypandoc))
    gallery_conf['pypandoc'] = dict() if pypandoc is True else pypandoc
    has_pypandoc, version = _has_pypandoc()
    if isinstance(gallery_conf['pypandoc'], dict) and has_pypandoc is None:
        logger.warning("'pypandoc' not available. Using Sphinx-Gallery to "
                       "convert rst text blocks to markdown for .ipynb files.")
        gallery_conf['pypandoc'] = False
    elif isinstance(gallery_conf['pypandoc'], dict):
        logger.info("Using pandoc version: %s to convert rst text blocks to "
                    "markdown for .ipynb files" % (version, ))
    else:
        logger.info("Using Sphinx-Gallery to convert rst text blocks to "
                    "markdown for .ipynb files.")
    if isinstance(pypandoc, dict):
        accepted_keys = ('extra_args', 'filters')
        for key in pypandoc:
            if key not in accepted_keys:
                raise ConfigError("'pypandoc' only accepts the following key "
                                  "values: %s, got: %s." %
                                  (accepted_keys, key))

    # Make it easy to know which builder we're in
    gallery_conf['builder_name'] = builder_name
    gallery_conf['titles'] = {}
    # Ensure 'backreferences_dir' is str, pathlib.Path or None
    backref = gallery_conf['backreferences_dir']
    if (not isinstance(backref, (str, pathlib.Path))) and \
            (backref is not None):
        raise ConfigError("The 'backreferences_dir' parameter must be of type "
                          "str, pathlib.Path or None, "
                          "found type %s" % type(backref))
    # if 'backreferences_dir' is pathlib.Path, make str for Python <=3.5
    # compatibility
    if isinstance(backref, pathlib.Path):
        gallery_conf['backreferences_dir'] = str(backref)

    # binder
    gallery_conf['binder'] = check_binder_conf(gallery_conf['binder'])

    if not isinstance(gallery_conf['css'], (list, tuple)):
        raise ConfigError('gallery_conf["css"] must be list or tuple, got %r' %
                          (gallery_conf['css'], ))
    for css in gallery_conf['css']:
        if css not in _KNOWN_CSS:
            raise ConfigError('Unknown css %r, must be one of %r' %
                              (css, _KNOWN_CSS))
        if gallery_conf['app'] is not None:  # can be None in testing
            gallery_conf['app'].add_css_file(css + '.css')

    return gallery_conf
Пример #18
0
    def __init__(self,
                 srcdir,
                 confdir,
                 outdir,
                 doctreedir,
                 buildername,
                 confoverrides=None,
                 status=sys.stdout,
                 warning=sys.stderr,
                 freshenv=False,
                 warningiserror=False,
                 tags=None,
                 verbosity=0,
                 parallel=0):
        self.verbosity = verbosity
        self.next_listener_id = 0
        self._extensions = {}
        self._listeners = {}
        self.domains = BUILTIN_DOMAINS.copy()
        self.builderclasses = BUILTIN_BUILDERS.copy()
        self.builder = None
        self.env = None

        self.srcdir = srcdir
        self.confdir = confdir
        self.outdir = outdir
        self.doctreedir = doctreedir

        self.parallel = parallel

        if status is None:
            self._status = StringIO()
            self.quiet = True
        else:
            self._status = status
            self.quiet = False

        if warning is None:
            self._warning = StringIO()
        else:
            self._warning = warning
        self._warncount = 0
        self.warningiserror = warningiserror

        self._events = events.copy()

        # say hello to the world
        self.info(bold('Running Sphinx v%s' % sphinx.__version__))

        # status code for command-line application
        self.statuscode = 0

        # read config
        self.tags = Tags(tags)
        self.config = Config(confdir, CONFIG_FILENAME, confoverrides or {},
                             self.tags)
        self.config.check_unicode(self.warn)

        # set confdir to srcdir if -C given (!= no confdir); a few pieces
        # of code expect a confdir to be set
        if self.confdir is None:
            self.confdir = self.srcdir

        # backwards compatibility: activate old C markup
        self.setup_extension('sphinx.ext.oldcmarkup')
        # load all user-given extension modules
        for extension in self.config.extensions:
            self.setup_extension(extension)
        # the config file itself can be an extension
        if self.config.setup:
            # py31 doesn't have 'callable' function for bellow check
            if hasattr(self.config.setup, '__call__'):
                self.config.setup(self)
            else:
                raise ConfigError(
                    "'setup' that is specified in the conf.py has not been " +
                    "callable. Please provide a callable `setup` function " +
                    "in order to behave as a sphinx extension conf.py itself.")

        # now that we know all config values, collect them from conf.py
        self.config.init_values()

        # check the Sphinx version if requested
        if self.config.needs_sphinx and \
           self.config.needs_sphinx > sphinx.__version__[:3]:
            raise VersionRequirementError(
                'This project needs at least Sphinx v%s and therefore cannot '
                'be built with this version.' % self.config.needs_sphinx)

        # set up translation infrastructure
        self._init_i18n()
        # set up the build environment
        self._init_env(freshenv)
        # set up the builder
        self._init_builder(buildername)
Пример #19
0
# -*- coding: utf-8 -*-

import os
import sys
from sphinx.errors import ConfigError

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.

kodo_dir = os.path.join("..", "build_current")

if not os.path.exists(kodo_dir):
    raise ConfigError('\nUnable to find the path "{}"'.format(kodo_dir) +
                      "\nDid you successfully build to kodo-python library?")

sys.path.insert(0, os.path.abspath(kodo_dir))

# -- General configuration ----------------------------------------------------

# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
    "sphinx.ext.autodoc",
    "sphinx.ext.doctest",
    "sphinx.ext.intersphinx",
    "sphinx.ext.todo",
    "sphinx.ext.coverage",
    "sphinx.ext.mathjax",
    "sphinx.ext.ifconfig",
Пример #20
0
def post_process_automodule(app, what, name, obj, options, lines):
    """Insert a table listing and describing an executable script's command-line
    arguments into its ``:automodule:`` documentation.
    
    Any :term:`main-like function` decorated with the :func:`noargdoc` decorator
    will be skipped. A function is determined to be a :term:`main-like function`
    if its name matches the name set in the configuration option
    `argdoc_main_func` inside ``conf.py``. The default value for
    `argdoc_main_func` is `main`.
    
    
    Notes
    -----
    Per the `autodoc`_ spec, this function modifies `lines` in place.
    
    
    Parameters
    ----------
    app
        Sphinx application instance
    
    what : str
        Type of object (e.g. "module", "function", "class")
    
    name : str
        Fully-qualified name of object
    
    obj : object
        Object (e.g. module, class, function) to document
    
    options : object
        Options given to the directive, whose boolean properties are set to `True`
        if their corresponding flag was given in the directive

    lines : list
        List of strings encoding the module docstrings after `Sphinx`_ processing

    Raises
    ------
    :class:`~sphinx.errors.ConfigError`
       If `argdoc_main_func` is defined in ``conf.py`` and is not a `str`
    """
    funcname = app.config.argdoc_main_func
    prefix_chars = app.config.argdoc_prefix_chars
    patterns = get_patterns(prefix_chars)

    errmsg = ""
    if not isinstance(funcname, str):
        errmsg += "[argdoc] Incorrect type for `argdoc_main_func. Expected `str`, found, `%s` with value `%s`)\n" % (
            type(funcname), funcname)
    if len(prefix_chars) == 0:
        errmsg += "[argdoc] Expected at least one prefix character (e.g. '-'). Found empty string.\n"

    if len(errmsg) > 0:
        raise ConfigError(errmsg)

    if what == "module" and obj.__dict__.get(funcname, None) is not None:
        if obj.__dict__.get(funcname).__dict__.get("noargdoc", False) == False:
            app.debug2("[argdoc] Processing module '%s'" % obj.__name__)
            call = shlex.split(
                "%s -m %s --help".replace("-", prefix_chars[0]) %
                (sys.executable, name))
            try:
                out = subprocess.check_output(call, env=os.environ.copy())
                if sys.version_info[0] == 2:
                    out = unicode(out, "utf-8")
                elif sys.version_info[0] == 3:
                    out = out.decode("utf-8")
                help_lines = out.split("\n")
                out_lines = format_argparser_as_docstring(app,
                                                          obj,
                                                          help_lines,
                                                          section_head=True,
                                                          header_level=1,
                                                          patterns=patterns)
                out_lines += _SEPARATOR
                lines.extend(out_lines)
                lines.extend(_OTHER_HEADER_LINES)
            except subprocess.CalledProcessError as e:
                note = "Could not call module %s as '%s'. Output:\n" % (
                    name, " ".join(e.cmd))
                app.warn(format_warning(note, e.output))
            except IndexError as e:
                note = "Error processing argparser into docstring for module %s: \n%s" % (
                    name, e.message)
                details = "\n\n%s\n\n%s" % (e.args, e)
                app.warn(format_warning(note, details))

        if app.config.argdoc_save_rst == True:
            filename = os.path.join(app.outdir, "%s_postargdoc.rst" % name)
            with codecs.open(filename, encoding="utf-8", mode="wb") as fout:
                for n, line in enumerate(lines):
                    try:
                        line = safeunicode(line)
                        fout.write(line)
                        fout.write(safeunicode("\n"))
                    except Exception as e:
                        app.warn(
                            "[argdoc] Could not write out line %s of file %s."
                            % (n, name))

            fout.close()

        app.emit("argdoc-process-docstring", what, name, obj, options, lines)
Пример #21
0
# All configuration values have a default; values that are commented out
# serve to show the default.

import os
import sys
import sphinx
from distutils.version import LooseVersion
from sphinx.errors import ConfigError

# Make Sphinx fail cleanly if using an old Python, rather than obscurely
# failing because some code in one of our extensions doesn't work there.
# In newer versions of Sphinx this will display nicely; in older versions
# Sphinx will also produce a Python backtrace but at least the information
# gets printed...
if sys.version_info < (3,6):
    raise ConfigError(
        "QEMU requires a Sphinx that uses Python 3.6 or better\n")

# The per-manual conf.py will set qemu_docdir for a single-manual build;
# otherwise set it here if this is an entire-manual-set build.
# This is always the absolute path of the docs/ directory in the source tree.
try:
    qemu_docdir
except NameError:
    qemu_docdir = os.path.abspath(".")

# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use an absolute path starting from qemu_docdir.
#
# Our extensions are in docs/sphinx; the qapidoc extension requires
# the QAPI modules from scripts/.
Пример #22
0
    "image_scrapers": image_scrapers,
    "default_thumb_file": "_static/code-example-icon.png",
    "thumbnail_size": (350, 350),
    # Support for binder
    # 'binder': {'org': 'sphinx-gallery',
    # 'repo': 'sphinx-gallery.github.io',
    # 'branch': 'master',
    # 'binderhub_url': 'https://mybinder.org',
    # 'dependencies': './binder/requirements.txt',
    # 'notebooks_dir': 'notebooks',
    # 'use_jupyter_lab': True,
    # },
}

if len(examples_dirs) != len(gallery_dirs):
    raise ConfigError(
        "examples_dirs and gallery_dirs aren't of the same length")

# Sphinx gallery makes specific assumptions about the structure of example gallery.
# The main one is the the gallery's entrypoint is a README.rst file and the rest
# of the files are *.py files that are auto-converted to .rst files. This makes
# sure that the only rst files in the example directories are README.rst
hide_download_page_ids = []


def hide_example_page(file_handler):
    """Heuristic that determines whether example file contains python code."""
    example_content = file_handler.read().strip()

    no_percent_comments = True
    no_imports = True