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
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,6 @@ TSWLatexianTemp*


.idea
log
**/_autosummary
*.pdf
/tex
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,5 @@
- `in2lambda spec run SPEC` runs a YAML file of selectors over the frozen source: it says which blocks are questions, parts and solutions, which to ignore, what to strip off the front of each one, and which of the four filters lays the solutions out. It fills in the draft's fields with the markdown of the lines each was taken from, records the spec's name and hash in the log so a replay runs the same file, and reports every block it made nothing of. Running an edited spec over a draft it has already filled in is refused, as freezing a document that has changed is: `in2lambda source add --start-over` begins the draft again. Reading a spec needs pyyaml, which the `convert` extra now installs alongside panflute. See [the spec page](https://lambda-feedback.github.io/in2lambda/spec.html) for the selectors and layouts.
- `in2lambda validate` checks a draft over as a whole and writes what it finds into it as a `report`: source blocks in no field and not marked ignore, two fields taken from the same lines, gaps in the numbering of the questions or their parts, parts nothing answers, and fields holding nothing. Each finding names the field and the lines it is about, so it can be acted on without reading the draft. Finding something is not a failure and the command still exits 0; the report is replaced by the next run of the checks and dropped by the next command that changes the draft, since it describes the draft as it stood.
- `in2lambda build` writes the draft in this directory out as a Lambda Feedback set: one question per `qN.text` field, holding the parts written for it and the worked solutions, with the images those fields refer to under `media/`, as `in2lambda convert` writes a set - a field naming an image that is not beside the draft is refused saying which file is missing, since the checks read the draft and not the folder it is in, and a question's own solution written beside a solution for every part it has becomes a part of its own holding just that solution, as `convert` pairs them up. It is refused unless `in2lambda validate` has been run since the draft last changed - every command that changes one drops its report - and found nothing, and the refusal prints what the report says so it can be acted on without opening the draft. `in2lambda render` writes each question as a PDF instead, compiled as Lambda Feedback's own PDF generator compiles it, which needs pandoc and xelatex; it is gated on nothing, since looking at a draft is how what the checks found gets fixed. Both take `-o/--out`, as `convert` does.
- Importing `in2lambda.katex_convert` no longer writes a file called `log` into the working directory. What it has to say about a converted expression goes to the `in2lambda.katex_convert` logger, which is silent unless the application configures logging.
- The Python API is unchanged: `in2lambda.main.runner` and everything under `in2lambda.api` take the same arguments and return the same objects.
22 changes: 4 additions & 18 deletions in2lambda/katex_convert/katex_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,10 @@
import re
from pathlib import Path

# Create a logger object with a name and a level
logger = logging.getLogger("log")
logger.setLevel(logging.INFO)

# Create a file handler to write the messages to a file
file_handler = logging.FileHandler("log", mode="w") # Clears log with every run
file_handler.setLevel(logging.INFO)

# Create a formatter to format the messages
formatter = logging.Formatter("%(message)s")

# Add the formatter to the file handler
file_handler.setFormatter(formatter)

# Add the file handler to the logger
logger.addHandler(file_handler)
# No handler: one attached here wrote a file called `log` into whatever directory the
# importing process happened to be run from. Where these messages go is the
# application's to decide, by configuring the `in2lambda` logger or this child of it.
logger = logging.getLogger("in2lambda.katex_convert")


def latex_to_katex(latex_string: str) -> str:
Expand Down Expand Up @@ -123,8 +111,6 @@ def replace_functions(latex_string: str) -> str:
Returns:
The same LaTeX string with some commands replaced where necessary.
"""
logger.info("")

# replace the incompatible functions with their KaTeX equivalents using re.sub
for old, new in unsupported_commands().items():
if new is None: # Deleted rather than replaced; see delete_functions.
Expand Down
Empty file added log
Empty file.
28 changes: 28 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,34 @@ def test_old_form_fails_when_run_as_the_installed_command(
assert not (tmp_path / "out").exists()


def test_convert_leaves_only_what_it_writes(filters_dir: str, tmp_path) -> None:
"""Converting in an empty directory leaves the input and the output and nothing else.

In a subprocess because importing the package is what used to leave a file behind:
within pytest it happens once, before any test can chdir somewhere of its own.
"""
shutil.copy(os.path.join(filters_dir, "PartsSepSol", "example.tex"), tmp_path)

result = subprocess.run(
[
sys.executable,
"-c",
"from in2lambda.main import cli; cli()",
"convert",
"example.tex",
"PartsSepSol",
"-o",
"out",
],
capture_output=True,
text=True,
cwd=tmp_path,
)

assert result.returncode == 0, result.stdout + result.stderr
assert {path.name for path in tmp_path.iterdir()} == {"example.tex", "out"}


def test_completing_the_old_form_offers_the_subcommand() -> None:
"""Completion resolves half-typed command lines, so the guard must not fire there.

Expand Down
Loading