123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- # Copyright 2011 OpenStack Foundation
- # Copyright 2012-2013 Hewlett-Packard Development Company, L.P.
- # All Rights Reserved.
- #
- # Licensed under the Apache License, Version 2.0 (the "License"); you may
- # not use this file except in compliance with the License. You may obtain
- # a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
- # License for the specific language governing permissions and limitations
- # under the License.
- from distutils import log
- import fnmatch
- import os
- import sys
- try:
- import cStringIO
- except ImportError:
- import io as cStringIO
- try:
- import sphinx
- # NOTE(dhellmann): Newer versions of Sphinx have moved the apidoc
- # module into sphinx.ext and the API is slightly different (the
- # function expects sys.argv[1:] instead of sys.argv[:]. So, figure
- # out where we can import it from and set a flag so we can invoke
- # it properly. See this change in sphinx for details:
- # https://github.com/sphinx-doc/sphinx/commit/87630c8ae8bff8c0e23187676e6343d8903003a6
- try:
- from sphinx.ext import apidoc
- apidoc_use_padding = False
- except ImportError:
- from sphinx import apidoc
- apidoc_use_padding = True
- from sphinx import application
- from sphinx import setup_command
- except Exception as e:
- # NOTE(dhellmann): During the installation of docutils, setuptools
- # tries to import pbr code to find the egg_info.writer hooks. That
- # imports this module, which imports sphinx, which imports
- # docutils, which is being installed. Because docutils uses 2to3
- # to convert its code during installation under python 3, the
- # import fails, but it fails with an error other than ImportError
- # (today it's a NameError on StandardError, an exception base
- # class). Convert the exception type here so it can be caught in
- # packaging.py where we try to determine if we can import and use
- # sphinx by importing this module. See bug #1403510 for details.
- raise ImportError(str(e))
- from pbr import git
- from pbr import options
- from pbr import version
- _rst_template = """%(heading)s
- %(underline)s
- .. automodule:: %(module)s
- :members:
- :undoc-members:
- :show-inheritance:
- """
- def _find_modules(arg, dirname, files):
- for filename in files:
- if filename.endswith('.py') and filename != '__init__.py':
- arg["%s.%s" % (dirname.replace('/', '.'),
- filename[:-3])] = True
- class LocalBuildDoc(setup_command.BuildDoc):
- builders = ['html']
- command_name = 'build_sphinx'
- sphinx_initialized = False
- def _get_source_dir(self):
- option_dict = self.distribution.get_option_dict('build_sphinx')
- pbr_option_dict = self.distribution.get_option_dict('pbr')
- _, api_doc_dir = pbr_option_dict.get('api_doc_dir', (None, 'api'))
- if 'source_dir' in option_dict:
- source_dir = os.path.join(option_dict['source_dir'][1],
- api_doc_dir)
- else:
- source_dir = 'doc/source/' + api_doc_dir
- if not os.path.exists(source_dir):
- os.makedirs(source_dir)
- return source_dir
- def generate_autoindex(self, excluded_modules=None):
- log.info("[pbr] Autodocumenting from %s"
- % os.path.abspath(os.curdir))
- modules = {}
- source_dir = self._get_source_dir()
- for pkg in self.distribution.packages:
- if '.' not in pkg:
- for dirpath, dirnames, files in os.walk(pkg):
- _find_modules(modules, dirpath, files)
- def include(module):
- return not any(fnmatch.fnmatch(module, pat)
- for pat in excluded_modules)
- module_list = sorted(mod for mod in modules.keys() if include(mod))
- autoindex_filename = os.path.join(source_dir, 'autoindex.rst')
- with open(autoindex_filename, 'w') as autoindex:
- autoindex.write(""".. toctree::
- :maxdepth: 1
- """)
- for module in module_list:
- output_filename = os.path.join(source_dir,
- "%s.rst" % module)
- heading = "The :mod:`%s` Module" % module
- underline = "=" * len(heading)
- values = dict(module=module, heading=heading,
- underline=underline)
- log.info("[pbr] Generating %s"
- % output_filename)
- with open(output_filename, 'w') as output_file:
- output_file.write(_rst_template % values)
- autoindex.write(" %s.rst\n" % module)
- def _sphinx_tree(self):
- source_dir = self._get_source_dir()
- cmd = ['-H', 'Modules', '-o', source_dir, '.']
- if apidoc_use_padding:
- cmd.insert(0, 'apidoc')
- apidoc.main(cmd + self.autodoc_tree_excludes)
- def _sphinx_run(self):
- if not self.verbose:
- status_stream = cStringIO.StringIO()
- else:
- status_stream = sys.stdout
- confoverrides = {}
- if self.project:
- confoverrides['project'] = self.project
- if self.version:
- confoverrides['version'] = self.version
- if self.release:
- confoverrides['release'] = self.release
- if self.today:
- confoverrides['today'] = self.today
- if self.sphinx_initialized:
- confoverrides['suppress_warnings'] = [
- 'app.add_directive', 'app.add_role',
- 'app.add_generic_role', 'app.add_node',
- 'image.nonlocal_uri',
- ]
- app = application.Sphinx(
- self.source_dir, self.config_dir,
- self.builder_target_dir, self.doctree_dir,
- self.builder, confoverrides, status_stream,
- freshenv=self.fresh_env, warningiserror=self.warning_is_error)
- self.sphinx_initialized = True
- try:
- app.build(force_all=self.all_files)
- except Exception as err:
- from docutils import utils
- if isinstance(err, utils.SystemMessage):
- sys.stder.write('reST markup error:\n')
- sys.stderr.write(err.args[0].encode('ascii',
- 'backslashreplace'))
- sys.stderr.write('\n')
- else:
- raise
- if self.link_index:
- src = app.config.master_doc + app.builder.out_suffix
- dst = app.builder.get_outfilename('index')
- os.symlink(src, dst)
- def run(self):
- option_dict = self.distribution.get_option_dict('pbr')
- if git._git_is_installed():
- git.write_git_changelog(option_dict=option_dict)
- git.generate_authors(option_dict=option_dict)
- tree_index = options.get_boolean_option(option_dict,
- 'autodoc_tree_index_modules',
- 'AUTODOC_TREE_INDEX_MODULES')
- auto_index = options.get_boolean_option(option_dict,
- 'autodoc_index_modules',
- 'AUTODOC_INDEX_MODULES')
- if not os.getenv('SPHINX_DEBUG'):
- # NOTE(afazekas): These options can be used together,
- # but they do a very similar thing in a different way
- if tree_index:
- self._sphinx_tree()
- if auto_index:
- self.generate_autoindex(
- set(option_dict.get(
- "autodoc_exclude_modules",
- [None, ""])[1].split()))
- self.finalize_options()
- is_multibuilder_sphinx = version.SemanticVersion.from_pip_string(
- sphinx.__version__) >= version.SemanticVersion(1, 6)
- # TODO(stephenfin): Remove support for Sphinx < 1.6 in 4.0
- if not is_multibuilder_sphinx:
- log.warn('[pbr] Support for Sphinx < 1.6 will be dropped in '
- 'pbr 4.0. Upgrade to Sphinx 1.6+')
- # TODO(stephenfin): Remove this at the next MAJOR version bump
- if self.builders != ['html']:
- log.warn("[pbr] Sphinx 1.6 added native support for "
- "specifying multiple builders in the "
- "'[sphinx_build] builder' configuration option, "
- "found in 'setup.cfg'. As a result, the "
- "'[sphinx_build] builders' option has been "
- "deprecated and will be removed in pbr 4.0. Migrate "
- "to the 'builder' configuration option.")
- if is_multibuilder_sphinx:
- self.builder = self.builders
- if is_multibuilder_sphinx:
- # Sphinx >= 1.6
- return setup_command.BuildDoc.run(self)
- # Sphinx < 1.6
- for builder in self.builder:
- self.builder = builder
- self.finalize_options()
- self._sphinx_run()
- def initialize_options(self):
- # Not a new style class, super keyword does not work.
- setup_command.BuildDoc.initialize_options(self)
- # NOTE(dstanek): exclude setup.py from the autodoc tree index
- # builds because all projects will have an issue with it
- self.autodoc_tree_excludes = ['setup.py']
- def finalize_options(self):
- from pbr import util
- # Not a new style class, super keyword does not work.
- setup_command.BuildDoc.finalize_options(self)
- # Handle builder option from command line - override cfg
- option_dict = self.distribution.get_option_dict('build_sphinx')
- if 'command line' in option_dict.get('builder', [[]])[0]:
- self.builders = option_dict['builder'][1]
- # Allow builders to be configurable - as a comma separated list.
- if not isinstance(self.builders, list) and self.builders:
- self.builders = self.builders.split(',')
- self.project = self.distribution.get_name()
- self.version = self.distribution.get_version()
- self.release = self.distribution.get_version()
- # NOTE(dstanek): check for autodoc tree exclusion overrides
- # in the setup.cfg
- opt = 'autodoc_tree_excludes'
- option_dict = self.distribution.get_option_dict('pbr')
- if opt in option_dict:
- self.autodoc_tree_excludes = util.split_multiline(
- option_dict[opt][1])
- # handle Sphinx < 1.5.0
- if not hasattr(self, 'warning_is_error'):
- self.warning_is_error = False
|