Compare commits
No commits in common. "51d900844de5a39bc7d172864cb76b6ed186461b" and "3030798f71c04119d0120d1db8868cbfd56d4a8a" have entirely different histories.
51d900844d
...
3030798f71
@ -1,6 +1,6 @@
|
|||||||
# Pytex
|
# Pytex
|
||||||
|
|
||||||
Pytex is a simple package which make a bridge between Latex and Python.
|
Pytex is a simple package which make aa bridge between Latex and Python.
|
||||||
|
|
||||||
## texenv: Bring Python inside latex
|
## texenv: Bring Python inside latex
|
||||||
|
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
#!/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
|
|
@ -9,21 +9,10 @@ import logging
|
|||||||
import math as m
|
import math as m
|
||||||
import subprocess
|
import subprocess
|
||||||
import random as rd
|
import random as rd
|
||||||
from pathlib import Path
|
from path import Path
|
||||||
import os
|
|
||||||
from .texenv import *
|
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 = logging.getLogger(__name__)
|
||||||
logger.setLevel(logging.ERROR)
|
|
||||||
logger.addHandler(steam_handler)
|
|
||||||
|
|
||||||
|
|
||||||
EXPORT_DICT = {}
|
EXPORT_DICT = {}
|
||||||
EXPORT_DICT.update(m.__dict__)
|
EXPORT_DICT.update(m.__dict__)
|
||||||
@ -49,8 +38,6 @@ def feed(template, data, output="", force=0):
|
|||||||
:param output: name of the output file
|
:param output: name of the output file
|
||||||
(by default: tpl is replaced by a 2 digits number)
|
(by default: tpl is replaced by a 2 digits number)
|
||||||
:param force: Override is the output already exists
|
:param force: Override is the output already exists
|
||||||
|
|
||||||
:return: name of fed template
|
|
||||||
"""
|
"""
|
||||||
logger.info(f"Getting template {template}")
|
logger.info(f"Getting template {template}")
|
||||||
tpl = texenv.get_template(str(template))
|
tpl = texenv.get_template(str(template))
|
||||||
@ -68,62 +55,56 @@ def feed(template, data, output="", force=0):
|
|||||||
logger.error(f"{output} exists. Use force=1 do override it")
|
logger.error(f"{output} exists. Use force=1 do override it")
|
||||||
raise ValueError(f"{output} exists. Use force=1 do override it")
|
raise ValueError(f"{output} exists. Use force=1 do override it")
|
||||||
|
|
||||||
output_dir = output_p.parent
|
output_dir = output_p.dirname()
|
||||||
if output_dir and not output_dir.exists():
|
if output_dir and not output_dir.exists():
|
||||||
logger.debug(f"Creating output dir {output_dir}")
|
output_dir.mkdir_p()
|
||||||
output_dir.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
with open(output_p, "w") as output_f:
|
with open(output_p, "w") as output_f:
|
||||||
output_f.write(tpl.render(**EXPORT_DICT, **data))
|
output_f.write(tpl.render(**EXPORT_DICT, **data))
|
||||||
logger.info(f"{template} has been rendered to {output}.")
|
logger.info(f"{template} has been rendered to {output}.")
|
||||||
return output_p
|
|
||||||
|
|
||||||
|
|
||||||
def pdflatex(tex_filename, output_dir=""):
|
def pdflatex(latex_file, output_dir=""):
|
||||||
""" Compile a latex file with pdflatex
|
""" Compile latex file
|
||||||
|
|
||||||
If output_dir is not set, it produce it next to the latex file.
|
If output_dir is not set, it produce it next to the latex file.
|
||||||
"""
|
"""
|
||||||
latex_file = Path(tex_filename)
|
|
||||||
if not output_dir:
|
if not output_dir:
|
||||||
output_dir = latex_file.parent.resolve()
|
output_dir = Path(latex_file).dirname().abspath()
|
||||||
logger.debug(f"output_dir for pdflatex is {output_dir}")
|
logger.debug(f"output_dir for dflatex is {output_dir}")
|
||||||
|
|
||||||
prev_cwd = Path.cwd()
|
pwd = Path('./').abspath()
|
||||||
os.chdir(output_dir)
|
Path(output_dir).cd()
|
||||||
compilation = subprocess.Popen(
|
compilation = subprocess.Popen(
|
||||||
[
|
[
|
||||||
"pdflatex",
|
"pdflatex",
|
||||||
f"-output-directory={output_dir}",
|
# f"-output-directory={output_dir}",
|
||||||
# "-halt-on-error",
|
# "-halt-on-error",
|
||||||
"-interaction=nonstopmode",
|
"-interaction=nonstopmode",
|
||||||
"-shell-escape",
|
"-shell-escape",
|
||||||
str(latex_file.name),
|
str(Path(latex_file).name),
|
||||||
],
|
],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE,
|
stderr=subprocess.PIPE,
|
||||||
# shell=True
|
# shell=True
|
||||||
)
|
)
|
||||||
|
|
||||||
latex_error_logger = filter_errors(generic_sink(logger.error))
|
|
||||||
for line in compilation.stdout:
|
for line in compilation.stdout:
|
||||||
latex_error_logger.send(line.decode("latin-1").rstrip('\r\n'))
|
if b"Error" in line:
|
||||||
compilation_status = compilation.wait()
|
logger.error(line)
|
||||||
|
|
||||||
logger.debug(f"{latex_file.name} has been compiled in {output_dir}")
|
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 """
|
""" Clean the directory from aux and log latex files """
|
||||||
if not dirname:
|
if not dirname:
|
||||||
dirname = Path("./")
|
dirname = Path("./")
|
||||||
for g in garbages:
|
for g in garbages:
|
||||||
g_files = Path(dirname).glob(g)
|
g_files = Path(dirname).files(g)
|
||||||
logger.debug(f"Remove {g_files}")
|
logger.debug(f"Remove {g_files}")
|
||||||
for g_file in g_files:
|
for g_file in g_files:
|
||||||
g_file.unlink()
|
g_file.remove()
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Reglages pour 'vim'
|
# Reglages pour 'vim'
|
||||||
|
@ -24,6 +24,7 @@ texenv = jinja2.Environment(
|
|||||||
line_statement_prefix='%-',
|
line_statement_prefix='%-',
|
||||||
line_comment_prefix='%#',
|
line_comment_prefix='%#',
|
||||||
loader=jinja2.ChoiceLoader([
|
loader=jinja2.ChoiceLoader([
|
||||||
|
# jinja2.PackageLoader("notes_tools.reports", "templates"),
|
||||||
jinja2.FileSystemLoader(['./']),
|
jinja2.FileSystemLoader(['./']),
|
||||||
]),
|
]),
|
||||||
extensions=['jinja2.ext.do']
|
extensions=['jinja2.ext.do']
|
||||||
|
3
setup.py
3
setup.py
@ -5,7 +5,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='mypytex',
|
name='mypytex',
|
||||||
version='0.3',
|
version='0.2',
|
||||||
description='Writing latex files and compile it with python and jinja2',
|
description='Writing latex files and compile it with python and jinja2',
|
||||||
url='https://git.opytex.org/lafrite/Pytex',
|
url='https://git.opytex.org/lafrite/Pytex',
|
||||||
author='Bertrand Benjamin',
|
author='Bertrand Benjamin',
|
||||||
@ -14,6 +14,7 @@ setup(
|
|||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'jinja2',
|
'jinja2',
|
||||||
|
'path.py',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,61 +0,0 @@
|
|||||||
#!/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
|
|
@ -1,15 +0,0 @@
|
|||||||
\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}
|
|
Loading…
Reference in New Issue
Block a user