Blame lib/contrib/debian/errorhandler.py

Packit Service 76cb02
#
Packit Service 76cb02
# Attempt to analyse a dblatex failure occured on a Debian platform.
Packit Service 76cb02
#
Packit Service 76cb02
# Author: Andreas Hoenen
Packit Service 76cb02
#
Packit Service 76cb02
import subprocess
Packit Service 76cb02
import sys
Packit Service 76cb02
import apt
Packit Service 76cb02
import os
Packit Service 76cb02
Packit Service 76cb02
from dbtexmf.core.error import ErrorHandler
Packit Service 76cb02
from dbtexmf.core.imagedata import ImageConverter
Packit Service 76cb02
from dbtexmf.core.dbtex import DbTexCommand
Packit Service 76cb02
Packit Service 76cb02
class AptSilentProgress(apt.progress.text.OpProgress):
Packit Service 76cb02
    """
Packit Service 76cb02
    Avoid the annoying progress messages when building the apt cache:
Packit Service 76cb02
    Reading package lists... Done
Packit Service 76cb02
    Building dependency tree
Packit Service 76cb02
    Reading state information... Done
Packit Service 76cb02
    Building data structures... Done
Packit Service 76cb02
    """
Packit Service 76cb02
    def __init__(self, outfile=None):
Packit Service 76cb02
        pass
Packit Service 76cb02
    def done(self):
Packit Service 76cb02
        pass
Packit Service 76cb02
    def update(self, percent=None):
Packit Service 76cb02
        pass
Packit Service 76cb02
Packit Service 76cb02
class DebianHandler(ErrorHandler):
Packit Service 76cb02
    def __init__(self):
Packit Service 76cb02
        ErrorHandler.__init__(self)
Packit Service 76cb02
        self.object = None
Packit Service 76cb02
        self.aptcache = None
Packit Service 76cb02
Packit Service 76cb02
    def signal(self, failed_object, *args, **kwargs):
Packit Service 76cb02
        self.object = failed_object
Packit Service 76cb02
        if not self.aptcache:
Packit Service 76cb02
            self.aptcache = apt.Cache(progress=AptSilentProgress())
Packit Service 76cb02
        if (isinstance(self.object, DbTexCommand)):
Packit Service 76cb02
            error_handled = self._check_dbtexrun()
Packit Service 76cb02
        elif (isinstance(self.object, ImageConverter)):
Packit Service 76cb02
            error_handled = self._check_imagerun(*args)
Packit Service 76cb02
        else:
Packit Service 76cb02
            error_handled = False
Packit Service 76cb02
        if not error_handled:
Packit Service 76cb02
            super(DebianHandler, self).signal(failed_object, *args, **kwargs)
Packit Service 76cb02
Packit Service 76cb02
    def _check_dbtexrun(self):
Packit Service 76cb02
        # First, check the XML input sanity
Packit Service 76cb02
        if (self._check_input()):
Packit Service 76cb02
            return True
Packit Service 76cb02
        # Check that all the required utilities are there
Packit Service 76cb02
        if (self._check_dependencies()):
Packit Service 76cb02
            return True
Packit Service 76cb02
        # Check some alternative reasons
Packit Service 76cb02
        if (self._check_cyrillic()):
Packit Service 76cb02
            return True
Packit Service 76cb02
        return False
Packit Service 76cb02
Packit Service 76cb02
    def _check_imagerun(self, cmd):
Packit Service 76cb02
        """
Packit Service 76cb02
        In case of failed image converter calls check on dependency problems.
Packit Service 76cb02
Packit Service 76cb02
        In Debian dblatex package dependencies on image converters are not
Packit Service 76cb02
        absolute, as image conversion is not dblatex's core functionality.
Packit Service 76cb02
        Thus the converters may be not installed.  Therefore check for each one:
Packit Service 76cb02
        If it is used but missing, dump an appropriate hint.
Packit Service 76cb02
        """
Packit Service 76cb02
        aptcache = self.aptcache
Packit Service 76cb02
        warn_msgs = []
