Skip to content

Remove cleanup working directory changes; always use absolute paths. #252

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
92 changes: 33 additions & 59 deletions jupyterlab_latex/build.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
""" JupyterLab LaTex : live LaTeX editing for JupyterLab """

import glob, json, re, os
from contextlib import contextmanager
import shutil

from tornado import gen, web
Expand All @@ -11,53 +10,6 @@
from .config import LatexConfig
from .util import run_command

@contextmanager
def latex_cleanup(cleanup=True, workdir='.', whitelist=None, greylist=None):
"""Context manager for changing directory and removing files when done.

By default it works in the current directory, and removes all files that
were not present in the working directory.

Parameters
----------

workdir = string, optional
This represents a path to the working directory for running LaTeX (the
default is '.').
whitelist = list or None, optional
This is the set of files not present before running the LaTeX commands
that are not to be removed when cleaning up. Defaults to None.
greylist = list or None, optional
This is the set of files that need to be removed before running LaTeX
commands but which, if present, will not by removed when cleaning up.
Defaults to None.
"""
orig_work_dir = os.getcwd()
os.chdir(os.path.abspath(workdir))

keep_files = set()
for fp in greylist:
try:
os.remove(fp)
keep_files.add(fp)
except FileNotFoundError:
pass

before = set(glob.glob("*"))
keep_files = keep_files.union(before,
set(whitelist if whitelist else [])
)
yield
if cleanup:
after = set(glob.glob("*"))
for fn in set(after-keep_files):
if not os.path.isdir(fn):
os.remove(fn)
else:
shutil.rmtree(fn)
os.chdir(orig_work_dir)



class LatexBuildHandler(APIHandler):
"""
Expand Down Expand Up @@ -245,7 +197,7 @@ def filter_output(self, latex_output):
return '\n'.join(filtered_output)

@gen.coroutine
def run_latex(self, command_sequence):
def run_latex(self, command_sequence, working_directory):
"""Run commands sequentially, returning a 500 code on an error.

Parameters
Expand All @@ -269,9 +221,9 @@ def run_latex(self, command_sequence):
"""

for cmd in command_sequence:
self.log.debug(f'jupyterlab-latex: run: {" ".join(cmd)} (CWD: {os.getcwd()})')
self.log.debug(f'jupyterlab-latex: run: {" ".join(cmd)} (CWD: {working_directory})')

code, output = yield run_command(cmd)
code, output = yield run_command(cmd, working_directory)
if code != 0:
self.set_status(500)
self.log.error((f'LaTeX command `{" ".join(cmd)}` '
Expand All @@ -290,6 +242,7 @@ def get(self, path = ''):
# Parse the path into the base name and extension of the file
tex_file_path = os.path.join(self.root_dir, path.strip('/'))
tex_base_name, ext = os.path.splitext(os.path.basename(tex_file_path))
working_directory = os.path.abspath(os.path.dirname(tex_file_path))
c = LatexConfig(config=self.config)

self.log.debug((f"jupyterlab-latex: get: path=({path}), "
Expand All @@ -303,12 +256,33 @@ def get(self, path = ''):
out = (f"The file at `{tex_file_path}` does not end with .tex. "
"You can only run LaTeX on a file ending with .tex.")
else:
with latex_cleanup(
cleanup=c.cleanup,
workdir=os.path.dirname(tex_file_path),
whitelist=[tex_base_name+'.pdf', tex_base_name+'.synctex.gz'],
greylist=[tex_base_name+'.aux']
):
cmd_sequence = self.build_tex_cmd_sequence(tex_base_name)
out = yield self.run_latex(cmd_sequence)
whitelist = [tex_base_name+'.pdf', tex_base_name+'.synctex.gz']
greylist = [tex_base_name+'.aux']

keep_files = set()
for fp in greylist:
try:
os.remove(os.path.join(working_directory, fp))
keep_files.add(fp)
except FileNotFoundError:
pass

before = set(glob.glob("*", root_dir=working_directory))
keep_files = keep_files.union(before,
set(whitelist if whitelist else [])
)


cmd_sequence = self.build_tex_cmd_sequence(tex_base_name)
out = yield self.run_latex(cmd_sequence, working_directory)

if c.cleanup:
after = set(glob.glob("*", root_dir=working_directory))
for fn in set(after-keep_files):
abs_fn = os.path.join(working_directory, fn)
if not os.path.isdir(abs_fn):
os.remove(abs_fn)
else:
shutil.rmtree(abs_fn)

self.finish(out)
8 changes: 4 additions & 4 deletions jupyterlab_latex/synctex.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def build_synctex_view_cmd(self, tex_name, pos):


@gen.coroutine
def run_synctex(self, cmd):
def run_synctex(self, cmd, working_directory):
"""Run commands sequentially, returning a 500 code on an error.

Parameters
Expand All @@ -145,8 +145,8 @@ def run_synctex(self, cmd):
there.

"""
self.log.debug(f'jupyterlab-latex: run: {" ".join(cmd)} (CWD: {os.getcwd()})')
code, output = yield run_command(cmd)
self.log.debug(f'jupyterlab-latex: run: {" ".join(cmd)} (CWD: {working_directory})')
code, output = yield run_command(cmd, working_directory)
if code != 0:
self.set_status(500)
self.log.error((f'SyncTex command `{" ".join(cmd)}` '
Expand Down Expand Up @@ -194,7 +194,7 @@ def get(self, path = ''):
else:
cmd, pos = self.build_synctex_cmd(relative_base_path, ext)

out = yield self.run_synctex(cmd)
out = yield self.run_synctex(cmd, workdir)
out = json.dumps(parse_synctex_response(out, pos))
self.finish(out)

Expand Down
9 changes: 5 additions & 4 deletions jupyterlab_latex/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from tornado.process import Subprocess, CalledProcessError

@gen.coroutine
def run_command_sync(cmd):
def run_command_sync(cmd, working_directory):
"""
Run a command using the synchronous `subprocess.run`.
The asynchronous `run_command_async` should be preferred,
Expand All @@ -22,15 +22,15 @@ def run_command_sync(cmd):
A tuple containing the (return code, stdout)
"""
try:
process = subprocess.run(cmd, stdout=subprocess.PIPE)
process = subprocess.run(cmd, stdout=subprocess.PIPE, cwd=working_directory)
except subprocess.CalledProcessError as err:
pass
code = process.returncode
out = process.stdout.decode('utf-8')
return (code, out)

@gen.coroutine
def run_command_async(cmd):
def run_command_async(cmd, working_directory):
"""
Run a command using the asynchronous `tornado.process.Subprocess`.

Expand All @@ -45,7 +45,8 @@ def run_command_async(cmd):
"""
process = Subprocess(cmd,
stdout=Subprocess.STREAM,
stderr=Subprocess.STREAM)
stderr=Subprocess.STREAM,
cwd=working_directory)
try:
yield process.wait_for_exit()
except CalledProcessError as err:
Expand Down