Break tools out into their own dirs
This commit is contained in:
parent
b92d95b80b
commit
9211668d9e
15 changed files with 59 additions and 66 deletions
|
@ -32,35 +32,6 @@ toolchain(
|
|||
toolchain_type = "@bazel_tools//tools/python:toolchain_type",
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "autoflake",
|
||||
main = "autoflake_shim.py",
|
||||
deps = [
|
||||
py_requirement("autoflake"),
|
||||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "isort",
|
||||
main = "isort_shim.py",
|
||||
deps = [
|
||||
py_requirement("isort"),
|
||||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "sphinx",
|
||||
main = "sphinx_shim.py",
|
||||
deps = [
|
||||
py_requirement("click"),
|
||||
py_requirement("recommonmark"),
|
||||
py_requirement("sphinx"),
|
||||
py_requirement("sphinxcontrib-openapi"),
|
||||
py_requirement("sphinxcontrib-programoutput"),
|
||||
py_requirement("livereload"),
|
||||
]
|
||||
)
|
||||
|
||||
py_pytest(
|
||||
name = "test_licenses",
|
||||
srcs = [
|
||||
|
@ -75,14 +46,6 @@ py_pytest(
|
|||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "unify",
|
||||
main = "unify_shim.py",
|
||||
deps = [
|
||||
py_requirement("unify"),
|
||||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "openapi",
|
||||
main = "openapi_shim.py",
|
||||
|
@ -90,32 +53,3 @@ py_binary(
|
|||
py_requirement("openapi-spec-validator"),
|
||||
]
|
||||
)
|
||||
|
||||
# WARNING: YAMLLINT is GLP3'd code. Do not extend, modify or depend on this as a lib.
|
||||
py_binary(
|
||||
name = "yamllint",
|
||||
main = "yamllint_shim.py",
|
||||
deps = [
|
||||
py_requirement("yamllint"),
|
||||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "templater",
|
||||
main = "templater.py",
|
||||
deps = [
|
||||
py_requirement("click"),
|
||||
py_requirement("jinja2"),
|
||||
py_requirement("PyYAML"),
|
||||
]
|
||||
)
|
||||
|
||||
py_binary(
|
||||
name = "xfmt",
|
||||
main = "xfmt.py",
|
||||
deps = [
|
||||
py_requirement("beautifulsoup4"),
|
||||
py_requirement("click"),
|
||||
py_requirement("lxml"),
|
||||
]
|
||||
)
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Shim for executing autoflake.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from autoflake import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv[0] = re.sub(r"(-script\.pyw|\.exe)?$", "", sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Shim for executing isort.
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from isort.main import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv[0] = re.sub(r"(-script\.pyw?|\.exe)?$", "", sys.argv[0])
|
||||
sys.exit(main())
|
|
@ -1,218 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""A documentation generator.
|
||||
|
||||
This is a shim tool which wraps up a whole bunch of Sphinx internals in a single "convenient"
|
||||
entrypoint. Former tweeps may recognize some parallels to the `docbird` tool developed by Twitter's
|
||||
techdocs team.
|
||||
|
||||
"""
|
||||
|
||||
import builtins
|
||||
from functools import wraps
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
import click
|
||||
import livereload
|
||||
from sphinx.application import Sphinx
|
||||
from sphinx.cmd.build import main as build
|
||||
from sphinx.cmd.quickstart import main as new
|
||||
from sphinx.ext.apidoc import main as apidoc
|
||||
from sphinx.ext.autosummary.generate import main as autosummary
|
||||
from sphinx.util.docutils import docutils_namespace, patch_docutils
|
||||
|
||||
|
||||
@click.group()
|
||||
def cli():
|
||||
"""A documentation generator.
|
||||
|
||||
Just a shim to a variety of upstream Sphinx commands typically distributed as separate binaries
|
||||
for some dang reason.
|
||||
|
||||
Note that due to subcommand argument parsing '-- --help' is likely required.
|
||||
|
||||
Subcommands have not been renamed (or customized, yet) from their Sphinx equivalents.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@cli.group()
|
||||
def generate():
|
||||
"""Subcommands for doing RST header generation."""
|
||||
|
||||
|
||||
@generate.command(
|
||||
"apidoc",
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
),
|
||||
)
|
||||
@click.argument("argv", nargs=-1, type=click.UNPROCESSED)
|
||||
def do_apidoc(argv):
|
||||
"""Use sphinx.ext.apidoc to generate API documentation."""
|
||||
return apidoc(argv)
|
||||
|
||||
|
||||
@generate.command(
|
||||
"summary",
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
),
|
||||
)
|
||||
@click.argument("argv", nargs=-1, type=click.UNPROCESSED)
|
||||
def do_summary(argv):
|
||||
"""Use sphinx.ext.autosummary to generate module summaries."""
|
||||
return autosummary(argv)
|
||||
|
||||
|
||||
@cli.command(
|
||||
"new",
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
),
|
||||
)
|
||||
@click.argument("argv", nargs=-1, type=click.UNPROCESSED)
|
||||
def do_new(argv):
|
||||
"""Create a new Sphinx in the current directory."""
|
||||
return new(argv)
|
||||
|
||||
|
||||
@cli.command(
|
||||
"build",
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
),
|
||||
)
|
||||
@click.argument("sourcedir")
|
||||
@click.argument("outputdir")
|
||||
@click.option("-c", "--confdir")
|
||||
@click.option("-d", "--doctreedir")
|
||||
@click.option("-b", "--builder", default="html")
|
||||
@click.option("--freshenv/--no-freshenv", default=False)
|
||||
@click.option("-W", "--warning-is-error", "werror", is_flag=True, flag_value=True)
|
||||
@click.option("-t", "--tag", "tags", multiple=True)
|
||||
def do_build(
|
||||
sourcedir, outputdir, confdir, doctreedir, builder, freshenv, werror, tags
|
||||
):
|
||||
"""Build a single Sphinx project."""
|
||||
|
||||
if not confdir:
|
||||
confdir = sourcedir
|
||||
|
||||
if not doctreedir:
|
||||
doctreedir = os.path.join(outputdir, ".doctrees")
|
||||
|
||||
status = sys.stdout
|
||||
warning = sys.stderr
|
||||
error = sys.stderr
|
||||
|
||||
confdir = confdir or sourcedir
|
||||
confoverrides = {} # FIXME: support these
|
||||
with patch_docutils(confdir), docutils_namespace():
|
||||
app = Sphinx(
|
||||
sourcedir,
|
||||
confdir,
|
||||
outputdir,
|
||||
doctreedir,
|
||||
builder,
|
||||
confoverrides,
|
||||
status,
|
||||
warning,
|
||||
freshenv,
|
||||
werror,
|
||||
tags,
|
||||
1,
|
||||
4,
|
||||
False,
|
||||
)
|
||||
app.build(True, [])
|
||||
return app.statuscode
|
||||
|
||||
|
||||
@cli.command(
|
||||
"serve",
|
||||
context_settings=dict(
|
||||
ignore_unknown_options=True,
|
||||
),
|
||||
)
|
||||
@click.option("-h", "--host", default="localhost")
|
||||
@click.option("-p", "--port", type=int, default=8080)
|
||||
@click.argument("sourcedir")
|
||||
@click.argument("outputdir")
|
||||
def do_serve(host, port, sourcedir, outputdir):
|
||||
"""Build and then serve a Sphinx tree."""
|
||||
|
||||
sourcedir = os.path.realpath(sourcedir)
|
||||
outputdir = os.path.realpath(outputdir)
|
||||
|
||||
server = livereload.Server()
|
||||
|
||||
# HACK (arrdem 2020-10-31):
|
||||
# Okay. This is an elder hack, and I'm proud of it.
|
||||
#
|
||||
# The naive implementation of the watching server is to watch the input files, which is
|
||||
# obviously correct. However, Sphinx has a BUNCH of operators like include and mdinclude and
|
||||
# soforth which can cause a Sphinx doctree to have file dependencies OUTSIDE of the "trivial"
|
||||
# source path dependency set.
|
||||
#
|
||||
# In order to make sure that rebuilding does what the user intends, we trace calls to the
|
||||
# open() function and attempt to dynamically discover the dependency set of the site. This
|
||||
# allows us to trigger strictly correct rebuilds unlike other Sphinx implementations which
|
||||
# need to be restarted under some circumstances.
|
||||
def opener(old_open):
|
||||
@wraps(old_open)
|
||||
def tracking_open(path, mode="r", *args, **kw):
|
||||
file = old_open(path, mode, *args, **kw)
|
||||
if isinstance(path, int):
|
||||
# If you're doing something weird with file pointers, ignore it.
|
||||
pass
|
||||
else:
|
||||
path = os.path.realpath(path)
|
||||
|
||||
if "w" in mode:
|
||||
# If we're writing a file, it's an output for sure. Ignore it.
|
||||
ignorelist.add(path)
|
||||
elif (
|
||||
not path.startswith(outputdir)
|
||||
and path not in ignorelist
|
||||
and not path in watchlist
|
||||
):
|
||||
# Watch any source file (file we open for reading)
|
||||
server.watch(path, build)
|
||||
watchlist.add(path)
|
||||
return file
|
||||
|
||||
return tracking_open
|
||||
|
||||
ignorelist = set()
|
||||
watchlist = set()
|
||||
|
||||
def build():
|
||||
try:
|
||||
old_open = open
|
||||
builtins.open = opener(old_open)
|
||||
io.open = opener(old_open)
|
||||
do_build([sourcedir, outputdir])
|
||||
except SystemExit:
|
||||
pass
|
||||
finally:
|
||||
builtins.open = old_open
|
||||
io.open = old_open
|
||||
|
||||
build()
|
||||
server.watch(
|
||||
"conf.py", build
|
||||
) # Not sure why this isn't picked up, but it doesn't seem to be.
|
||||
|
||||
server.serve(port=port, host=host, root=outputdir)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Hack in a -- delimeter to bypass click arg parsing
|
||||
if not (sys.argv + [""])[1].startswith("-"):
|
||||
sys.argv = [sys.argv[0], "--"] + sys.argv[1:]
|
||||
|
||||
# Use click subcommands for everything else
|
||||
exit(cli())
|
|
@ -1,76 +0,0 @@
|
|||
"""A tiny template(s) tool.
|
||||
|
||||
Processes Jekyll/Hyde/Hugo/... style 'fontmatter' headers, applying Jinja2/Liquid templating from an
|
||||
optional templates and includes directory.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
import re
|
||||
|
||||
import click
|
||||
import jinja2
|
||||
import yaml
|
||||
|
||||
|
||||
FONTMATTER_PATTERN = re.compile(
|
||||
r"^(---\n\r?(?P<fontmatter>.*?)\n\r?---\n\r?)?(?P<content>.+)$", re.DOTALL
|
||||
)
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.option("-i", "--include", "include_dir", multiple=True)
|
||||
@click.option("-t", "--template", "template_dir", multiple=True)
|
||||
@click.option("-c", "--config", "config_file")
|
||||
@click.argument("infile")
|
||||
@click.argument("outfile")
|
||||
def main(include_dir, template_dir, config_file, infile, outfile):
|
||||
"""Apply templating.
|
||||
|
||||
Consume infile, processing it with templating and write the results to outfile.
|
||||
|
||||
"""
|
||||
|
||||
loaders = []
|
||||
for d in include_dir:
|
||||
loaders.append(jinja2.FileSystemLoader(os.path.realpath(d)))
|
||||
|
||||
for d in template_dir:
|
||||
loaders.append(jinja2.FileSystemLoader(os.path.realpath(d)))
|
||||
|
||||
# Build a j2 environment using the potentially various loaders..
|
||||
environment = jinja2.Environment(loader=jinja2.ChoiceLoader(loaders))
|
||||
|
||||
# Load a site config
|
||||
if config_file:
|
||||
with open(config_file) as f:
|
||||
site = yaml.safe_load(f.read())
|
||||
else:
|
||||
site = {}
|
||||
|
||||
# Figure out doing the fontmatter nonsense...
|
||||
with open(infile, "r") as f:
|
||||
buff = f.read()
|
||||
|
||||
match = re.match(FONTMATTER_PATTERN, buff)
|
||||
if fontmatter := match.group("fontmatter"):
|
||||
fontmatter = yaml.safe_load(fontmatter)
|
||||
else:
|
||||
fontmatter = {}
|
||||
|
||||
# Render the file contents
|
||||
template = environment.from_string(match.group("content"))
|
||||
content = template.render(site=site, page=fontmatter)
|
||||
|
||||
# If there's a configured `layout:` stick the content in the layout.
|
||||
if "layout" in fontmatter:
|
||||
template = environment.get_template(fontmatter.get("layout"))
|
||||
content = template.render(content=content, site=site, page=fontmatter)
|
||||
|
||||
# And dump the results
|
||||
with open(outfile, "w") as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,12 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
Shim for executing isort.
|
||||
"""
|
||||
|
||||
|
||||
from unify import main
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
exit(main())
|
|
@ -1,25 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
"""
|
||||
A quick and dirty XML formatter.
|
||||
"""
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
import click
|
||||
|
||||
|
||||
@click.command()
|
||||
@click.argument("filename")
|
||||
def main(filename):
|
||||
with open(filename) as f:
|
||||
bs = BeautifulSoup(f, "xml")
|
||||
|
||||
with open(filename, "w") as of:
|
||||
of.write(bs.prettify())
|
||||
of.write("\n")
|
||||
|
||||
print(f"Formatted {filename}!")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
|
@ -1,15 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# WARNING: Yamllint is GPL3'd code.
|
||||
|
||||
"""A shim for executing yamllint."""
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from yamllint.cli import run
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.argv[0] = re.sub(r"(-script\.pyw?|\.exe)?$", "", sys.argv[0])
|
||||
sys.exit(run())
|
Loading…
Add table
Add a link
Reference in a new issue