Skip to content

Commit 013108a

Browse files
authored
Implement a dependency scanner for LaTeX documents and releases v0.0.9. (#12)
1 parent 7245f71 commit 013108a

File tree

13 files changed

+163
-45
lines changed

13 files changed

+163
-45
lines changed

.conda/meta.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ requirements:
2020

2121
run:
2222
- python >=3.6
23-
- pytask >=0.0.9
23+
- pytask >=0.0.11
24+
- latex-dependency-scanner
2425

2526
test:
2627
requires:

.github/workflows/continuous-integration-workflow.yml

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -66,23 +66,3 @@ jobs:
6666
if: runner.os == 'Linux' && matrix.python-version == '3.8'
6767
shell: bash -l {0}
6868
run: cat codecov.yml | curl --data-binary @- https://codecov.io/validate
69-
70-
71-
pre-commit:
72-
73-
name: Run pre-commit.
74-
runs-on: ubuntu-latest
75-
76-
steps:
77-
- uses: actions/checkout@v2
78-
79-
- name: Set up Python 3.8
80-
uses: actions/setup-python@v1
81-
with:
82-
python-version: 3.8
83-
84-
- name: Install dependencies
85-
run: pip install tox
86-
87-
- name: Run pre-commit
88-
run: tox -e pre-commit

CHANGES.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@ all releases are available on `Anaconda.org
77
<https://anaconda.org/pytask/pytask-latex>`_.
88

99

10-
0.0.9 - 2020-xx-xx
10+
0.0.9 - 2020-12-28
1111
------------------
1212

13+
- :gh:`12` integrates the latex-dependency-scanner to automatically detect dependencies
14+
of a LaTeX document and releases v0.0.9.
1315
- :gh:`13` fixes the CI.
1416

1517

README.rst

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ pytask-latex
2020

2121
pytask-latex allows you to compile LaTeX documents.
2222

23+
It also tries to infer the dependency of the LaTeX document such as included images,
24+
bibliography files and other .tex files automatically to compile LaTeX documents when it
25+
is possible.
26+
2327

2428
Installation
2529
------------
@@ -32,8 +36,9 @@ Install the plugin with
3236
$ conda install pytask-latex
3337
3438
You also need to have ``latexmk`` installed which determines the necessary number of
35-
compilation steps. To test whether it is installed, type the following on the command
36-
line
39+
compilation steps (`here <https://tex.stackexchange.com/a/249243/194826>`_ is an
40+
explanation for what latexmk achieves). To test whether it is installed, type the
41+
following on the command line
3742

3843
.. code-block:: console
3944
@@ -190,15 +195,33 @@ to include the latex decorator in the parametrization just like with
190195
Configuration
191196
-------------
192197

193-
If you want to change the names of the keys which identify the source file and the
194-
compiled document, change the following default configuration in your pytask
195-
configuration file.
198+
latex_source_key
199+
If you want to change the name of the key which identifies the source file, change
200+
the following default configuration in your pytask configuration file.
201+
202+
.. code-block:: ini
203+
204+
latex_source_key = source
205+
206+
latex_document_key
207+
If you want to change the name of the key which identifies the compiled document,
208+
change the following default configuration in your pytask configuration file.
209+
210+
.. code-block:: ini
211+
212+
latex_source_key = source
196213
197-
.. code-block:: ini
214+
infer_latex_dependencies
215+
pytask-latex tries to scan your LaTeX document for included files with the help of
216+
`latex-dependency-scanner <https://github.com/pytask-dev/latex-dependency-scanner>`_
217+
if the following configuration value is true which is also the default.
198218

199-
latex_source_key = source
200-
latex_document_key = document
219+
infer_latex_dependencies = true
201220

221+
Since the package is in its early development phase and LaTeX provides a myriad of
222+
ways to include files as well as providing shortcuts for paths (e.g.,
223+
``\graphicspath``), there are definitely some rough edges left. File an issue here
224+
or in the other project to make us aware of the problem.
202225

203226

204227
Changes

environment.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ channels:
33
- conda-forge
44
- pytask
55
dependencies:
6-
- python=3.7
6+
- python = 3.8
77
- pip
88

99
# Conda
@@ -12,8 +12,9 @@ dependencies:
1212
- conda-verify
1313

1414
# Package dependencies
15-
- pytask >= 0.0.9
15+
- pytask >= 0.0.11
1616
- pytask-parallel >= 0.0.4
17+
- latex-dependency-scanner
1718

1819
# Misc
1920
- black

setup.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[bumpversion]
2-
current_version = 0.0.8
2+
current_version = 0.0.9
33
parse = (?P<major>\d+)\.(?P<minor>\d+)(\.(?P<patch>\d+))(\-?((dev)?(?P<dev>\d+))?)
44
serialize =
55
{major}.{minor}.{patch}dev{dev}

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
setup(
55
name="pytask-latex",
6-
version="0.0.8",
6+
version="0.0.9",
77
packages=find_packages(where="src"),
88
package_dir={"": "src"},
99
entry_points={"pytask": ["pytask_latex = pytask_latex.plugin"]},

src/pytask_latex/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.0.8"
1+
__version__ = "0.0.9"

src/pytask_latex/collect.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111
from _pytask.config import hookimpl
1212
from _pytask.mark import get_specific_markers_from_task
1313
from _pytask.mark import has_marker
14+
from _pytask.mark import Mark
15+
from _pytask.nodes import _collect_nodes
1416
from _pytask.nodes import FilePathNode
1517
from _pytask.nodes import PythonFunctionTask
1618
from _pytask.parametrize import _copy_func
19+
from latex_dependency_scanner import scan
1720

1821

1922
DEFAULT_OPTIONS = ["--pdf", "--interaction=nonstopmode", "--synctex=1", "--cd"]
@@ -93,6 +96,9 @@ def pytask_collect_task_teardown(session, task):
9396

9497
task.function = latex_function
9598

99+
if session.config["infer_latex_dependencies"]:
100+
task = _add_latex_dependencies_retroactively(task, session)
101+
96102

97103
def _get_node_from_dictionary(obj, key, fallback=0):
98104
if isinstance(obj, Path):
@@ -102,6 +108,54 @@ def _get_node_from_dictionary(obj, key, fallback=0):
102108
return obj
103109

104110

111+
def _add_latex_dependencies_retroactively(task, session):
112+
"""Add dependencies from LaTeX document to task.
113+
114+
Unfortunately, the dependencies have to be added retroactively, after the task has
115+
been created, since one needs access to the LaTeX document which is validated after
116+
the task is created.
117+
118+
1. Collect all possible files from the LaTeX document.
119+
2. Keep only files which exist or which are produced by some other task. Note that
120+
only tasks are considered which have been collected until this point.
121+
3. Mark the task such that it will be evaluated at last.
122+
123+
Parameters
124+
----------
125+
task
126+
The LaTeX task.
127+
session : _pytask.session.Session
128+
The session.
129+
130+
"""
131+
source = _get_node_from_dictionary(
132+
task.depends_on, session.config["latex_source_key"]
133+
)
134+
135+
# Scan the LaTeX document for included files.
136+
latex_dependencies = set(scan(source.path))
137+
138+
# Remove duplicated dependencies which have already been added by the user.
139+
existing_paths = {
140+
i.path for i in task.depends_on.values() if isinstance(i, FilePathNode)
141+
}
142+
new_dependencies = latex_dependencies - existing_paths
143+
144+
used_integer_keys = [i for i in task.depends_on if isinstance(i, int)]
145+
max_int = max(used_integer_keys) if used_integer_keys else 0
146+
147+
new_dependencies = dict(enumerate(new_dependencies, max_int + 1))
148+
collected_dependencies = _collect_nodes(
149+
session, task.path, task.name, new_dependencies
150+
)
151+
task.depends_on = {**task.depends_on, **collected_dependencies}
152+
153+
# Mark the task as being delayed to avoid conflicts with unmatched dependencies.
154+
task.markers.append(Mark("try_last", (), {}))
155+
156+
return task
157+
158+
105159
def _merge_all_markers(task):
106160
"""Combine all information from markers for the compile latex function."""
107161
latex_marks = get_specific_markers_from_task(task, "latex")

src/pytask_latex/config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Configure pytask."""
22
from _pytask.config import hookimpl
3+
from _pytask.shared import convert_truthy_or_falsy_to_bool
4+
from _pytask.shared import get_first_non_none_value
35

46

57
@hookimpl
@@ -10,3 +12,9 @@ def pytask_parse_config(config, config_from_file):
1012
config["latex_document_key"] = config_from_file.get(
1113
"latex_document_key", "document"
1214
)
15+
config["infer_latex_dependencies"] = get_first_non_none_value(
16+
config_from_file,
17+
key="infer_latex_dependencies",
18+
callback=convert_truthy_or_falsy_to_bool,
19+
default=True,
20+
)

tests/test_collect.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -91,20 +91,35 @@ def test_pytask_collect_task(name, expected):
9191
)
9292
@pytest.mark.parametrize("latex_source_key", ["source", "script", "main"])
9393
@pytest.mark.parametrize("latex_document_key", ["document", "compiled_doc"])
94+
@pytest.mark.parametrize("infer_latex_dependencies", [True, False])
9495
def test_pytask_collect_task_teardown(
95-
depends_on, produces, expectation, latex_source_key, latex_document_key
96+
tmp_path,
97+
depends_on,
98+
produces,
99+
expectation,
100+
latex_source_key,
101+
latex_document_key,
102+
infer_latex_dependencies,
96103
):
104+
if infer_latex_dependencies:
105+
tmp_path.joinpath(depends_on[0]).touch()
106+
97107
session = DummyClass()
98108
session.config = {
99109
"latex_source_key": latex_source_key,
100110
"latex_document_key": latex_document_key,
111+
"infer_latex_dependencies": infer_latex_dependencies,
101112
}
102113

103114
task = DummyClass()
115+
task.path = tmp_path / "task_dummy.py"
116+
task.name = tmp_path.as_posix() + "task_dummy.py::task_dummy"
104117
task.depends_on = {
105-
i: FilePathNode.from_path(Path(n)) for i, n in enumerate(depends_on)
118+
i: FilePathNode.from_path(tmp_path / n) for i, n in enumerate(depends_on)
119+
}
120+
task.produces = {
121+
i: FilePathNode.from_path(tmp_path / n) for i, n in enumerate(produces)
106122
}
107-
task.produces = {i: FilePathNode.from_path(Path(n)) for i, n in enumerate(produces)}
108123
task.markers = [Mark("latex", (), {})]
109124
task.function = task_dummy
110125
task.function.pytaskmark = task.markers
@@ -116,12 +131,7 @@ def test_pytask_collect_task_teardown(
116131
@pytest.mark.unit
117132
@pytest.mark.parametrize(
118133
"obj, key, expected",
119-
[
120-
(1, "asds", 1),
121-
(1, None, 1),
122-
({"a": 1}, "a", 1),
123-
({0: 1}, "a", 1),
124-
],
134+
[(1, "asds", 1), (1, None, 1), ({"a": 1}, "a", 1), ({0: 1}, "a", 1)],
125135
)
126136
def test_get_node_from_dictionary(obj, key, expected):
127137
result = _get_node_from_dictionary(obj, key)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import textwrap
2+
3+
import pytest
4+
from conftest import needs_latexmk
5+
from conftest import skip_on_github_actions_with_win
6+
from pytask import main
7+
8+
9+
@needs_latexmk
10+
@skip_on_github_actions_with_win
11+
@pytest.mark.end_to_end
12+
def test_infer_dependencies_from_task(tmp_path):
13+
task_source = """
14+
import pytask
15+
16+
@pytask.mark.latex
17+
@pytask.mark.depends_on("document.tex")
18+
@pytask.mark.produces("document.pdf")
19+
def task_compile_document():
20+
pass
21+
22+
"""
23+
tmp_path.joinpath("task_dummy.py").write_text(textwrap.dedent(task_source))
24+
25+
latex_source = r"""
26+
\documentclass{report}
27+
\begin{document}
28+
\input{sub_document}
29+
\end{document}
30+
"""
31+
tmp_path.joinpath("document.tex").write_text(textwrap.dedent(latex_source))
32+
tmp_path.joinpath("sub_document.tex").write_text("Lorem ipsum.")
33+
34+
session = main({"paths": tmp_path})
35+
36+
assert session.exit_code == 0
37+
assert len(session.tasks) == 1
38+
assert len(session.tasks[0].depends_on) == 2

tox.ini

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ basepython = python
99

1010
[testenv:pytest]
1111
conda_deps =
12-
pytask >=0.0.9
12+
pytask >=0.0.11
1313
pytask-parallel >=0.0.4
14+
latex-dependency-scanner
1415
pytest
1516
pytest-cov
1617
pytest-xdist

0 commit comments

Comments
 (0)