Fixes bug when date-time is recognized as time

Date-time was recognized incorrectly as a date or time. This resulted
in wrong representation of some iCalendar strings.

Also adds "errors" list in Component for saving error strings from parsing.

https://github.com/collective/icalendar/issues/174
https://github.com/collective/icalendar/issues/168
pull/175/head
Stanislav Laznicka 2015-12-18 16:48:59 +01:00
rodzic ff1f2eec3d
commit 70a7b5a167
7 zmienionych plików z 35 dodań i 20 usunięć

Wyświetl plik

@ -10,6 +10,10 @@ New:
Fixes:
- Fixed date-time being recognized as date or time during parsing. Added
better error handling to parsing from ical strings.
[stlaz]
- Added __version__ attribute to init.py
[TomTry]

Wyświetl plik

@ -36,6 +36,7 @@ icalendar contributors
- Ronan Dunklau <ronan@dunklau.fr>
- Russ <russ@rw.id.au>
- Sidnei da Silva <sidnei@enfoldsystems.com>
- Stanislav Láznička <slaznick@redhat.com>
- Stanislav Ochotnicky <sochotnicky@redhat.com>
- Stefan Schwarzer <sschwarzer@sschwarzer.net>
- Thomas Bruederli <thomas@roundcube.net>

Wyświetl plik

@ -80,8 +80,8 @@ class Component(CaselessDict):
super(Component, self).__init__(*args, **kwargs)
# set parameters here for properties that use non-default values
self.subcomponents = [] # Components can be nested.
self.is_broken = False # True if we ignored an exception while
# parsing a property
self.errors = list() # If we ignored exception(s) while
# parsing a property, contains error strings
# def is_compliant(self, name):
# """Returns True is the given property name is compliant with the
@ -309,14 +309,14 @@ class Component(CaselessDict):
try:
name, params, vals = line.parts()
except ValueError:
except ValueError as e:
# if unable to parse a line within a component
# that ignores exceptions, mark the component
# as broken and skip the line. otherwise raise.
component = stack[-1] if stack else None
if not component or not component.ignore_exceptions:
raise
component.is_broken = True
component.errors.append((None, str(e)))
continue
uname = name.upper()
@ -338,8 +338,7 @@ class Component(CaselessDict):
if not stack: # we are at the end
comps.append(component)
else:
if not component.is_broken:
stack[-1].add_component(component)
stack[-1].add_component(component)
if vals == 'VTIMEZONE' and \
'TZID' in component and \
component['TZID'] not in pytz.all_timezones and \
@ -356,10 +355,11 @@ class Component(CaselessDict):
vals = factory(factory.from_ical(vals, params['TZID']))
else:
vals = factory(factory.from_ical(vals))
except ValueError:
except ValueError as e:
if not component.ignore_exceptions:
raise
component.is_broken = True
component.errors.append((uname, str(e)))
component.add(name, None, encode=0)
else:
vals.params = params
component.add(name, vals, encode=0)

Wyświetl plik

@ -341,7 +341,7 @@ class Contentline(compat.unicode_type):
return (name, params, values)
except ValueError as exc:
raise ValueError(
u"Content line could not be parsed into parts: %r: %s"
u"Content line could not be parsed into parts: '%s': %s"
% (self, exc)
)

Wyświetl plik

@ -313,13 +313,15 @@ class vDDDTypes(object):
u = ical.upper()
if u.startswith(('P', '-P', '+P')):
return vDuration.from_ical(ical)
try:
if len(ical) in (15, 16):
return vDatetime.from_ical(ical, timezone=timezone)
except ValueError:
try:
return vDate.from_ical(ical)
except ValueError:
return vTime.from_ical(ical)
elif len(ical) == 8:
return vDate.from_ical(ical)
elif len(ical) in (6,7):
return vTime.from_ical(ical)
else:
raise ValueError("Expected datetime, date, or time, got: '%s'" % ical)
class vDate(object):

Wyświetl plik

@ -200,7 +200,9 @@ X
END:VEVENT"""
event = icalendar.Calendar.from_ical(ical_str)
self.assertTrue(isinstance(event, icalendar.Event))
self.assertTrue(event.is_broken)
self.assertEqual(event.errors,
[(None, "Content line could not be parsed into parts: 'X': Invalid content line")]
)
def test_issue_104__no_ignore_exceptions(self):
"""

Wyświetl plik

@ -389,8 +389,9 @@ class TestCal(unittest.TestCase):
directory = tempfile.mkdtemp()
open(os.path.join(directory, 'test.ics'), 'wb').write(cal.to_ical())
# Parsing a complete calendar from a string will silently ignore bogus
# events. The bogosity in the following is the third EXDATE: it has an
# Parsing a complete calendar from a string will silently ignore wrong
# events but adding the error information to the component's 'errors'
# attribute. The error in the following is the third EXDATE: it has an
# empty DATE.
s = '\r\n'.join(('BEGIN:VCALENDAR',
'PRODID:-//Google Inc//Google Calendar 70.9054//EN',
@ -405,7 +406,7 @@ class TestCal(unittest.TestCase):
'EXDATE;VALUE=DATE:20080311',
'END:VEVENT',
'BEGIN:VEVENT',
'DESCRIPTION:Bogus event',
'DESCRIPTION:Wrong event',
'DTSTART;VALUE=DATE:20080303',
'DTEND;VALUE=DATE:20080304',
'RRULE:FREQ=DAILY;UNTIL=20080323T235959Z',
@ -416,4 +417,9 @@ class TestCal(unittest.TestCase):
self.assertEqual(
[e['DESCRIPTION'].to_ical()
for e in icalendar.cal.Calendar.from_ical(s).walk('VEVENT')],
[b'Perfectly OK event'])
[b'Perfectly OK event', b'Wrong event'])
self.assertEqual(
[e.errors
for e in icalendar.cal.Calendar.from_ical(s).walk('VEVENT')],
[[], [('EXDATE', "Expected datetime, date, or time, got: ''")]]
)