Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# The fixture is CRLF because that is the thing it is for; nothing may rewrite it.
tests/fixtures/sources/crlf/source.md -text
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
- Converting a document is now `in2lambda convert FILE FILTER`, with the same options as before (`-o/--out`, `-a/--answers`). Scripts and Docker invocations that run `in2lambda FILE FILTER` need the extra word.
- `in2lambda FILE FILTER` exits with an error naming the command to run instead, rather than printing its usage and exiting successfully.
- beartype is now `^0.22`. At 0.20.0 and below its import hook leaves `cli` a plain function rather than a group, so the new command line either fails to import or runs `convert` whatever the arguments; 0.20.1 is the first version that works.
- `in2lambda source add FILE` freezes a document: it converts .docx and .tex to markdown beside the file, and writes a `draft.json` holding the markdown's hash and every block in it with the lines it spans, so that another tool can quote the source by line range. `in2lambda source show` prints that markdown numbered with the block ids. Freezing a file that has changed since is refused unless `--start-over` says to discard the draft, and so is showing one, since its block ids would name lines they are not the ids of. Both need pandoc and the `convert` extra, as `convert` does.
- The Python API is unchanged: `in2lambda.main.runner` and everything under `in2lambda.api` take the same arguments and return the same objects.
109 changes: 42 additions & 67 deletions in2lambda/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,32 +7,24 @@
# sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

import importlib
import importlib.util
import shlex
import shutil
import subprocess
from typing import Optional

import rich_click as click

import in2lambda.filters
import in2lambda.source
from in2lambda.api.set import Set


class ConversionToolsMissing(RuntimeError):
"""Document conversion was asked for without pandoc or panflute installed."""


def _require_conversion_tools() -> None:
missing = []
if shutil.which("pandoc") is None:
missing.append("pandoc (see https://pandoc.org/installing.html)")
if importlib.util.find_spec("panflute") is None:
missing.append("panflute (pip install 'in2lambda[convert]')")
if missing:
raise ConversionToolsMissing(
f"Converting documents needs {' and '.join(missing)}."
)
# Both were defined here before there was an in2lambda.source, and are in other
# people's scripts as in2lambda.main names.
from in2lambda.source import (
ConversionToolsMissing,
SourceError,
_pandoc,
_require_conversion_tools,
file_type,
)


def docx_to_md(docx_file: str) -> str:
Expand All @@ -44,53 +36,7 @@ def docx_to_md(docx_file: str) -> str:
Returns:
the contents of the .docx file in markdown formatting
"""
md_output = subprocess.check_output(["pandoc", docx_file, "-t", "markdown"])
return md_output.decode("utf-8")


def file_type(file: str) -> str:
"""Determines which pandoc file format to use for a given file.

See https://github.com/jgm/pandoc/blob/bad922a69236e22b20d51c4ec0b90c5a6c038433/src/Text/Pandoc/Format.hs#L171
(or any newer commit) for pandoc's supported file extensions.

Args:
file: A file path with the file extension included.

Returns:
An option in `pandoc --list-input-formats` that matches the given file type

Examples:
>>> from in2lambda.main import file_type
>>> file_type("example.tex")
'latex'
>>> file_type("/some/random/path/demo.md")
'markdown'
>>> file_type("no_extension")
Traceback (most recent call last):
RuntimeError: Unsupported file extension: .no_extension
>>> file_type("demo.unknown_extension")
Traceback (most recent call last):
RuntimeError: Unsupported file extension: .unknown_extension
"""
match (extension := file.split(".")[-1].lower()):
case "tex" | "latex" | "ltx":
return "latex"
case (
"md"
| "rmd"
| "markdown"
| "mdown"
| "mdwn"
| "mkd"
| "mkdn"
| "text"
| "txt"
):
return "markdown"
case "docx":
return "docx" # Pandoc doesn't seem to support .doc, and panflute doesn't like .docx.
raise RuntimeError(f"Unsupported file extension: .{extension}")
return _pandoc(docx_file, "markdown").decode("utf-8")


def runner(
Expand Down Expand Up @@ -154,9 +100,7 @@ def runner(

# If separate answer TeX file provided, parse that as well.
if answer_file:

if file_type(answer_file) == "docx":

answer_text = docx_to_md(answer_file)
answer_format = "markdown"
else:
Expand Down Expand Up @@ -251,5 +195,36 @@ def convert(
raise click.ClickException(str(error)) from None


@cli.group("source")
def source_group() -> None:
"""Freezes a source document, so its text can be quoted by line range."""


@source_group.command("add")
@click.argument("file", type=click.Path(exists=True, dir_okay=False, resolve_path=True))
@click.option(
"--start-over",
is_flag=True,
help="Freeze FILE again, discarding the draft already there.",
)
def source_add(file: str, start_over: bool) -> None:
"""Converts FILE to markdown and records its blocks in draft.json beside it."""
try:
draft = in2lambda.source.add(file, start_over)
except SourceError as error:
# Exit with what to do about it rather than a traceback.
raise click.ClickException(str(error)) from None
click.echo(f"Wrote {draft}")


@source_group.command("show")
def source_show() -> None:
"""Prints the frozen markdown of the draft in this directory, numbered."""
try:
click.echo(in2lambda.source.show())
except SourceError as error:
raise click.ClickException(str(error)) from None


if __name__ == "__main__":
cli()
Loading
Loading