Source code for validphys.pdfoutput

"""
pdfoutput.py

reportengine helpers to enable outputing PDFs.

This module provides one decorator, ``pdfset`` that is used to mark a provider
as generating a PDF set. The providers must take a ``set_name`` and an
``output_path`` argument. ``set_name`` will be required to be a unique string
that does not correspond to any installed LHAPDF grid, and ``output_path`` will
be modified to actually correspond to <output>/pdfsets.
Within reportengine, the return value of the sets marked with ``@pdfset`` will
be discarded, and the relative path to the output folder will be used instead.
This can be used to formulate links within the report.
"""
import logging
import re

from reportengine.checks import CheckError, check, make_argcheck, make_check
from validphys import lhaindex

log = logging.getLogger(__name__)

PDFSETS_PATH = 'pdfsets'


@make_argcheck
def _check_set_name(set_name):
    check(re.fullmatch(r'[\w\-]+', set_name), "Invalid set_name. Must be alphanumeric.")


@make_check
def _setup_pdf_output(*, callspec, ns, **kwargs):
    set_name = ns['set_name']
    rootns = ns.maps[-1]

    if lhaindex.isinstalled(set_name):
        raise CheckError(
            "The PDF set that would be "
            "generated already exists in the LHAPDF path:\n"
            f"{lhaindex.finddir(set_name)}\n"
            "Either delete it or explicitly assign a set_name for "
            "the new PDF."
        )

    output_path = ns['output_path']
    pdfpath = output_path / PDFSETS_PATH
    pdfpath.mkdir(exist_ok=True)
    ns['output_path'] = pdfpath

    # TODO: Enable this someday
    # Ugly hack to allow analyzing the generated pdf some day (as in smpdf).
    # For now, this is used to prevent duplicated reweighted sets.
    if '_future_pdfs' not in rootns:
        rootns['_future_pdfs'] = {}

    future_pdfs = rootns['_future_pdfs']

    if set_name in future_pdfs:
        raise CheckError(
            f"PDF set with name '{set_name}' would already be "
            "generated by another action and would be overwritten."
        )

    # lhapdf.pathsAppend(str(ns['output_path']))
    future_pdfs[set_name] = callspec


def _prepare(namespace, *args, **kwargs):
    return {
        'set_name': namespace['set_name'],
        # Prepare executes before the checks for some reason
        'output_path': namespace['output_path'] / PDFSETS_PATH,
    }


def _return_set_name(result, set_name, output_path):
    if result is not None:
        log.warning("Result of provider marked with @pdfset discarded.")
    return (output_path / set_name).relative_to(output_path.parent)


[docs] def pdfset(f): """Mark the function as returning a PDF set. Make sure that providers marked with this decorator take ``set_name`` and ``output_path`` as arguments.""" f.highlight = 'pdfset' f.prepare = _prepare f.final_action = _return_set_name return _check_set_name(_setup_pdf_output(f))