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])
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)
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,) )
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
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()
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)
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)
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
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', [])
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, ))
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', [])
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 {})
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()
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()
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()
# # 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"))
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
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)
# -*- 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",
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)
# 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/.
"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