Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Bower components Debian packages RPM packages NuGet packages

beebox / crossover   deb

Repository URL to install this package:

Version: 18.5.0-1 

/ opt / cxoffice / lib / python / cxfixesparser.py

# (c) Copyright 2014-2015. CodeWeavers, Inc.

import sys
import time
import traceback
import xml.sax
import xml.sax.handler

import cxlog
import cxfixesobjs

def install_sax_workaround():
    # work-around for a bug in xml.sax.xmlreader
    # see http://mail.python.org/pipermail/python-bugs-list/2006-December/036370.html
    def _self_contains(self, item):
        try:
            # pylint: disable=W0104
            self[item] # Yes, pylint, this really does have an effect.
            return True
        except KeyError:
            return False

    # pylint: disable=W0621
    import xml.sax.xmlreader

    if not hasattr(xml.sax.xmlreader.AttributesImpl, '__contains__'):
        xml.sax.xmlreader.AttributesImpl.__contains__ = _self_contains

install_sax_workaround()

class _CXFixesHandler(xml.sax.handler.ContentHandler):


    # This is the default for the property indicating which C4 profile to
    # install in the case of standalone c4p files.
    autorun = None

    def __init__(self):
        xml.sax.handler.ContentHandler.__init__(self)
        self.release = None
        self.distributions = {}
        self.fixes = {}
        self.parse_errors = []
        self.locator = None

        self._objects = [self]
        self._states = [_CXFixesHandler.state_init]
        self._skip = 0

        self._key = None
        self._property = None
        self._string = None
        self._default = False


    #####
    #
    # Standard SAX callbacks
    #
    #####

    def setDocumentLocator(self, locator):
        # pylint: disable=C0103
        self.locator = locator

    def startElement(self, name, attrs):
        """Called when the SAX parser finds an opening XML tag, <c4p> for
        instance.

        This calls the startElement handler which is at the top of the state
        stack. In turn this handler normally pushes a new state on top of the
        state stack to handle the tags contained therein.

        If the opening tag corresponds to an object, for instance <app>, then
        it will also push the nwe object on top of the object stack.
        """
        # pylint: disable=C0103
        self._states[-1][0](self, name, attrs)

    def endElement(self, name):
        """Called when the SAX parser finds a closing XML tag, </c4p> for
        instance.

        endElement() calls can always be exactly matched to a startElement()
        call. If the XML file is malformed, for instance if it has a mismatched
        closing tag, then the SAX parser will raise an exception rather than
        call endElement() on the mismatched tag.

        This calls the endElement handler which is at the top of the state
        stack. This handler must always pop the state stack, and should also
        pop the object stack if an object was created by the opening XML tag.
        """
        # pylint: disable=C0103
        self._states[-1][1](self, name)

    def characters(self, content):
        """Called by the SAX parser to handle the characters in between tags.

        This calls the characters handler which is at the top of the state
        stack. Note that these characters may be given by the SAX parser in
        arbitrarily small chunks. So it is usually necessary to concatenate
        them together.
        """
        self._states[-1][2](self, content)

    def endDocument(self):
        # pylint: disable=C0103
        if len(self._states) != 1:
            self.fail("an internal error occurred: len(_states) = %d" % len(self._states))


    #####
    #
    # Error reporting and debugging
    #
    #####

    def fail(self, error):
        """Reports a fatal error as a SAXParseException."""
        # SAXParseException really derives from Exception
        # pylint: disable=E0012,W0710
        raise xml.sax.SAXParseException(error, None, self.locator)

    def warn(self, message):
        """Prints a warning as a debug trace in the cxfixesparser channel."""
        cxlog.log_("cxfixesparser",
                   "%s:%d:%d: %s" % (self.locator.getSystemId(),
                                     self.locator.getLineNumber(),
                                     self.locator.getColumnNumber(), cxlog.to_str(message)))


    #####
    #
    # Some common handlers
    #
    #####

    def _startof_unexpected(self, name, _attrs):
        self.fail("unexpected opening tag %s" % name)

    def _end_simple(self, name):
        """Pops the state stack but leave the object stack as is.

        This is mostly used by attribute handlers.
        """
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))
        self._states.pop()

    def _end_popobj(self, name):
        """Pops the state and object stacks."""
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))
        self._objects.pop()
        self._states.pop()

    def _endof_unexpected(self, name):
        self.fail("unexpected closing tag %s" % name)

    def _unexpected_characters(self, content):
        """Warns about unexpected string snippets."""
        if not content.isspace():
            self.warn("unexpected characters '%s'" % repr(content))

    def _string_characters(self, content):
        """Collects the string snippets as SAX returns them so we can
        concatenate them later.
        """
        self._string.append(content)

    def _string_unexpected(self, _content):
        self.fail("unexpected string content")


    #####
    #
    # Properties represented by an object
    #
    # This parses a tag of the form '<tag><child>...</child></tag>' where
    # the <child> tag has already beed converted to an object which is to
    # be stored in the <current-object>.<prop> list.
    #
    #####

    def _startof_objectlist(self, prop, child, state):
        obj = self._objects[-1]
        if prop not in obj.__dict__:
            obj.__dict__[prop] = [child]
        else:
            obj.__dict__[prop].append(child)
        self._objects.append(child)
        self._states.append(state)


    #####
    #
    # String properties
    #
    # This parses a tag of the form '<tag>string</tag>' and puts the string
    # in the <current-object>.<prop> field or warns if it has already been set.
    #
    #####

    def _startof_string(self, prop):
        self._property = prop
        self._string = []
        self._states.append(_CXFixesHandler.state_string)
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(prop)))

    def _endof_string(self, _unused):
        if self._property is not None:
            obj = self._objects[-1]
            value = ''.join(self._string)
            cxlog.log_('cxfixesparser', "obj=%s property=%s value=%s" % (cxlog.debug_str(obj), cxlog.to_str(self._property), cxlog.debug_str(value)))
            if self._property not in obj.__dict__:
                obj.__dict__[self._property] = value
            else:
                self.warn("%s has been set already" % self._property)

        self._states.pop()
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(self._property)))

    state_string = (_startof_unexpected, _endof_string, _string_characters)


    #####
    #
    # Attribute string properties
    #
    # This parses a tag of the form '<tag attr="string"/>' and puts the string
    # in the <current-object>.<prop> field.
    #
    #####

    def _startof_attr2string(self, prop, attr, attrs):
        obj = self._objects[-1]
        value = attrs.get(attr, '')
        if prop not in obj.__dict__:
            obj.__dict__[prop] = value
        else:
            self.warn("%s has been set already" % prop)
        self._states.append(_CXFixesHandler.state_attr2string)
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(prop)))

    state_attr2string = (_startof_unexpected, _end_simple, _string_unexpected)


    #####
    #
    # String set properties
    #
    # This parses a tag of the form '<tag>string</tag>' and puts the string
    # in the <current-object>.<prop> set.
    #
    #####

    def _startof_stringset(self, prop):
        self._property = prop
        self._string = []
        self._states.append(_CXFixesHandler.state_stringset)
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(prop)))

    def _endof_stringset(self, _unused):
        obj = self._objects[-1]
        value = ''.join(self._string)
        cxlog.log_('cxfixesparser', "obj=%s property=%s value=%s" % (cxlog.debug_str(obj), cxlog.to_str(self._property), cxlog.debug_str(value)))
        if self._property not in obj.__dict__:
            obj.__dict__[self._property] = set((value,))
        else:
            obj.__dict__[self._property].add(value)
        self._states.pop()
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(self._property)))

    state_stringset = (_startof_unexpected, _endof_stringset, _string_characters)


    #####
    #
    # Attribute string set properties
    #
    # This parses a tag of the form '<tag attr="string"/>' and puts the string
    # in the <current-object>.<prop> set.
    #
    #####

    def _startof_attr2stringset(self, prop, attr, attrs):
        obj = self._objects[-1]
        value = attrs.get(attr, '')
        if prop not in obj.__dict__:
            obj.__dict__[prop] = set((value,))
        else:
            obj.__dict__[prop].add(value)
        self._states.append(_CXFixesHandler.state_attr2stringset)
        cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(prop)))

    state_attr2stringset = (_startof_unexpected, _end_simple, _string_unexpected)


    #####
    #
    # Skipping tags
    #
    #####

    def _startof_skip(self, name, _attrs=None):
        if not self._skip:
            self.warn("skipping unknown tag %s" % name)
            self._states.append(_CXFixesHandler.state_skip)
        self._skip += 1

    def _endof_skip(self, _unused):
        self._skip -= 1
        if not self._skip:
            self._states.pop()

    def _ignore_characters(self, _content):
        pass

    state_skip = (_startof_skip, _endof_skip, _ignore_characters)


    #####
    #
    # Document handler
    #
    #####

    def _childof_init(self, name, attrs):
        if name == 'cxfixes':
            self.release = int(attrs.get('release', '0'), 10)
            self._states.append(_CXFixesHandler.state_cxfixes)
            cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))
        else:
            self.warn("expecting c4p tag, got %s" % name)

    state_init = (_childof_init, _endof_unexpected, _string_unexpected)


    #####
    #
    # <cxfixes> handler
    #
    #####

    def _childof_cxfixes(self, name, _attrs):
        if name == 'distributions':
            self._states.append(_CXFixesHandler.state_distributions)
            cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))
        elif name == 'fixes':
            self._states.append(_CXFixesHandler.state_fixes)
            cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))
        else:
            self._startof_skip(name)

    state_cxfixes = (_childof_cxfixes, _end_simple, _unexpected_characters)


    #####
    #
    # <distributions> handler
    #
    #####

    def _childof_distributions(self, name, attrs):
        if name == 'distribution':
            priority = int(attrs.get('priority', '0'), 10)
            distro = cxfixesobjs.CXDistribution(attrs['id'], attrs['name'],
                                                priority)
            self._objects.append(distro)
            self._states.append(_CXFixesHandler.state_distribution)
            cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))

        else:
            self._startof_skip(name)

    state_distributions = (_childof_distributions, _end_simple, _unexpected_characters)



    #####
    #
    # <distribution> handler
    #
    #####

    def _childof_distribution(self, name, attrs):
        if name == 'glob':
            distglob = cxfixesobjs.CXDistGlob(attrs['file'])
            self._startof_objectlist('globs', distglob,
                                     _CXFixesHandler.state_glob)
        elif name == 'updatecmd':
            self._startof_string('updatecmd')
        elif name == 'packagecmd':
            self._startof_string('packagecmd')
        elif name == 'fallback':
            self._startof_attr2string('fallback', 'distribution', attrs)
        else:
            self._startof_skip(name)

    def _endof_distribution(self, _name):
        distro = self._objects.pop()
        try:
            distro.validate()
            self.distributions[distro.distid] = distro
        except AttributeError:
            exception = sys.exc_info()[1]
            self.parse_errors.append(str(exception))
            msg = "the %s distribution is not valid, ignoring it" % distro.distid
            self.parse_errors.append(msg)
            if cxlog.is_on('cxfixesparser'):
                traceback.print_exc()
                cxlog.warn(msg)
        except: # pylint: disable=W0702
            exception = sys.exc_info()[1] # Python 2 and 3 compatible
            self.parse_errors.append(str(exception))
            msg = "an unexpected error occurred while parsing the %s distribution, ignoring it" % distro.distid
            self.parse_errors.append(msg)
            if cxlog.is_on('cxfixesparser'):
                traceback.print_exc()
                cxlog.warn(msg)

        # To simplify debugging, also check that the object stack only contains
        # 'self' at this point
        if len(self._objects) != 1:
            self.fail("internal error: len(_objects) = %d" % len(self._objects))
        self._states.pop()

    state_distribution = (_childof_distribution, _endof_distribution, _unexpected_characters)


    #####
    #
    # <glob> handler
    #
    #####

    def _childof_glob(self, name, _attrs):
        if name == 'pattern':
            self._startof_stringset('patterns')
        else:
            self._startof_skip(name)

    state_glob = (_childof_glob, _end_popobj, _unexpected_characters)


    #####
    #
    # <fixes> handler
    #
    #####

    def _childof_fixes(self, name, attrs):
        if name == 'fix':
            fix = cxfixesobjs.CXFix(attrs['id'], attrs.get('tiedto'))
            self._objects.append(fix)
            self._states.append(_CXFixesHandler.state_fix)
            cxlog.log_('cxfixesparser', "objects=%d states=%d %s" % (len(self._objects), len(self._states), cxlog.to_str(name)))

        else:
            self._startof_skip(name)

    state_fixes = (_childof_fixes, _end_simple, _unexpected_characters)

    #####
    #
    # <fix> handler
    #
    #####

    def _childof_fix(self, name, attrs):
        if name == 'tiedto':
            self._startof_attr2stringset('tiedto', 'id', attrs)
        elif name == 'fixfor':
            fix = self._objects[-1]
            distfix = cxfixesobjs.CXDistFix(attrs['distribution'],
                                            attrs.get('bitness', None))
            # This is a CXDistFix, not a _CXFixesHandler as Pylint believes
            # pylint: disable=E1101
            fix.add_distfix(distfix)
            self._objects.append(distfix)
            self._states.append(_CXFixesHandler.state_fixfor)
        else:
            self._startof_skip(name)

    def _endof_fix(self, _name):
        fix = self._objects.pop()
        try:
            fix.validate()
            self.fixes[fix.errid] = fix
        except AttributeError:
            exception = sys.exc_info()[1]
            self.parse_errors.append(str(exception))
            msg = "the %s fix is not valid, ignoring it" % fix.errid
            self.parse_errors.append(msg)
            if cxlog.is_on('cxfixesparser'):
                traceback.print_exc()
                cxlog.warn(msg)
        except: # pylint: disable=W0702
            exception = sys.exc_info()[1] # Python 2 and 3 compatible
            self.parse_errors.append(str(exception))
            msg = "an unexpected error occurred while parsing the %s fix, ignoring it" % fix.errid
            self.parse_errors.append(msg)
            if cxlog.is_on('cxfixesparser'):
                traceback.print_exc()
                cxlog.warn(msg)

        # To simplify debugging, also check that the object stack only contains
        # 'self' at this point
        if len(self._objects) != 1:
            self.fail("internal error: len(_objects) = %d" % len(self._objects))
        self._states.pop()

    state_fix = (_childof_fix, _endof_fix, _unexpected_characters)



    #####
    #
    # <fixfor> handler
    #
    #####

    def _childof_fixfor(self, name, attrs):
        if name == 'package':
            self._startof_attr2stringset('packages', 'name', attrs)
        else:
            self._startof_skip(name)

    state_fixfor = (_childof_fixfor, _end_popobj, _unexpected_characters)


#####
#
# The public API
#
#####

def read_file(filename):
    start = time.time()
    handler = _CXFixesHandler()

    try:
        xml.sax.parse(filename, handler)
        errors = cxfixesobjs.validate(handler.distributions, handler.fixes)
        if not errors:
            cxlog.log("parsing '%s' took %0.3fs" % (cxlog.to_str(filename), time.time() - start))
            return (handler.release, handler.distributions, handler.fixes, handler.parse_errors)
        handler.parse_errors.extend(errors)
    except: # pylint: disable=W0702
        exception = sys.exc_info()[1] # Python 2 and 3 compatible
        import errno
        if hasattr(exception, 'errno') and exception.errno == errno.ENOENT:
            handler.parse_errors.append("'%s' does not exist" % filename)
        else:
            msg = "an unexpected error occurred while parsing '%s'. Ignoring this file" % filename
            handler.parse_errors.append(msg)
            if cxlog.is_on('cxfixesparser'):
                traceback.print_exc()
                cxlog.warn(msg)

    return (None, None, None, handler.parse_errors)