Beispiel #1
0
    def validatedValue(self, value):
        assert value

        if value not in self.choices:
            raise FieldValueError(
                u"value is %r but must be one of: %s" % (value, _tools.humanReadableList(self.choices))
            )
        return value
Beispiel #2
0
    def validatedValue(self, value):
        assert value

        if value not in self.choices:
            raise FieldValueError(
                u"value is %r but must be one of: %s" %
                (value, _tools.humanReadableList(self.choices)))
        return value
Beispiel #3
0
def main(arguments):
    assert arguments is not None

    _FORMAT_CSV = "csv"
    _FORMAT_DOCBOOK = "docbook"
    _FORMAT_RST = "rst"
    _FORMATS = [_FORMAT_CSV, _FORMAT_DOCBOOK, _FORMAT_RST]
    _FORMAT_TO_SUFFIX_MAP = {
                             _FORMAT_CSV: ".csv",
                             _FORMAT_DOCBOOK: ".xml",
                             _FORMAT_RST: ".rst"
                             }
    usage = "usage: %prog [options] ODS-FILE [OUTPUT-FILE]"
    parser = optparse.OptionParser(usage)
    parser.set_defaults(format=_FORMAT_CSV, id="insert-id", sheet=1, title="Insert Title")
    parser.add_option("-f", "--format", metavar="FORMAT", type="choice", choices=_FORMATS, dest="format",
                      help="output format: %s (default: %%default)" % _tools.humanReadableList(_FORMATS))
    parser.add_option("-i", "--id", metavar="ID", dest="id", help="XML ID table can be referenced with (default: %default)")
    parser.add_option("-1", "--heading", action="store_true", dest="firstRowIsHeading", help="render first row as heading")
    parser.add_option("-s", "--sheet", metavar="SHEET", type="long", dest="sheet", help="sheet to convert (default: %default)")
    parser.add_option("-t", "--title", metavar="TITLE", dest="title", help="title to be used for XML table (default: %default)")
    options, others = parser.parse_args(arguments)

    # TODO: If no output file is specified, derive name from input file.
    if options.sheet < 1:
        _log.error("option --sheet is %d but must be at least 1" % options.sheet)
        sys.exit(1)
    elif len(others) in [1, 2]:
        sourceFilePath = others[0]
        if len(others) == 2:
            targetFilePath = others[1]
        else:
            assert options.format in _FORMAT_TO_SUFFIX_MAP
            suffix = _FORMAT_TO_SUFFIX_MAP[options.format]
            targetFilePath = _tools.withSuffix(sourceFilePath, suffix)
        _log.info("convert %r to %r using format %r" % (sourceFilePath, targetFilePath, options.format))
        try:
            if options.format == _FORMAT_CSV:
                if options.firstRowIsHeading:
                    _log.error("option --heading can not be used with --format=csv")
                    sys.exit(1)
                toCsv(sourceFilePath, targetFilePath, sheet=options.sheet)
            elif options.format == _FORMAT_DOCBOOK:
                # FIXME: Add support for --heading with DocBook.
                assert not options.firstRowIsHeading
                toDocBookXml(sourceFilePath, targetFilePath, xmlId=options.id, title=options.title, sheet=options.sheet)
            elif options.format == _FORMAT_RST:
                toRst(sourceFilePath, targetFilePath, firstRowIsHeading=options.firstRowIsHeading, sheet=options.sheet)
            else:  # pragma: no cover
                raise NotImplementedError(u"format=%r" % (options.format))
        except EnvironmentError, error:
            _log.error("cannot convert ods to csv: %s" % error)
            sys.exit(1)
        except Exception, error:
            _log.error("cannot convert ods to csv: %s" % error, exc_info=1)
            sys.exit(1)
