CaselessDict keys are now automatically converted to unicode.

Test fixes. Small refactoring and polishing.
pull/96/head
Victor Varvaryuk 2013-03-19 12:35:00 +04:00
rodzic f44a20b1d9
commit 104387cc4a
11 zmienionych plików z 164 dodań i 184 usunięć

Wyświetl plik

@ -1,7 +1,23 @@
from __future__ import absolute_import
SEQUENCE_TYPES = (list, tuple)
DEFAULT_ENCODING = 'utf-8'
from icalendar.cal import (
def to_unicode(value, encoding='utf-8'):
"""Converts a value to unicode, even if it is already a unicode string.
"""
if isinstance(value, unicode):
return value
elif isinstance(value, str):
try:
return value.decode(encoding)
except UnicodeDecodeError:
return value.decode('utf-8', 'replace')
raise AssertionError('A str/unicode expected.')
from .cal import (
Calendar,
Event,
Todo,
@ -14,7 +30,7 @@ from icalendar.cal import (
ComponentFactory,
)
# Property Data Value Types
from icalendar.prop import (
from .prop import (
vBinary,
vBoolean,
vCalAddress,
@ -36,13 +52,13 @@ from icalendar.prop import (
TypesFactory,
)
# useful tzinfo subclasses
from icalendar.prop import (
from .prop import (
FixedOffset,
LocalTimezone,
)
# Parameters and helper methods for splitting and joining string with escaped
# chars.
from icalendar.parser import (
from .parser import (
Parameters,
q_split,
q_join,

Wyświetl plik

@ -5,17 +5,18 @@ files according to rfc2445.
These are the defined components.
"""
from __future__ import absolute_import
import pytz
from datetime import datetime
from icalendar.caselessdict import CaselessDict
from icalendar.parser import (
from .caselessdict import CaselessDict
from .parser import (
Contentlines,
Contentline,
Parameters,
q_split,
q_join,
)
from icalendar.prop import (
from .prop import (
TypesFactory,
vText,
)
@ -172,8 +173,7 @@ class Component(CaselessDict):
"""Returns a list of values (split on comma).
"""
vals = [v.strip('" ').encode(vText.encoding)
for v in q_split(self[name])]
vals = [v.strip('" ') for v in q_split(self[name])]
if decode:
return [self._decode(name, val) for val in vals]
return vals
@ -185,8 +185,7 @@ class Component(CaselessDict):
"""
if encode:
values = [self._encode(name, value, 1) for value in values]
joined = q_join(values).encode(vText.encoding)
self[name] = types_factory['inline'](joined)
self[name] = types_factory['inline'](q_join(values))
#########################
# Handling of components

Wyświetl plik

@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
from icalendar import DEFAULT_ENCODING
from __future__ import absolute_import
from . import DEFAULT_ENCODING, to_unicode
def canonsort_keys(keys, canonical_order=None):
"""Sorts leading keys according to canonical_order. Keys not specified in
@ -27,61 +29,62 @@ class CaselessDict(dict):
"""
def __init__(self, *args, **kwargs):
"Set keys to upper for initial dict"
"""Set keys to upper for initial dict.
"""
dict.__init__(self, *args, **kwargs)
for k, v in self.items():
k_upper = k.upper()
if k != k_upper:
dict.__delitem__(self, k)
self[k_upper] = v
for key, value in self.items():
key_upper = to_unicode(key).upper()
if key != key_upper:
dict.__delitem__(self, key)
self[key_upper] = value
def __getitem__(self, key):
key = to_unicode(key)
return dict.__getitem__(self, key.upper())
def __setitem__(self, key, value):
key = to_unicode(key)
dict.__setitem__(self, key.upper(), value)
def __delitem__(self, key):
key = to_unicode(key)
dict.__delitem__(self, key.upper())
def __contains__(self, key):
key = to_unicode(key)
return dict.__contains__(self, key.upper())
def get(self, key, default=None):
key = to_unicode(key)
return dict.get(self, key.upper(), default)
def setdefault(self, key, value=None):
key = to_unicode(key)
return dict.setdefault(self, key.upper(), value)
def pop(self, key, default=None):
key = to_unicode(key)
return dict.pop(self, key.upper(), default)
def popitem(self):
return dict.popitem(self)
def has_key(self, key):
key = to_unicode(key)
return dict.__contains__(self, key.upper())
def update(self, indict):
"""
Multiple keys where key1.upper() == key2.upper() will be lost.
"""
for entry in indict:
self[entry] = indict[entry]
for key, value in indict.iteritems():
self[key] = value
def copy(self):
return CaselessDict(dict.copy(self))
def clear(self):
dict.clear(self)
def __repr__(self):
# TODO: not necessary when to_ical also outs unicode
dict_repr = dict(map(
lambda item: (item[0].encode(DEFAULT_ENCODING), item[1]),
self.iteritems()
))
return 'CaselessDict(%s)' % dict_repr
return 'CaselessDict(%s)' % self
# A list of keys that must appear first in sorted_keys and sorted_items;
# must be uppercase.

Wyświetl plik

@ -6,30 +6,17 @@ Eg. RFC 2426 (vCard)
It is stupid in the sense that it treats the content purely as strings. No type
conversion is attempted.
"""
from __future__ import absolute_import
import re
from icalendar import DEFAULT_ENCODING
from icalendar import SEQUENCE_TYPES
from icalendar.caselessdict import CaselessDict
def safe_unicode(value, encoding='utf-8'):
"""Converts a value to unicode, even if it is already a unicode string.
Taken from from Products.CMFPlone.utils.
"""
if isinstance(value, unicode):
return value
elif isinstance(value, basestring):
try:
value = unicode(value, encoding)
except (UnicodeDecodeError):
value = value.decode('utf-8', 'replace')
return value
from . import DEFAULT_ENCODING, SEQUENCE_TYPES, to_unicode
from .caselessdict import CaselessDict
def escape_char(text):
"""Format value according to iCalendar TEXT escaping rules.
"""
assert isinstance(text, basestring)
# TODO: optimize this
# NOTE: ORDER MATTERS!
return text.replace('\N', '\n')\
.replace('\\', '\\\\')\
@ -40,6 +27,8 @@ def escape_char(text):
def unescape_char(text):
assert isinstance(text, basestring)
# TODO: optimize this
# NOTE: ORDER MATTERS!
return text.replace(r'\N', r'\n')\
.replace(r'\r\n', '\n')\
@ -114,12 +103,12 @@ def foldline(text, length=75, newline='\r\n'):
#################################################################
# Property parameter stuff
def paramVal(val):
def param_value(value):
"""Returns a parameter value.
"""
if type(val) in SEQUENCE_TYPES:
return q_join(val)
return dQuote(val)
if isinstance(value, SEQUENCE_TYPES):
return q_join(value)
return dquote(value)
# Could be improved
@ -148,8 +137,8 @@ def validate_param_value(value, quoted=True):
QUOTABLE = re.compile("[,;: ']")
def dQuote(val):
"""Parameter values containing [,;:] must be double quoted.
def dquote(val):
"""Enclose parameter values containing [,;:] in double quotes.
"""
# a double-quote character is forbidden to appear in a parameter value
# so replace it with a single-quote character
@ -182,7 +171,7 @@ def q_split(st, sep=','):
def q_join(lst, sep=','):
"""Joins a list on sep, quoting strings with QUOTABLE chars.
"""
return sep.join(dQuote(itm) for itm in lst)
return sep.join(dquote(itm) for itm in lst)
class Parameters(CaselessDict):
@ -225,10 +214,11 @@ class Parameters(CaselessDict):
items = self.items()
items.sort() # To make doctests work
for key, value in items:
value = paramVal(value)
value = param_value(value)
if isinstance(value, unicode):
value = value.encode(DEFAULT_ENCODING)
result.append('%s=%s' % (key.upper(), value))
# CaselessDict keys are always unicode
result.append('%s=%s' % (key.upper().encode('utf-8'), value))
return ';'.join(result)
@staticmethod
@ -269,12 +259,14 @@ class Parameters(CaselessDict):
def escape_string(val):
# TODO: optimize this
# '%{:02X}'.format(i)
return val.replace(r'\,', '%2C').replace(r'\:', '%3A')\
.replace(r'\;', '%3B').replace(r'\\', '%5C')
def unsescape_string(val):
# TODO: optimize this
return val.replace('%2C', ',').replace('%3A', ':')\
.replace('%3B', ';').replace('%5C', '\\')
@ -288,7 +280,7 @@ class Contentline(unicode):
"""
def __new__(cls, value, strict=False, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(Contentline, cls).__new__(cls, value)
self.strict = strict
return self
@ -307,10 +299,10 @@ class Contentline(unicode):
# values = escape_char(values)
# TODO: after unicode only, remove
name = safe_unicode(name)
values = safe_unicode(values)
name = to_unicode(name)
values = to_unicode(values)
if params:
params = safe_unicode(params.to_ical())
params = to_unicode(params.to_ical())
return Contentline(u'%s;%s:%s' % (name, params, values))
return Contentline(u'%s:%s' % (name, values))
except Exception:
@ -349,22 +341,20 @@ class Contentline(unicode):
raise ValueError("Content line could not be parsed into parts: %r:"
" %s" % (self, exc))
@staticmethod
def from_ical(st, strict=False):
@classmethod
def from_ical(cls, ical, strict=False):
"""Unfold the content lines in an iCalendar into long content lines.
"""
try:
# a fold is carriage return followed by either a space or a tab
return Contentline(FOLD.sub('', st), strict=strict)
except:
raise ValueError(u'Expected StringType with content line')
ical = to_unicode(ical)
# a fold is carriage return followed by either a space or a tab
return cls(FOLD.sub('', ical), strict=strict)
def to_ical(self):
"""Long content lines are folded so they are less than 75 characters.
wide.
"""
value = self.encode(DEFAULT_ENCODING)
return foldline(value, newline='\r\n')
return foldline(value)
class Contentlines(list):
@ -392,31 +382,5 @@ class Contentlines(list):
raise ValueError('Expected StringType with content lines')
# ran this:
# sample = open('./samples/test.ics', 'rb').read() # binary file in windows!
# lines = Contentlines.from_ical(sample)
# for line in lines[:-1]:
# print line.parts()
# got this:
# ('BEGIN', Parameters({}), 'VCALENDAR')
# ('METHOD', Parameters({}), 'Request')
# ('PRODID', Parameters({}), '-//My product//mxm.dk/')
# ('VERSION', Parameters({}), '2.0')
# ('BEGIN', Parameters({}), 'VEVENT')
# ('DESCRIPTION', Parameters({}), 'This is a very long description that ...')
# ('PARTICIPANT', Parameters({'CN': 'Max M'}), 'MAILTO:maxm@mxm.dk')
# ('DTEND', Parameters({}), '20050107T160000')
# ('DTSTART', Parameters({}), '20050107T120000')
# ('SUMMARY', Parameters({}), 'A second event')
# ('END', Parameters({}), 'VEVENT')
# ('BEGIN', Parameters({}), 'VEVENT')
# ('DTEND', Parameters({}), '20050108T235900')
# ('DTSTART', Parameters({}), '20050108T230000')
# ('SUMMARY', Parameters({}), 'A single event')
# ('UID', Parameters({}), '42')
# ('END', Parameters({}), 'VEVENT')
# ('END', Parameters({}), 'VCALENDAR')
# XXX: what kind of hack is this? import depends to be at end
from icalendar.prop import vText
from .prop import vText

Wyświetl plik

@ -36,6 +36,7 @@ These types are mainly used for parsing and file generation. But you can set
them directly.
"""
from __future__ import absolute_import
import re
import pytz
import binascii
@ -48,15 +49,13 @@ from datetime import (
tzinfo,
)
from dateutil.tz import tzutc
from icalendar import SEQUENCE_TYPES
from icalendar import DEFAULT_ENCODING
from icalendar.caselessdict import CaselessDict
from icalendar.parser import (
from . import SEQUENCE_TYPES, DEFAULT_ENCODING, to_unicode
from .caselessdict import CaselessDict
from .parser import (
Parameters,
escape_char,
unescape_char,
tzid_from_dt,
safe_unicode
)
DATE_PART = r'(\d+)D'
@ -96,7 +95,7 @@ class vBoolean(int):
"""Returns specific string according to state.
"""
bool_map = CaselessDict(true=True, false=False)
BOOL_MAP = CaselessDict(true=True, false=False)
def __new__(cls, *args, **kwargs):
self = super(vBoolean, cls).__new__(cls, *args, **kwargs)
@ -108,10 +107,10 @@ class vBoolean(int):
return 'TRUE'
return 'FALSE'
@staticmethod
def from_ical(ical):
@classmethod
def from_ical(cls, ical):
try:
return vBoolean.bool_map[ical]
return cls.BOOL_MAP[ical]
except:
raise ValueError("Expected 'TRUE' or 'FALSE'. Got %s" % ical)
@ -121,7 +120,7 @@ class vCalAddress(unicode):
"""
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vCalAddress, cls).__new__(cls, value)
self.params = Parameters()
return self
@ -551,7 +550,7 @@ class vWeekday(unicode):
})
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vWeekday, cls).__new__(cls, value)
match = WEEKDAY_RULE.match(self)
if match is None:
@ -593,7 +592,7 @@ class vFrequency(unicode):
})
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vFrequency, cls).__new__(cls, value)
if not self in vFrequency.frequencies:
raise ValueError('Expected frequency, got: %s' % self)
@ -650,9 +649,9 @@ class vRecur(CaselessDict):
result = []
for key, vals in self.sorted_items():
typ = self.types[key]
if not type(vals) in SEQUENCE_TYPES:
if not isinstance(vals, SEQUENCE_TYPES):
vals = [vals]
vals = ','.join([typ(val).to_ical() for val in vals])
vals = ','.join(typ(val).to_ical() for val in vals)
result.append('%s=%s' % (key, vals))
return ';'.join(result)
@ -680,7 +679,7 @@ class vText(unicode):
"""
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vText, cls).__new__(cls, value)
self.encoding = encoding
self.params = Parameters()
@ -694,11 +693,8 @@ class vText(unicode):
@staticmethod
def from_ical(ical):
try:
ical_unesc = unescape_char(ical)
return vText(ical_unesc)
except:
raise ValueError('Expected ical text, got: %s' % ical)
ical_unesc = unescape_char(ical)
return vText(ical_unesc)
class vTime(object):
@ -734,7 +730,7 @@ class vUri(unicode):
"""
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vUri, cls).__new__(cls, value)
self.params = Parameters()
return self
@ -834,7 +830,7 @@ class vInline(unicode):
"""
def __new__(cls, value, encoding=DEFAULT_ENCODING):
value = safe_unicode(value, encoding=encoding)
value = to_unicode(value, encoding=encoding)
self = super(vInline, cls).__new__(cls, value)
self.params = Parameters()
return self

Wyświetl plik

@ -7,10 +7,8 @@ import pytz
class TestCalComponent(unittest.TestCase):
def test_cal_Component(self):
Component = icalendar.cal.Component
Calendar = icalendar.cal.Calendar
Event = icalendar.cal.Event
prop = icalendar.prop
from icalendar.cal import Component, Calendar, Event
from icalendar import prop
# A component is like a dictionary with extra methods and attributes.
c = Component()
@ -105,7 +103,7 @@ class TestCalComponent(unittest.TestCase):
# Text fields which span multiple mulitple lines require proper
# indenting
c = Calendar()
c['description']=u'Paragraph one\n\nParagraph two'
c['description'] = u'Paragraph one\n\nParagraph two'
self.assertEqual(c.to_ical(),
'BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two'
+ '\r\nEND:VCALENDAR\r\n')
@ -154,10 +152,13 @@ class TestCalComponent(unittest.TestCase):
# timezone, crated, dtstamp and last-modified must be in UTC.
Component = icalendar.cal.Component
comp = Component()
comp.add('dtstart', datetime(2010,10,10,10,0,0,tzinfo=pytz.timezone("Europe/Vienna")))
comp.add('created', datetime(2010,10,10,12,0,0))
comp.add('dtstamp', datetime(2010,10,10,14,0,0,tzinfo=pytz.timezone("Europe/Vienna")))
comp.add('last-modified', datetime(2010,10,10,16,0,0,tzinfo=pytz.utc))
comp.add('dtstart', datetime(2010, 10, 10, 10, 0, 0,
tzinfo=pytz.timezone("Europe/Vienna")))
comp.add('created', datetime(2010, 10, 10, 12, 0, 0))
comp.add('dtstamp', datetime(2010, 10, 10, 14, 0, 0,
tzinfo=pytz.timezone("Europe/Vienna")))
comp.add('last-modified', datetime(2010, 10, 10, 16, 0, 0,
tzinfo=pytz.utc))
lines = comp.to_ical().splitlines()
self.assertTrue(
@ -168,8 +169,6 @@ class TestCalComponent(unittest.TestCase):
self.assertTrue(
"LAST-MODIFIED;VALUE=DATE-TIME:20101010T160000Z" in lines)
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
@ -212,7 +211,7 @@ class TestCal(unittest.TestCase):
event = icalendar.cal.Event()
event['summary'] = 'Python meeting about calendaring'
event['uid'] = '42'
event.set('dtstart', datetime(2005,4,4,8,0,0))
event.set('dtstart', datetime(2005, 4, 4, 8, 0, 0))
cal.add_component(event)
self.assertEqual(
cal.subcomponents[0].to_ical(),

Wyświetl plik

@ -133,14 +133,14 @@ class IcalendarTestCase (unittest.TestCase):
parts = ('SUMMARY', Parameters(), vText('INternational char æ ø å'))
self.assertEqual(
Contentline.from_parts(parts),
'SUMMARY:INternational char \xc3\xa6 \xc3\xb8 \xc3\xa5'
u'SUMMARY:INternational char æ ø å'
)
# A value can also be unicode
parts = ('SUMMARY', Parameters(), vText(u'INternational char æ ø å'))
self.assertEqual(
Contentline.from_parts(parts),
'SUMMARY:INternational char \xc3\xa6 \xc3\xb8 \xc3\xa5'
u'SUMMARY:INternational char æ ø å'
)
# Traversing could look like this.
@ -227,10 +227,10 @@ class IcalendarTestCase (unittest.TestCase):
)
def test_value_double_quoting(self):
from icalendar.parser import dQuote
self.assertEqual(dQuote('Max'), 'Max')
self.assertEqual(dQuote('Rasmussen, Max'), '"Rasmussen, Max"')
self.assertEqual(dQuote('name:value'), '"name:value"')
from icalendar.parser import dquote
self.assertEqual(dquote('Max'), 'Max')
self.assertEqual(dquote('Rasmussen, Max'), '"Rasmussen, Max"')
self.assertEqual(dquote('name:value'), '"name:value"')
def test_q_split(self):
from icalendar.parser import q_split

Wyświetl plik

@ -1,19 +1,20 @@
# -*- coding: utf-8 -*-
import unittest
import icalendar
from .. import to_unicode
class TestCalComponent(unittest.TestCase):
def test_cal_Component(self):
safe_unicode = icalendar.parser.safe_unicode
self.assertEqual(safe_unicode('spam'), u'spam')
self.assertEqual(safe_unicode(u'spam'), u'spam')
self.assertEqual(safe_unicode(u'spam'.encode('utf-8')), u'spam')
self.assertEqual(safe_unicode('\xc6\xb5'), u'\u01b5')
self.assertEqual(safe_unicode(u'\xc6\xb5'.encode('iso-8859-1')),
self.assertEqual(to_unicode('spam'), u'spam')
self.assertEqual(to_unicode(u'spam'), u'spam')
self.assertEqual(to_unicode(u'spam'.encode('utf-8')), u'spam')
self.assertEqual(to_unicode('\xc6\xb5'), u'\u01b5')
self.assertEqual(to_unicode(u'\xc6\xb5'.encode('iso-8859-1')),
u'\u01b5')
self.assertEqual(safe_unicode('\xc6\xb5', encoding='ascii'), u'\u01b5')
self.assertEqual(safe_unicode(1), 1)
self.assertEqual(safe_unicode(None), None)
self.assertEqual(to_unicode('\xc6\xb5', encoding='ascii'), u'\u01b5')
with self.assertRaises(AssertionError):
to_unicode(1)
with self.assertRaises(AssertionError):
to_unicode(None)

Wyświetl plik

@ -1,14 +1,13 @@
from datetime import datetime, date, timedelta, time
import unittest
import icalendar
import pytz
class TestProp(unittest.TestCase):
def test_prop_vBinary(self):
vBinary = icalendar.prop.vBinary
from ..prop import vBinary
txt = 'This is gibberish'
txt_ical = 'VGhpcyBpcyBnaWJiZXJpc2g='
@ -23,7 +22,7 @@ class TestProp(unittest.TestCase):
self.assertEqual(
str(vBinary('txt').params),
"Parameters({'VALUE': 'BINARY', 'ENCODING': 'BASE64'})"
"Parameters({u'VALUE': 'BINARY', u'ENCODING': 'BASE64'})"
)
# Long data should not have line breaks, as that would interfere
@ -33,7 +32,7 @@ class TestProp(unittest.TestCase):
self.assertEqual(vBinary.from_ical(txt_ical), txt)
def test_prop_vBoolean(self):
vBoolean = icalendar.prop.vBoolean
from ..prop import vBoolean
self.assertEqual(vBoolean(True).to_ical(), 'TRUE')
self.assertEqual(vBoolean(0).to_ical(), 'FALSE')
@ -43,29 +42,29 @@ class TestProp(unittest.TestCase):
self.assertEqual(vBoolean.from_ical('true'), True)
def test_prop_vCalAddress(self):
vCalAddress = icalendar.prop.vCalAddress
from ..prop import vCalAddress
txt = 'MAILTO:maxm@mxm.dk'
a = vCalAddress(txt)
a.params['cn'] = 'Max M'
self.assertEqual(a.to_ical(), txt)
self.assertEqual(str(a.params), "Parameters({'CN': 'Max M'})")
self.assertEqual(str(a.params), "Parameters({u'CN': 'Max M'})")
self.assertEqual(vCalAddress.from_ical(txt), 'MAILTO:maxm@mxm.dk')
def test_prop_vFloat(self):
vFloat = icalendar.prop.vFloat
from ..prop import vFloat
self.assertEqual(vFloat(1.0).to_ical(), '1.0')
self.assertEqual(vFloat.from_ical('42'), 42.0)
self.assertEqual(vFloat(42).to_ical(), '42.0')
def test_prop_vInt(self):
vInt = icalendar.prop.vInt
from ..prop import vInt
self.assertEqual(vInt(42).to_ical(), '42')
self.assertEqual(vInt.from_ical('13'), 13)
self.assertRaises(ValueError, vInt.from_ical, '1s3')
def test_prop_vDDDLists(self):
vDDDLists = icalendar.prop.vDDDLists
from ..prop import vDDDLists
dt_list = vDDDLists.from_ical('19960402T010000Z')
self.assertTrue(isinstance(dt_list, list))
@ -89,7 +88,7 @@ class TestProp(unittest.TestCase):
self.assertEqual(dt_list.to_ical(), '20000101T000000,20001111T000000')
def test_prop_vDDDTypes(self):
vDDDTypes = icalendar.prop.vDDDTypes
from ..prop import vDDDTypes
self.assertTrue(isinstance(vDDDTypes.from_ical('20010101T123000'),
datetime))
@ -107,7 +106,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vDDDTypes, 42)
def test_prop_vDate(self):
vDate = icalendar.prop.vDate
from ..prop import vDate
self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), '20010101')
self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), '18990101')
@ -117,7 +116,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vDate, 'd')
def test_prop_vDatetime(self):
vDatetime = icalendar.prop.vDatetime
from ..prop import vDatetime
dt = datetime(2001, 1, 1, 12, 30, 0)
self.assertEqual(vDatetime(dt).to_ical(), '20010101T123000')
@ -153,7 +152,7 @@ class TestProp(unittest.TestCase):
self.assertEqual(vDatetime(dat).to_ical(), '20101010T000000')
def test_prop_vDuration(self):
vDuration = icalendar.prop.vDuration
from ..prop import vDuration
self.assertEqual(vDuration(timedelta(11)).to_ical(), 'P11D')
self.assertEqual(vDuration(timedelta(-14)).to_ical(), '-P14D')
@ -181,7 +180,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vDuration, 11)
def test_prop_vPeriod(self):
vPeriod = icalendar.prop.vPeriod
from ..prop import vPeriod
# One day in exact datetimes
per = (datetime(2000, 1, 1), datetime(2000, 1, 2))
@ -226,7 +225,7 @@ class TestProp(unittest.TestCase):
self.assertEqual(p.to_ical(), '20000101T000000/P31D')
def test_prop_vWeekday(self):
vWeekday = icalendar.prop.vWeekday
from ..prop import vWeekday
self.assertEqual(vWeekday('mo').to_ical(), 'MO')
self.assertRaises(ValueError, vWeekday, 'erwer')
@ -238,14 +237,14 @@ class TestProp(unittest.TestCase):
self.assertEqual(vWeekday('-tu').to_ical(), '-TU')
def test_prop_vFrequency(self):
vFrequency = icalendar.prop.vFrequency
from ..prop import vFrequency
self.assertRaises(ValueError, vFrequency, 'bad test')
self.assertEqual(vFrequency('daily').to_ical(), 'DAILY')
self.assertEqual(vFrequency('daily').from_ical('MONTHLY'), 'MONTHLY')
def test_prop_vRecur(self):
vRecur = icalendar.prop.vRecur
from ..prop import vRecur
# Let's see how close we can get to one from the rfc:
# FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30
@ -317,7 +316,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vRecur.from_ical, 'BYDAY=12')
def test_prop_vText(self):
vText = icalendar.prop.vText
from ..prop import vText
self.assertEqual(vText(u'Simple text').to_ical(), 'Simple text')
@ -350,7 +349,7 @@ class TestProp(unittest.TestCase):
# with the official U+FFFD REPLACEMENT CHARACTER.
def test_prop_vTime(self):
vTime = icalendar.prop.vTime
from ..prop import vTime
self.assertEqual(vTime(12, 30, 0).to_ical(), '123000')
self.assertEqual(vTime.from_ical('123000'), time(12, 30))
@ -359,7 +358,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vTime.from_ical, '263000')
def test_prop_vUri(self):
vUri = icalendar.prop.vUri
from ..prop import vUri
self.assertEqual(vUri('http://www.example.com/').to_ical(),
'http://www.example.com/')
@ -367,7 +366,7 @@ class TestProp(unittest.TestCase):
'http://www.example.com/')
def test_prop_vGeo(self):
vGeo = icalendar.prop.vGeo
from ..prop import vGeo
# Pass a list
self.assertEqual(vGeo([1.2, 3.0]).to_ical(), '1.2;3.0')
@ -383,7 +382,7 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vGeo, 'g')
def test_prop_vUTCOffset(self):
vUTCOffset = icalendar.prop.vUTCOffset
from ..prop import vUTCOffset
self.assertEqual(vUTCOffset(timedelta(hours=2)).to_ical(), '+0200')
@ -418,17 +417,18 @@ class TestProp(unittest.TestCase):
self.assertRaises(ValueError, vUTCOffset.from_ical, '+2400')
def test_prop_vInline(self):
vInline = icalendar.prop.vInline
from ..prop import vInline
self.assertEqual(vInline('Some text'), 'Some text')
self.assertEqual(vInline.from_ical('Some text'), 'Some text')
t2 = vInline('other text')
t2.params['cn'] = 'Test Osterone'
self.assertEqual(str(t2.params), "Parameters({'CN': 'Test Osterone'})")
self.assertEqual(str(t2.params),
"Parameters({u'CN': 'Test Osterone'})")
def test_prop_TypesFactory(self):
TypesFactory = icalendar.prop.TypesFactory
from ..prop import TypesFactory
# To get a type you can use it like this.
factory = TypesFactory()
@ -463,7 +463,8 @@ class TestPropertyValues(unittest.TestCase):
def test_vDDDLists_timezone(self):
"""Test vDDDLists with timezone information.
"""
vevent = icalendar.Event()
from .. import Event
vevent = Event()
at = pytz.timezone('Europe/Vienna')
dt1 = at.localize(datetime(2013, 1, 1))
dt2 = at.localize(datetime(2013, 1, 2))

