@@ -4913,16 +4913,30 @@
Loading
4913 4913
        for repstr, wday in zip(with_n_reprs, with_n_wdays):
4914 4914
            self.assertEqual(repr(wday), repstr)
4915 4915
4916 -
    def testMalformedRuleErrorWhenTimeFormatIsNotCorrect(self):
4916 +
    def testIncorrectTimestampWhenTimeFormatIsNotCorrect(self):
4917 4917
        rule = "FREQ=YEARLY;WKST=MO;UNTIL=2019-03-29T00:59:59+01:00"
4918 4918
        with pytest.raises(ValueError) as exception:
4919 4919
            rrulestr(rule, dtstart=datetime(1997, 9, 2, 9, 0))
4920 -
        self.assertEqual("Malformed rule: " + rule,
4920 +
        self.assertEqual("Incorrect timestamp format in rule: " + rule + ". Timestamp should not have ':'",
4921 +
                         str(exception.value))
4922 +
4923 +
    def testIncorrectTimestampWhenTimeFormatIsNotCorrectWithoutTimeZone(self):
4924 +
        rule = "FREQ=YEARLY;WKST=MO;UNTIL=2019-03-29T00:59:59"
4925 +
        with pytest.raises(ValueError) as exception:
4926 +
            rrulestr(rule, dtstart=datetime(1997, 9, 2, 9, 0))
4927 +
        self.assertEqual("Incorrect timestamp format in rule: " + rule + ". Timestamp should not have ':'",
4921 4928
                         str(exception.value))
4922 4929
4923 4930
    def testUnsupportedPropertyErrorWhenRuleNameIsNotCorrect(self):
4924 -
        rule = "XYZ:19970902T090000"
4931 +
        rule = "X-ABC-MMSUBJ:19970902T090000"
4932 +
        with pytest.raises(ValueError) as exception:
4933 +
            rrulestr(rule, dtstart=datetime(1997, 9, 2, 9, 0))
4934 +
        self.assertEqual("unsupported property: " + "X-ABC-MMSUBJ",
4935 +
                         str(exception.value))
4936 +
4937 +
    def testMalformedRuleErrorWhenOnlyOneColonPresentInRule(self):
4938 +
        rule = "FREQ=YEARLY;UNTIL=20190329T005959+01:00"
4925 4939
        with pytest.raises(ValueError) as exception:
4926 4940
            rrulestr(rule, dtstart=datetime(1997, 9, 2, 9, 0))
4927 -
        self.assertEqual("unsupported property: " + "XYZ",
4941 +
        self.assertEqual("Rule cannot be parsed correctly: " + rule,
4928 4942
                         str(exception.value))

@@ -1664,8 +1664,6 @@
Loading
1664 1664
                    name = "RRULE"
1665 1665
                    value = line
1666 1666
                else:
1667 -
                    if line.count(':') > 1:
1668 -
                        raise ValueError("Malformed rule: " + line)
1669 1667
                    name, value = line.split(':', 1)
1670 1668
                parms = name.split(';')
1671 1669
                if not parms:
@@ -1699,7 +1697,9 @@
Loading
1699 1697
                                         value)
1700 1698
                    dtstart = dtvals[0]
1701 1699
                else:
1702 -
                    raise ValueError("unsupported property: " + name)
1700 +
                    self.__raise_error_if_unsupported_property(name)
1701 +
                    self.__raise_error_if_incorrect_timestamp(line)
1702 +
                    raise ValueError("Rule cannot be parsed correctly: " + line)
1703 1703
            if (forceset or len(rrulevals) > 1 or rdatevals
1704 1704
                    or exrulevals or exdatevals):
1705 1705
                if not parser and (rdatevals or exdatevals):
@@ -1730,6 +1730,19 @@
Loading
1730 1730
                                             ignoretz=ignoretz,
1731 1731
                                             tzinfos=tzinfos)
1732 1732
1733 +
    @staticmethod
1734 +
    def __raise_error_if_unsupported_property(name):
1735 +
        valid_pattern = re.compile("^([A-Z]|[a-z]|-)+$")
1736 +
        if valid_pattern.match(name):
1737 +
            raise ValueError("unsupported property: " + name)
1738 +
1739 +
    @staticmethod
1740 +
    def __raise_error_if_incorrect_timestamp(line):
1741 +
        hour_min_sec_format = ".*([\\d]{2}:[\\d]{2}:[\\d]{2}).*"
1742 +
        time_pattern = re.compile(hour_min_sec_format)
1743 +
        if time_pattern.match(line):
1744 +
            raise ValueError("Incorrect timestamp format in rule: " + line + ". Timestamp should not have ':'")
1745 +
1733 1746
    def __call__(self, s, **kwargs):
1734 1747
        return self._parse_rfc(s, **kwargs)
1735 1748
Files Coverage
dateutil 96.02%
Project Totals (32 files) 96.02%
windows-latest:pypy2
Build #187222560 -
windows-latest:pypy3
Build #187222560 -
d78bsxtmcekcejlb
TOXENV=py37
ohgmpm8u1ye0utp7
TOXENV=py35
ocf3omgwpmc3apr5
TOXENV=py38
6yuw76jrdxqrhmam
TOXENV=py37
ur6c26wnunogkov3
TOXENV=py38
yadbuajp7gu92pbc
TOXENV=py27
2234.1
TRAVIS_PYTHON_VERSION=2.7
TRAVIS_OS_NAME=linux
TOXENV=py
2234.2
TRAVIS_PYTHON_VERSION=3.4
TRAVIS_OS_NAME=linux
TOXENV=py
2234.4
TRAVIS_PYTHON_VERSION=3.6
TRAVIS_OS_NAME=linux
TOXENV=py
2234.3
TRAVIS_PYTHON_VERSION=3.5
TRAVIS_OS_NAME=linux
TOXENV=py
2234.5
TRAVIS_PYTHON_VERSION=3.7
TRAVIS_OS_NAME=linux
TOXENV=py
2234.6
TRAVIS_PYTHON_VERSION=3.8
TRAVIS_OS_NAME=linux
TOXENV=py
ubuntu-latest:pypy3
Build #187222560 -
macos-latest:pypy3
Build #187222560 -
2234.11
TRAVIS_PYTHON_VERSION=nightly
TRAVIS_OS_NAME=linux
TOXENV=py
1
coverage:
2
  status:
3
    patch: false
4
    changes: false
5
    project:
6
      default:
7
        target: '80'
8

9
comment: false
Sunburst
The inner-most circle is the entire project, moving away from the center are folders then, finally, a single file. The size and color of each slice is representing the number of statements and the coverage, respectively.
Icicle
The top section represents the entire project. Proceeding with folders and finally individual files. The size and color of each slice is representing the number of statements and the coverage, respectively.
Grid
Each block represents a single file in the project. The size and color of each block is represented by the number of statements and the coverage, respectively.
Loading