All patches and comments are welcome. Please squash your changes to logical
commits before using git-format-patch and git-send-email to
patches@git.madduck.net.
If you'd read over the Git project's submission guidelines and adhered to them,
I'd be especially grateful.
1 # -*- coding: utf-8 -*-
3 # Configuration file for the Sphinx documentation builder.
5 # This file does only contain a selection of the most common options. For a
6 # full list see the documentation:
7 # http://www.sphinx-doc.org/en/stable/config
9 # -- Path setup --------------------------------------------------------------
11 # If extensions (or modules to document with autodoc) are in another directory,
12 # add these directories to sys.path here. If the directory is relative to the
13 # documentation root, use os.path.abspath to make it absolute, like shown here.
15 from pathlib import Path
18 from typing import Callable, List, Optional, Pattern, Tuple, Set
19 from dataclasses import dataclass
23 from pkg_resources import get_distribution
24 from recommonmark.parser import CommonMarkParser
26 logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO)
28 LOG = logging.getLogger(__name__)
30 # Get a relative path so logs printing out SRC isn't too long.
31 CURRENT_DIR = Path(__file__).parent.relative_to(os.getcwd())
32 README = CURRENT_DIR / ".." / "README.md"
33 REFERENCE_DIR = CURRENT_DIR / "reference"
34 STATIC_DIR = CURRENT_DIR / "_static"
39 """Tracks which part of a file to get a section's content.
42 start_line: The line where the section starts (i.e. its sub-header) (inclusive).
43 end_line: The line where the section ends (usually next sub-header) (exclusive).
52 """Tracks information about a section of documentation.
55 name: The section's name. This will used to detect duplicate sections.
56 src: The filepath to get its contents.
57 processors: The processors to run before writing the section to CURRENT_DIR.
58 out_filename: The filename to use when writing the section to CURRENT_DIR.
59 src_range: The line range of SRC to gets its contents.
64 src_range: SrcRange = SrcRange(0, 1_000_000)
65 out_filename: str = ""
66 processors: Tuple[Callable, ...] = ()
68 def get_out_filename(self) -> str:
69 if not self.out_filename:
70 return self.name + ".md"
72 return self.out_filename
75 def make_pypi_svg(version: str) -> None:
76 template: Path = CURRENT_DIR / "_static" / "pypi_template.svg"
77 target: Path = CURRENT_DIR / "_static" / "pypi.svg"
78 with open(str(template), "r", encoding="utf8") as f:
79 svg: str = string.Template(f.read()).substitute(version=version)
80 with open(str(target), "w", encoding="utf8") as f:
84 def make_filename(line: str) -> str:
85 non_letters: Pattern = re.compile(r"[^a-z]+")
86 filename: str = line[3:].rstrip().lower()
87 filename = non_letters.sub("_", filename)
88 if filename.startswith("_"):
89 filename = filename[1:]
90 if filename.endswith("_"):
91 filename = filename[:-1]
92 return filename + ".md"
95 def get_contents(section: DocSection) -> str:
96 """Gets the contents for the DocSection."""
97 contents: List[str] = []
98 src: Path = section.src
99 start_line: int = section.src_range.start_line
100 end_line: int = section.src_range.end_line
101 with open(src, "r", encoding="utf-8") as f:
102 for lineno, line in enumerate(f, start=1):
103 if lineno >= start_line and lineno < end_line:
104 contents.append(line)
105 return "".join(contents)
108 def get_sections_from_readme() -> List[DocSection]:
109 """Gets the sections from README so they can be processed by process_sections.
111 It opens README and goes down line by line looking for sub-header lines which
112 denotes a section. Once it finds a sub-header line, it will create a DocSection
113 object with all of the information currently available. Then on every line, it will
114 track the ending line index of the section. And it repeats this for every sub-header
117 sections: List[DocSection] = []
118 section: Optional[DocSection] = None
119 with open(README, "r", encoding="utf-8") as f:
120 for lineno, line in enumerate(f, start=1):
121 if line.startswith("## "):
122 filename = make_filename(line)
123 section_name = filename[:-3]
124 section = DocSection(
125 name=str(section_name),
127 src_range=SrcRange(lineno, lineno),
128 out_filename=filename,
129 processors=(fix_headers,),
131 sections.append(section)
132 if section is not None:
133 section.src_range.end_line += 1
137 def fix_headers(contents: str) -> str:
138 """Fixes the headers of sections copied from README.
140 Removes one octothorpe (#) from all headers since the contents are no longer nested
141 in a root document (i.e. the README).
143 lines: List[str] = contents.splitlines()
144 fixed_contents: List[str] = []
146 if line.startswith("##"):
148 fixed_contents.append(line + "\n") # splitlines strips the leading newlines
149 return "".join(fixed_contents)
152 def process_sections(
153 custom_sections: List[DocSection], readme_sections: List[DocSection]
155 """Reads, processes, and writes sections to CURRENT_DIR.
157 For each section, the contents will be fetched, processed by processors
158 required by the section, and written to CURRENT_DIR. If it encounters duplicate
159 sections (i.e. shares the same name attribute), it will skip processing the
162 It processes custom sections before the README generated sections so sections in the
163 README can be overwritten with custom options.
165 processed_sections: Set[str] = set()
166 modified_files: Set[Path] = set()
167 sections: List[DocSection] = custom_sections
168 sections.extend(readme_sections)
169 for section in sections:
170 LOG.info(f"Processing '{section.name}' from {section.src}")
171 if section.name in processed_sections:
173 f"Skipping '{section.name}' from '{section.src}' as it is a duplicate"
177 target_path: Path = CURRENT_DIR / section.get_out_filename()
178 if target_path in modified_files:
180 f"{target_path} has been already written to, its contents will be"
181 " OVERWRITTEN and notices will be duplicated"
183 contents: str = get_contents(section)
185 # processors goes here
186 if fix_headers in section.processors:
187 contents = fix_headers(contents)
189 with open(target_path, "w", encoding="utf-8") as f:
190 if section.src.suffix == ".md":
192 "[//]: # (NOTE: THIS FILE WAS AUTOGENERATED FROM"
193 f" {section.src})\n\n"
196 processed_sections.add(section.name)
197 modified_files.add(target_path)
200 # -- Project information -----------------------------------------------------
203 copyright = "2018, Łukasz Langa and contributors to Black"
204 author = "Łukasz Langa and contributors to Black"
206 # Autopopulate version
207 # The version, including alpha/beta/rc tags, but not commit hash and datestamps
208 release = get_distribution("black").version.split("+")[0]
209 # The short X.Y version.
212 version = version.split(sp)[0]
215 DocSection("the_black_code_style", CURRENT_DIR / "the_black_code_style.md",),
216 DocSection("pragmatism", CURRENT_DIR / "the_black_code_style.md",),
217 DocSection("editor_integration", CURRENT_DIR / "editor_integration.md"),
218 DocSection("blackd", CURRENT_DIR / "blackd.md"),
219 DocSection("black_primer", CURRENT_DIR / "black_primer.md"),
220 DocSection("contributing_to_black", CURRENT_DIR / ".." / "CONTRIBUTING.md"),
221 DocSection("change_log", CURRENT_DIR / ".." / "CHANGES.md"),
225 make_pypi_svg(release)
226 readme_sections = get_sections_from_readme()
227 process_sections(custom_sections, readme_sections)
230 # -- General configuration ---------------------------------------------------
232 # If your documentation needs a minimal Sphinx version, state it here.
234 # needs_sphinx = '1.0'
236 # Add any Sphinx extension module names here, as strings. They can be
237 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
239 extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.napoleon"]
241 # Add any paths that contain templates here, relative to this directory.
242 templates_path = ["_templates"]
244 source_parsers = {".md": CommonMarkParser}
246 # The suffix(es) of source filenames.
247 # You can specify multiple suffix as a list of string:
248 source_suffix = [".rst", ".md"]
250 # The master toctree document.
253 # The language for content autogenerated by Sphinx. Refer to documentation
254 # for a list of supported languages.
256 # This is also used if you do content translation via gettext catalogs.
257 # Usually you set "language" from the command line for these cases.
260 # List of patterns, relative to source directory, that match files and
261 # directories to ignore when looking for source files.
262 # This pattern also affects html_static_path and html_extra_path .
264 exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
266 # The name of the Pygments (syntax highlighting) style to use.
267 pygments_style = "sphinx"
270 # -- Options for HTML output -------------------------------------------------
272 # The theme to use for HTML and HTML Help pages. See the documentation for
273 # a list of builtin themes.
275 html_theme = "alabaster"
287 html_theme_options = {
288 "show_related": False,
289 "description": "“Any color you like.”",
290 "github_button": True,
291 "github_user": "psf",
292 "github_repo": "black",
293 "github_type": "star",
294 "show_powered_by": True,
295 "fixed_sidebar": True,
297 "travis_button": True,
301 # Add any paths that contain custom static files (such as style sheets) here,
302 # relative to this directory. They are copied after the builtin static files,
303 # so a file named "default.css" will overwrite the builtin "default.css".
304 html_static_path = ["_static"]
306 # Custom sidebar templates, must be a dictionary that maps document names
309 # The default sidebars (for documents that don't match any pattern) are
310 # defined by theme itself. Builtin themes are using these templates by
311 # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
312 # 'searchbox.html']``.
317 # -- Options for HTMLHelp output ---------------------------------------------
319 # Output file base name for HTML help builder.
320 htmlhelp_basename = "blackdoc"
323 # -- Options for LaTeX output ------------------------------------------------
326 # The paper size ('letterpaper' or 'a4paper').
328 # 'papersize': 'letterpaper',
329 # The font size ('10pt', '11pt' or '12pt').
331 # 'pointsize': '10pt',
332 # Additional stuff for the LaTeX preamble.
335 # Latex figure (float) alignment
337 # 'figure_align': 'htbp',
340 # Grouping the document tree into LaTeX files. List of tuples
341 # (source start file, target name, title,
342 # author, documentclass [howto, manual, or own class]).
347 "Documentation for Black",
348 "Łukasz Langa and contributors to Black",
354 # -- Options for manual page output ------------------------------------------
356 # One entry per manual page. List of tuples
357 # (source start file, name, description, authors, manual section).
358 man_pages = [(master_doc, "black", "Documentation for Black", [author], 1)]
361 # -- Options for Texinfo output ----------------------------------------------
363 # Grouping the document tree into Texinfo files. List of tuples
364 # (source start file, target name, title, author,
365 # dir menu entry, description, category)
366 texinfo_documents = [
370 "Documentation for Black",
373 "The uncompromising Python code formatter",
379 # -- Options for Epub output -------------------------------------------------
381 # Bibliographic Dublin Core info.
384 epub_publisher = author
385 epub_copyright = copyright
387 # The unique identifier of the text. This can be a ISBN number
388 # or the project homepage.
390 # epub_identifier = ''
392 # A unique identification for the text.
396 # A list of files that should not be packed into the epub file.
397 epub_exclude_files = ["search.html"]
400 # -- Extension configuration -------------------------------------------------
402 autodoc_member_order = "bysource"
404 # -- Options for intersphinx extension ---------------------------------------
406 # Example configuration for intersphinx: refer to the Python standard library.
407 intersphinx_mapping = {"https://docs.python.org/3/": None}