You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
106 lines
3.5 KiB
106 lines
3.5 KiB
"""
|
|
sphinx.project
|
|
~~~~~~~~~~~~~~
|
|
|
|
Utility function and classes for Sphinx projects.
|
|
|
|
:copyright: Copyright 2007-2020 by the Sphinx team, see AUTHORS.
|
|
:license: BSD, see LICENSE for details.
|
|
"""
|
|
|
|
import os
|
|
from glob import glob
|
|
|
|
from sphinx.locale import __
|
|
from sphinx.util import get_matching_files, logging, path_stabilize
|
|
from sphinx.util.matching import compile_matchers
|
|
from sphinx.util.osutil import SEP, relpath
|
|
|
|
if False:
|
|
# For type annotation
|
|
from typing import Dict, List, Set # NOQA
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
EXCLUDE_PATHS = ['**/_sources', '.#*', '**/.#*', '*.lproj/**']
|
|
|
|
|
|
class Project:
|
|
"""A project is source code set of Sphinx document."""
|
|
|
|
def __init__(self, srcdir, source_suffix):
|
|
# type: (str, Dict[str, str]) -> None
|
|
#: Source directory.
|
|
self.srcdir = srcdir
|
|
|
|
#: source_suffix. Same as :confval:`source_suffix`.
|
|
self.source_suffix = source_suffix
|
|
|
|
#: The name of documents belongs to this project.
|
|
self.docnames = set() # type: Set[str]
|
|
|
|
def restore(self, other):
|
|
# type: (Project) -> None
|
|
"""Take over a result of last build."""
|
|
self.docnames = other.docnames
|
|
|
|
def discover(self, exclude_paths=[]):
|
|
# type: (List[str]) -> Set[str]
|
|
"""Find all document files in the source directory and put them in
|
|
:attr:`docnames`.
|
|
"""
|
|
self.docnames = set()
|
|
excludes = compile_matchers(exclude_paths + EXCLUDE_PATHS)
|
|
for filename in get_matching_files(self.srcdir, excludes): # type: ignore
|
|
docname = self.path2doc(filename)
|
|
if docname:
|
|
if docname in self.docnames:
|
|
pattern = os.path.join(self.srcdir, docname) + '.*'
|
|
files = [relpath(f, self.srcdir) for f in glob(pattern)]
|
|
logger.warning(__('multiple files found for the document "%s": %r\n'
|
|
'Use %r for the build.'),
|
|
docname, files, self.doc2path(docname), once=True)
|
|
elif os.access(os.path.join(self.srcdir, filename), os.R_OK):
|
|
self.docnames.add(docname)
|
|
else:
|
|
logger.warning(__("document not readable. Ignored."), location=docname)
|
|
|
|
return self.docnames
|
|
|
|
def path2doc(self, filename):
|
|
# type: (str) -> str
|
|
"""Return the docname for the filename if the file is document.
|
|
|
|
*filename* should be absolute or relative to the source directory.
|
|
"""
|
|
if filename.startswith(self.srcdir):
|
|
filename = relpath(filename, self.srcdir)
|
|
for suffix in self.source_suffix:
|
|
if filename.endswith(suffix):
|
|
filename = path_stabilize(filename)
|
|
return filename[:-len(suffix)]
|
|
|
|
# the file does not have docname
|
|
return None
|
|
|
|
def doc2path(self, docname, basedir=True):
|
|
# type: (str, bool) -> str
|
|
"""Return the filename for the document name.
|
|
|
|
If *basedir* is True, return as an absolute path.
|
|
Else, return as a relative path to the source directory.
|
|
"""
|
|
docname = docname.replace(SEP, os.path.sep)
|
|
basename = os.path.join(self.srcdir, docname)
|
|
for suffix in self.source_suffix:
|
|
if os.path.isfile(basename + suffix):
|
|
break
|
|
else:
|
|
# document does not exist
|
|
suffix = list(self.source_suffix)[0]
|
|
|
|
if basedir:
|
|
return basename + suffix
|
|
else:
|
|
return docname + suffix
|