diff options
author | Norbert Preining <norbert@preining.info> | 2020-07-25 03:02:45 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2020-07-25 03:02:45 +0000 |
commit | a731c2c045c14b88099c4576c874b8df2990f2ea (patch) | |
tree | 52b977278f2646adc4c59309ffd5f0765f1da49c /support/spix | |
parent | 48c502455fb3c2b9214390662ee706a10dea0818 (diff) |
CTAN sync 202007250302
Diffstat (limited to 'support/spix')
-rw-r--r-- | support/spix/CHANGELOG.md | 21 | ||||
-rw-r--r-- | support/spix/spix.1 | 48 | ||||
-rw-r--r-- | support/spix/spix.pdf | bin | 1521970 -> 1528250 bytes | |||
-rwxr-xr-x | support/spix/spix.py | 145 |
4 files changed, 211 insertions, 3 deletions
diff --git a/support/spix/CHANGELOG.md b/support/spix/CHANGELOG.md index dea80d9162..59fbcaeb63 100644 --- a/support/spix/CHANGELOG.md +++ b/support/spix/CHANGELOG.md @@ -1,18 +1,33 @@ +* spix 1.1.0 (2020-07-23) + + * Python 3.9 support. + * Replace `pathlib.PosixPath` with `pathlib.Path`. + * Merge `__init__.py` and `__main__.py` into a single `spix.py` file. + * Add a man page. + + -- Louis Paternault <spalax@gresille.org> + +* spix 1.0.1 (2020-06-23) + + * Do not crash when processing non-UTF8 files (closes #1). + + -- Louis Paternault <spalax@gresille.org> + * spix 1.0.0 (2020-06-19) * [doc] Minor documentation improvements. - -- Louis Paternault <spalax+python@gresille.org> + -- Louis Paternault <spalax@gresille.org> * spix 1.0.0-beta2 (2020-06-12) * [command line] Use FILE instead of TEX in description of command line arguments. * Code snippet can use environment variables `$basename`, `$texname`. - -- Louis Paternault <spalax+python@gresille.org> + -- Louis Paternault <spalax@gresille.org> * spix 1.0.0-beta (2020-06-11) * First published version. - -- Louis Paternault <spalax+python@gresille.org> + -- Louis Paternault <spalax@gresille.org> diff --git a/support/spix/spix.1 b/support/spix/spix.1 new file mode 100644 index 0000000000..e2ade137ec --- /dev/null +++ b/support/spix/spix.1 @@ -0,0 +1,48 @@ +.TH SPIX 1 +.SH NAME +spix \- Compile a .tex file, executing commands that are set inside the file itself. +.SH SYNOPSIS +.B spix +[\fB\--version\fR] +[\fB\-h\fR] +[\fB\-n\fR] +.IR file +.SH DESCRIPTION +.B spix +parses a .tex file to find lines starting with \fI%$\fR (before the preambule). +Those lines are shell commands that are executed by \fBspix\fR. +.PP +Commands are executed as-is, excepted that: +.RS +.PP +- command are run from the directory of the file given in argument; +.PP +- shell variables \fI$texname\fR and \fI$basename\fR are set to the name of the tex file (respectively with and without the .tex extension). +.RE +.SH OPTIONS +.TP +.BR \-n ", " \-\-dry\-run +Print the commands that would be executed, but do not execute them. +.TP +.BR \-h ", " \-\-help +Print help, and exit. +.TP +.BR \-\-version +Print version, and exit. +.SH EXAMPLES +Let \fIfoo.tex\fR be the following file. +.PP +.nf +.RS +% Compile this file twice with lualatex. +%$ lualatex foo.tex +%$ lualatex foo.tex + +\\documentclass{article} +\\begin{document} +Hello, world! +\\end{document} +.RE +.fi +.PP +When running \fBspix\fR on this file, it is compiled twice using \fBlualatex(1)\fR (as written in lines 2 and 3 of the file). diff --git a/support/spix/spix.pdf b/support/spix/spix.pdf Binary files differindex 89ad286c90..699bacd9cc 100644 --- a/support/spix/spix.pdf +++ b/support/spix/spix.pdf diff --git a/support/spix/spix.py b/support/spix/spix.py new file mode 100755 index 0000000000..1369703a7a --- /dev/null +++ b/support/spix/spix.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 + +# Copyright 2020 Louis Paternault +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <https://www.gnu.org/licenses/>. + +"""Compile a `.tex` file, executing commands that are set inside the file itself.""" + +import argparse +import logging +import os +import pathlib +import re +import subprocess +import sys + +NAME = "SpiX" +VERSION = "1.1.0" + +RE_EMPTY = re.compile("^ *$") +RE_COMMENT = re.compile("^ *%") +RE_COMMAND = re.compile(r"^%\$ ?(.*)$") + + +class SpixError(Exception): + """Exception that should be catched and nicely displayed to user.""" + + +def parse_lines(lines): + """Parse line to find code snippets. + + :param iterable lines: Lines to parte (typically ``open("foo.tex").readlines()``. + :return: Iterator over snippets (as strings). + """ + snippet = None + for line in lines: + line = line.rstrip("\n") + if RE_COMMAND.match(line): + match = RE_COMMAND.match(line) + if snippet is None: + snippet = "" + else: + snippet += "\n" + snippet += match.groups()[0] + elif RE_EMPTY.match(line) or RE_COMMENT.match(line): + if snippet is not None: + yield snippet + snippet = None + else: + break + if snippet is not None: + yield snippet + + +def compiletex(filename, *, dryrun=False): + """Read commands from file, and execute them. + + :param str filename: File to process. + :param bool dryrun: If ``True``, print commands to run, but do not execute them. + """ + env = os.environ + filename = pathlib.Path(filename) + env["texname"] = filename.name + env["basename"] = filename.stem + + try: + with open(filename, errors="ignore") as file: + for snippet in parse_lines(file.readlines()): + print(snippet) + if dryrun: + continue + + subprocess.check_call( + ["sh", "-c", snippet, NAME, filename.name], + cwd=(pathlib.Path.cwd() / filename).parent, + env=env, + ) + except subprocess.CalledProcessError: + raise SpixError() + except IsADirectoryError as error: + raise SpixError(str(error)) + + +def commandline_parser(): + """Return a command line parser. + + :rtype: argparse.ArgumentParser + """ + parser = argparse.ArgumentParser( + prog="spix", + description=( + "Compile a `.tex` file, " + "executing commands that are set inside the file itself." + ), + ) + parser.add_argument( + "-n", + "--dry-run", + action="store_true", + help="Print the commands that would be executed, but do not execute them.", + ) + parser.add_argument( + "--version", + help="Show version and exit.", + action="version", + version=f"{NAME} {VERSION}", + ) + parser.add_argument("FILE", nargs=1, help="File to process.") + + return parser + + +def main(): + """Main function.""" + arguments = commandline_parser().parse_args() + + if os.path.exists(arguments.FILE[0]): + arguments.FILE = arguments.FILE[0] + elif os.path.exists(f"{arguments.FILE[0]}.tex"): + arguments.FILE = f"{arguments.FILE[0]}.tex" + else: + logging.error("""File not found: "%s".""", arguments.FILE[0]) + sys.exit(1) + + try: + compiletex(arguments.FILE, dryrun=arguments.dry_run) + except SpixError as error: + if str(error): + logging.error(error) + sys.exit(1) + + +if __name__ == "__main__": + main() |