Example #1
0
 def test_can_validate_python_name(self):
     self.assertEqual(_tools.validated_python_name("x", "abc_123"), "abc_123")
     self.assertEqual(_tools.validated_python_name("x", " abc_123 "), "abc_123")
     self.assertRaises(NameError, _tools.validated_python_name, "x", "1337")
     self.assertRaises(NameError, _tools.validated_python_name, "x", "")
     self.assertRaises(NameError, _tools.validated_python_name, "x", " ")
     self.assertRaises(NameError, _tools.validated_python_name, "x", "a.b")
Example #2
0
 def test_can_validate_python_name(self):
     self.assertEqual(_tools.validated_python_name('x', 'abc_123'),
                      'abc_123')
     self.assertEqual(_tools.validated_python_name('x', ' abc_123 '),
                      'abc_123')
     self.assertRaises(NameError, _tools.validated_python_name, 'x', '1337')
     self.assertRaises(NameError, _tools.validated_python_name, 'x', '')
     self.assertRaises(NameError, _tools.validated_python_name, 'x', ' ')
     self.assertRaises(NameError, _tools.validated_python_name, 'x', 'a.b')
Example #3
0
    def add_field_format_row(self, possibly_incomplete_items):
        """
        Add field as described by `possibly_incomplete_items`, which is a
        list consisting of:

        1) field name
        2) optional: example value (can be empty)
        3) optional: empty flag ("X" = field is allowed to be empty)
        4) optional: length (using the syntax of :py:class:`cutplace.ranges.Range`)
        5) optional: field type (e.g. 'Integer' for :py:class:`cutplace.fields.IntegerFieldFormat`)
        6) optional: rule to validate field (depending on type)

        Any missing items are interpreted as empty string (``''``).
        Additional items are ignored.

        :raises cutplace.errors.InterfaceError: on broken \
          ``possibly_incomplete_items``
        """
        assert possibly_incomplete_items is not None
        assert self._location is not None

        if self._data_format is None:
            raise errors.InterfaceError(
                "data format must be specified before first field",
                self._location)

        # Assert that the various lists and maps related to fields are in a consistent state.
        # Ideally this would be a class invariant, but this is Python, not Eiffel.
        field_count = len(self.field_names)
        assert len(self._field_formats) == field_count
        assert len(self._field_name_to_format_map) == field_count
        assert len(self._field_name_to_index_map) == field_count

        items = (possibly_incomplete_items + 6 * [''])[:6]

        # Obtain field name.
        field_name = fields.validated_field_name(items[0], self._location)
        if field_name in self._field_name_to_format_map:
            # TODO: Add see_also_location pointing to previous declaration.
            raise errors.InterfaceError(
                'duplicate field name must be changed to a unique one: %s' %
                field_name, self._location)

        # Obtain example.
        self._location.advance_cell()
        field_example = items[1]

        # Obtain "empty" mark.
        self._location.advance_cell()
        field_is_allowed_to_be_empty_text = items[2].strip().lower()
        if field_is_allowed_to_be_empty_text == '':
            field_is_allowed_to_be_empty = False
        elif field_is_allowed_to_be_empty_text == self._EMPTY_INDICATOR:
            field_is_allowed_to_be_empty = True
        else:
            raise errors.InterfaceError(
                "mark for empty field must be %s or empty but is %s" %
                (self._EMPTY_INDICATOR, field_is_allowed_to_be_empty_text),
                self._location)

        # Obtain length.
        self._location.advance_cell()
        field_length = items[3]

        # Obtain field type and rule.
        self._location.advance_cell()
        field_type_item = items[4].strip()
        if field_type_item == '':
            field_type = 'Text'
        else:
            field_type = ''
            field_type_parts = field_type_item.split(".")
            try:
                for part in field_type_parts:
                    if field_type:
                        field_type += "."
                    field_type += _tools.validated_python_name(
                        "field type part", part)
                assert field_type, "empty field type must be detected by validated_python_name()"
            except NameError as error:
                raise errors.InterfaceError(six.text_type(error),
                                            self._location)
        field_class = self._create_field_format_class(field_type)
        self._location.advance_cell()
        field_rule = items[5].strip()
        _log.debug("create field: %s(%r, %r, %r)", field_class.__name__,
                   field_name, field_type, field_rule)
        try:
            field_format = field_class.__new__(field_class, field_name,
                                               field_is_allowed_to_be_empty,
                                               field_length, field_rule)
            field_format.__init__(field_name, field_is_allowed_to_be_empty,
                                  field_length, field_rule, self._data_format)
        except errors.InterfaceError as error:
            error_location = error.location if error.location is not None else self._location
            error.prepend_message(
                'cannot declare field %s' % _compat.text_repr(field_name),
                error_location)
            raise error

        # Validate field length.
        # TODO #82: Cleanup validation for declared field formats.
        self._location.set_cell(4)
        field_length = field_format.length
        if self._data_format.format == data.FORMAT_FIXED:
            if field_length.items is None:
                raise errors.InterfaceError(
                    "length of field %s must be specified with fixed data format"
                    % _compat.text_repr(field_name), self._location)
            if field_length.lower_limit != field_length.upper_limit:
                raise errors.InterfaceError(
                    "length of field %s for fixed data format must be a specific number but is: %s"
                    % (_compat.text_repr(field_name), field_format.length),
                    self._location)
            if field_length.lower_limit < 1:
                raise errors.InterfaceError(
                    "length of field %s for fixed data format must be at least 1 but is: %d"
                    % (_compat.text_repr(field_name),
                       field_format.length.lower_limit), self._location)
        elif field_length.lower_limit is not None:
            if field_length.lower_limit < 0:
                raise errors.InterfaceError(
                    "lower limit for length of field %s must be at least 0 but is: %d"
                    % (_compat.text_repr(field_name),
                       field_format.length.lower_limit), self._location)
        elif field_length.upper_limit is not None:
            # Note: 0 as upper limit is valid for a field that must always be empty.
            if field_length.upper_limit < 0:
                raise errors.InterfaceError(
                    "upper limit for length of field %s must be at least 0 but is: %d"
                    % (_compat.text_repr(field_name),
                       field_format.length.upper_limit), self._location)

        # Set and validate example in case there is one.
        if field_example != '':
            try:
                field_format.example = field_example
            except errors.FieldValueError as error:
                self._location.set_cell(2)
                raise errors.InterfaceError(
                    "cannot validate example for field %s: %s" %
                    (_compat.text_repr(field_name), error), self._location)

        self._location.set_cell(1)

        assert field_name
        assert field_type
        assert field_rule is not None

        self.add_field_format(field_format)
