Don't let Component.add re-encode already encoded values. This simplifies

the API, since there is no need explicitly pass encode=False. Fixes #82.
pull/98/merge
Johannes Raggam 2013-03-20 10:20:27 +01:00
rodzic 2f53d1f180
commit 71da2fa8ea
5 zmienionych plików z 116 dodań i 60 usunięć

Wyświetl plik

@ -5,6 +5,10 @@ Changelog
3.4dev (unreleased)
-------------------
- Don't let ``Component.add`` re-encode already encoded values. This simplifies
the API, since there is no need explicitly pass ``encode=False``. Fixes #82.
[thet]
- Switch to unicode internally.
[thet]

Wyświetl plik

@ -103,6 +103,9 @@ class Component(CaselessDict):
"""
if not cond:
return value
if type(value) in types_factory.all_types:
# Don't encode already encoded values.
return value
klass = types_factory.for_property(name)
obj = klass(value)
if hasattr(value, 'params') and len(value.params.keys()) > 0:

Wyświetl plik

@ -68,6 +68,64 @@ WEEKDAY_RULE = re.compile('(?P<signal>[+-]?)(?P<relative>[\d]?)'
'(?P<weekday>[\w]{2})$')
####################################################
# handy tzinfo classes you can use.
#
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
DSTOFFSET = timedelta(seconds=-_time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class FixedOffset(tzinfo):
"""Fixed offset in minutes east from UTC.
"""
def __init__(self, offset, name):
self.__offset = timedelta(minutes=offset)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
class LocalTimezone(tzinfo):
"""Timezone of the machine where the code is running.
"""
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, -1)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
class vBinary(object):
"""Binary property values are base 64 encoded.
@ -139,63 +197,6 @@ class vCalAddress(unicode):
raise ValueError(u'Expected vCalAddress, got: %s' % ical)
####################################################
# handy tzinfo classes you can use.
#
ZERO = timedelta(0)
HOUR = timedelta(hours=1)
STDOFFSET = timedelta(seconds=-_time.timezone)
if _time.daylight:
DSTOFFSET = timedelta(seconds=-_time.altzone)
else:
DSTOFFSET = STDOFFSET
DSTDIFF = DSTOFFSET - STDOFFSET
class FixedOffset(tzinfo):
"""Fixed offset in minutes east from UTC.
"""
def __init__(self, offset, name):
self.__offset = timedelta(minutes=offset)
self.__name = name
def utcoffset(self, dt):
return self.__offset
def tzname(self, dt):
return self.__name
def dst(self, dt):
return ZERO
class LocalTimezone(tzinfo):
"""Timezone of the machine where the code is running.
"""
def utcoffset(self, dt):
if self._isdst(dt):
return DSTOFFSET
else:
return STDOFFSET
def dst(self, dt):
if self._isdst(dt):
return DSTDIFF
else:
return ZERO
def tzname(self, dt):
return _time.tzname[self._isdst(dt)]
def _isdst(self, dt):
tt = (dt.year, dt.month, dt.day,
dt.hour, dt.minute, dt.second,
dt.weekday(), 0, -1)
stamp = _time.mktime(tt)
tt = _time.localtime(stamp)
return tt.tm_isdst > 0
class vFloat(float):
"""Just a float.
@ -855,6 +856,28 @@ class TypesFactory(CaselessDict):
def __init__(self, *args, **kwargs):
"Set keys to upper for initial dict"
CaselessDict.__init__(self, *args, **kwargs)
self.all_types = [
vBinary,
vBoolean,
vCalAddress,
vDDDLists,
vDDDTypes,
vDate,
vDatetime,
vDuration,
vFloat,
vFrequency,
vGeo,
vInline,
vInt,
vPeriod,
vRecur,
vText,
vTime,
vUTCOffset,
vUri,
vWeekday,
]
self['binary'] = vBinary
self['boolean'] = vBoolean
self['cal-address'] = vCalAddress

Wyświetl plik

@ -169,6 +169,19 @@ class TestCalComponent(unittest.TestCase):
self.assertTrue(
"LAST-MODIFIED;VALUE=DATE-TIME:20101010T160000Z" in lines)
def test_cal_Component_add_no_reencode(self):
"""Already encoded values should not be re-encoded.
"""
from icalendar import cal, prop
comp = cal.Component()
comp.add('ATTACH', 'me')
comp.add('ATTACH', 'you', encode=False)
binary = prop.vBinary('us')
comp.add('ATTACH', binary)
self.assertEqual(comp['ATTACH'], [u'me', 'you', binary])
def test_cal_Component_from_ical(self):
# RecurrenceIDs may contain a TZID parameter, if so, they should create
# a tz localized datetime, otherwise, create a naive datetime

Wyświetl plik

@ -3,10 +3,10 @@ from . import unittest
import icalendar
import os
class TestCases(unittest.TestCase):
class TestIssues(unittest.TestCase):
def test_case_meetup(self):
# broken description
def test_issue_53(self):
# parsing failure on some descriptions?
# see: https://github.com/collective/icalendar/issues/53
directory = os.path.dirname(__file__)
ics = open(os.path.join(directory, 'case_meetup.ics'), 'rb')
@ -21,3 +21,16 @@ class TestCases(unittest.TestCase):
self.assertEqual(len(timezones), 1)
tz = timezones[0]
self.assertEqual(tz['tzid'].to_ical(), "America/New_York")
def test_issue_82(self):
# vBinary __repr__ called rather than to_ical from container types
# https://github.com/collective/icalendar/issues/82
b = icalendar.vBinary('text')
b.params['FMTTYPE'] = 'text/plain'
self.assertEqual(b.to_ical(), 'dGV4dA==')
e = icalendar.Event()
e.add('ATTACH', b)
self.assertEqual(e.to_ical(),
"BEGIN:VEVENT\r\nATTACH;ENCODING=BASE64;FMTTYPE=text/plain;"
"VALUE=BINARY:dGV4dA==\r\nEND:VEVENT\r\n"
)