Beispiel #4
0
 def _validatedChoice(self, key, value, choices):
     """
     Validate that `value` is one of the available `choices` and otherwise raise `DataFormatValueError`.
     Always returns `value`. To be called from `validated()`.
     """
     assert key
     assert choices
     if value not in choices:
         raise DataFormatValueError(u"value for data format property %r is %r but must be one of: %s"
                                    % (key, value, _tools.humanReadableList(choices)))
     return value
Beispiel #5
0
 def _validatedChoice(self, key, value, choices):
     """
     Validate that `value` is one of the available `choices` and otherwise raise `DataFormatValueError`.
     Always returns `value`. To be called from `validated()`.
     """
     assert key
     assert choices
     if value not in choices:
         raise DataFormatValueError(
             u"value for data format property %r is %r but must be one of: %s"
             % (key, value, _tools.humanReadableList(choices)))
     return value
Beispiel #6
0
    def _normalizedKey(self, key):
        assert key is not None

        # Normalize key.
        keyParts = key.lower().split()
        result = ""
        for keyPart in keyParts:
            if result:
                result += " "
            result += keyPart

        # Validate key.
        if result not in self._allKeys:
            raise DataFormatSyntaxError(u"data format property is %r but must be one of: %s"
                                        % (result, _tools.humanReadableList(self._allKeys)))

        return result
Beispiel #7
0
def getFieldNameIndex(supposedFieldName, availableFieldNames):
    """
    The index of `supposedFieldName` in `availableFieldNames`.

    In case it is missing, raise a `FieldLookupError`.
    """
    assert supposedFieldName is not None
    assert supposedFieldName == supposedFieldName.strip()
    assert availableFieldNames

    fieldName = supposedFieldName.strip()
    try:
        fieldIndex = availableFieldNames.index(fieldName)
    except ValueError:
        raise FieldLookupError(
            u"unknown field name %r must be replaced by one of: %s" %
            (fieldName, _tools.humanReadableList(availableFieldNames)))
    return fieldIndex
Beispiel #8
0
    def _normalizedKey(self, key):
        assert key is not None

        # Normalize key.
        keyParts = key.lower().split()
        result = ""
        for keyPart in keyParts:
            if result:
                result += " "
            result += keyPart

        # Validate key.
        if result not in self._allKeys:
            raise DataFormatSyntaxError(
                u"data format property is %r but must be one of: %s" %
                (result, _tools.humanReadableList(self._allKeys)))

        return result
Beispiel #9
0
def getFieldNameIndex(supposedFieldName, availableFieldNames):
    """
    The index of `supposedFieldName` in `availableFieldNames`.

    In case it is missing, raise a `FieldLookupError`.
    """
    assert supposedFieldName is not None
    assert supposedFieldName == supposedFieldName.strip()
    assert availableFieldNames

    fieldName = supposedFieldName.strip()
    try:
        fieldIndex = availableFieldNames.index(fieldName)
    except ValueError:
        raise FieldLookupError(
            u"unknown field name %r must be replaced by one of: %s"
            % (fieldName, _tools.humanReadableList(availableFieldNames))
        )
    return fieldIndex
Beispiel #10
0
def createDataFormat(name):
    """
    Factory function to create the specified data format.
    """
    assert name is not None

    _NAME_TO_FORMAT_MAP = {
                            FORMAT_CSV: CsvDataFormat,
                            FORMAT_DELIMITED: DelimitedDataFormat,
                            FORMAT_EXCEL: ExcelDataFormat,
                            FORMAT_FIXED: FixedDataFormat,
                            FORMAT_ODS: OdsDataFormat
                           }
    actualName = name.lower()
    formatClass = _NAME_TO_FORMAT_MAP.get(actualName)
    if formatClass is None:
        dataFormatNames = _NAME_TO_FORMAT_MAP.keys()
        dataFormatNames.sort()
        raise DataFormatSyntaxError(u"data format is %r but must be one of: %r"
                                    % (actualName, _tools.humanReadableList(dataFormatNames)))
    result = formatClass()
    return result
