From 038e0ecc158d47058c9b9a802c6611590e223ce3 Mon Sep 17 00:00:00 2001 From: Louis Date: Mon, 31 Mar 2014 14:47:55 +0200 Subject: [PATCH 1/2] Added --steps command line option --- songbook | 34 +++++++++++++++++++-- songbook_core/build.py | 67 ++++++++++++++++++++++++----------------- songbook_core/errors.py | 11 +++++++ 3 files changed, 83 insertions(+), 29 deletions(-) diff --git a/songbook b/songbook index 9e5e3083..bda01f79 100755 --- a/songbook +++ b/songbook @@ -12,11 +12,21 @@ import os.path import textwrap import sys -from songbook_core.build import buildsongbook +from songbook_core.build import buildsongbook, DEFAULT_STEPS from songbook_core import __STR_VERSION__ from songbook_core import errors +# pylint: disable=too-few-public-methods +class ParseStepsAction(argparse.Action): + """Argparse action to split a string into a list.""" + def __call__(self, __parser, namespace, values, __option_string=None): + setattr( + namespace, + self.dest, + [value.strip() for value in values[0].split(',')], + ) + def argument_parser(args): """Parse argumnts""" parser = argparse.ArgumentParser(description="A song book compiler") @@ -34,6 +44,19 @@ def argument_parser(args): subdirectories are 'songs', 'img', 'latex', 'templates'. """)) + parser.add_argument('--steps', '-s', nargs=1, type=str, + action=ParseStepsAction, + help=textwrap.dedent("""\ + Steps to run. Default is "{steps}". + Available steps are: + "tex" produce .tex file from templates; + "pdf" compile .tex file; + "sbx" compile index files; + "clean" remove temporary files. + """.format(steps=','.join(DEFAULT_STEPS))), + default=DEFAULT_STEPS, + ) + options = parser.parse_args(args) return options @@ -71,8 +94,15 @@ def main(): ) else: songbook['datadir'] = os.path.dirname(songbook_path) + try: - buildsongbook(songbook, basename, interactive=True, logger=logger) + buildsongbook( + songbook, + basename, + steps=options.steps, + interactive=True, + logger=logger, + ) except errors.SongbookError as error: logger.error(error) sys.exit(1) diff --git a/songbook_core/build.py b/songbook_core/build.py index f81f00ab..bf86d76d 100644 --- a/songbook_core/build.py +++ b/songbook_core/build.py @@ -23,6 +23,7 @@ DEFAULT_AUTHWORDS = { "ignore": ["unknown"], "sep": ["and"], } +DEFAULT_STEPS = ['tex', 'pdf', 'sbx', 'pdf', 'clean'] # pylint: disable=too-few-public-methods @@ -137,31 +138,38 @@ def clean(basename): ] for ext in generated_extensions: - try: - os.unlink(basename + ext) - except Exception as exception: - raise errors.CleaningError(basename + ext, exception) + if os.path.isfile(basename + ext): + try: + os.unlink(basename + ext) + except Exception as exception: + raise errors.CleaningError(basename + ext, exception) def buildsongbook( raw_songbook, basename, + steps=None, interactive=False, - logger=logging.getLogger() + logger=logging.getLogger(), ): """Build a songbook Arguments: - raw_songbook: Python representation of the .sb songbook configuration file. + - steps: list of steps to perform to compile songbook. Available steps are: + - tex: build .tex file from templates; + - pdf: compile .tex using pdflatex; + - sbx: compile song and author indexes; + - clean: remove temporary files. - basename: basename of the songbook to be built. - interactive: in False, do not expect anything from stdin. """ - songbook = Songbook(raw_songbook, basename) - with codecs.open("{}.tex".format(basename), 'w', 'utf-8') as output: - songbook.write_tex(output) + if steps is None: + steps = DEFAULT_STEPS + songbook = Songbook(raw_songbook, basename) if not 'TEXINPUTS' in os.environ.keys(): os.environ['TEXINPUTS'] = '' os.environ['TEXINPUTS'] += os.pathsep + os.path.join( @@ -179,22 +187,27 @@ def buildsongbook( if not interactive: pdflatex_options.append("-halt-on-error") - # First pdflatex pass - if subprocess.call(["pdflatex"] + pdflatex_options + [basename]): - raise errors.LatexCompilationError(basename) - - # Make index - sxd_files = glob.glob("%s_*.sxd" % basename) - for sxd_file in sxd_files: - logger.info("processing " + sxd_file) - idx = process_sxd(sxd_file) - index_file = open(sxd_file[:-3] + "sbx", "w") - index_file.write(idx.entries_to_str().encode('utf8')) - index_file.close() - - # Second pdflatex pass - if subprocess.call(["pdflatex"] + pdflatex_options + [basename]): - raise errors.LatexCompilationError(basename) - - # Cleaning - clean(basename) + # Compilation + for step in steps: + if step == 'tex': + # Building .tex file from templates + with codecs.open("{}.tex".format(basename), 'w', 'utf-8') as output: + songbook.write_tex(output) + elif step == 'pdf': + if subprocess.call(["pdflatex"] + pdflatex_options + [basename]): + raise errors.LatexCompilationError(basename) + elif step == 'sbx': + # Make index + sxd_files = glob.glob("%s_*.sxd" % basename) + for sxd_file in sxd_files: + logger.info("processing " + sxd_file) + idx = process_sxd(sxd_file) + index_file = open(sxd_file[:-3] + "sbx", "w") + index_file.write(idx.entries_to_str().encode('utf8')) + index_file.close() + elif step == 'clean': + # Cleaning + clean(basename) + else: + # Unknown step name + raise errors.UnknownStep(step) diff --git a/songbook_core/errors.py b/songbook_core/errors.py index 7a231042..00e6b3d8 100644 --- a/songbook_core/errors.py +++ b/songbook_core/errors.py @@ -36,3 +36,14 @@ class CleaningError(SongbookError): filename=self.filename, exception=str(self.exception) ) + +class UnknownStep(SongbookError): + """Unknown compilation step.""" + + def __init__(self, step): + super(UnknownStep, self).__init__() + self.step = step + + def __str__(self): + return """Compilation step "{step}" unknown.""".format(step=self.step) + From 5ebe179b8d6bff0d912f411d041e1a3d47f0c3fc Mon Sep 17 00:00:00 2001 From: Louis Date: Wed, 2 Apr 2014 12:58:45 +0200 Subject: [PATCH 2/2] =?UTF-8?q?Possibilit=C3=A9=20d'ajouter=20=C3=A0=20la?= =?UTF-8?q?=20cha=C3=AEne=20de=20compilation=20une=20commande=20personnali?= =?UTF-8?q?s=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cette commande personnalisée apparait comme `--steps "%commande arg1 arg2"`. --- songbook | 15 ++++++++++++--- songbook_core/build.py | 6 ++++++ songbook_core/errors.py | 17 ++++++++++++++--- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/songbook b/songbook index bda01f79..b5eefa51 100755 --- a/songbook +++ b/songbook @@ -21,10 +21,15 @@ from songbook_core import errors class ParseStepsAction(argparse.Action): """Argparse action to split a string into a list.""" def __call__(self, __parser, namespace, values, __option_string=None): + if not getattr(namespace, self.dest): + setattr(namespace, self.dest, []) setattr( namespace, self.dest, - [value.strip() for value in values[0].split(',')], + ( + getattr(namespace, self.dest) + + [value.strip() for value in values[0].split(',')] + ), ) def argument_parser(args): @@ -52,9 +57,13 @@ def argument_parser(args): "tex" produce .tex file from templates; "pdf" compile .tex file; "sbx" compile index files; - "clean" remove temporary files. + "clean" remove temporary files; + any string beginning with '%%' (in this case, it will be run + in a shell). Several steps (excepted the custom shell + command) can be combinend in one --steps argument, as a + comma separated string. """.format(steps=','.join(DEFAULT_STEPS))), - default=DEFAULT_STEPS, + default=None, ) options = parser.parse_args(args) diff --git a/songbook_core/build.py b/songbook_core/build.py index bf86d76d..e32533ef 100644 --- a/songbook_core/build.py +++ b/songbook_core/build.py @@ -208,6 +208,12 @@ def buildsongbook( elif step == 'clean': # Cleaning clean(basename) + elif step.startswith("%"): + # Shell command + command = step[1:] + exit_code = subprocess.call(command, shell=True) + if exit_code: + raise errors.StepCommandError(command, exit_code) else: # Unknown step name raise errors.UnknownStep(step) diff --git a/songbook_core/errors.py b/songbook_core/errors.py index 00e6b3d8..bf8675fa 100644 --- a/songbook_core/errors.py +++ b/songbook_core/errors.py @@ -18,11 +18,22 @@ class LatexCompilationError(SongbookError): self.basename = basename def __str__(self): - return ( - """Error while pdfLaTeX compilation of "{basename}.tex" - (see {basename}.log for more information).""" + return ("""Error while pdfLaTeX compilation of "{basename}.tex" """ + """(see {basename}.log for more information).""" ).format(basename=self.basename) +class StepCommandError(SongbookError): + """Error during LaTeX compilation.""" + + def __init__(self, command, code): + super(StepCommandError, self).__init__() + self.command = command + self.code = code + + def __str__(self): + return ("""Error while running custom command "{command}": got return""" + " code {code}.").format(command=self.command, code=self.code) + class CleaningError(SongbookError): """Error during cleaning of LaTeX auxiliary files."""