Packit Service 76cb02
        if ((cmd.startswith('convert') or cmd.find('&& convert') > -1)
Packit Service 76cb02
            and not aptcache['graphicsmagick-imagemagick-compat'].is_installed
Packit Service 76cb02
            and not aptcache['imagemagick'].is_installed):
Packit Service 76cb02
            warn_msgs.append('For image conversion one of Debian packages'
Packit Service 76cb02
                             + ' graphicsmagick-imagemagick-compat')
Packit Service 76cb02
            warn_msgs.append('or imagemagick is needed')
Packit Service 76cb02
        if ((cmd.startswith('epstopdf') or cmd.find('&& epstopdf') > -1)
Packit Service 76cb02
            and not aptcache['ghostscript'].is_installed):
Packit Service 76cb02
            warn_msgs.append('For image conversion Debian package ghostscript'
Packit Service 76cb02
                             + ' is needed')
Packit Service 76cb02
        if ((cmd.startswith('fig2dev') or cmd.find('&& fig2dev') > -1)
Packit Service 76cb02
            and not aptcache['transfig'].is_installed):
Packit Service 76cb02
            warn_msgs.append('For image conversion Debian package transfig is'
Packit Service 76cb02
                             + ' needed')
Packit Service 76cb02
        if ((cmd.startswith('inkscape') or cmd.find('&& inkscape') > -1)
Packit Service 76cb02
            and not aptcache['inkscape'].is_installed):
Packit Service 76cb02
            warn_msgs.append('For image conversion Debian package inkscape is'
Packit Service 76cb02
                             + ' needed')
Packit Service 76cb02
        if warn_msgs:
Packit Service 76cb02
            print >> sys.stderr, "\n" + "\n".join(warn_msgs) + "\n"
Packit Service 76cb02
            return True
Packit Service 76cb02
        else:
Packit Service 76cb02
            return False
Packit Service 76cb02
Packit Service 76cb02
    def _check_input(self):
Packit Service 76cb02
        """
Packit Service 76cb02
        In case of failed processing try to validate the input.
Packit Service 76cb02
Packit Service 76cb02
        As invalid DocBook sometimes results in strange TeX error messages, a
Packit Service 76cb02
        hint about the failure cause may be helpful.
Packit Service 76cb02
        Post failure validation is a convenience function and thus works in
Packit Service 76cb02
        a best effort approach, that is it will silently skip any problems,
Packit Service 76cb02
        e.g. the external validation program xmllint not installed.
Packit Service 76cb02
        """
Packit Service 76cb02
        obj = self.object.run
Packit Service 76cb02
        nulldev0 = open(os.devnull, "r")
Packit Service 76cb02
        nulldev1 = open(os.devnull, "w")
Packit Service 76cb02
        try:
Packit Service 76cb02
            rc = subprocess.Popen(['xmllint', '--noout', '--postvalid',
Packit Service 76cb02
                                   '--xinclude', obj.input],
Packit Service 76cb02
                                   stdin=nulldev0,
Packit Service 76cb02
                                   stderr=nulldev1,
Packit Service 76cb02
                                   stdout=nulldev1).wait()
Packit Service 76cb02
        except:
Packit Service 76cb02
            rc = -1
Packit Service 76cb02
Packit Service 76cb02
        nulldev0.close()
Packit Service 76cb02
        nulldev1.close()
Packit Service 76cb02
Packit Service 76cb02
        if rc == 3 or rc == 4:
Packit Service 76cb02
            print >> sys.stderr
Packit Service 76cb02
            print >> sys.stderr, 'A possible reason for transformation',
Packit Service 76cb02
            print >> sys.stderr, 'failure is invalid DocBook'
Packit Service 76cb02
            print >> sys.stderr, '(as reported by xmllint)'
Packit Service 76cb02
            print >> sys.stderr
Packit Service 76cb02
            return True
Packit Service 76cb02
        else:
Packit Service 76cb02
            return False
Packit Service 76cb02
Packit Service 76cb02
    def _check_dependencies(self):