Beispiel #11
0
def createDataFormat(name):
    """
    Factory function to create the specified data format.
    """
    assert name is not None

    _NAME_TO_FORMAT_MAP = {
        FORMAT_CSV: CsvDataFormat,
        FORMAT_DELIMITED: DelimitedDataFormat,
        FORMAT_EXCEL: ExcelDataFormat,
        FORMAT_FIXED: FixedDataFormat,
        FORMAT_ODS: OdsDataFormat
    }
    actualName = name.lower()
    formatClass = _NAME_TO_FORMAT_MAP.get(actualName)
    if formatClass is None:
        dataFormatNames = _NAME_TO_FORMAT_MAP.keys()
        dataFormatNames.sort()
        raise DataFormatSyntaxError(
            u"data format is %r but must be one of: %r" %
            (actualName, _tools.humanReadableList(dataFormatNames)))
    result = formatClass()
    return result
Beispiel #12
0
 def testCanBuildHumanReadableList(self):
     self.assertEqual(_tools.humanReadableList([]), "")
     self.assertEqual(_tools.humanReadableList(["a"]), "'a'")
     self.assertEqual(_tools.humanReadableList(["a", "b"]), "'a' or 'b'")
     self.assertEqual(_tools.humanReadableList(["a", "b", "c"]),
                      "'a', 'b' or 'c'")
Beispiel #13
0
 def testCanBuildHumanReadableList(self):
     self.assertEqual(_tools.humanReadableList([]), "")
     self.assertEqual(_tools.humanReadableList(["a"]), "'a'")
     self.assertEqual(_tools.humanReadableList(["a", "b"]), "'a' or 'b'")
     self.assertEqual(_tools.humanReadableList(["a", "b", "c"]), "'a', 'b' or 'c'")
Beispiel #14
0
    def _validatedCharacter(self, key, value):
        r"""
        A single character intended as value for data format property ``key``
        derived from ``value``, which can be:

        * a decimal or hex number (prefixed with "0x") referring to the ASCII/Unicode of the character
        * a string containing a single character such as "\t".
        * a symbolic name such as "Tab".

        Anything else yields a `DataFormatSyntaxError`.

        >>> format = DelimitedDataFormat()
        >>> format._validatedCharacter("x", "34")
        '"'
        >>> format._validatedCharacter("x", "9")
        '\t'
        >>> format._validatedCharacter("x", "0x9")
        '\t'
        >>> format._validatedCharacter("x", "Tab")
        '\t'
        >>> format._validatedCharacter("x", "\t")
        '\t'
        >>> format._validatedCharacter("x", "")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must be specified
        >>> format._validatedCharacter("x", "Tab Tab")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must describe a single character but is: 'Tab Tab'
        >>> format._validatedCharacter("x", "17.23")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: numeric value for data format property 'x' must be an integer but is: '17.23'
        >>> format._validatedCharacter("x", "Hugo")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: symbolic name 'Hugo' for data format property 'x' must be one of: 'cr', 'ff', 'lf', 'tab' or 'vt'
        >>> format._validatedCharacter("x", "( ")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must a number, a single character or a symbolic name but is: '( '
        >>> format._validatedCharacter("x", "\"\\")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must a number, a single character or a symbolic name but is: '"\\'
        >>> format._validatedCharacter("x", "\"abc\"")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: text for data format property 'x' must be a single character but is: '"abc"'
        """
        # TODO: Consolidate code with `ranges.__init__()`.
        assert key
        assert value is not None
        if len(value) == 1 and (value < "0" or value > "9"):
            result = value
        else:
            result = None
            tokens = tokenize.generate_tokens(StringIO.StringIO(value).readline)
            nextToken = tokens.next()
            if _tools.isEofToken(nextToken):
                raise DataFormatSyntaxError(u"value for data format property %r must be specified" % key)
            nextType = nextToken[0]
            nextValue = nextToken[1]
            if nextType == token.NUMBER:
                try:
                    if nextValue[:2].lower() == "0x":
                        nextValue = nextValue[2:]
                        base = 16
                    else:
                        base = 10
                    longValue = long(nextValue, base)
                except ValueError:
                    raise DataFormatSyntaxError(u"numeric value for data format property %r must be an integer but is: %r" % (key, value))
            elif nextType == token.NAME:
                try:
                    longValue = tools.SYMBOLIC_NAMES_MAP[nextValue.lower()]
                except KeyError:
                    validSymbols = _tools.humanReadableList(sorted(tools.SYMBOLIC_NAMES_MAP.keys()))
                    raise DataFormatSyntaxError(u"symbolic name %r for data format property %r must be one of: %s" % (value, key, validSymbols))
            elif nextType == token.STRING:
                if len(nextValue) != 3:
                    raise DataFormatSyntaxError(u"text for data format property %r must be a single character but is: %r" % (key, value))
                leftQuote = nextValue[0]
                rightQuote = nextValue[2]
                assert leftQuote in "\"\'", u"leftQuote=%r" % leftQuote
                assert rightQuote in "\"\'", u"rightQuote=%r" % rightQuote
                longValue = ord(nextValue[1])
            else:
                raise DataFormatSyntaxError(u"value for data format property %r must a number, a single character or a symbolic name but is: %r" % (key, value))
            # Ensure there are no further tokens.
            nextToken = tokens.next()
            if not _tools.isEofToken(nextToken):
                raise DataFormatSyntaxError(u"value for data format property %r must describe a single character but is: %r" % (key, value))

            assert longValue is not None
            assert longValue >= 0
            result = chr(longValue)
        assert result is not None
        return result
