Merge remote-tracking branch 'origin/pr/5290'

This commit is contained in:
Florian Bruhin 2020-05-27 17:14:19 +02:00
commit 75cc295e7d
1 changed files with 65 additions and 72 deletions

View File

@ -20,21 +20,24 @@
"""Generate the html documentation based on the asciidoc files."""
from typing import List, Optional
import re
import os
import os.path
import sys
import subprocess
import glob
import shutil
import tempfile
import argparse
import io
import pathlib
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
from scripts import utils
DOC_DIR = pathlib.Path("qutebrowser/html/doc")
class AsciiDoc:
@ -42,31 +45,32 @@ class AsciiDoc:
FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
def __init__(self, asciidoc, website):
self._cmd = None
def __init__(self,
asciidoc: Optional[List[str]],
website: Optional[str]) -> None:
self._cmd = None # type: Optional[List[str]]
self._asciidoc = asciidoc
self._website = website
self._homedir = None
self._themedir = None
self._tempdir = None
self._homedir = None # type: Optional[pathlib.Path]
self._themedir = None # type: Optional[pathlib.Path]
self._tempdir = None # type: Optional[pathlib.Path]
self._failed = False
def prepare(self):
def prepare(self) -> None:
"""Get the asciidoc command and create the homedir to use."""
self._cmd = self._get_asciidoc_cmd()
self._homedir = tempfile.mkdtemp()
self._themedir = os.path.join(
self._homedir, '.asciidoc', 'themes', 'qute')
self._tempdir = os.path.join(self._homedir, 'tmp')
os.makedirs(self._tempdir)
os.makedirs(self._themedir)
self._homedir = pathlib.Path(tempfile.mkdtemp())
self._themedir = self._homedir / '.asciidoc' / 'themes' / 'qute'
self._tempdir = self._homedir / 'tmp'
self._tempdir.mkdir(parents=True)
self._themedir.mkdir(parents=True)
def cleanup(self):
def cleanup(self) -> None:
"""Clean up the temporary home directory for asciidoc."""
if self._homedir is not None and not self._failed:
shutil.rmtree(self._homedir)
def build(self):
def build(self) -> None:
"""Build either the website or the docs."""
if self._website:
self._build_website()
@ -74,14 +78,12 @@ class AsciiDoc:
self._build_docs()
self._copy_images()
def _build_docs(self):
def _build_docs(self) -> None:
"""Render .asciidoc files to .html sites."""
files = [('doc/{}.asciidoc'.format(f),
'qutebrowser/html/doc/{}.html'.format(f))
for f in self.FILES]
for src in glob.glob('doc/help/*.asciidoc'):
name, _ext = os.path.splitext(os.path.basename(src))
dst = 'qutebrowser/html/doc/{}.html'.format(name)
files = [(pathlib.Path('doc/{}.asciidoc'.format(f)),
DOC_DIR / (f + ".html")) for f in self.FILES]
for src in pathlib.Path('doc/help/').glob('*.asciidoc'):
dst = DOC_DIR / (src.stem + ".html")
files.append((src, dst))
# patch image links to use local copy
@ -94,8 +96,8 @@ class AsciiDoc:
asciidoc_args = ['-a', 'source-highlighter=pygments']
for src, dst in files:
src_basename = os.path.basename(src)
modified_src = os.path.join(self._tempdir, src_basename)
assert self._tempdir is not None # for mypy
modified_src = self._tempdir / src.name
with open(modified_src, 'w', encoding='utf-8') as modified_f, \
open(src, 'r', encoding='utf-8') as f:
for line in f:
@ -104,34 +106,26 @@ class AsciiDoc:
modified_f.write(line)
self.call(modified_src, dst, *asciidoc_args)
def _copy_images(self):
def _copy_images(self) -> None:
"""Copy image files to qutebrowser/html/doc."""
print("Copying files...")
dst_path = os.path.join('qutebrowser', 'html', 'doc', 'img')
try:
os.mkdir(dst_path)
except FileExistsError:
pass
dst_path = DOC_DIR / 'img'
dst_path.mkdir(exist_ok=True)
for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
src = os.path.join('doc', 'img', filename)
dst = os.path.join(dst_path, filename)
src = pathlib.Path('doc') / 'img' / filename
dst = dst_path / filename
shutil.copy(src, dst)
def _build_website_file(self, root, filename):
def _build_website_file(self, root: pathlib.Path, filename: str) -> None:
"""Build a single website file."""
src = os.path.join(root, filename)
src_basename = os.path.basename(src)
parts = [self._website[0]]
dirname = os.path.dirname(src)
if dirname:
parts.append(os.path.relpath(os.path.dirname(src)))
parts.append(
os.extsep.join((os.path.splitext(src_basename)[0],
'html')))
dst = os.path.join(*parts)
os.makedirs(os.path.dirname(dst), exist_ok=True)
src = root / filename
assert self._website is not None # for mypy
dst = pathlib.Path(self._website)
dst = dst / src.parent.relative_to('.') / (src.stem + ".html")
dst.parent.mkdir(exist_ok=True)
modified_src = os.path.join(self._tempdir, src_basename)
assert self._tempdir is not None # for mypy
modified_src = self._tempdir / src.name
shutil.copy('www/header.asciidoc', modified_src)
outfp = io.StringIO()
@ -187,25 +181,24 @@ class AsciiDoc:
'-a', 'source-highlighter=pygments']
self.call(modified_src, dst, *asciidoc_args)
def _build_website(self):
def _build_website(self) -> None:
"""Prepare and build the website."""
theme_file = os.path.abspath(os.path.join('www', 'qute.css'))
theme_file = (pathlib.Path('www') / 'qute.css').resolve()
assert self._themedir is not None # for mypy
shutil.copy(theme_file, self._themedir)
outdir = self._website[0]
assert self._website is not None # for mypy
outdir = pathlib.Path(self._website)
for root, _dirs, files in os.walk(os.getcwd()):
for filename in files:
basename, ext = os.path.splitext(filename)
if (ext != '.asciidoc' or
basename in ['header', 'OpenSans-License']):
continue
self._build_website_file(root, filename)
for item_path in pathlib.Path().rglob('*.asciidoc'):
if item_path.stem in ['header', 'OpenSans-License']:
continue
self._build_website_file(item_path.parent, item_path.name)
copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}
for src, dest in copy.items():
full_dest = os.path.join(outdir, dest)
full_dest = outdir / dest
try:
shutil.rmtree(full_dest)
except FileNotFoundError:
@ -214,13 +207,15 @@ class AsciiDoc:
for dst, link_name in [
('README.html', 'index.html'),
(os.path.join('doc', 'quickstart.html'), 'quickstart.html')]:
((pathlib.Path('doc') / 'quickstart.html'),
'quickstart.html')]:
assert isinstance(dst, (str, pathlib.Path)) # for mypy
try:
os.symlink(dst, os.path.join(outdir, link_name))
(outdir / link_name).symlink_to(dst)
except FileExistsError:
pass
def _get_asciidoc_cmd(self):
def _get_asciidoc_cmd(self) -> List[str]:
"""Try to find out what commandline to use to invoke asciidoc."""
if self._asciidoc is not None:
return self._asciidoc
@ -243,7 +238,7 @@ class AsciiDoc:
raise FileNotFoundError
def call(self, src, dst, *args):
def call(self, src: pathlib.Path, dst: pathlib.Path, *args):
"""Call asciidoc for the given files.
Args:
@ -251,15 +246,16 @@ class AsciiDoc:
dst: The destination .html file, or None to auto-guess.
*args: Additional arguments passed to asciidoc.
"""
print("Calling asciidoc for {}...".format(os.path.basename(src)))
print("Calling asciidoc for {}...".format(src.name))
assert self._cmd is not None # for mypy
cmdline = self._cmd[:]
if dst is not None:
cmdline += ['--out-file', dst]
cmdline += ['--out-file', str(dst)]
cmdline += args
cmdline.append(src)
cmdline.append(str(src))
try:
env = os.environ.copy()
env['HOME'] = self._homedir
env['HOME'] = str(self._homedir)
subprocess.run(cmdline, check=True, env=env)
except (subprocess.CalledProcessError, OSError) as e:
self._failed = True
@ -269,11 +265,11 @@ class AsciiDoc:
sys.exit(1)
def parse_args():
def parse_args() -> argparse.Namespace:
"""Parse command-line arguments."""
parser = argparse.ArgumentParser()
parser.add_argument('--website', help="Build website into a given "
"directory.", nargs=1)
"directory.")
parser.add_argument('--asciidoc', help="Full path to python and "
"asciidoc.py. If not given, it's searched in PATH.",
nargs=2, required=False,
@ -281,12 +277,9 @@ def parse_args():
return parser.parse_args()
def run(**kwargs):
def run(**kwargs) -> None:
"""Regenerate documentation."""
try:
os.mkdir('qutebrowser/html/doc')
except FileExistsError:
pass
DOC_DIR.mkdir(exist_ok=True)
asciidoc = AsciiDoc(**kwargs)
try:
@ -303,7 +296,7 @@ def run(**kwargs):
asciidoc.cleanup()
def main(colors=False):
def main(colors: bool = False) -> None:
"""Generate html files for the online documentation."""
utils.change_cwd()
utils.use_color = colors