Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strong types, not ignoring the VALUE parameter, better error handling #196

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
Changelog
=========

3.11.6 (unreleased)
4.0 (unreleased)
-------------------

Breaking changes:

- *add item here*

- Improved error handling. The value and parameters of a property should no longer
be lost upon error.
Refs #158 #174
[stlaz]

- Added strong typing of property values. Unknown properties with VALUE parameter
should now be represented as the appropriate type
Refs #187
[stlaz]

New features:

- *add item here*
Expand Down
56 changes: 40 additions & 16 deletions src/icalendar/cal.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,14 +141,19 @@ def _encode(self, name, value, parameters=None, encode=1):
if isinstance(value, types_factory.all_types):
# Don't encode already encoded values.
return value
klass = types_factory.for_property(name)
obj = klass(value)
if parameters:
if isinstance(parameters, dict):
params = Parameters()
for key, item in parameters.items():
params[key] = item
parameters = params
klass = types_factory.for_property(
name, parameters.get('VALUE') if parameters else None)
if types_factory.is_list_property(name):
obj = vDDDLists(value, klass)
else:
obj = klass(value)
if parameters:
assert isinstance(parameters, Parameters)
obj.params = parameters
return obj
Expand Down Expand Up @@ -185,7 +190,7 @@ def add(self, name, value, parameters=None, encode=1):

# encode value
if encode and isinstance(value, list) \
and name.lower() not in ['rdate', 'exdate']:
and not types_factory.is_list_property(name):
# Individually convert each value to an ical type except rdate and
# exdate, where lists of dates might be passed to vDDDLists.
value = [self._encode(name, v, parameters, encode) for v in value]
Expand Down Expand Up @@ -217,19 +222,20 @@ def _decode(self, name, value):
if isinstance(value, vDDDLists):
# TODO: Workaround unfinished decoding
return value
decoded = types_factory.from_ical(name, value)
try:
valtype = value.params['VALUE']
except (AttributeError, KeyError):
valtype = None
decoded = types_factory.from_ical(name, value, valtype)
# TODO: remove when proper decoded is implemented in every prop.* class
# Workaround to decode vText properly
if isinstance(decoded, vText):
decoded = decoded.encode(DEFAULT_ENCODING)
return decoded

def decoded(self, name, default=_marker):
"""Returns decoded value of property.
"""Returns value of a property as a python native type.
"""
# XXX: fail. what's this function supposed to do in the end?
# -rnix

if name in self:
value = self[name]
if isinstance(value, list):
Expand Down Expand Up @@ -369,26 +375,44 @@ def from_ical(cls, st, multiple=False):
_timezone_cache[component['TZID']] = component.to_tz()
# we are adding properties to the current top of the stack
else:
factory = types_factory.for_property(name)
component = stack[-1] if stack else None
if not component:
raise ValueError('Property "{prop}" does not have '
'a parent component.'.format(prop=name))
datetime_names = ('DTSTART', 'DTEND', 'RECURRENCE-ID', 'DUE',
'FREEBUSY', 'RDATE', 'EXDATE')
try:
if name in datetime_names and 'TZID' in params:
vals = factory(factory.from_ical(vals, params['TZID']))
factory = types_factory.for_property(name,
params.get('VALUE'))
except ValueError as e:
if not component.ignore_exceptions:
raise
else:
# add error message and fall back to vText value type
component.errors.append((uname, str(e)))
factory = types_factory['text']
try:
if (types_factory.is_list_property(name) and
factory != vText):
# TODO: list type currenty supports only datetime types
vals = vDDDLists(
vDDDLists.from_ical(vals, params.get('TZID'),
factory))
else:
vals = factory(factory.from_ical(vals))
if name in datetime_names and 'TZID' in params:
vals = factory(
factory.from_ical(vals, params['TZID']))
else:
vals = factory(factory.from_ical(vals))
except ValueError as e:
if not component.ignore_exceptions:
raise
component.errors.append((uname, unicode_type(e)))
component.add(name, None, encode=0)
else:
vals.params = params
component.add(name, vals, encode=0)
# fallback to vText and store the original value
vals = types_factory['text'](vals)

vals.params = params
component.add(name, vals, encode=0)

if multiple:
return comps
Expand Down
Loading