Beispiel #15
0
    def _validatedCharacter(self, key, value):
        r"""
        A single character intended as value for data format property ``key``
        derived from ``value``, which can be:

        * a decimal or hex number (prefixed with "0x") referring to the ASCII/Unicode of the character
        * a string containing a single character such as "\t".
        * a symbolic name such as "Tab".

        Anything else yields a `DataFormatSyntaxError`.

        >>> format = DelimitedDataFormat()
        >>> format._validatedCharacter("x", "34")
        '"'
        >>> format._validatedCharacter("x", "9")
        '\t'
        >>> format._validatedCharacter("x", "0x9")
        '\t'
        >>> format._validatedCharacter("x", "Tab")
        '\t'
        >>> format._validatedCharacter("x", "\t")
        '\t'
        >>> format._validatedCharacter("x", "")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must be specified
        >>> format._validatedCharacter("x", "Tab Tab")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must describe a single character but is: 'Tab Tab'
        >>> format._validatedCharacter("x", "17.23")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: numeric value for data format property 'x' must be an integer but is: '17.23'
        >>> format._validatedCharacter("x", "Hugo")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: symbolic name 'Hugo' for data format property 'x' must be one of: 'cr', 'ff', 'lf', 'tab' or 'vt'
        >>> format._validatedCharacter("x", "( ")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must a number, a single character or a symbolic name but is: '( '
        >>> format._validatedCharacter("x", "\"\\")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: value for data format property 'x' must a number, a single character or a symbolic name but is: '"\\'
        >>> format._validatedCharacter("x", "\"abc\"")
        Traceback (most recent call last):
            ...
        DataFormatSyntaxError: text for data format property 'x' must be a single character but is: '"abc"'
        """
        # TODO: Consolidate code with `ranges.__init__()`.
        assert key
        assert value is not None
        if len(value) == 1 and (value < "0" or value > "9"):
            result = value
        else:
            result = None
            tokens = tokenize.generate_tokens(
                StringIO.StringIO(value).readline)
            nextToken = tokens.next()
            if _tools.isEofToken(nextToken):
                raise DataFormatSyntaxError(
                    u"value for data format property %r must be specified" %
                    key)
            nextType = nextToken[0]
            nextValue = nextToken[1]
            if nextType == token.NUMBER:
                try:
                    if nextValue[:2].lower() == "0x":
                        nextValue = nextValue[2:]
                        base = 16
                    else:
                        base = 10
                    longValue = long(nextValue, base)
                except ValueError:
                    raise DataFormatSyntaxError(
                        u"numeric value for data format property %r must be an integer but is: %r"
                        % (key, value))
            elif nextType == token.NAME:
                try:
                    longValue = tools.SYMBOLIC_NAMES_MAP[nextValue.lower()]
                except KeyError:
                    validSymbols = _tools.humanReadableList(
                        sorted(tools.SYMBOLIC_NAMES_MAP.keys()))
                    raise DataFormatSyntaxError(
                        u"symbolic name %r for data format property %r must be one of: %s"
                        % (value, key, validSymbols))
            elif nextType == token.STRING:
                if len(nextValue) != 3:
                    raise DataFormatSyntaxError(
                        u"text for data format property %r must be a single character but is: %r"
                        % (key, value))
                leftQuote = nextValue[0]
                rightQuote = nextValue[2]
                assert leftQuote in "\"\'", u"leftQuote=%r" % leftQuote
                assert rightQuote in "\"\'", u"rightQuote=%r" % rightQuote
                longValue = ord(nextValue[1])
            else:
                raise DataFormatSyntaxError(
                    u"value for data format property %r must a number, a single character or a symbolic name but is: %r"
                    % (key, value))
            # Ensure there are no further tokens.
            nextToken = tokens.next()
            if not _tools.isEofToken(nextToken):
                raise DataFormatSyntaxError(
                    u"value for data format property %r must describe a single character but is: %r"
                    % (key, value))

            assert longValue is not None
            assert longValue >= 0
            result = chr(longValue)
        assert result is not None
        return result
