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
22 from pkg_resources import get_distribution
24 logging.basicConfig(format="%(levelname)s: %(message)s", level=logging.INFO)
26 LOG = logging.getLogger(__name__)
28 CURRENT_DIR = Path(__file__).parent
29 README = CURRENT_DIR / ".." / "README.md"
30 REFERENCE_DIR = CURRENT_DIR / "reference"
31 STATIC_DIR = CURRENT_DIR / "_static"
36 """Tracks which part of a file to get a section's content.
39 start_line: The line where the section starts (i.e. its sub-header) (inclusive).
40 end_line: The line where the section ends (usually next sub-header) (exclusive).
49 """Tracks information about a section of documentation.
52 name: The section's name. This will used to detect duplicate sections.
53 src: The filepath to get its contents.
54 processors: The processors to run before writing the section to CURRENT_DIR.
55 out_filename: The filename to use when writing the section to CURRENT_DIR.
56 src_range: The line range of SRC to gets its contents.
61 src_range: SrcRange = SrcRange(0, 1_000_000)
62 out_filename: str = ""
63 processors: Tuple[Callable, ...] = ()
65 def get_out_filename(self) -> str:
66 if not self.out_filename:
67 return self.name + ".md"
69 return self.out_filename
72 def make_pypi_svg(version: str) -> None:
73 template: Path = CURRENT_DIR / "_static" / "pypi_template.svg"
74 target: Path = CURRENT_DIR / "_static" / "pypi.svg"
75 with open(str(template), "r", encoding="utf8") as f:
76 svg: str = string.Template(f.read()).substitute(version=version)
77 with open(str(target), "w", encoding="utf8") as f:
81 def make_filename(line: str) -> str:
82 non_letters: Pattern = re.compile(r"[^a-z]+")
83 filename: str = line[3:].rstrip().lower()
84 filename = non_letters.sub("_", filename)
85 if filename.startswith("_"):
86 filename = filename[1:]
87 if filename.endswith("_"):
88 filename = filename[:-1]
89 return filename + ".md"
92 def get_contents(section: DocSection) -> str:
93 """Gets the contents for the DocSection."""
94 contents: List[str] = []
95 src: Path = section.src
96 start_line: int = section.src_range.start_line
97 end_line: int = section.src_range.end_line
98 with open(src, "r", encoding="utf-8") as f:
99 for lineno, line in enumerate(f, start=1):
100 if lineno >= start_line and lineno < end_line:
101 contents.append(line)
102 return "".join(contents)
105 def get_sections_from_readme() -> List[DocSection]:
106 """Gets the sections from README so they can be processed by process_sections.
108 It opens README and goes down line by line looking for sub-header lines which
109 denotes a section. Once it finds a sub-header line, it will create a DocSection
110 object with all of the information currently available. Then on every line, it will
111 track the ending line index of the section. And it repeats this for every sub-header
114 sections: List[DocSection] = []
115 section: Optional[DocSection] = None
116 with open(README, "r", encoding="utf-8") as f:
117 for lineno, line in enumerate(f, start=1):
118 if line.startswith("## "):
119 filename = make_filename(line)
120 section_name = filename[:-3]
121 section = DocSection(
122 name=str(section_name),
124 src_range=SrcRange(lineno, lineno),
125 out_filename=filename,
126 processors=(fix_headers,),
128 sections.append(section)
129 if section is not None:
130 section.src_range.end_line += 1
134 def fix_headers(contents: str) -> str:
135 """Fixes the headers of sections copied from README.
137 Removes one octothorpe (#) from all headers since the contents are no longer nested
138 in a root document (i.e. the README).
140 lines: List[str] = contents.splitlines()
141 fixed_contents: List[str] = []
143 if line.startswith("##"):
145 fixed_contents.append(line + "\n") # splitlines strips the leading newlines
146 return "".join(fixed_contents)
149 def process_sections(
150 custom_sections: List[DocSection], readme_sections: List[DocSection]
152 """Reads, processes, and writes sections to CURRENT_DIR.
154 For each section, the contents will be fetched, processed by processors
155 required by the section, and written to CURRENT_DIR. If it encounters duplicate
156 sections (i.e. shares the same name attribute), it will skip processing the
159 It processes custom sections before the README generated sections so sections in the
160 README can be overwritten with custom options.
162 processed_sections: Set[str] = set()
163 modified_files: Set[Path] = set()
164 sections: List[DocSection] = custom_sections
165 sections.extend(readme_sections)
166 for section in sections:
167 LOG.info(f"Processing '{section.name}' from {section.src}")
168 if section.name in processed_sections:
170 f"Skipping '{section.name}' from '{section.src}' as it is a duplicate"
174 target_path: Path = CURRENT_DIR / section.get_out_filename()
175 if target_path in modified_files:
177 f"{target_path} has been already written to, its contents will be"
178 " OVERWRITTEN and notices will be duplicated"
180 contents: str = get_contents(section)
182 # processors goes here
183 if fix_headers in section.processors:
184 contents = fix_headers(contents)
186 with open(target_path, "w", encoding="utf-8") as f:
187 if section.src.suffix == ".md":
189 "[//]: # (NOTE: THIS FILE WAS AUTOGENERATED FROM"
190 f" {section.src})\n\n"
193 processed_sections.add(section.name)
194 modified_files.add(target_path)
197 # -- Project information -----------------------------------------------------
200 copyright = "2020, Łukasz Langa and contributors to Black"
201 author = "Łukasz Langa and contributors to Black"
203 # Autopopulate version
204 # The version, including alpha/beta/rc tags, but not commit hash and datestamps
205 release = get_distribution("black").version.split("+")[0]
206 # The short X.Y version.
209 version = version.split(sp)[0]
212 DocSection("the_black_code_style", CURRENT_DIR / "the_black_code_style.md",),
213 DocSection("editor_integration", CURRENT_DIR / "editor_integration.md"),
214 DocSection("blackd", CURRENT_DIR / "blackd.md"),
215 DocSection("black_primer", CURRENT_DIR / "black_primer.md"),
216 DocSection("contributing_to_black", CURRENT_DIR / ".." / "CONTRIBUTING.md"),
217 DocSection("change_log", CURRENT_DIR / ".." / "CHANGES.md"),
220 # Sphinx complains when there is a source file that isn't referenced in any of the docs.
221 # Since some sections autogenerated from the README are unused warnings will appear.
223 # Sections must be listed to what their name is when passed through make_filename().
224 blocklisted_sections_from_readme = {
231 make_pypi_svg(release)
232 readme_sections = get_sections_from_readme()
234 x for x in readme_sections if x.name not in blocklisted_sections_from_readme
237 process_sections(custom_sections, readme_sections)
240 # -- General configuration ---------------------------------------------------
242 # If your documentation needs a minimal Sphinx version, state it here.
245 # Add any Sphinx extension module names here, as strings. They can be
246 # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
249 "sphinx.ext.autodoc",
250 "sphinx.ext.intersphinx",
251 "sphinx.ext.napoleon",
255 # If you need extensions of a certain version or higher, list them here.
256 needs_extensions = {"recommonmark": "0.5"}
258 # Add any paths that contain templates here, relative to this directory.
259 templates_path = ["_templates"]
261 # The suffix(es) of source filenames.
262 # You can specify multiple suffix as a list of string:
263 source_suffix = [".rst", ".md"]
265 # The master toctree document.
268 # The language for content autogenerated by Sphinx. Refer to documentation
269 # for a list of supported languages.
271 # This is also used if you do content translation via gettext catalogs.
272 # Usually you set "language" from the command line for these cases.
275 # List of patterns, relative to source directory, that match files and
276 # directories to ignore when looking for source files.
277 # This pattern also affects html_static_path and html_extra_path .
279 exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
281 # The name of the Pygments (syntax highlighting) style to use.
282 pygments_style = "sphinx"
285 # -- Options for HTML output -------------------------------------------------
287 # The theme to use for HTML and HTML Help pages. See the documentation for
288 # a list of builtin themes.
290 html_theme = "alabaster"
302 html_theme_options = {
303 "show_related": False,
304 "description": "“Any color you like.”",
305 "github_button": True,
306 "github_user": "psf",
307 "github_repo": "black",
308 "github_type": "star",
309 "show_powered_by": True,
310 "fixed_sidebar": True,
312 "travis_button": True,
316 # Add any paths that contain custom static files (such as style sheets) here,
317 # relative to this directory. They are copied after the builtin static files,
318 # so a file named "default.css" will overwrite the builtin "default.css".
319 html_static_path = ["_static"]
321 # Custom sidebar templates, must be a dictionary that maps document names
324 # The default sidebars (for documents that don't match any pattern) are
325 # defined by theme itself. Builtin themes are using these templates by
326 # default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
327 # 'searchbox.html']``.
332 # -- Options for HTMLHelp output ---------------------------------------------
334 # Output file base name for HTML help builder.
335 htmlhelp_basename = "blackdoc"
338 # -- Options for LaTeX output ------------------------------------------------
341 # The paper size ('letterpaper' or 'a4paper').
343 # 'papersize': 'letterpaper',
344 # The font size ('10pt', '11pt' or '12pt').
346 # 'pointsize': '10pt',
347 # Additional stuff for the LaTeX preamble.
350 # Latex figure (float) alignment
352 # 'figure_align': 'htbp',
355 # Grouping the document tree into LaTeX files. List of tuples
356 # (source start file, target name, title,
357 # author, documentclass [howto, manual, or own class]).
362 "Documentation for Black",
363 "Łukasz Langa and contributors to Black",
369 # -- Options for manual page output ------------------------------------------
371 # One entry per manual page. List of tuples
372 # (source start file, name, description, authors, manual section).
373 man_pages = [(master_doc, "black", "Documentation for Black", [author], 1)]
376 # -- Options for Texinfo output ----------------------------------------------
378 # Grouping the document tree into Texinfo files. List of tuples
379 # (source start file, target name, title, author,
380 # dir menu entry, description, category)
381 texinfo_documents = [
385 "Documentation for Black",
388 "The uncompromising Python code formatter",
394 # -- Options for Epub output -------------------------------------------------
396 # Bibliographic Dublin Core info.
399 epub_publisher = author
400 epub_copyright = copyright
402 # The unique identifier of the text. This can be a ISBN number
403 # or the project homepage.
405 # epub_identifier = ''
407 # A unique identification for the text.
411 # A list of files that should not be packed into the epub file.
412 epub_exclude_files = ["search.html"]
415 # -- Extension configuration -------------------------------------------------
417 autodoc_member_order = "bysource"
419 # -- Options for intersphinx extension ---------------------------------------
421 # Example configuration for intersphinx: refer to the Python standard library.
422 intersphinx_mapping = {"https://docs.python.org/3/": None}