Example #4
0
    def add_field_format_row(self, possibly_incomplete_items):
        """
        Add field as described by `possibly_incomplete_items`, which is a
        list consisting of:

        1) field name
        2) optional: example value (can be empty)
        3) optional: empty flag ("X" = field is allowed to be empty)
        4) optional: length (using the syntax of :py:class:`cutplace.ranges.Range`)
        5) optional: field type (e.g. 'Integer' for :py:class:`cutplace.fields.IntegerFieldFormat`)
        6) optional: rule to validate field (depending on type)

        Any missing items are interpreted as empty string (``''``).
        Additional items are ignored.

        :raises cutplace.errors.InterfaceError: on broken \
          ``possibly_incomplete_items``
        """
        assert possibly_incomplete_items is not None
        assert self._location is not None

        if self._data_format is None:
            raise errors.InterfaceError("data format must be specified before first field", self._location)

        # Assert that the various lists and maps related to fields are in a consistent state.
        # Ideally this would be a class invariant, but this is Python, not Eiffel.
        field_count = len(self.field_names)
        assert len(self._field_formats) == field_count
        assert len(self._field_name_to_format_map) == field_count
        assert len(self._field_name_to_index_map) == field_count

        items = (possibly_incomplete_items + 6 * [''])[:6]

        # Obtain field name.
        field_name = fields.validated_field_name(items[0], self._location)
        if field_name in self._field_name_to_format_map:
            # TODO: Add see_also_location pointing to previous declaration.
            raise errors.InterfaceError(
                'duplicate field name must be changed to a unique one: %s' % field_name, self._location)

        # Obtain example.
        self._location.advance_cell()
        field_example = items[1]

        # Obtain "empty" mark.
        self._location.advance_cell()
        field_is_allowed_to_be_empty_text = items[2].strip().lower()
        if field_is_allowed_to_be_empty_text == '':
            field_is_allowed_to_be_empty = False
        elif field_is_allowed_to_be_empty_text == self._EMPTY_INDICATOR:
            field_is_allowed_to_be_empty = True
        else:
            raise errors.InterfaceError(
                "mark for empty field must be %s or empty but is %s"
                % (self._EMPTY_INDICATOR, field_is_allowed_to_be_empty_text), self._location)

        # Obtain length.
        self._location.advance_cell()
        field_length = items[3]

        # Obtain field type and rule.
        self._location.advance_cell()
        field_type_item = items[4].strip()
        if field_type_item == '':
            field_type = 'Text'
        else:
            field_type = ''
            field_type_parts = field_type_item.split(".")
            try:
                for part in field_type_parts:
                    if field_type:
                        field_type += "."
                    field_type += _tools.validated_python_name("field type part", part)
                assert field_type, "empty field type must be detected by validated_python_name()"
            except NameError as error:
                raise errors.InterfaceError(six.text_type(error), self._location)
        field_class = self._create_field_format_class(field_type)
        self._location.advance_cell()
        field_rule = items[5].strip()
        _log.debug("create field: %s(%r, %r, %r)", field_class.__name__, field_name, field_type, field_rule)
        try:
            field_format = field_class.__new__(
                field_class, field_name, field_is_allowed_to_be_empty, field_length, field_rule)
            field_format.__init__(
                field_name, field_is_allowed_to_be_empty, field_length, field_rule, self._data_format)
        except errors.InterfaceError as error:
            error_location = error.location if error.location is not None else self._location
            error.prepend_message('cannot declare field %s' % _compat.text_repr(field_name), error_location)
            raise error

        # Validate field length.
        # TODO #82: Cleanup validation for declared field formats.
        self._location.set_cell(4)
        field_length = field_format.length
        if self._data_format.format == data.FORMAT_FIXED:
            if field_length.items is None:
                raise errors.InterfaceError(
                    "length of field %s must be specified with fixed data format" % _compat.text_repr(field_name),
                    self._location)
            if field_length.lower_limit != field_length.upper_limit:
                raise errors.InterfaceError(
                    "length of field %s for fixed data format must be a specific number but is: %s"
                    % (_compat.text_repr(field_name), field_format.length), self._location)
            if field_length.lower_limit < 1:
                raise errors.InterfaceError(
                    "length of field %s for fixed data format must be at least 1 but is: %d"
                    % (_compat.text_repr(field_name), field_format.length.lower_limit), self._location)
        elif field_length.lower_limit is not None:
            if field_length.lower_limit < 0:
                raise errors.InterfaceError(
                    "lower limit for length of field %s must be at least 0 but is: %d"
                    % (_compat.text_repr(field_name), field_format.length.lower_limit), self._location)
        elif field_length.upper_limit is not None:
            # Note: 0 as upper limit is valid for a field that must always be empty.
            if field_length.upper_limit < 0:
                raise errors.InterfaceError(
                    "upper limit for length of field %s must be at least 0 but is: %d"
                    % (_compat.text_repr(field_name), field_format.length.upper_limit), self._location)

        # Set and validate example in case there is one.
        if field_example != '':
            try:
                field_format.example = field_example
            except errors.FieldValueError as error:
                self._location.set_cell(2)
                raise errors.InterfaceError(
                    "cannot validate example for field %s: %s" % (_compat.text_repr(field_name), error),
                    self._location)

        self._location.set_cell(1)

        assert field_name
        assert field_type
        assert field_rule is not None

        self.add_field_format(field_format)