Beispiel #16
0
    def __init__(self, text, default=None):
        """
        Setup a range as specified by ``text``.

        ``text`` must be of the form "lower:upper" or "limit". In case ``text`` is empty (""), any
        value will be accepted by `validate()`. For example, "1:40" accepts values between 1
        and 40.

        ``default`` is an alternative text to use in case ``text`` is ``None`` or empty.
        """
        assert default is None or default.strip(), u"default=%r" % default

        # Find out if a `text` has been specified and if not, use optional `default` instead.
        hasText = (text is not None) and text.strip()
        if not hasText and default is not None:
            text = default
            hasText = True

        if not hasText:
            # Use empty ranges.
            self._description = None
            self._items = None
        else:
            self._description = text
            self._items = []
            # TODO: Consolidate code with `DelimitedDataFormat._validatedCharacter()`.
            tokens = tokenize.generate_tokens(StringIO.StringIO(text).readline)
            endReached = False
            while not endReached:
                lower = None
                upper = None
                colonFound = False
                afterHyphen = False
                nextToken = tokens.next()
                while not _tools.isEofToken(
                        nextToken) and not _tools.isCommaToken(nextToken):
                    nextType = nextToken[0]
                    nextValue = nextToken[1]
                    if nextType in (token.NAME, token.NUMBER, token.STRING):
                        if nextType == token.NUMBER:
                            try:
                                if nextValue[:2].lower() == "0x":
                                    nextValue = nextValue[2:]
                                    base = 16
                                else:
                                    base = 10
                                longValue = long(nextValue, base)
                            except ValueError:
                                raise RangeSyntaxError(
                                    u"number must be an integer but is: %r" %
                                    nextValue)
                            if afterHyphen:
                                longValue = -1 * longValue
                                afterHyphen = False
                        elif nextType == token.NAME:
                            try:
                                longValue = tools.SYMBOLIC_NAMES_MAP[
                                    nextValue.lower()]
                            except KeyError:
                                validSymbols = _tools.humanReadableList(
                                    sorted(tools.SYMBOLIC_NAMES_MAP.keys()))
                                raise RangeSyntaxError(
                                    u"symbolic name %r must be one of: %s" %
                                    (nextValue, validSymbols))
                        elif nextType == token.STRING:
                            if len(nextValue) != 3:
                                raise RangeSyntaxError(
                                    u"text for range must contain a single character but is: %r"
                                    % nextValue)
                            leftQuote = nextValue[0]
                            rightQuote = nextValue[2]
                            assert leftQuote in "\"\'", u"leftQuote=%r" % leftQuote
                            assert rightQuote in "\"\'", u"rightQuote=%r" % rightQuote
                            longValue = ord(nextValue[1])
                        if colonFound:
                            if upper is None:
                                upper = longValue
                            else:
                                raise RangeSyntaxError(
                                    "range must have at most lower and upper limit but found another number: %r"
                                    % nextValue)
                        elif lower is None:
                            lower = longValue
                        else:
                            raise RangeSyntaxError(
                                u"number must be followed by colon (:) but found: %r"
                                % nextValue)
                    elif afterHyphen:
                        raise RangeSyntaxError(
                            u"hyphen (-) must be followed by number but found: %r"
                            % nextValue)
                    elif (nextType == token.OP) and (nextValue == "-"):
                        afterHyphen = True
                    elif (nextType == token.OP) and (nextValue == ":"):
                        if colonFound:
                            raise RangeSyntaxError(
                                u"range item must contain at most one colon (:)"
                            )
                        colonFound = True
                    else:
                        message = u"range must be specified using integer numbers, text, symbols and colon (:) but found: %r [token type: %r]" % (
                            nextValue, nextType)
                        raise RangeSyntaxError(message)
                    nextToken = tokens.next()
                if afterHyphen:
                    raise RangeSyntaxError(
                        u"hyphen (-) at end must be followed by number")

                # Decide upon the result.
                if (lower is None):
                    if (upper is None):
                        if colonFound:
                            # Handle ":".
                            # TODO: Handle ":" same as ""?
                            raise RangeSyntaxError(
                                u"colon (:) must be preceded and/or succeeded by number"
                            )
                        else:
                            # Handle "".
                            result = None
                    else:
                        assert colonFound
                        # Handle ":y".
                        result = (None, upper)
                elif colonFound:
                    # Handle "x:" and "x:y".
                    if (upper is not None) and (lower > upper):
                        raise RangeSyntaxError(
                            u"lower range %d must be greater or equal to upper range %d"
                            % (lower, upper))
                    result = (lower, upper)
                else:
                    # Handle "x".
                    result = (lower, lower)
                if result is not None:
                    for item in self._items:
                        if self._itemsOverlap(item, result):
                            # TODO: use _repr_item() or something to display item in error message.
                            raise RangeSyntaxError(
                                u"range items must not overlap: %r and %r" %
                                (self._repr_item(item),
                                 self._repr_item(result)))
                    self._items.append(result)
                if _tools.isEofToken(nextToken):
                    endReached = True