Wyświetl plik

@ -1,8 +1,9 @@
# coding: utf-8
import icalendar
import unittest
from .. import vCalAddress, Calendar, Event, Parameters
class TestPropertyParams(unittest.TestCase):
@ -10,12 +11,12 @@ class TestPropertyParams(unittest.TestCase):
# Property parameters with values containing a COLON character, a
# SEMICOLON character or a COMMA character MUST be placed in quoted
# text.
cal_address = icalendar.vCalAddress('mailto:john.doe@example.org')
cal_address = vCalAddress('mailto:john.doe@example.org')
cal_address.params["CN"] = "Doe, John"
ical = icalendar.Calendar()
ical = Calendar()
ical.add('organizer', cal_address)
ical_str = icalendar.Calendar.to_ical(ical)
ical_str = Calendar.to_ical(ical)
exp_str = """BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":"""\
"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n"""
@ -23,13 +24,13 @@ class TestPropertyParams(unittest.TestCase):
# other way around: ensure the property parameters can be restored from
# an icalendar string.
ical2 = icalendar.Calendar.from_ical(ical_str)
ical2 = Calendar.from_ical(ical_str)
self.assertEqual(ical2.get('ORGANIZER').params.get('CN'), 'Doe, John')
def test_unicode_param(self):
cal_address = icalendar.vCalAddress('mailto:john.doe@example.org')
cal_address = vCalAddress('mailto:john.doe@example.org')
cal_address.params["CN"] = "Джон Доу"
vevent = icalendar.Event()
vevent = Event()
vevent['ORGANIZER'] = cal_address
self.assertEqual(
vevent.to_ical(),
@ -55,8 +56,8 @@ class TestPropertyParams(unittest.TestCase):
@param cn_param: CN parameter value to test for quoting
@param cn_quoted: expected quoted parameter in icalendar format
"""
vevent = icalendar.Event()
attendee = icalendar.vCalAddress('test@mail.com')
vevent = Event()
attendee = vCalAddress('test@mail.com')
attendee.params['CN'] = cn_param
vevent.add('ATTENDEE', attendee)
self.assertEqual(
@ -71,14 +72,14 @@ class TestPropertyParams(unittest.TestCase):
for char in NON_SAFE_CHARS:
cn_escaped = ur"Society\%s 2014" % char
cn_decoded = ur"Society%s 2014" % char
vevent = icalendar.Event.from_ical(
vevent = Event.from_ical(
u'BEGIN:VEVENT\r\n'
u'ORGANIZER;CN=%s:that\r\n'
u'END:VEVENT\r\n' % cn_escaped
)
self.assertEqual(vevent['ORGANIZER'].params['CN'], cn_decoded)
vevent = icalendar.Event.from_ical(
vevent = Event.from_ical(
u'BEGIN:VEVENT\r\n'
u'ORGANIZER;CN=that\\, that\\; that\\\\ that\\:'
u':это\\, то\\; that\\\\ that\\:\r\n'
@ -94,7 +95,6 @@ class TestPropertyParams(unittest.TestCase):
)
def test_parameters_class(self):
from icalendar import Parameters
# Simple parameter:value pair
p = Parameters(parameter1='Value1')

Wyświetl plik

@ -1,3 +1,4 @@
from __future__ import absolute_import
import sys
import random
import textwrap
@ -6,7 +7,7 @@ from string import (
digits,
)
from datetime import datetime
from icalendar.prop import (
from .prop import (
vText,
vDatetime,
)