Packit Service 76cb02
        """
Packit Service 76cb02
        In case of failed processing check on dependency problems.
Packit Service 76cb02
Packit Service 76cb02
        For not commonly used dblatex functionality the Debian package
Packit Service 76cb02
        dependencies are not absolute, thus the functionality may be not
Packit Service 76cb02
        installed.  Therefore check for each one:
Packit Service 76cb02
        If it is used but a needed dependency is missing, dump an appropriate
Packit Service 76cb02
        hint.
Packit Service 76cb02
        """
Packit Service 76cb02
        obj = self.object.run
Packit Service 76cb02
        aptcache = self.aptcache
Packit Service 76cb02
        warn_msgs = []
Packit Service 76cb02
        if obj.backend == 'xetex':
Packit Service 76cb02
            for debian_pkg in 'texlive-xetex', 'lmodern':
Packit Service 76cb02
                if not aptcache[debian_pkg].is_installed:
Packit Service 76cb02
                    warn_msgs.append('For xetex backend Debian package '
Packit Service 76cb02
                                     + debian_pkg + ' is needed')
Packit Service 76cb02
        if obj.input_format == 'sgml':
Packit Service 76cb02
            for debian_pkg in 'docbook', 'opensp':
Packit Service 76cb02
                if not aptcache[debian_pkg].is_installed:
Packit Service 76cb02
                    warn_msgs.append('For SGML documents Debian package '
Packit Service 76cb02
                                     + debian_pkg + ' is needed')
Packit Service 76cb02
        if obj.runtex.texer.encoding == 'utf8':
Packit Service 76cb02
            debian_pkg = 'texlive-lang-cyrillic'
Packit Service 76cb02
            if not aptcache[debian_pkg].is_installed:
Packit Service 76cb02
                warn_msgs.append('For utf8 encoding Debian package '
Packit Service 76cb02
                                 + debian_pkg + ' is needed')
Packit Service 76cb02
        if warn_msgs:
Packit Service 76cb02
            print >> sys.stderr, "\n" + "\n".join(warn_msgs) + "\n"
Packit Service 76cb02
            return True
Packit Service 76cb02
        else:
Packit Service 76cb02
            return False
Packit Service 76cb02
Packit Service 76cb02
    def _check_cyrillic(self):
Packit Service 76cb02
        obj = self.object.run
Packit Service 76cb02
        """
Packit Service 76cb02
        In case of failed processing check on the "cyrillic scenario":
Packit Service 76cb02
Packit Service 76cb02
        Transforming cyrillic documents will fail when neither using the
Packit Service 76cb02
        XeTeX backend nor setting option latex.unicode.use
Packit Service 76cb02
        In this case a hint to XeTeX (as the preferred way) may be helpful.
Packit Service 76cb02
        Post failure validation is a convenience function and thus works in
Packit Service 76cb02
        a best effort approach, that is it will silently skip any problems.
Packit Service 76cb02
        """
Packit Service 76cb02
        # This kind of error cannot occur with backends that natively support
Packit Service 76cb02
        # Unicode
Packit Service 76cb02
        if obj.backend == 'xetex':
Packit Service 76cb02
            return False
Packit Service 76cb02
Packit Service 76cb02
        try:
Packit Service 76cb02
            for log_entry in obj.runtex.texer.tex.log.get_errors():
Packit Service 76cb02
                if (log_entry['text']
Packit Service 76cb02
                    == r'Undefined control sequence \cyrchar.'):
Packit Service 76cb02
                    print >> sys.stderr
Packit Service 76cb02
                    print >> sys.stderr, 'Transformation failure',
Packit Service 76cb02
                    print >> sys.stderr, 'might be caused by handling a',
Packit Service 76cb02
                    print >> sys.stderr, 'cyrillic document'
Packit Service 76cb02
                    print >> sys.stderr, 'without the XeTeX backend'
Packit Service 76cb02
                    print >> sys.stderr
Packit Service 76cb02
                    return True
Packit Service 76cb02
        except:
Packit Service 76cb02
            pass
Packit Service 76cb02
        return False
Packit Service 76cb02