Beispiel #17
0
    def __init__(self, text, default=None):
        """
        Setup a range as specified by ``text``.

        ``text`` must be of the form "lower:upper" or "limit". In case ``text`` is empty (""), any
        value will be accepted by `validate()`. For example, "1:40" accepts values between 1
        and 40.

        ``default`` is an alternative text to use in case ``text`` is ``None`` or empty.
        """
        assert default is None or default.strip(), u"default=%r" % default

        # Find out if a `text` has been specified and if not, use optional `default` instead.
        hasText = (text is not None) and text.strip()
        if not hasText and default is not None:
            text = default
            hasText = True

        if not hasText:
            # Use empty ranges.
            self._description = None
            self._items = None
        else:
            self._description = text
            self._items = []
            # TODO: Consolidate code with `DelimitedDataFormat._validatedCharacter()`.
            tokens = tokenize.generate_tokens(StringIO.StringIO(text).readline)
            endReached = False
            while not endReached:
                lower = None
                upper = None
                colonFound = False
                afterHyphen = False
                nextToken = tokens.next()
                while not _tools.isEofToken(nextToken) and not _tools.isCommaToken(nextToken):
                    nextType = nextToken[0]
                    nextValue = nextToken[1]
                    if nextType in (token.NAME, token.NUMBER, token.STRING):
                        if nextType == token.NUMBER:
                            try:
                                if nextValue[:2].lower() == "0x":
                                    nextValue = nextValue[2:]
                                    base = 16
                                else:
                                    base = 10
                                longValue = long(nextValue, base)
                            except ValueError:
                                raise RangeSyntaxError(u"number must be an integer but is: %r" % nextValue)
                            if afterHyphen:
                                longValue = - 1 * longValue
                                afterHyphen = False
                        elif nextType == token.NAME:
                            try:
                                longValue = tools.SYMBOLIC_NAMES_MAP[nextValue.lower()]
                            except KeyError:
                                validSymbols = _tools.humanReadableList(sorted(tools.SYMBOLIC_NAMES_MAP.keys()))
                                raise RangeSyntaxError(u"symbolic name %r must be one of: %s" % (nextValue, validSymbols))
                        elif nextType == token.STRING:
                            if len(nextValue) != 3:
                                raise RangeSyntaxError(u"text for range must contain a single character but is: %r" % nextValue)
                            leftQuote = nextValue[0]
                            rightQuote = nextValue[2]
                            assert leftQuote in "\"\'", u"leftQuote=%r" % leftQuote
                            assert rightQuote in "\"\'", u"rightQuote=%r" % rightQuote
                            longValue = ord(nextValue[1])
                        if colonFound:
                            if upper is None:
                                upper = longValue
                            else:
                                raise RangeSyntaxError("range must have at most lower and upper limit but found another number: %r" % nextValue)
                        elif lower is None:
                            lower = longValue
                        else:
                            raise RangeSyntaxError(u"number must be followed by colon (:) but found: %r" % nextValue)
                    elif afterHyphen:
                        raise RangeSyntaxError(u"hyphen (-) must be followed by number but found: %r" % nextValue)
                    elif (nextType == token.OP) and (nextValue == "-"):
                        afterHyphen = True
                    elif (nextType == token.OP) and (nextValue == ":"):
                        if colonFound:
                            raise RangeSyntaxError(u"range item must contain at most one colon (:)")
                        colonFound = True
                    else:
                        message = u"range must be specified using integer numbers, text, symbols and colon (:) but found: %r [token type: %r]" % (nextValue, nextType)
                        raise RangeSyntaxError(message)
                    nextToken = tokens.next()
                if afterHyphen:
                    raise RangeSyntaxError(u"hyphen (-) at end must be followed by number")

                # Decide upon the result.
                if (lower is None):
                    if (upper is None):
                        if colonFound:
                            # Handle ":".
                            # TODO: Handle ":" same as ""?
                            raise RangeSyntaxError(u"colon (:) must be preceded and/or succeeded by number")
                        else:
                            # Handle "".
                            result = None
                    else:
                        assert colonFound
                        # Handle ":y".
                        result = (None, upper)
                elif colonFound:
                    # Handle "x:" and "x:y".
                    if (upper is not None) and (lower > upper):
                        raise RangeSyntaxError(u"lower range %d must be greater or equal to upper range %d" % (lower, upper))
                    result = (lower, upper)
                else:
                    # Handle "x".
                    result = (lower, lower)
                if result is not None:
                    for item in self._items:
                        if self._itemsOverlap(item, result):
                            # TODO: use _repr_item() or something to display item in error message.
                            raise RangeSyntaxError(u"range items must not overlap: %r and %r"
                                                   % (self._repr_item(item), self._repr_item(result)))
                    self._items.append(result)
                if _tools.isEofToken(nextToken):
                    endReached = True