diff --git a/latexpp/__main__.py b/latexpp/__main__.py index 2847f5d..921ee7b 100644 --- a/latexpp/__main__.py +++ b/latexpp/__main__.py @@ -1,4 +1,4 @@ -import os.path +import os import sys import argparse import logging @@ -182,6 +182,8 @@ def main(argv=None, omit_processed_by=False): default=None, help='output directory (overrides setting from config file)') + parser.add_argument('--texinputs', default=os.environ.get("TEXINPUTS", ".")) + parser.add_argument('-f', '--output-fname', dest='output_fname', default=None, help='output file name in output directory (overrides ' @@ -234,11 +236,14 @@ def main(argv=None, omit_processed_by=False): if args.output_fname: output_fname = args.output_fname + tex_inputs = lppconfig.get('tex_inputs', ['.']) + pp = LatexPreprocessor( output_dir=output_dir, main_doc_fname=fname, main_doc_output_fname=output_fname, - config_dir=config_dir + config_dir=config_dir, + tex_inputs=tex_inputs, ) # for tests diff --git a/latexpp/fixes/input.py b/latexpp/fixes/input.py index fe277e9..d325804 100644 --- a/latexpp/fixes/input.py +++ b/latexpp/fixes/input.py @@ -1,5 +1,3 @@ -import os.path as os_path # allow tests to monkey-patch this - import logging logger = logging.getLogger(__name__) @@ -7,8 +5,7 @@ from latexpp.fix import BaseFix - -exts = ['', '.tex', '.latex'] +exts = ["", ".tex", ".latex"] class EvalInput(BaseFix): r""" @@ -39,13 +36,9 @@ def fix_node(self, n, **kwargs): logger.info("Input ‘%s’", infname) - for e in exts: - # FIXME: resolve path relative to main document source - if os_path.exists(infname+e): - infname = infname+e - break - else: - logger.warning("File not found: ‘%s’. Tried extensions %r", infname, exts) + try: + infname = self.lpp.resolve_tex_fname(infname, extensions=exts) + except FileNotFoundError: return None # keep the node as it is # open that file and go through it, too @@ -107,12 +100,9 @@ def fix_node(self, n, **kwargs): infname = self.preprocess_arg_latex(n, 0) - for e in exts: - if os_path.exists(infname+e): - infname = infname+e - break - else: - logger.warning("File not found: ‘%s’. Tried extensions %r", infname, exts) + try: + infname = self.lpp.resolve_tex_fname(infname, extentions=exts) + except FileNotFoundError: return None # keep the node as it is logger.info("Preprocessing ‘%s’", infname) diff --git a/latexpp/fixes/usepackage.py b/latexpp/fixes/usepackage.py index f4dab0a..a0f755b 100644 --- a/latexpp/fixes/usepackage.py +++ b/latexpp/fixes/usepackage.py @@ -97,25 +97,29 @@ def initialize(self): self.subpp.initialize() else: self.subpp = None + self.non_local_pkgs = [] def fix_node(self, n, **kwargs): pkgname = node_get_usepackage(n, self) if pkgname is not None and pkgname not in self.blacklist: - pkgnamesty = pkgname + '.sty' - if os_path.exists(pkgnamesty): - self.lpp.copy_file(pkgnamesty, destfname=pkgnamesty) - if self.recursive: - with self.subpp.open_file(pkgnamesty) as f: - pkgcontents = f.read() - try: - self.subpp.execute_string(pkgcontents, - omit_processed_by=True, input_source=pkgnamesty) - except LatexWalkerParseError as e: - logger.warning("Couldn't parse file ‘%s’, cannot recursively " - "check for packages to include in that file: %s", - pkgnamesty, e) - return None # keep node the same + try: + pkgnamesty = self.lpp.resolve_tex_fname(pkgname, + extensions=[".sty"], issue_warning=False) + except FileNotFoundError: + self.non_local_pkgs.append(pkgname) + return None # keep node the same + self.lpp.copy_file(pkgnamesty, destfname=os_path.basename(pkgnamesty)) + if self.recursive: + with self.subpp.open_file(pkgnamesty) as f: + pkgcontents = f.read() + try: + self.subpp.execute_string(pkgcontents, + omit_processed_by=True, input_source=pkgnamesty) + except LatexWalkerParseError as e: + logger.warning("Couldn't parse file ‘%s’, cannot recursively " + "check for packages to include in that file: %s", + pkgnamesty, e) return None @@ -130,6 +134,7 @@ def finalize(self): self.finalized = True if self.subpp is not None: self.subpp.finalize() + logging.info("%s assumes the following non-local packages: %s.", self.__class__.__name__, ', '.join(self.non_local_pkgs)) class InputLocalPkgs(BaseFix): @@ -181,25 +186,28 @@ def initialize(self): ) if self.fixes: self.subpp.install_fixes_from_config(self.fixes) - self.subpp.initialize() def fix_node(self, n, **kwargs): pkgname = node_get_usepackage(n, self) if pkgname is not None and pkgname in self.packages: - pkgnamesty = pkgname + '.sty' - if os_path.exists(pkgnamesty): - logger.debug("Processing input package ‘%s’", pkgnamesty) - with self.lpp.open_file(pkgnamesty) as f: - pkgcontents = f.read() - if self.subpp: - pkgcontents = \ - self.subpp.execute_string(pkgcontents, - omit_processed_by=True, - input_source=pkgnamesty) - pkgcontents = r"\makeatletter " + pkgcontents + r"\makeatother " - return pkgcontents + try: + pkgnamesty = self.lpp.resolve_tex_fname(pkgname, + extensions=[".sty"], issue_warning=True) + except FileNotFoundError: + return None # keep node the same + + logger.debug("Processing input package ‘%s’", pkgnamesty) + with self.lpp.open_file(pkgnamesty) as f: + pkgcontents = f.read() + if self.subpp: + pkgcontents = \ + self.subpp.execute_string(pkgcontents, + omit_processed_by=True, + input_source=pkgnamesty) + pkgcontents = r"\makeatletter " + pkgcontents + r"\makeatother " + return pkgcontents return None diff --git a/latexpp/preprocessor.py b/latexpp/preprocessor.py index 8c7f094..7733a0f 100644 --- a/latexpp/preprocessor.py +++ b/latexpp/preprocessor.py @@ -80,6 +80,9 @@ class LatexPreprocessor: specified to some helpers such as :py:meth:`copy_file()` are interpreted as relative to this directory. + - `tex_inputs` is a list of search paths, similar to the LaTeX environment + varibale `TEXINPUTS`. + The fixes can be installed directly via a configuration data structure with :py:meth:`install_fixes_from_config()` (as extracted from a YaML reader from a `lppconfig.yml` file, for instance), or fix instances can be installed @@ -119,7 +122,8 @@ def __init__(self, *, output_dir='_latexpp_output', main_doc_fname=None, main_doc_output_fname=None, - config_dir=None): + config_dir=".", + tex_inputs=(".",)): super().__init__() @@ -128,6 +132,7 @@ def __init__(self, *, self.main_doc_output_fname = main_doc_output_fname # directory relative to which to search for custom python fixes: self.config_dir = config_dir + self.tex_inputs = tex_inputs # version of output_dir for displaying purposes self.display_output_dir = output_dir.rstrip('/') + '/' @@ -299,9 +304,25 @@ def execute_main(self): def _resolve_source_fname(self, fname): - if self.config_dir: - return os.path.join(self.config_dir, fname) - return fname + return os.path.join(self.config_dir, fname) + + + def resolve_tex_fname(self, fname, extensions=('',), issue_warning=False): + """Resolves a TEX file based on the search paths of tex_inputs, returns a + relative path to config_dir.""" + + for p in self.tex_inputs: + for ext in extensions: + f = os.path.join(p, fname + ext) + if os.path.exists(f): + return os.path.relpath(f, self.config_dir) + + if issue_warning: + logger.warning("File not found: ‘%s’. Tried extensions %r with TEXINPUS='%s'", + fname, extensions, ';'.join(self.tex_inputs)) + + raise FileNotFoundError() + def execute_file(self, fname, *, output_fname, omit_processed_by=False): r""" @@ -314,7 +335,7 @@ def execute_file(self, fname, *, output_fname, omit_processed_by=False): *latexpp*. """ - with open(self._resolve_source_fname(fname), 'r') as f: + with open(self._resolve_source_fname(self.resolve_tex_fname(fname)), 'r') as f: s = f.read() outdata = self.execute_string(s, input_source='file ‘{}’'.format(fname)) @@ -500,7 +521,9 @@ def create_subpreprocessor(self, *, lppconfig_fixes=None): """ pp = LatexPreprocessor(output_dir=self.output_dir, main_doc_fname=self.main_doc_fname, - main_doc_output_fname=self.main_doc_output_fname) + main_doc_output_fname=self.main_doc_output_fname, + config_dir=self.config_dir, + tex_inputs=self.tex_inputs) pp.parent_preprocessor = self if lppconfig_fixes: pp.install_fixes_from_config(lppconfig_fixes)