diff --git a/README.md b/README.md index 40cbb9e..f83d1dd 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Pytex -Pytex is a simple package which make aa bridge between Latex and Python. +Pytex is a simple package which make a bridge between Latex and Python. ## texenv: Bring Python inside latex diff --git a/pytex/latex_error_parser.py b/pytex/latex_error_parser.py new file mode 100644 index 0000000..3dd8eb0 --- /dev/null +++ b/pytex/latex_error_parser.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python +# encoding: utf-8 + +""" +Parsing latex error to bubble up import ones +""" + +from functools import wraps + +def coroutine(func): + @wraps(func) + def start(*args, **kwargs): + cr = func(*args, **kwargs) + next(cr) + return cr + return start + +@coroutine +def generic_sink(func): + """ Generic sink + + :param function: function that define the end of the sink + + >>> print_sink = generic_sink(print) + >>> print_sink.send("coucou") + coucou + """ + while True: + c = (yield) + if c: + func(c) + +@coroutine +def filter_errors(target): + """ Filter pdflatex log to bubble up error + + https://en.wikibooks.org/wiki/LaTeX/Errors_and_Warnings + + >>> s = generic_sink(print) + >>> tex_filter = filter_errors(s) + >>> tex_filter.send("! Undefined control sequence.") + ! Undefined control sequence. + >>> tex_filter.send("l.43 \\includegraphics") + l.43 \\includegraphics + >>> tex_filter.send("l.43 \\includegraphics") + >>> + """ + while True: + line = (yield) + if line.startswith("!"): + target.send(line) + line = (yield) + while not line.startswith('See'): + target.send(line) + line = (yield) + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/pytex/pytex.py b/pytex/pytex.py index 0ed0050..2ecffab 100644 --- a/pytex/pytex.py +++ b/pytex/pytex.py @@ -9,10 +9,21 @@ import logging import math as m import subprocess import random as rd -from path import Path +from pathlib import Path +import os from .texenv import * +from .latex_error_parser import generic_sink, filter_errors +formatter = logging.Formatter('%(name)s :: %(levelname)s :: %(message)s') +steam_handler = logging.StreamHandler() +steam_handler.setLevel(logging.DEBUG) +steam_handler.setFormatter(formatter) +# création de l'objet logger qui va nous servir à écrire dans les logs +# on met le niveau du logger à DEBUG, comme ça il écrit tout logger = logging.getLogger(__name__) +logger.setLevel(logging.ERROR) +logger.addHandler(steam_handler) + EXPORT_DICT = {} EXPORT_DICT.update(m.__dict__) @@ -38,6 +49,8 @@ def feed(template, data, output="", force=0): :param output: name of the output file (by default: tpl is replaced by a 2 digits number) :param force: Override is the output already exists + + :return: name of fed template """ logger.info(f"Getting template {template}") tpl = texenv.get_template(str(template)) @@ -55,56 +68,62 @@ def feed(template, data, output="", force=0): logger.error(f"{output} exists. Use force=1 do override it") raise ValueError(f"{output} exists. Use force=1 do override it") - output_dir = output_p.dirname() + output_dir = output_p.parent if output_dir and not output_dir.exists(): - output_dir.mkdir_p() + logger.debug(f"Creating output dir {output_dir}") + output_dir.mkdir(exist_ok=True) with open(output_p, "w") as output_f: output_f.write(tpl.render(**EXPORT_DICT, **data)) logger.info(f"{template} has been rendered to {output}.") + return output_p -def pdflatex(latex_file, output_dir=""): - """ Compile latex file +def pdflatex(tex_filename, output_dir=""): + """ Compile a latex file with pdflatex If output_dir is not set, it produce it next to the latex file. """ + latex_file = Path(tex_filename) if not output_dir: - output_dir = Path(latex_file).dirname().abspath() - logger.debug(f"output_dir for dflatex is {output_dir}") + output_dir = latex_file.parent.resolve() + logger.debug(f"output_dir for pdflatex is {output_dir}") - pwd = Path('./').abspath() - Path(output_dir).cd() + prev_cwd = Path.cwd() + os.chdir(output_dir) compilation = subprocess.Popen( [ "pdflatex", - # f"-output-directory={output_dir}", + f"-output-directory={output_dir}", # "-halt-on-error", "-interaction=nonstopmode", "-shell-escape", - str(Path(latex_file).name), + str(latex_file.name), ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, # shell=True ) + latex_error_logger = filter_errors(generic_sink(logger.error)) for line in compilation.stdout: - if b"Error" in line: - logger.error(line) + latex_error_logger.send(line.decode("latin-1").rstrip('\r\n')) + compilation_status = compilation.wait() + logger.debug(f"{latex_file.name} has been compiled in {output_dir}") - pwd.cd() + + os.chdir(prev_cwd) -def clean(dirname=".", garbages=["*.aux", "*.log"]): +def clean(dirname="", garbages=["*.aux", "*.log"]): """ Clean the directory from aux and log latex files """ if not dirname: dirname = Path("./") for g in garbages: - g_files = Path(dirname).files(g) + g_files = Path(dirname).glob(g) logger.debug(f"Remove {g_files}") for g_file in g_files: - g_file.remove() + g_file.unlink() # ----------------------------- # Reglages pour 'vim' diff --git a/pytex/texenv.py b/pytex/texenv.py index 98e792b..a319c72 100644 --- a/pytex/texenv.py +++ b/pytex/texenv.py @@ -24,7 +24,6 @@ texenv = jinja2.Environment( line_statement_prefix='%-', line_comment_prefix='%#', loader=jinja2.ChoiceLoader([ - # jinja2.PackageLoader("notes_tools.reports", "templates"), jinja2.FileSystemLoader(['./']), ]), extensions=['jinja2.ext.do'] diff --git a/setup.py b/setup.py index b0d577f..d25c896 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ from setuptools import setup, find_packages setup( name='mypytex', - version='0.2', + version='0.3', description='Writing latex files and compile it with python and jinja2', url='https://git.opytex.org/lafrite/Pytex', author='Bertrand Benjamin', @@ -14,7 +14,6 @@ setup( packages=find_packages(), install_requires=[ 'jinja2', - 'path.py', ], ) diff --git a/tests/test_pytex.py b/tests/test_pytex.py new file mode 100644 index 0000000..f8e6485 --- /dev/null +++ b/tests/test_pytex.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from pytex import feed, pdflatex, clean +import os +from shutil import copyfile +from pathlib import Path +import pytest + +TESTDIR = Path("tests") +GOOD_TEMPLATE = TESTDIR / "tpl_good.tex" + +GOOD_DATA = { + "name": "Bob", + "repetitions": 4, +} + + +@pytest.fixture() +def prepare_templates(tmpdir): + wdir = tmpdir + good_tpl = Path(GOOD_TEMPLATE) + copyfile(good_tpl, wdir / good_tpl.name) + prev_cwd = Path.cwd() + + os.chdir(wdir) + yield wdir + os.chdir(prev_cwd) + + +def test_feed_once(prepare_templates): + output = feed(GOOD_TEMPLATE.name, GOOD_DATA) + + +def test_feed_twice(prepare_templates): + output1 = feed(GOOD_TEMPLATE.name, GOOD_DATA) + output2 = feed(GOOD_TEMPLATE.name, GOOD_DATA) + + +def test_feed_output(prepare_templates): + dest = "./tests/special_name.tex" + output = feed(GOOD_TEMPLATE.name, GOOD_DATA, dest) + assert output.samefile(dest) + + +def test_feed_output_noforce(): + pass + # output = feed(GOOD_TEMPLATE, GOOD_DATA, ) + # os.system(f"rm {output}") + + +def test_feed_pdflatex(prepare_templates): + latex_file = feed(GOOD_TEMPLATE.name, GOOD_DATA) + pdflatex(latex_file) + clean(garbages=["*.aux", "*.log", "*.pdf"]) + + +# ----------------------------- +# Reglages pour 'vim' +# vim:set autoindent expandtab tabstop=4 shiftwidth=4: +# cursor: 16 del diff --git a/tests/tpl_good.tex b/tests/tpl_good.tex new file mode 100644 index 0000000..445fefc --- /dev/null +++ b/tests/tpl_good.tex @@ -0,0 +1,15 @@ +\documentclass{article} +\usepackage[utf8]{inputenc} % Unicode support (Umlauts etc.) +\usepackage[french]{babel} % Change hyphenation rules + + +\begin{document} +Coucou comment allez vous? + +Je m'appelle \Var{name}. + +%- for j in range(repetitions) + Je peux faire \Var{j} \\ +%- endfor + +\end{document}