Projects
Kolab:3.4
python-icalendar
Log In
Username
Password
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
Expand all
Collapse all
Changes of Revision 10
View file
python-icalendar.spec
Changed
@@ -3,7 +3,7 @@ %endif Name: python-icalendar -Version: 3.4 +Version: 3.8.2 Release: 1%{?dist} Summary: Parser/generator of iCalendar files following the RFC 2445 @@ -59,6 +59,9 @@ %{python_sitelib}/*.egg-info %changelog +* Thu Aug 21 2014 Thomas Bruederli <bruederli@kolabsys.com> - 3.8.2 +- New upstream version + * Sun Jun 9 2013 Jeroen van Meeuwen <vanmeeuwen@kolabsys.com> - 3.4-1 - New upstream version
View file
debian.changelog
Changed
@@ -1,3 +1,9 @@ +python-icalendar (3.8.2) unstable; urgency=low + + * Imported Upstream version 3.8.2 + + -- Thomas Bruederli (Kolab Systems) <bruederli@kolabsys.com> Thu, 21 Aug 2014 17:01:00 +0200 + python-icalendar (3.4-1) unstable; urgency=low * Imported Upstream version 3.4
View file
icalendar-3.4.tar.gz/.gitignore
Deleted
@@ -1,24 +0,0 @@ -*.pyc -*.pyo -*.swp -*.swo - -.DT_Store -.coverage -.tox/ -bin/ -build/ -coverage-* -dist/ -docs/_build/ -include/ -lib/ -src/icalendar.egg-info/ -.installed.cfg -.project -.pydevproject -.settings/ -develop-eggs/ -eggs/ -parts/ -htmlcov/
View file
icalendar-3.4.tar.gz/.travis.yml
Deleted
@@ -1,12 +0,0 @@ -language: python -python: -# - "2.4" - - "2.5" - - "2.6" - - "2.7" -# - "3.2" -install: - - pip install . --use-mirrors - - pip install unittest2 --use-mirrors -script: - - $HOME/virtualenv/python$TRAVIS_PYTHON_VERSION/bin/unit2 discover icalendar []
View file
icalendar-3.4.tar.gz/bootstrap.py
Deleted
@@ -1,121 +0,0 @@ -############################################################################## -# -# Copyright (c) 2006 Zope Corporation and Contributors. -# All Rights Reserved. -# -# This software is subject to the provisions of the Zope Public License, -# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. -# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED -# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS -# FOR A PARTICULAR PURPOSE. -# -############################################################################## -"""Bootstrap a buildout-based project - -Simply run this script in a directory containing a buildout.cfg. -The script accepts buildout command-line options, so you can -use the -c option to specify an alternate configuration file. -""" - -import os, shutil, sys, tempfile, urllib2 -from optparse import OptionParser - -tmpeggs = tempfile.mkdtemp() - -is_jython = sys.platform.startswith('java') - -# parsing arguments -parser = OptionParser() -parser.add_option("-v", "--version", dest="version", - help="use a specific zc.buildout version") -parser.add_option("-d", "--distribute", - action="store_true", dest="distribute", default=False, - help="Use Disribute rather than Setuptools.") - -parser.add_option("-c", None, action="store", dest="config_file", - help=("Specify the path to the buildout configuration " - "file to be used.")) - -options, args = parser.parse_args() - -# if -c was provided, we push it back into args for buildout' main function -if options.config_file is not None: - args += ['-c', options.config_file] - -if options.version is not None: - VERSION = '==%s' % options.version -else: - VERSION = '' - -# We decided to always use distribute, make sure this is the default for us -# USE_DISTRIBUTE = options.distribute -USE_DISTRIBUTE = True -args = args + ['bootstrap'] - -to_reload = False -try: - import pkg_resources - if not hasattr(pkg_resources, '_distribute'): - to_reload = True - raise ImportError -except ImportError: - ez = {} - if USE_DISTRIBUTE: - exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) - else: - exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' - ).read() in ez - ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) - - if to_reload: - reload(pkg_resources) - else: - import pkg_resources - -if sys.platform == 'win32': - def quote(c): - if ' ' in c: - return '"%s"' % c # work around spawn lamosity on windows - else: - return c -else: - def quote (c): - return c - -cmd = 'from setuptools.command.easy_install import main; main()' -ws = pkg_resources.working_set - -if USE_DISTRIBUTE: - requirement = 'distribute' -else: - requirement = 'setuptools' - -if is_jython: - import subprocess - - assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', - quote(tmpeggs), 'zc.buildout' + VERSION], - env=dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ).wait() == 0 - -else: - assert os.spawnle( - os.P_WAIT, sys.executable, quote (sys.executable), - '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, - dict(os.environ, - PYTHONPATH= - ws.find(pkg_resources.Requirement.parse(requirement)).location - ), - ) == 0 - -ws.add_entry(tmpeggs) -ws.require('zc.buildout' + VERSION) -import zc.buildout.buildout -zc.buildout.buildout.main(args) -shutil.rmtree(tmpeggs)
View file
icalendar-3.4.tar.gz/buildout.cfg
Deleted
@@ -1,26 +0,0 @@ -[buildout] -develop = . -parts += - test - py - coverage - -[test] -recipe = zc.recipe.testrunner -eggs = - icalendar[test] -defaults = ['--auto-color', '--auto-progress'] - -[py] -recipe = zc.recipe.egg -interpreter = py -eggs = ${test:eggs} -scripts = - -[coverage] -recipe = collective.recipe.template -input = inline: - #!/bin/sh - ./bin/test --coverage ../../coverage -v --auto-progress "$@" -output = ${buildout:directory}/bin/coverage -mode = 755
View file
icalendar-3.4.tar.gz/docs/changelog.rst
Deleted
@@ -1,1 +0,0 @@ -.. include:: ../CHANGES.rst
View file
icalendar-3.4.tar.gz/docs/examples.rst
Deleted
@@ -1,45 +0,0 @@ -======== -Examples -======== - -To open and parse a file:: - - >>> from icalendar import Calendar, Event - >>> cal = Calendar.from_ical(open('test.ics','rb').read()) - >>> cal - VCALENDAR({'VERSION': vText(u'2.0'), 'METHOD': vText(u'Request'), 'PRODID': vText(u'-//My product//mxm.dk/')}) - - >>> for component in cal.walk(): - ... component.name - 'VCALENDAR' - 'VEVENT' - 'VEVENT' - -To create a calendar and write it to disk:: - - >>> cal = Calendar() - >>> from datetime import datetime - >>> cal.add('prodid', '-//My calendar product//mxm.dk//') - >>> cal.add('version', '2.0') - - >>> import pytz - >>> event = Event() - >>> event.add('summary', 'Python meeting about calendaring') - >>> event.add('dtstart', datetime(2005,4,4,8,0,0,tzinfo=pytz.utc)) - >>> event.add('dtend', datetime(2005,4,4,10,0,0,tzinfo=pytz.utc)) - >>> event.add('dtstamp', datetime(2005,4,4,0,10,0,tzinfo=pytz.utc)) - >>> event['uid'] = '20050115T101010/27346262376@mxm.dk' - >>> event.add('priority', 5) - - >>> cal.add_component(event) - - >>> f = open('example.ics', 'wb') - >>> f.write(cal.to_ical()) - >>> f.close() - -More documentation -================== - -Have a look at the doctests in the tests directory of the package to get more -examples. All modules and classes also have doctests that show how they work. -There is also an `interfaces.py` file which describes the API.
View file
icalendar-3.4.tar.gz/src/icalendar/tests/example.rst
Deleted
@@ -1,287 +0,0 @@ -iCalendar package -================= - -This package is used for parsing and generating iCalendar files following the -standard in RFC 2445. - -It should be fully compliant, but it is possible to generate and parse invalid -files if you really want to. - -File structure --------------- - -An iCalendar file is a text file (utf-8) with a special format. Basically it -consists of content lines. - -Each content line defines a property that has 3 parts (name, parameters, -values). Parameters are optional. - -A simple content line with only name and value could look like this:: - - BEGIN:VCALENDAR - -A content line with parameters can look like this:: - - ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:example@example.com - -And the parts are:: - - Name: ATTENDEE - Params: CN=Max Rasmussen;ROLE=REQ-PARTICIPANT - Value: MAILTO:example@example.com - -Long content lines are usually "folded" to less than 75 character, but the -package takes care of that. - -Overview --------- - -On a higher level iCalendar files consists of components. Components can have -sub components. - -The root component is the VCALENDAR:: - - BEGIN:VCALENDAR - ... vcalendar properties ... - END:VCALENDAR - -The most frequent subcomponent to a VCALENDAR is a VEVENT. They are -nested like this:: - - BEGIN:VCALENDAR - ... vcalendar properties ... - BEGIN:VEVENT - ... vevent properties ... - END:VEVENT - END:VCALENDAR - -Inside the components there are properties with values. The values -have special types. like integer, text, datetime etc. These values are -encoded in a special text format in an iCalendar file. - -There are methods for converting to and from these encodings in the package. - -These are the most important imports:: - - >>> from icalendar import Calendar, Event - -Components ----------- - -Components are like (Case Insensitive) dicts. So if you want to set a property -you do it like this. The calendar is a component:: - - >>> cal = Calendar() - >>> cal['dtstart'] = '20050404T080000' - >>> cal['summary'] = 'Python meeting about calendaring' - >>> for k,v in cal.items(): - ... k,v - (u'DTSTART', '20050404T080000') - (u'SUMMARY', 'Python meeting about calendaring') - -NOTE: the recommended way to add components to the calendar is to use -create the subcomponent and add it via Calendar.add! The example above adds a -string, but not a vText component. - - -You can generate a string for a file with the to_ical() method:: - - >>> cal.to_ical() - 'BEGIN:VCALENDAR\r\nDTSTART:20050404T080000\r\nSUMMARY:Python meeting about calendaring\r\nEND:VCALENDAR\r\n' - -The rendered view is easier to read:: - - BEGIN:VCALENDAR - DTSTART:20050404T080000 - SUMMARY:Python meeting about calendaring - END:VCALENDAR - -So, let's define a function so we can easily display to_ical() output:: - - >>> def display(cal): - ... return cal.to_ical().replace('\r\n', '\n').strip() - -You can set multiple properties like this:: - - >>> cal = Calendar() - >>> cal['attendee'] = ['MAILTO:maxm@mxm.dk','MAILTO:test@example.com'] - >>> print display(cal) - BEGIN:VCALENDAR - ATTENDEE:MAILTO:maxm@mxm.dk - ATTENDEE:MAILTO:test@example.com - END:VCALENDAR - -If you don't want to care about whether a property value is a list or -a single value, just use the add() method. It will automatically -convert the property to a list of values if more than one value is -added. Here is an example:: - - >>> cal = Calendar() - >>> cal.add('attendee', 'MAILTO:maxm@mxm.dk') - >>> cal.add('attendee', 'MAILTO:test@example.com') - >>> print display(cal) - BEGIN:VCALENDAR - ATTENDEE:MAILTO:maxm@mxm.dk - ATTENDEE:MAILTO:test@example.com - END:VCALENDAR - -Note: this version doesn't check for compliance, so you should look in -the RFC 2445 spec for legal properties for each component, or look in -the icalendar/calendar.py file, where it is at least defined for each -component. - -Subcomponents -------------- - -Any component can have subcomponents. Eg. inside a calendar there can -be events. They can be arbitrarily nested. First by making a new -component:: - - >>> event = Event() - >>> event['uid'] = '42' - >>> event['dtstart'] = '20050404T080000' - -And then appending it to a "parent":: - - >>> cal.add_component(event) - >>> print display(cal) - BEGIN:VCALENDAR - ATTENDEE:MAILTO:maxm@mxm.dk - ATTENDEE:MAILTO:test@example.com - BEGIN:VEVENT - DTSTART:20050404T080000 - UID:42 - END:VEVENT - END:VCALENDAR - -Subcomponents are appended to the subcomponents property on the component:: - - >>> cal.subcomponents - [VEVENT({'DTSTART': '20050404T080000', 'UID': '42'})] - -Value types ------------ - -Property values are utf-8 encoded strings. - -This is impractical if you want to use the data for further -computation. Eg. the datetime format looks like this: -'20050404T080000'. But the package makes it simple to Parse and -generate iCalendar formatted strings. - -Basically you can make the add() method do the thinking, or you can do it -yourself. - -To add a datetime value, you can use Pythons built in datetime types, -and the set the encode parameter to true, and it will convert to the -type defined in the spec:: - - >>> from datetime import datetime - >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) - >>> cal['dtstart'].to_ical() - '20050404T080000' - -If that doesn't work satisfactorily for some reason, you can also do it -manually. - -In 'icalendar.prop', all the iCalendar data types are defined. Each -type has a class that can parse and encode the type. - -So if you want to do it manually:: - - >>> from icalendar import vDatetime - >>> now = datetime(2005,4,4,8,0,0) - >>> vDatetime(now).to_ical() - '20050404T080000' - -So the drill is to initialise the object with a python built in type, -and then call the "to_ical()" method on the object. That will return an -ical encoded string. - -You can do it the other way around too. To parse an encoded string, just call -the "from_ical()" method, and it will return an instance of the corresponding -Python type:: - - >>> vDatetime.from_ical('20050404T080000') - datetime.datetime(2005, 4, 4, 8, 0) - - >>> dt = vDatetime.from_ical('20050404T080000Z') - >>> repr(dt)[:62] - 'datetime.datetime(2005, 4, 4, 8, 0, tzinfo=<UTC>)' - -You can also choose to use the decoded() method, which will return a decoded -value directly:: - - >>> cal = Calendar() - >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) - >>> cal['dtstart'].to_ical() - '20050404T080000' - >>> cal.decoded('dtstart') - datetime.datetime(2005, 4, 4, 8, 0) - -Example -------- - -Here is an example generating a complete iCal calendar file with a -single event that can be loaded into the Mozilla calendar - -Init the calendar:: - - >>> cal = Calendar() - >>> from datetime import datetime - -Some properties are required to be compliant:: - - >>> cal.add('prodid', '-//My calendar product//mxm.dk//') - >>> cal.add('version', '2.0') - -We need at least one subcomponent for a calendar to be compliant:: - - >>> import pytz - >>> event = Event() - >>> event.add('summary', 'Python meeting about calendaring') - >>> event.add('dtstart', datetime(2005,4,4,8,0,0,tzinfo=pytz.utc)) - >>> event.add('dtend', datetime(2005,4,4,10,0,0,tzinfo=pytz.utc)) - >>> event.add('dtstamp', datetime(2005,4,4,0,10,0,tzinfo=pytz.utc)) - -A property with parameters. Notice that they are an attribute on the value:: - - >>> from icalendar import vCalAddress, vText - >>> organizer = vCalAddress('MAILTO:noone@example.com') - -Automatic encoding is not yet implemented for parameter values, so you -must use the 'v*' types you can import from the icalendar package -(they're defined in ``icalendar.prop``):: - - >>> organizer.params['cn'] = vText('Max Rasmussen') - >>> organizer.params['role'] = vText('CHAIR') - >>> event['organizer'] = organizer - >>> event['location'] = vText('Odense, Denmark') - - >>> event['uid'] = '20050115T101010/27346262376@mxm.dk' - >>> event.add('priority', 5) - - >>> attendee = vCalAddress('MAILTO:maxm@example.com') - >>> attendee.params['cn'] = vText('Max Rasmussen') - >>> attendee.params['ROLE'] = vText('REQ-PARTICIPANT') - >>> event.add('attendee', attendee, encode=0) - - >>> attendee = vCalAddress('MAILTO:the-dude@example.com') - >>> attendee.params['cn'] = vText('The Dude') - >>> attendee.params['ROLE'] = vText('REQ-PARTICIPANT') - >>> event.add('attendee', attendee, encode=0) - -Add the event to the calendar:: - - >>> cal.add_component(event) - -Write to disk:: - - >>> import tempfile, os - >>> directory = tempfile.mkdtemp() - >>> f = open(os.path.join(directory, 'example.ics'), 'wb') - >>> f.write(cal.to_ical()) - >>> f.close() - - XXX We should check whether the write succeeded here.. -
View file
icalendar-3.4.tar.gz/src/icalendar/tests/groupscheduled.ics
Deleted
@@ -1,36 +0,0 @@ -BEGIN:VCALENDAR -PRODID:-//RDU Software//NONSGML HandCal//EN -VERSION:2.0 -BEGIN:VTIMEZONE -TZID:US-Eastern -BEGIN:STANDARD -DTSTART:19981025T020000 -RDATE:19981025T020000 -TZOFFSETFROM:-0400 -TZOFFSETTO:-0500 -TZNAME:EST -END:STANDARD -BEGIN:DAYLIGHT -DTSTART:19990404T020000 -RDATE:19990404T020000 -TZOFFSETFROM:-0500 -TZOFFSETTO:-0400 -TZNAME:EDT -END:DAYLIGHT -END:VTIMEZONE -BEGIN:VEVENT -DTSTAMP:19980309T231000Z -UID:guid-1.host1.com -ORGANIZER;ROLE=CHAIR:MAILTO:mrbig@host.com -ATTENDEE;RSVP=TRUE;ROLE=REQ-PARTICIPANT;CUTYPE=GROUP: -MAILTO:employee-A@host.com -DESCRIPTION:Project XYZ Review Meeting -CATEGORIES:MEETING -CLASS:PUBLIC -CREATED:19980309T130000Z -SUMMARY:XYZ Project Review -DTSTART;TZID=US-Eastern:19980312T083000 -DTEND;TZID=US-Eastern:19980312T093000 -LOCATION:1CP Conference Room 4350 -END:VEVENT -END:VCALENDAR
View file
icalendar-3.4.tar.gz/src/icalendar/tests/groupscheduled.rst
Deleted
@@ -1,21 +0,0 @@ -An example from the RFC 2445 spec:: - - >>> from icalendar import Calendar - >>> import os - >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_ical( - ... open(os.path.join(directory, 'groupscheduled.ics'),'rb').read()) - >>> cal - VCALENDAR({'VERSION': '2.0', 'PRODID': '-//RDU Software//NONSGML HandCal//EN'}) - - >>> timezones = cal.walk('VTIMEZONE') - >>> len(timezones) - 1 - - >>> tz = timezones[0] - >>> tz - VTIMEZONE({'TZID': 'US-Eastern'}) - - >>> std = tz.walk('STANDARD')[0] - >>> std.decoded('TZOFFSETFROM') - datetime.timedelta(-1, 72000)
View file
icalendar-3.4.tar.gz/src/icalendar/tests/multiple.rst
Deleted
@@ -1,19 +0,0 @@ -A exmaple with multiple VCALENDAR components:: - - >>> from icalendar import Calendar - >>> import os - >>> directory = os.path.dirname(__file__) - >>> cals = Calendar.from_ical( - ... open(os.path.join(directory, 'multiple.ics'),'rb').read(), multiple=True) - - >>> for cal in cals: - ... for component in cal.walk(): - ... component.name - 'VCALENDAR' - 'VEVENT' - 'VCALENDAR' - 'VEVENT' - 'VEVENT' - - >>> cals[0]['prodid'] - vText('-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN')
View file
icalendar-3.4.tar.gz/src/icalendar/tests/recurrence.rst
Deleted
@@ -1,21 +0,0 @@ -Testing recurrence. - - >>> from icalendar import Calendar - >>> import os - >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_ical( - ... open(os.path.join(directory, 'recurrence.ics'),'rb').read()) - >>> first_event = cal.walk('vevent')[0] - - >>> first_event['rrule'] - CaselessDict({'COUNT': [100], 'FREQ': ['DAILY']}) - - >>> first_event['exdate'].dts[0].dt - datetime.datetime(1996, 4, 2, 1, 0, tzinfo=<UTC>) - - >>> first_event['exdate'].dts[1].dt - datetime.datetime(1996, 4, 3, 1, 0, tzinfo=<UTC>) - - >>> first_event['exdate'].dts[2].dt - datetime.datetime(1996, 4, 4, 1, 0, tzinfo=<UTC>) -
View file
icalendar-3.4.tar.gz/src/icalendar/tests/small.ics
Deleted
@@ -1,25 +0,0 @@ -BEGIN:VCALENDAR -METHOD:Request -PRODID:-//My product//mxm.dk/ -VERSION:2.0 -BEGIN:VEVENT -DESCRIPTION:This is a very long description that will be folded This is a - very long description that will be folded This is a very long description - that will be folded This is a very long description that will be folded Th - is is a very long description that will be folded This is a very long desc - ription that will be folded This is a very long description that will be f - olded This is a very long description that will be folded This is a very l - ong description that will be folded This is a very long description that w - ill be folded -PARTICIPANT;CN=Max M:MAILTO:maxm@mxm.dk -DTEND:20050107T160000 -DTSTART:20050107T120000 -SUMMARY:A second event -END:VEVENT -BEGIN:VEVENT -DTEND:20050108T235900 -DTSTART:20050108T230000 -SUMMARY:A single event -UID:42 -END:VEVENT -END:VCALENDAR
View file
icalendar-3.4.tar.gz/src/icalendar/tests/small.rst
Deleted
@@ -1,30 +0,0 @@ -A small example:: - - >>> from icalendar import Calendar - >>> import os - >>> directory = os.path.dirname(__file__) - >>> cal = Calendar.from_ical( - ... open(os.path.join(directory, 'small.ics'),'rb').read()) - >>> cal - VCALENDAR({'VERSION': '2.0', - 'METHOD': 'Request', - 'PRODID': '-//My product//mxm.dk/'}) - - >>> for component in cal.walk(): - ... component.name - 'VCALENDAR' - 'VEVENT' - 'VEVENT' - - >>> cal['prodid'] - vText('-//My product//mxm.dk/') - - >>> cal.decoded('prodid') - '-//My product//mxm.dk/' - - >>> first_event = cal.walk('vevent')[0] - >>> first_event['description'][:75] - u'This is a very long description that will be folded This is a very long des' - - >>> first_event['summary'] - vText('A second event')
View file
icalendar-3.4.tar.gz/CHANGES.rst -> icalendar-3.8.2.tar.gz/CHANGES.rst
Changed
@@ -2,6 +2,120 @@ Changelog ========= +3.8.2 (2014-07-22) +------------------ + +- Exclude editor backup files from egg distributions. Fixes #144. + [thet] + + +3.8.1 (2014-07-17) +------------------ + +- The representation of CaselessDicts in 3.8 changed the name attribute of + Components and therefore broke the external API. This has been fixed. + [untitaker] + + +3.8 (2014-07-17) +---------------- + +- Allow dots in property names (Needed for vCard compatibility). Refs #143. + [untitaker] + +- Change class representation for CaselessDict objects to always include the + class name or the class' name attribute, if available. Also show + subcomponents for Component objects. + [thet] + +- Don't use data_encode for CaselessDict class representation but use dict's + __repr__ method. + [t-8ch] + +- Handle parameters with multiple values, which is needed for vCard 3.0. + Refs #142. + [t-8ch] + + +3.7 (2014-06-02) +---------------- + +- For components with ``ignore_exceptions`` set to ``True``, mark unparseable + lines as broken instead rising a ``ValueError``. ``VEVENT`` components have + ``ignore_exceptions`` set to ``True`` by default. Ref #131. Fixes #104. + [jkiang13] + +- Make ``python-dateutil`` a soft-dependency. + [boltnev] + +- Add optional ``sorted`` parameter to ``Component.to_ical``. Setting it to + false allows the user to preserve the original property and parameter order. + Ref #136. Fixes #133. + [untitaker] + +- Fix tests for latest ``pytz``. Don't set ``tzinfo`` directly on datetime + objects, but use pytz's ``localize`` function. Ref #138. + [untitaker, thet] + +- Remove incorrect use of __all__. We don't encourage using ``from package + import *`` imports. Fixes #129. + [eric-wieser] + + +3.6.2 (2014-04-05) +------------------ + +- Pep8 and cleanup. + [lasudry] + +3.6.1 (2014-01-13) +------------------ + +- Open text files referenced by setup.py as utf-8, no matter what the locale + settings are set to. Fixes #122. + [sochotnicky] + +- Add tox.ini to source tarball, which simplifies testing for in distributions. + [sochotnicky] + + +3.6 (2014-01-06) +---------------- + +- Python3 (3.3+) + Python 2 (2.6+) support [geier] + +- Made sure to_ical() always returns bytes [geier] + +- Support adding lists to a component property, which value already was a list + and remove the Component.set method, which was only used by the add method. + [thet] + +- Remove ability to add property parameters via a value's params attribute when + adding via cal.add (that was only possible for custom value objects and makes + up a strange API), but support a parameter attribute on cal.add's method + signature to pass a dictionary with property parameter key/value pairs. + Fixes #116. + [thet] + +- Backport some of Regebro's changes from his regebro-refactor branch. + [thet] + +- Raise explicit error on another malformed content line case. + [hajdbo] + +- Correctly parse datetime component property values with timezone information + when parsed from ical strings. + [untitaker] + + +3.5 (2013-07-03) +---------------- + +- Let to_unicode be more graceful for non-unicode strings, as like CMFPlone's + safe_unicode does it. + [thet] + + 3.4 (2013-04-24) ----------------
View file
icalendar-3.4.tar.gz/MANIFEST.in -> icalendar-3.8.2.tar.gz/MANIFEST.in
Changed
@@ -1,4 +1,4 @@ -include *.rst +include *.rst tox.ini graft docs recursive-include src/icalendar * -recursive-exclude src/icalendar *.pyc +recursive-exclude src/icalendar *.pyc *~
View file
icalendar-3.4.tar.gz/PKG-INFO -> icalendar-3.8.2.tar.gz/PKG-INFO
Changed
@@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: icalendar -Version: 3.4 +Version: 3.8.2 Summary: iCalendar parser/generator Home-page: https://github.com/collective/icalendar Author: Plone Foundation @@ -19,12 +19,23 @@ :Code: http://github.com/collective/icalendar :Mailing list: http://github.com/collective/icalendar/issues :Dependencies: `setuptools`_ and since version 3.0 we depend on `pytz`_. - :Compatible with: Python 2.6 and 2.7 + :Compatible with: Python 2.6, 2.7 and 3.3+ :License: `BSD`_ ---- + .. image:: https://travis-ci.org/collective/icalendar.svg?branch=master + :target: https://travis-ci.org/collective/icalendar + + + Roadmap + ======= + + - 4.0: API refactoring + + + Changes in version 3.0 ====================== @@ -55,21 +66,6 @@ Instead of the own UTC tzinfo implementation we use pytz UTC tzinfo object now. - Roadmap - ======= - - 1) Internally Unicode will be used exclusively. - - 2) On API Functions, accept or return Unicode or en/decoded strings. - - 3) This will make the 3.4 Release. - - 4) API change: API calls also only accept/return Unicode. - - 5) This will make the 4.0 release. - - - About this fork which is not a fork anymore =========================================== @@ -95,21 +91,136 @@ Output from coverage test:: - Name Stmts Miss Cover - ---------------------------------------------------------------------------------- - .tox/py27/lib/python2.7/site-packages/icalendar/__init__ 6 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/cal 223 8 96% - .tox/py27/lib/python2.7/site-packages/icalendar/caselessdict 55 2 96% - .tox/py27/lib/python2.7/site-packages/icalendar/parser 181 18 90% - .tox/py27/lib/python2.7/site-packages/icalendar/parser_tools 19 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/prop 521 59 89% - .tox/py27/lib/python2.7/site-packages/icalendar/tools 12 0 100% - ---------------------------------------------------------------------------------- - TOTAL 1017 87 91% + Name Stmts Miss Cover + ------------------------------------------------ + src/icalendar/__init__ 4 0 100% + src/icalendar/cal 243 7 97% + src/icalendar/caselessdict 66 7 89% + src/icalendar/compat 1 0 100% + src/icalendar/parser 192 6 97% + src/icalendar/parser_tools 20 0 100% + src/icalendar/prop 536 64 88% + src/icalendar/tools 16 0 100% + ------------------------------------------------ + TOTAL 1078 84 92% Changelog ========= + 3.8.2 (2014-07-22) + ------------------ + + - Exclude editor backup files from egg distributions. Fixes #144. + [thet] + + + 3.8.1 (2014-07-17) + ------------------ + + - The representation of CaselessDicts in 3.8 changed the name attribute of + Components and therefore broke the external API. This has been fixed. + [untitaker] + + + 3.8 (2014-07-17) + ---------------- + + - Allow dots in property names (Needed for vCard compatibility). Refs #143. + [untitaker] + + - Change class representation for CaselessDict objects to always include the + class name or the class' name attribute, if available. Also show + subcomponents for Component objects. + [thet] + + - Don't use data_encode for CaselessDict class representation but use dict's + __repr__ method. + [t-8ch] + + - Handle parameters with multiple values, which is needed for vCard 3.0. + Refs #142. + [t-8ch] + + + 3.7 (2014-06-02) + ---------------- + + - For components with ``ignore_exceptions`` set to ``True``, mark unparseable + lines as broken instead rising a ``ValueError``. ``VEVENT`` components have + ``ignore_exceptions`` set to ``True`` by default. Ref #131. Fixes #104. + [jkiang13] + + - Make ``python-dateutil`` a soft-dependency. + [boltnev] + + - Add optional ``sorted`` parameter to ``Component.to_ical``. Setting it to + false allows the user to preserve the original property and parameter order. + Ref #136. Fixes #133. + [untitaker] + + - Fix tests for latest ``pytz``. Don't set ``tzinfo`` directly on datetime + objects, but use pytz's ``localize`` function. Ref #138. + [untitaker, thet] + + - Remove incorrect use of __all__. We don't encourage using ``from package + import *`` imports. Fixes #129. + [eric-wieser] + + + 3.6.2 (2014-04-05) + ------------------ + + - Pep8 and cleanup. + [lasudry] + + 3.6.1 (2014-01-13) + ------------------ + + - Open text files referenced by setup.py as utf-8, no matter what the locale + settings are set to. Fixes #122. + [sochotnicky] + + - Add tox.ini to source tarball, which simplifies testing for in distributions. + [sochotnicky] + + + 3.6 (2014-01-06) + ---------------- + + - Python3 (3.3+) + Python 2 (2.6+) support [geier] + + - Made sure to_ical() always returns bytes [geier] + + - Support adding lists to a component property, which value already was a list + and remove the Component.set method, which was only used by the add method. + [thet] + + - Remove ability to add property parameters via a value's params attribute when + adding via cal.add (that was only possible for custom value objects and makes + up a strange API), but support a parameter attribute on cal.add's method + signature to pass a dictionary with property parameter key/value pairs. + Fixes #116. + [thet] + + - Backport some of Regebro's changes from his regebro-refactor branch. + [thet] + + - Raise explicit error on another malformed content line case. + [hajdbo] + + - Correctly parse datetime component property values with timezone information + when parsed from ical strings. + [untitaker] + + + 3.5 (2013-07-03) + ---------------- + + - Let to_unicode be more graceful for non-unicode strings, as like CMFPlone's + safe_unicode does it. + [thet] + + 3.4 (2013-04-24) ---------------- @@ -481,5 +592,10 @@ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent
View file
icalendar-3.4.tar.gz/README.rst -> icalendar-3.8.2.tar.gz/README.rst
Changed
@@ -11,12 +11,23 @@ :Code: http://github.com/collective/icalendar :Mailing list: http://github.com/collective/icalendar/issues :Dependencies: `setuptools`_ and since version 3.0 we depend on `pytz`_. - :Compatible with: Python 2.6 and 2.7 + :Compatible with: Python 2.6, 2.7 and 3.3+ :License: `BSD`_ ---- +.. image:: https://travis-ci.org/collective/icalendar.svg?branch=master + :target: https://travis-ci.org/collective/icalendar + + +Roadmap +======= + +- 4.0: API refactoring + + + Changes in version 3.0 ====================== @@ -47,21 +58,6 @@ Instead of the own UTC tzinfo implementation we use pytz UTC tzinfo object now. -Roadmap -======= - -1) Internally Unicode will be used exclusively. - -2) On API Functions, accept or return Unicode or en/decoded strings. - -3) This will make the 3.4 Release. - -4) API change: API calls also only accept/return Unicode. - -5) This will make the 4.0 release. - - - About this fork which is not a fork anymore =========================================== @@ -87,14 +83,15 @@ Output from coverage test:: - Name Stmts Miss Cover - ---------------------------------------------------------------------------------- - .tox/py27/lib/python2.7/site-packages/icalendar/__init__ 6 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/cal 223 8 96% - .tox/py27/lib/python2.7/site-packages/icalendar/caselessdict 55 2 96% - .tox/py27/lib/python2.7/site-packages/icalendar/parser 181 18 90% - .tox/py27/lib/python2.7/site-packages/icalendar/parser_tools 19 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/prop 521 59 89% - .tox/py27/lib/python2.7/site-packages/icalendar/tools 12 0 100% - ---------------------------------------------------------------------------------- - TOTAL 1017 87 91% + Name Stmts Miss Cover + ------------------------------------------------ + src/icalendar/__init__ 4 0 100% + src/icalendar/cal 243 7 97% + src/icalendar/caselessdict 66 7 89% + src/icalendar/compat 1 0 100% + src/icalendar/parser 192 6 97% + src/icalendar/parser_tools 20 0 100% + src/icalendar/prop 536 64 88% + src/icalendar/tools 16 0 100% + ------------------------------------------------ + TOTAL 1078 84 92%
View file
icalendar-3.4.tar.gz/docs/credits.rst -> icalendar-3.8.2.tar.gz/docs/credits.rst
Changed
@@ -7,6 +7,7 @@ - Andrey Nikolaev <nikolaeff@gmail.com> - Barak Michener <me@barakmich.com> - Christophe de Vienne <cdevienne@gmail.com> +- Christian Geier <contact@lostpackets.de> - Dai MIKURUBE <dmikurube@acm.org> - Dan Stovall <dbstovall@gmail.com> - Eric Hanchrow <erich@cozi.com> @@ -29,7 +30,10 @@ - Ronan Dunklau <ronan@dunklau.fr> - Sidnei da Silva <sidnei@enfoldsystems.com> - Stanislav Ochotnicky <sochotnicky@redhat.com> +- Stefan Schwarzer <sschwarzer@sschwarzer.net> - Victor Varvaryuk <victor.varvariuc@gmail.com> - Wichert Akkerman <wichert@wiggy.net> - spanktar <spanky@kapanka.com> - tgecho <tgecho@gmail.com> +- Markus Unterwaditzer <markus@unterwaditzer.net> +- Thomas Weißschuh <thomas@t-8ch.de>
View file
icalendar-3.4.tar.gz/docs/index.rst -> icalendar-3.8.2.tar.gz/docs/index.rst
Changed
@@ -9,8 +9,7 @@ about install - examples + usage RFC 5545 <rfc5545/index> - changelog credits license
View file
icalendar-3.8.2.tar.gz/docs/usage.rst
Added
@@ -0,0 +1,324 @@ +iCalendar package +================= + +This package is used for parsing and generating iCalendar files following the +standard in RFC 2445. + +It should be fully compliant, but it is possible to generate and parse invalid +files if you really want to. + + +File structure +-------------- + +An iCalendar file is a text file (utf-8) with a special format. Basically it +consists of content lines. + +Each content line defines a property that has 3 parts (name, parameters, +values). Parameters are optional. + +A simple content line with only name and value could look like this:: + + BEGIN:VCALENDAR + +A content line with parameters can look like this:: + + ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:MAILTO:example@example.com + +And the parts are:: + + Name: ATTENDEE + Params: CN=Max Rasmussen;ROLE=REQ-PARTICIPANT + Value: MAILTO:example@example.com + +Long content lines are usually "folded" to less than 75 character, but the +package takes care of that. + + +Overview +-------- + +On a higher level iCalendar files consists of components. Components can have +sub components. + +The root component is the VCALENDAR:: + + BEGIN:VCALENDAR + ... vcalendar properties ... + END:VCALENDAR + +The most frequent subcomponent to a VCALENDAR is a VEVENT. They are +nested like this:: + + BEGIN:VCALENDAR + ... vcalendar properties ... + BEGIN:VEVENT + ... vevent properties ... + END:VEVENT + END:VCALENDAR + +Inside the components there are properties with values. The values +have special types. like integer, text, datetime etc. These values are +encoded in a special text format in an iCalendar file. + +There are methods for converting to and from these encodings in the package. + +These are the most important imports:: + + >>> from icalendar import Calendar, Event + + +Components +---------- + +Components are like (Case Insensitive) dicts. So if you want to set a property +you do it like this. The calendar is a component:: + + >>> cal = Calendar() + >>> cal['dtstart'] = '20050404T080000' + >>> cal['summary'] = 'Python meeting about calendaring' + >>> for k,v in cal.items(): + ... k,v + (u'DTSTART', '20050404T080000') + (u'SUMMARY', 'Python meeting about calendaring') + +NOTE: the recommended way to add components to the calendar is to use +create the subcomponent and add it via Calendar.add! The example above adds a +string, but not a vText component. + + +You can generate a string for a file with the to_ical() method:: + + >>> cal.to_ical() + 'BEGIN:VCALENDAR\r\nDTSTART:20050404T080000\r\nSUMMARY:Python meeting about calendaring\r\nEND:VCALENDAR\r\n' + +The rendered view is easier to read:: + + BEGIN:VCALENDAR + DTSTART:20050404T080000 + SUMMARY:Python meeting about calendaring + END:VCALENDAR + +So, let's define a function so we can easily display to_ical() output:: + + >>> def display(cal): + ... return cal.to_ical().replace('\r\n', '\n').strip() + +You can set multiple properties like this:: + + >>> cal = Calendar() + >>> cal['attendee'] = ['MAILTO:maxm@mxm.dk','MAILTO:test@example.com'] + >>> print display(cal) + BEGIN:VCALENDAR + ATTENDEE:MAILTO:maxm@mxm.dk + ATTENDEE:MAILTO:test@example.com + END:VCALENDAR + +If you don't want to care about whether a property value is a list or +a single value, just use the add() method. It will automatically +convert the property to a list of values if more than one value is +added. Here is an example:: + + >>> cal = Calendar() + >>> cal.add('attendee', 'MAILTO:maxm@mxm.dk') + >>> cal.add('attendee', 'MAILTO:test@example.com') + >>> print display(cal) + BEGIN:VCALENDAR + ATTENDEE:MAILTO:maxm@mxm.dk + ATTENDEE:MAILTO:test@example.com + END:VCALENDAR + +Note: this version doesn't check for compliance, so you should look in +the RFC 2445 spec for legal properties for each component, or look in +the icalendar/calendar.py file, where it is at least defined for each +component. + + +Subcomponents +------------- + +Any component can have subcomponents. Eg. inside a calendar there can +be events. They can be arbitrarily nested. First by making a new +component:: + + >>> event = Event() + >>> event['uid'] = '42' + >>> event['dtstart'] = '20050404T080000' + +And then appending it to a "parent":: + + >>> cal.add_component(event) + >>> print display(cal) + BEGIN:VCALENDAR + ATTENDEE:MAILTO:maxm@mxm.dk + ATTENDEE:MAILTO:test@example.com + BEGIN:VEVENT + DTSTART:20050404T080000 + UID:42 + END:VEVENT + END:VCALENDAR + +Subcomponents are appended to the subcomponents property on the component:: + + >>> cal.subcomponents + [VEVENT({'DTSTART': '20050404T080000', 'UID': '42'})] + + +Value types +----------- + +Property values are utf-8 encoded strings. + +This is impractical if you want to use the data for further +computation. Eg. the datetime format looks like this: +'20050404T080000'. But the package makes it simple to Parse and +generate iCalendar formatted strings. + +Basically you can make the add() method do the thinking, or you can do it +yourself. + +To add a datetime value, you can use Pythons built in datetime types, +and the set the encode parameter to true, and it will convert to the +type defined in the spec:: + + >>> from datetime import datetime + >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) + >>> cal['dtstart'].to_ical() + '20050404T080000' + +If that doesn't work satisfactorily for some reason, you can also do it +manually. + +In 'icalendar.prop', all the iCalendar data types are defined. Each +type has a class that can parse and encode the type. + +So if you want to do it manually:: + + >>> from icalendar import vDatetime + >>> now = datetime(2005,4,4,8,0,0) + >>> vDatetime(now).to_ical() + '20050404T080000' + +So the drill is to initialise the object with a python built in type, +and then call the "to_ical()" method on the object. That will return an +ical encoded string. + +You can do it the other way around too. To parse an encoded string, just call +the "from_ical()" method, and it will return an instance of the corresponding +Python type:: + + >>> vDatetime.from_ical('20050404T080000') + datetime.datetime(2005, 4, 4, 8, 0) + + >>> dt = vDatetime.from_ical('20050404T080000Z') + >>> repr(dt)[:62] + 'datetime.datetime(2005, 4, 4, 8, 0, tzinfo=<UTC>)' + +You can also choose to use the decoded() method, which will return a decoded +value directly:: + + >>> cal = Calendar() + >>> cal.add('dtstart', datetime(2005,4,4,8,0,0)) + >>> cal['dtstart'].to_ical() + '20050404T080000' + >>> cal.decoded('dtstart') + datetime.datetime(2005, 4, 4, 8, 0) + + +Property parameters +------------------- + +Property parameters are automatically added, depending on the input value. For +example, for date/time related properties, the value type and timezone +identifier (if applicable) are automatically added here:: + + >>> event = Event() + >>> event.add('dtstart', datetime(2010, 10, 10, 10, 0, 0, + ... tzinfo=pytz.timezone("Europe/Vienna"))) + + >>> lines = event.to_ical().splitlines() + >>> self.assertTrue( + ... b"DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20101010T100000" + ... in lines) + + +You can also add arbitrary property parameters by passing a parameters +dictionary to the add method like so:: + + >>> event = Event() + >>> event.add('X-TEST-PROP', 'tryout.', + .... parameters={'prop1': 'val1', 'prop2': 'val2'}) + >>> lines = event.to_ical().splitlines() + >>> self.assertTrue(b"X-TEST-PROP;PROP1=val1;PROP2=val2:tryout." in lines) + + +Example +------- + +Here is an example generating a complete iCal calendar file with a +single event that can be loaded into the Mozilla calendar + +Init the calendar:: + + >>> cal = Calendar() + >>> from datetime import datetime + +Some properties are required to be compliant:: + + >>> cal.add('prodid', '-//My calendar product//mxm.dk//') + >>> cal.add('version', '2.0') + +We need at least one subcomponent for a calendar to be compliant:: + + >>> import pytz + >>> event = Event() + >>> event.add('summary', 'Python meeting about calendaring') + >>> event.add('dtstart', datetime(2005,4,4,8,0,0,tzinfo=pytz.utc)) + >>> event.add('dtend', datetime(2005,4,4,10,0,0,tzinfo=pytz.utc)) + >>> event.add('dtstamp', datetime(2005,4,4,0,10,0,tzinfo=pytz.utc)) + +A property with parameters. Notice that they are an attribute on the value:: + + >>> from icalendar import vCalAddress, vText + >>> organizer = vCalAddress('MAILTO:noone@example.com') + +Automatic encoding is not yet implemented for parameter values, so you +must use the 'v*' types you can import from the icalendar package +(they're defined in ``icalendar.prop``):: + + >>> organizer.params['cn'] = vText('Max Rasmussen') + >>> organizer.params['role'] = vText('CHAIR') + >>> event['organizer'] = organizer + >>> event['location'] = vText('Odense, Denmark') + + >>> event['uid'] = '20050115T101010/27346262376@mxm.dk' + >>> event.add('priority', 5) + + >>> attendee = vCalAddress('MAILTO:maxm@example.com') + >>> attendee.params['cn'] = vText('Max Rasmussen') + >>> attendee.params['ROLE'] = vText('REQ-PARTICIPANT') + >>> event.add('attendee', attendee, encode=0) + + >>> attendee = vCalAddress('MAILTO:the-dude@example.com') + >>> attendee.params['cn'] = vText('The Dude') + >>> attendee.params['ROLE'] = vText('REQ-PARTICIPANT') + >>> event.add('attendee', attendee, encode=0) + +Add the event to the calendar:: + + >>> cal.add_component(event) + +Write to disk:: + + >>> import tempfile, os + >>> directory = tempfile.mkdtemp() + >>> f = open(os.path.join(directory, 'example.ics'), 'wb') + >>> f.write(cal.to_ical()) + >>> f.close() + + +More documentation +================== + +Have a look at the tests of this package to get more examples. +All modules and classes docstrings, which document how they work.
View file
icalendar-3.4.tar.gz/setup.py -> icalendar-3.8.2.tar.gz/setup.py
Changed
@@ -1,11 +1,29 @@ +import codecs import setuptools +import sys -version = '3.4' + +version = '3.8.2' shortdesc = 'iCalendar parser/generator' -longdesc = open('README.rst').read() -longdesc += open('CHANGES.rst').read() -longdesc += open('LICENSE.rst').read() -tests_require = ['unittest2'] +longdesc = codecs.open('README.rst', encoding='utf-8').read() +longdesc += codecs.open('CHANGES.rst', encoding='utf-8').read() +longdesc += codecs.open('LICENSE.rst', encoding='utf-8').read() + + +tests_require = [ + 'python-dateutil', +] +install_requires = [ + 'setuptools', + 'pytz' +] + +if sys.version_info[:2] == (2, 6): + # Python unittest2 only needed for Python 2.6 + tests_require.append('unittest2') + # OrderedDict was added in 2.7 + install_requires.append('ordereddict') + setuptools.setup( name='icalendar', @@ -15,24 +33,26 @@ classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', + "Programming Language :: Python", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', - ], + ], keywords='calendar calendaring ical icalendar event todo journal ' 'recurring', author='Plone Foundation', - author_email='plone-developers@lists.sourceforge.net', + author_email='plone-developers@lists.sourceforge.net', url='https://github.com/collective/icalendar', license='BSD', packages=setuptools.find_packages('src'), package_dir={'': 'src'}, include_package_data=True, zip_safe=False, - install_requires=[ - 'setuptools', - 'python-dateutil', - 'pytz', - ], + install_requires=install_requires, extras_require={ 'test': tests_require - }) + } +)
View file
icalendar-3.4.tar.gz/src/icalendar.egg-info/PKG-INFO -> icalendar-3.8.2.tar.gz/src/icalendar.egg-info/PKG-INFO
Changed
@@ -1,6 +1,6 @@ Metadata-Version: 1.1 Name: icalendar -Version: 3.4 +Version: 3.8.2 Summary: iCalendar parser/generator Home-page: https://github.com/collective/icalendar Author: Plone Foundation @@ -19,12 +19,23 @@ :Code: http://github.com/collective/icalendar :Mailing list: http://github.com/collective/icalendar/issues :Dependencies: `setuptools`_ and since version 3.0 we depend on `pytz`_. - :Compatible with: Python 2.6 and 2.7 + :Compatible with: Python 2.6, 2.7 and 3.3+ :License: `BSD`_ ---- + .. image:: https://travis-ci.org/collective/icalendar.svg?branch=master + :target: https://travis-ci.org/collective/icalendar + + + Roadmap + ======= + + - 4.0: API refactoring + + + Changes in version 3.0 ====================== @@ -55,21 +66,6 @@ Instead of the own UTC tzinfo implementation we use pytz UTC tzinfo object now. - Roadmap - ======= - - 1) Internally Unicode will be used exclusively. - - 2) On API Functions, accept or return Unicode or en/decoded strings. - - 3) This will make the 3.4 Release. - - 4) API change: API calls also only accept/return Unicode. - - 5) This will make the 4.0 release. - - - About this fork which is not a fork anymore =========================================== @@ -95,21 +91,136 @@ Output from coverage test:: - Name Stmts Miss Cover - ---------------------------------------------------------------------------------- - .tox/py27/lib/python2.7/site-packages/icalendar/__init__ 6 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/cal 223 8 96% - .tox/py27/lib/python2.7/site-packages/icalendar/caselessdict 55 2 96% - .tox/py27/lib/python2.7/site-packages/icalendar/parser 181 18 90% - .tox/py27/lib/python2.7/site-packages/icalendar/parser_tools 19 0 100% - .tox/py27/lib/python2.7/site-packages/icalendar/prop 521 59 89% - .tox/py27/lib/python2.7/site-packages/icalendar/tools 12 0 100% - ---------------------------------------------------------------------------------- - TOTAL 1017 87 91% + Name Stmts Miss Cover + ------------------------------------------------ + src/icalendar/__init__ 4 0 100% + src/icalendar/cal 243 7 97% + src/icalendar/caselessdict 66 7 89% + src/icalendar/compat 1 0 100% + src/icalendar/parser 192 6 97% + src/icalendar/parser_tools 20 0 100% + src/icalendar/prop 536 64 88% + src/icalendar/tools 16 0 100% + ------------------------------------------------ + TOTAL 1078 84 92% Changelog ========= + 3.8.2 (2014-07-22) + ------------------ + + - Exclude editor backup files from egg distributions. Fixes #144. + [thet] + + + 3.8.1 (2014-07-17) + ------------------ + + - The representation of CaselessDicts in 3.8 changed the name attribute of + Components and therefore broke the external API. This has been fixed. + [untitaker] + + + 3.8 (2014-07-17) + ---------------- + + - Allow dots in property names (Needed for vCard compatibility). Refs #143. + [untitaker] + + - Change class representation for CaselessDict objects to always include the + class name or the class' name attribute, if available. Also show + subcomponents for Component objects. + [thet] + + - Don't use data_encode for CaselessDict class representation but use dict's + __repr__ method. + [t-8ch] + + - Handle parameters with multiple values, which is needed for vCard 3.0. + Refs #142. + [t-8ch] + + + 3.7 (2014-06-02) + ---------------- + + - For components with ``ignore_exceptions`` set to ``True``, mark unparseable + lines as broken instead rising a ``ValueError``. ``VEVENT`` components have + ``ignore_exceptions`` set to ``True`` by default. Ref #131. Fixes #104. + [jkiang13] + + - Make ``python-dateutil`` a soft-dependency. + [boltnev] + + - Add optional ``sorted`` parameter to ``Component.to_ical``. Setting it to + false allows the user to preserve the original property and parameter order. + Ref #136. Fixes #133. + [untitaker] + + - Fix tests for latest ``pytz``. Don't set ``tzinfo`` directly on datetime + objects, but use pytz's ``localize`` function. Ref #138. + [untitaker, thet] + + - Remove incorrect use of __all__. We don't encourage using ``from package + import *`` imports. Fixes #129. + [eric-wieser] + + + 3.6.2 (2014-04-05) + ------------------ + + - Pep8 and cleanup. + [lasudry] + + 3.6.1 (2014-01-13) + ------------------ + + - Open text files referenced by setup.py as utf-8, no matter what the locale + settings are set to. Fixes #122. + [sochotnicky] + + - Add tox.ini to source tarball, which simplifies testing for in distributions. + [sochotnicky] + + + 3.6 (2014-01-06) + ---------------- + + - Python3 (3.3+) + Python 2 (2.6+) support [geier] + + - Made sure to_ical() always returns bytes [geier] + + - Support adding lists to a component property, which value already was a list + and remove the Component.set method, which was only used by the add method. + [thet] + + - Remove ability to add property parameters via a value's params attribute when + adding via cal.add (that was only possible for custom value objects and makes + up a strange API), but support a parameter attribute on cal.add's method + signature to pass a dictionary with property parameter key/value pairs. + Fixes #116. + [thet] + + - Backport some of Regebro's changes from his regebro-refactor branch. + [thet] + + - Raise explicit error on another malformed content line case. + [hajdbo] + + - Correctly parse datetime component property values with timezone information + when parsed from ical strings. + [untitaker] + + + 3.5 (2013-07-03) + ---------------- + + - Let to_unicode be more graceful for non-unicode strings, as like CMFPlone's + safe_unicode does it. + [thet] + + 3.4 (2013-04-24) ---------------- @@ -481,5 +592,10 @@ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.3 Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent
View file
icalendar-3.4.tar.gz/src/icalendar.egg-info/SOURCES.txt -> icalendar-3.8.2.tar.gz/src/icalendar.egg-info/SOURCES.txt
Changed
@@ -1,30 +1,26 @@ -.gitignore -.travis.yml CHANGES.rst CONTRIBUTING.rst LICENSE.rst MANIFEST.in README.rst TODO.rst -bootstrap.py -buildout.cfg setup.py tox.ini docs/Makefile docs/about.rst -docs/changelog.rst docs/conf.py docs/credits.rst -docs/examples.rst docs/index.rst docs/install.rst docs/license.rst +docs/usage.rst docs/_themes/icalendar/layout.html docs/_themes/icalendar/theme.conf docs/_themes/icalendar/static/icalendar.css src/icalendar/__init__.py src/icalendar/cal.py src/icalendar/caselessdict.py +src/icalendar/compat.py src/icalendar/parser.py src/icalendar/parser_tools.py src/icalendar/prop.py @@ -36,21 +32,17 @@ src/icalendar.egg-info/requires.txt src/icalendar.egg-info/top_level.txt src/icalendar/tests/__init__.py -src/icalendar/tests/case_meetup.ics src/icalendar/tests/encoding.ics -src/icalendar/tests/example.rst -src/icalendar/tests/groupscheduled.ics -src/icalendar/tests/groupscheduled.rst +src/icalendar/tests/issue_112_missing_tzinfo_on_exdate.ics +src/icalendar/tests/issue_53_parsing_failure.ics src/icalendar/tests/multiple.ics -src/icalendar/tests/multiple.rst src/icalendar/tests/recurrence.ics -src/icalendar/tests/recurrence.rst -src/icalendar/tests/small.ics -src/icalendar/tests/small.rst src/icalendar/tests/test_encoding.py src/icalendar/tests/test_fixed_issues.py src/icalendar/tests/test_icalendar.py +src/icalendar/tests/test_multiple.py src/icalendar/tests/test_property_params.py +src/icalendar/tests/test_recurrence.py src/icalendar/tests/test_time.py src/icalendar/tests/test_timezoned.py src/icalendar/tests/test_unit_cal.py
View file
icalendar-3.4.tar.gz/src/icalendar.egg-info/requires.txt -> icalendar-3.8.2.tar.gz/src/icalendar.egg-info/requires.txt
Changed
@@ -1,6 +1,5 @@ setuptools -python-dateutil pytz [test] -unittest2 \ No newline at end of file +python-dateutil \ No newline at end of file
View file
icalendar-3.4.tar.gz/src/icalendar/__init__.py -> icalendar-3.8.2.tar.gz/src/icalendar/__init__.py
Changed
@@ -1,7 +1,4 @@ -from __future__ import absolute_import - - -from .cal import ( +from icalendar.cal import ( Calendar, Event, Todo, @@ -14,7 +11,7 @@ ComponentFactory, ) # Property Data Value Types -from .prop import ( +from icalendar.prop import ( vBinary, vBoolean, vCalAddress, @@ -36,27 +33,14 @@ TypesFactory, ) # useful tzinfo subclasses -from .prop import ( +from icalendar.prop import ( FixedOffset, LocalTimezone, ) # Parameters and helper methods for splitting and joining string with escaped # chars. -from .parser import ( +from icalendar.parser import ( Parameters, q_split, q_join, ) - - -__all__ = [ - Calendar, Event, Todo, Journal, - FreeBusy, Alarm, ComponentFactory, - Timezone, TimezoneStandard, TimezoneDaylight, - vBinary, vBoolean, vCalAddress, vDatetime, vDate, - vDDDTypes, vDuration, vFloat, vInt, vPeriod, - vWeekday, vFrequency, vRecur, vText, vTime, vUri, - vGeo, vUTCOffset, TypesFactory, - FixedOffset, LocalTimezone, - Parameters, q_split, q_join, -]
View file
icalendar-3.4.tar.gz/src/icalendar/cal.py -> icalendar-3.8.2.tar.gz/src/icalendar/cal.py
Changed
@@ -3,23 +3,19 @@ files according to rfc2445. These are the defined components. - """ -from __future__ import absolute_import -import pytz from datetime import datetime -from .parser_tools import data_encode -from .caselessdict import CaselessDict -from .parser import ( - Contentlines, - Contentline, - Parameters, - q_split, - q_join, -) -from .prop import TypesFactory -from .prop import vText, vDDDLists -from .parser_tools import DEFAULT_ENCODING +from icalendar.caselessdict import CaselessDict +from icalendar.parser import Contentline +from icalendar.parser import Contentlines +from icalendar.parser import Parameters +from icalendar.parser import q_join +from icalendar.parser import q_split +from icalendar.parser_tools import DEFAULT_ENCODING +from icalendar.prop import TypesFactory +from icalendar.prop import vText, vDDDLists + +import pytz ###################################### @@ -28,13 +24,12 @@ class ComponentFactory(CaselessDict): """All components defined in rfc 2445 are registered in this factory class. To get a component you can use it like this. - """ def __init__(self, *args, **kwargs): """Set keys to upper for initial dict. """ - CaselessDict.__init__(self, *args, **kwargs) + super(ComponentFactory, self).__init__(*args, **kwargs) self['VEVENT'] = Event self['VTODO'] = Todo self['VJOURNAL'] = Journal @@ -48,9 +43,11 @@ # These Properties have multiple property values inlined in one propertyline # seperated by comma. Use CaselessDict as simple caseless set. -INLINE = CaselessDict( - [(cat, 1) for cat in ('CATEGORIES', 'RESOURCES', 'FREEBUSY')] -) +INLINE = CaselessDict({ + 'CATEGORIES': 1, + 'RESOURCES': 1, + 'FREEBUSY': 1, +}) _marker = [] @@ -59,15 +56,15 @@ """Component is the base object for calendar, Event and the other components defined in RFC 2445. normally you will not use this class directy, but rather one of the subclasses. - """ - name = '' # must be defined in each component - required = () # These properties are required - singletons = () # These properties must only appear once - multiple = () # may occur more than once - exclusive = () # These properties are mutually exclusive - inclusive = () # if any occurs the other(s) MUST occur ('duration', 'repeat') + name = None # should be defined in each component + required = () # These properties are required + singletons = () # These properties must only appear once + multiple = () # may occur more than once + exclusive = () # These properties are mutually exclusive + inclusive = () # if any occurs the other(s) MUST occur + # ('duration', 'repeat') ignore_exceptions = False # if True, and we cannot parse this # component, we will silently ignore # it, rather than let the exception @@ -76,12 +73,12 @@ def __init__(self, *args, **kwargs): """Set keys to upper for initial dict. - """ - CaselessDict.__init__(self, *args, **kwargs) + 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 iff we ignored an exception while parsing a property + self.subcomponents = [] # Components can be nested. + self.is_broken = False # True if we ignored an exception while + # parsing a property #def is_compliant(self, name): # """Returns True is the given property name is compliant with the @@ -93,38 +90,67 @@ # """ # return name in not_compliant - ############################# # handling of property values - def _encode(self, name, value, cond=1): - """Conditional convertion of values. + def _encode(self, name, value, parameters=None, encode=1): + """Encode values to icalendar property values. + + :param name: Name of the property. + :type name: string + + :param value: Value of the property. Either of a basic Python type of + any of the icalendar's own property types. + :type value: Python native type or icalendar property type. + :param parameters: Property parameter dictionary for the value. Only + available, if encode is set to True. + :type parameters: Dictionary + + :param encode: True, if the value should be encoded to one of + icalendar's own property types (Fallback is "vText") + or False, if not. + :type encode: Boolean + + :returns: icalendar property value """ - if not cond: + if not encode: return value - if type(value) in types_factory.all_types: + 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 hasattr(value, 'params') and len(value.params.keys()) > 0: - # TODO: How can a python native value have params? - obj.params = value.params + if parameters: + if isinstance(parameters, dict): + params = Parameters() + for key, item in parameters.items(): + params[key] = item + parameters = params + assert isinstance(parameters, Parameters) + obj.params = parameters return obj - def set(self, name, value, encode=1): - if encode and isinstance(value, list) \ - and name.lower() not in ['rdate', 'exdate']: - # Individually convert each value to an ical type except rdate and - # exdate, where lists of dates might be passed to vDDDLists. - self[name] = [self._encode(name, v, encode) for v in value] - else: - self[name] = self._encode(name, value, encode) - - def add(self, name, value, encode=1): + def add(self, name, value, parameters=None, encode=1): """Add a property. + :param name: Name of the property. + :type name: string + + :param value: Value of the property. Either of a basic Python type of + any of the icalendar's own property types. + :type value: Python native type or icalendar property type. + + :param parameters: Property parameter dictionary for the value. Only + available, if encode is set to True. + :type parameters: Dictionary + + :param encode: True, if the value should be encoded to one of + icalendar's own property types (Fallback is "vText") + or False, if not. + :type encode: Boolean + + :returns: None """ if isinstance(value, datetime) and\ name.lower() in ('dtstamp', 'created', 'last-modified'): @@ -135,20 +161,31 @@ # assume UTC for naive datetime instances value = pytz.utc.localize(value) - # If property already exists, append it. Otherwise create and set it. + # encode value + if encode and isinstance(value, list) \ + and name.lower() not in ['rdate', 'exdate']: + # 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] + else: + value = self._encode(name, value, parameters, encode) + + # set value if name in self: + # If property already exists, append it. oldval = self[name] - value = self._encode(name, value, encode) if isinstance(oldval, list): - oldval.append(value) + if isinstance(value, list): + value = oldval + value + else: + oldval.append(value) + value = oldval else: - self.set(name, [oldval, value], encode=0) - else: - self.set(name, value, encode) + value = [oldval, value] + self[name] = value def _decode(self, name, value): """Internal for decoding property values. - """ # TODO: Currently the decoded method calls the icalendar.prop instances @@ -167,7 +204,6 @@ def decoded(self, name, default=_marker): """Returns decoded value of property. - """ # XXX: fail. what's this function supposed to do in the end? # -rnix @@ -189,7 +225,6 @@ def get_inline(self, name, decode=1): """Returns a list of values (split on comma). - """ vals = [v.strip('" ') for v in q_split(self[name])] if decode: @@ -199,10 +234,9 @@ def set_inline(self, name, values, encode=1): """Converts a list of values into comma seperated string and sets value to that. - """ if encode: - values = [self._encode(name, value, 1) for value in values] + values = [self._encode(name, value, encode=1) for value in values] self[name] = types_factory['inline'](q_join(values)) ######################### @@ -210,13 +244,11 @@ def add_component(self, component): """Add a subcomponent to this component. - """ self.subcomponents.append(component) def _walk(self, name): """Walk to given component. - """ result = [] if name is None or self.name == name: @@ -228,23 +260,25 @@ def walk(self, name=None): """Recursively traverses component and subcomponents. Returns sequence of same. If name is passed, only components with name will be returned. - """ - if not name is None: + if name is not None: name = name.upper() return self._walk(name) ##################### # Generation - def property_items(self, recursive=True): + def property_items(self, recursive=True, sorted=True): """Returns properties in this component and subcomponents as: [(name, value), ...] - """ vText = types_factory['text'] properties = [('BEGIN', vText(self.name).to_ical())] - property_names = self.sorted_keys() + if sorted: + property_names = self.sorted_keys() + else: + property_names = self.keys() + for name in property_names: values = self[name] if isinstance(values, list): @@ -256,21 +290,32 @@ if recursive: # recursion is fun! for subcomponent in self.subcomponents: - properties += subcomponent.property_items() + properties += subcomponent.property_items(sorted=sorted) properties.append(('END', vText(self.name).to_ical())) return properties @classmethod def from_ical(cls, st, multiple=False): """Populates the component recursively from a string. - """ - stack = [] # a stack of components + stack = [] # a stack of components comps = [] - for line in Contentlines.from_ical(st): # raw parsing + for line in Contentlines.from_ical(st): # raw parsing if not line: continue - name, params, vals = line.parts() + + try: + name, params, vals = line.parts() + except ValueError: + # 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 + continue + uname = name.upper() # check for start of component if uname == 'BEGIN': @@ -279,7 +324,7 @@ c_name = vals.upper() c_class = component_factory.get(c_name, cls) component = c_class() - if not getattr(component, 'name', ''): # undefined components + if not getattr(component, 'name', ''): # undefined components component.name = c_name stack.append(component) # check for end of event @@ -287,7 +332,7 @@ # we are done adding properties to this component # so pop it from the stack and add it to the new top. component = stack.pop() - if not stack: # we are at the end + if not stack: # we are at the end comps.append(component) else: if not component.is_broken: @@ -296,9 +341,10 @@ else: factory = types_factory.for_property(name) component = stack[-1] + datetime_names = ('DTSTART', 'DTEND', 'RECURRENCE-ID', 'DUE', + 'FREEBUSY', 'RDATE', 'EXDATE') try: - if name in ('DTSTART', 'DTEND', 'RECURRENCE-ID')\ - and 'TZID' in params: # TODO: add DUE, FREEBUSY, RDATE, EXDATE .. + if name in datetime_names and 'TZID' in params: vals = factory(factory.from_ical(vals, params['TZID'])) else: vals = factory(factory.from_ical(vals)) @@ -321,29 +367,41 @@ '{st!r}'.format(**locals())) return comps[0] - def __repr__(self): - return '%s(%s)' % (self.name, data_encode(self)) - - def content_line(self, name, value): + def content_line(self, name, value, sorted=True): """Returns property as content line. """ params = getattr(value, 'params', Parameters()) - return Contentline.from_parts(name, params, value) + return Contentline.from_parts(name, params, value, sorted=sorted) - def content_lines(self): + def content_lines(self, sorted=True): """Converts the Component and subcomponents into content lines. """ contentlines = Contentlines() - for name, value in self.property_items(): - cl = self.content_line(name, value) + for name, value in self.property_items(sorted=sorted): + cl = self.content_line(name, value, sorted=sorted) contentlines.append(cl) - contentlines.append('') # remember the empty string in the end + contentlines.append('') # remember the empty string in the end return contentlines - def to_ical(self): - content_lines = self.content_lines() + def to_ical(self, sorted=True): + ''' + :param sorted: Whether parameters and properties should be + lexicographically sorted. + ''' + + content_lines = self.content_lines(sorted=sorted) return content_lines.to_ical() + def __repr__(self): + """String representation of class with all of it's subcomponents. + """ + subs = ', '.join([str(it) for it in self.subcomponents]) + return '%s(%s%s)' % ( + self.name or type(self).__name__, + dict(self), + ', %s' % subs if subs else '' + ) + ####################################### # components defined in RFC 2445 @@ -451,7 +509,6 @@ class Calendar(Component): """This is the base object for an iCalendar file. - """ name = 'VCALENDAR' canonical_order = ('VERSION', 'PRODID', 'CALSCALE', 'METHOD',)
View file
icalendar-3.4.tar.gz/src/icalendar/caselessdict.py -> icalendar-3.8.2.tar.gz/src/icalendar/caselessdict.py
Changed
@@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- -from __future__ import absolute_import -from .parser_tools import to_unicode, data_encode +from icalendar.compat import iteritems +from icalendar.parser_tools import to_unicode + +try: + from collections import OrderedDict +except ImportError: + from ordereddict import OrderedDict def canonsort_keys(keys, canonical_order=None): """Sorts leading keys according to canonical_order. Keys not specified in canonical_order will appear alphabetically at the end. - """ canonical_map = dict((k, i) for i, k in enumerate(canonical_order or [])) head = [k for k in keys if k in canonical_map] @@ -16,90 +20,91 @@ def canonsort_items(dict1, canonical_order=None): """Returns a list of items from dict1, sorted by canonical_order. - """ return [(k, dict1[k]) for \ k in canonsort_keys(dict1.keys(), canonical_order)] -class CaselessDict(dict): +class CaselessDict(OrderedDict): """A dictionary that isn't case sensitive, and only uses strings as keys. Values retain their case. - """ def __init__(self, *args, **kwargs): """Set keys to upper for initial dict. """ - dict.__init__(self, *args, **kwargs) + super(CaselessDict, self).__init__(*args, **kwargs) for key, value in self.items(): key_upper = to_unicode(key).upper() if key != key_upper: - dict.__delitem__(self, key) + super(CaselessDict, self).__delitem__(key) self[key_upper] = value def __getitem__(self, key): key = to_unicode(key) - return dict.__getitem__(self, key.upper()) + return super(CaselessDict, self).__getitem__(key.upper()) def __setitem__(self, key, value): key = to_unicode(key) - dict.__setitem__(self, key.upper(), value) + super(CaselessDict, self).__setitem__(key.upper(), value) def __delitem__(self, key): key = to_unicode(key) - dict.__delitem__(self, key.upper()) + super(CaselessDict, self).__delitem__(key.upper()) def __contains__(self, key): key = to_unicode(key) - return dict.__contains__(self, key.upper()) + return super(CaselessDict, self).__contains__(key.upper()) def get(self, key, default=None): key = to_unicode(key) - return dict.get(self, key.upper(), default) + return super(CaselessDict, self).get(key.upper(), default) def setdefault(self, key, value=None): key = to_unicode(key) - return dict.setdefault(self, key.upper(), value) + return super(CaselessDict, self).setdefault(key.upper(), value) def pop(self, key, default=None): key = to_unicode(key) - return dict.pop(self, key.upper(), default) + return super(CaselessDict, self).pop(key.upper(), default) def popitem(self): - return dict.popitem(self) + return super(CaselessDict, self).popitem() def has_key(self, key): key = to_unicode(key) - return dict.__contains__(self, key.upper()) + return super(CaselessDict, self).__contains__(key.upper()) - def update(self, indict): - """ - Multiple keys where key1.upper() == key2.upper() will be lost. - """ - for key, value in indict.iteritems(): - self[key] = value + def update(self, *args, **kwargs): + # Multiple keys where key1.upper() == key2.upper() will be lost. + mappings = list(args) + [kwargs] + for mapping in mappings: + if hasattr(mapping, 'items'): + mapping = iteritems(mapping) + for key, value in mapping: + self[key] = value def copy(self): - return CaselessDict(dict.copy(self)) + return type(self)(super(CaselessDict, self).copy()) def __repr__(self): - return 'CaselessDict(%s)' % data_encode(self) + return '%s(%s)' % (type(self).__name__, dict(self)) + + def __eq__(self, other): + return self is other or dict(self.items()) == dict(other.items()) # A list of keys that must appear first in sorted_keys and sorted_items; # must be uppercase. canonical_order = None def sorted_keys(self): - """ - Sorts keys according to the canonical_order for the derived class. + """Sorts keys according to the canonical_order for the derived class. Keys not specified in canonical_order will appear at the end. """ return canonsort_keys(self.keys(), self.canonical_order) def sorted_items(self): - """ - Sorts items according to the canonical_order for the derived class. + """Sorts items according to the canonical_order for the derived class. Items not specified in canonical_order will appear at the end. """ return canonsort_items(self, self.canonical_order)
View file
icalendar-3.8.2.tar.gz/src/icalendar/compat.py
Added
@@ -0,0 +1,11 @@ +import sys + + +if sys.version_info[0] == 2: # pragma: no cover + unicode_type = unicode + bytes_type = str + iteritems = lambda d, *args, **kwargs: iter(d.iteritems(*args, **kwargs)) +else: # pragma: no cover + unicode_type = str + bytes_type = bytes + iteritems = lambda d, *args, **kwargs: iter(d.items(*args, **kwargs))
View file
icalendar-3.4.tar.gz/src/icalendar/parser.py -> icalendar-3.8.2.tar.gz/src/icalendar/parser.py
Changed
@@ -6,23 +6,21 @@ It is stupid in the sense that it treats the content purely as strings. No type conversion is attempted. """ -from __future__ import absolute_import +from icalendar import compat +from icalendar.caselessdict import CaselessDict +from icalendar.parser_tools import DEFAULT_ENCODING +from icalendar.parser_tools import SEQUENCE_TYPES +from icalendar.parser_tools import to_unicode + import re -from .caselessdict import CaselessDict -from .parser_tools import ( - DEFAULT_ENCODING, - SEQUENCE_TYPES, - to_unicode, - data_encode -) def escape_char(text): """Format value according to iCalendar TEXT escaping rules. """ - assert isinstance(text, basestring) + assert isinstance(text, (compat.unicode_type, compat.bytes_type)) # NOTE: ORDER MATTERS! - return text.replace('\N', '\n')\ + return text.replace(r'\N', '\n')\ .replace('\\', '\\\\')\ .replace(';', r'\;')\ .replace(',', r'\,')\ @@ -31,14 +29,22 @@ def unescape_char(text): - assert isinstance(text, basestring) + assert isinstance(text, (compat.unicode_type, compat.bytes_type)) # NOTE: ORDER MATTERS! - return text.replace(r'\N', r'\n')\ - .replace(r'\r\n', '\n')\ - .replace(r'\n', '\n')\ - .replace(r'\,', ',')\ - .replace(r'\;', ';')\ - .replace('\\\\', '\\') + if isinstance(text, compat.unicode_type): + return text.replace(u'\\N', u'\\n')\ + .replace(u'\r\n', u'\n')\ + .replace(u'\\n', u'\n')\ + .replace(u'\\,', u',')\ + .replace(u'\\;', u';')\ + .replace(u'\\\\', u'\\') + elif isinstance(text, compat.bytes_type): + return text.replace(b'\N', b'\n')\ + .replace(b'\r\n', b'\n')\ + .replace(b'\n', b'\n')\ + .replace(b'\,', b',')\ + .replace(b'\;', b';')\ + .replace(b'\\\\', b'\\') def tzid_from_dt(dt): @@ -63,7 +69,7 @@ immediately followed by a single linear white-space character (i.e., SPACE or HTAB). """ - assert isinstance(line, unicode) + assert isinstance(line, compat.unicode_type) assert u'\n' not in line ret_line = u'' @@ -91,10 +97,15 @@ # Could be improved -NAME = re.compile('[\w-]+') + +# [\w-] because of the iCalendar RFC +# \. because of the vCard RFC +NAME = re.compile('[\w\.-]+') + UNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7F",:;]') QUNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7F"]') -FOLD = re.compile('(\r?\n)+[ \t]') +FOLD = re.compile(b'(\r?\n)+[ \t]') +uFOLD = re.compile(u'(\r?\n)+[ \t]') NEWLINE = re.compile(r'\r?\n') @@ -154,15 +165,13 @@ class Parameters(CaselessDict): - """ - Parser and generator of Property parameter strings. It knows nothing of + """Parser and generator of Property parameter strings. It knows nothing of datatypes. Its main concern is textual structure. """ def params(self): - """ - in rfc2445 keys are called parameters, so this is to be consitent with - the naming conventions + """In rfc2445 keys are called parameters, so this is to be consitent + with the naming conventions. """ return self.keys() @@ -185,24 +194,24 @@ # def decoded(self, name): # "returns a decoded value, or list of same" - def __repr__(self): - return 'Parameters(%s)' % data_encode(self) - - def to_ical(self): + def to_ical(self, sorted=True): result = [] - items = self.items() - items.sort() # To make doctests work + items = list(self.items()) + if sorted: + items.sort() + for key, value in items: value = param_value(value) - if isinstance(value, unicode): + if isinstance(value, compat.unicode_type): value = value.encode(DEFAULT_ENCODING) # CaselessDict keys are always unicode - result.append('%s=%s' % (key.upper().encode('utf-8'), value)) - return ';'.join(result) + key = key.upper().encode(DEFAULT_ENCODING) + result.append(key + b'=' + value) + return b';'.join(result) @classmethod def from_ical(cls, st, strict=False): - "Parses the parameter format from ical text format" + """Parses the parameter format from ical text format.""" # parse into strings result = cls() @@ -243,18 +252,24 @@ .replace(r'\;', '%3B').replace(r'\\', '%5C') -def unsescape_string(val): +def unescape_string(val): return val.replace('%2C', ',').replace('%3A', ':')\ .replace('%3B', ';').replace('%5C', '\\') +def unescape_list_or_string(val): + if isinstance(val, list): + return [unescape_string(s) for s in val] + else: + return unescape_string(val) + + ######################################### # parsing and generation of content lines -class Contentline(unicode): +class Contentline(compat.unicode_type): """A content line is basically a string that can be folded and parsed into parts. - """ def __new__(cls, value, strict=False, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) @@ -265,7 +280,7 @@ return self @classmethod - def from_parts(cls, name, params, values): + def from_parts(cls, name, params, values, sorted=True): """Turn a parts into a content line. """ assert isinstance(params, Parameters) @@ -281,7 +296,7 @@ name = to_unicode(name) values = to_unicode(values) if params: - params = to_unicode(params.to_ical()) + params = to_unicode(params.to_ical(sorted=sorted)) return cls(u'%s;%s:%s' % (name, params, values)) return cls(u'%s:%s' % (name, values)) @@ -301,23 +316,25 @@ value_split = i if ch == '"': in_quotes = not in_quotes - name = unsescape_string(st[:name_split]) + name = unescape_string(st[:name_split]) if not name: raise ValueError('Key name is required') validate_token(name) - if name_split + 1 == value_split: + if not name_split or name_split + 1 == value_split: raise ValueError('Invalid content line') params = Parameters.from_ical(st[name_split + 1: value_split], strict=self.strict) params = Parameters( - (unsescape_string(key), unsescape_string(value)) - for key, value in params.iteritems() + (unescape_string(key), unescape_list_or_string(value)) + for key, value in compat.iteritems(params) ) - values = unsescape_string(st[value_split + 1:]) + values = unescape_string(st[value_split + 1:]) return (name, params, values) except ValueError as exc: - raise ValueError("Content line could not be parsed into parts: %r:" - " %s" % (self, exc)) + raise ValueError( + u"Content line could not be parsed into parts: %r: %s" + % (self, exc) + ) @classmethod def from_ical(cls, ical, strict=False): @@ -325,10 +342,10 @@ """ ical = to_unicode(ical) # a fold is carriage return followed by either a space or a tab - return cls(FOLD.sub('', ical), strict=strict) + return cls(uFOLD.sub('', ical), strict=strict) def to_ical(self): - """Long content lines are folded so they are less than 75 characters. + """Long content lines are folded so they are less than 75 characters wide. """ return foldline(self).encode(DEFAULT_ENCODING) @@ -342,15 +359,16 @@ def to_ical(self): """Simply join self. """ - return '\r\n'.join(line.to_ical() for line in self if line) + '\r\n' + return b'\r\n'.join(line.to_ical() for line in self if line) + b'\r\n' @classmethod def from_ical(cls, st): """Parses a string into content lines. """ + st = to_unicode(st) try: # a fold is carriage return followed by either a space or a tab - unfolded = FOLD.sub('', st) + unfolded = uFOLD.sub('', st) lines = cls(Contentline(line) for line in unfolded.splitlines() if line) lines.append('') # '\r\n' at the end of every content line @@ -360,4 +378,4 @@ # XXX: what kind of hack is this? import depends to be at end -from .prop import vText +from icalendar.prop import vText
View file
icalendar-3.4.tar.gz/src/icalendar/parser_tools.py -> icalendar-3.8.2.tar.gz/src/icalendar/parser_tools.py
Changed
@@ -1,3 +1,6 @@ +from icalendar import compat + + SEQUENCE_TYPES = (list, tuple) DEFAULT_ENCODING = 'utf-8' @@ -5,14 +8,14 @@ def to_unicode(value, encoding='utf-8'): """Converts a value to unicode, even if it is already a unicode string. """ - if isinstance(value, unicode): + if isinstance(value, compat.unicode_type): return value - elif isinstance(value, str): + elif isinstance(value, compat.bytes_type): try: - return value.decode(encoding) + value = value.decode(encoding) except UnicodeDecodeError: - return value.decode('utf-8', 'replace') - raise AssertionError('A str/unicode expected.') + value = value.decode('utf-8', 'replace') + return value def data_encode(data, encoding=DEFAULT_ENCODING): @@ -20,10 +23,10 @@ Currently unicode strings, dicts and lists are supported. """ # http://stackoverflow.com/questions/1254454/fastest-way-to-convert-a-dicts-keys-values-from-unicode-to-str - if isinstance(data, unicode): + if isinstance(data, compat.unicode_type): return data.encode(encoding) elif isinstance(data, dict): - return dict(map(data_encode, data.iteritems())) + return dict(map(data_encode, compat.iteritems(data))) elif isinstance(data, list) or isinstance(data, tuple): return list(map(data_encode, data)) else:
View file
icalendar-3.4.tar.gz/src/icalendar/prop.py -> icalendar-3.8.2.tar.gz/src/icalendar/prop.py
Changed
@@ -34,29 +34,34 @@ 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 +from datetime import date +from datetime import datetime +from datetime import time +from datetime import timedelta +from datetime import tzinfo + +try: + from dateutil.tz import tzutc +except ImportError: + tzutc = None + +from icalendar import compat +from icalendar.caselessdict import CaselessDict +from icalendar.parser import Parameters +from icalendar.parser import escape_char +from icalendar.parser import tzid_from_dt +from icalendar.parser import unescape_char +from icalendar.parser_tools import DEFAULT_ENCODING +from icalendar.parser_tools import SEQUENCE_TYPES +from icalendar.parser_tools import to_unicode + +import base64 import binascii +import pytz +import re import time as _time -from datetime import ( - datetime, - timedelta, - time, - date, - tzinfo, -) -from dateutil.tz import tzutc -from .parser_tools import SEQUENCE_TYPES, DEFAULT_ENCODING, to_unicode -from .caselessdict import CaselessDict -from .parser import ( - Parameters, - escape_char, - unescape_char, - tzid_from_dt, -) + DATE_PART = r'(\d+)D' TIME_PART = r'T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?' @@ -128,32 +133,30 @@ class vBinary(object): """Binary property values are base 64 encoded. - """ def __init__(self, obj): - self.obj = obj + self.obj = to_unicode(obj) self.params = Parameters(encoding='BASE64', value="BINARY") def __repr__(self): return "vBinary('%s')" % self.to_ical() def to_ical(self): - return binascii.b2a_base64(self.obj)[:-1] + return binascii.b2a_base64(self.obj.encode('utf-8'))[:-1] @staticmethod def from_ical(ical): try: - return ical.decode('base-64') + return base64.b64decode(ical) except UnicodeError: raise ValueError('Not valid base 64 encoding.') 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) @@ -162,8 +165,8 @@ def to_ical(self): if self: - return 'TRUE' - return 'FALSE' + return b'TRUE' + return b'FALSE' @classmethod def from_ical(cls, ical): @@ -173,9 +176,8 @@ raise ValueError("Expected 'TRUE' or 'FALSE'. Got %s" % ical) -class vCalAddress(unicode): +class vCalAddress(compat.unicode_type): """This just returns an unquoted string. - """ def __new__(cls, value, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) @@ -196,7 +198,6 @@ class vFloat(float): """Just a float. - """ def __new__(cls, *args, **kwargs): self = super(vFloat, cls).__new__(cls, *args, **kwargs) @@ -204,7 +205,7 @@ return self def to_ical(self): - return str(self) + return compat.unicode_type(self).encode('utf-8') @classmethod def from_ical(cls, ical): @@ -216,7 +217,6 @@ class vInt(int): """Just an int. - """ def __new__(cls, *args, **kwargs): self = super(vInt, cls).__new__(cls, *args, **kwargs) @@ -224,7 +224,7 @@ return self def to_ical(self): - return str(self) + return compat.unicode_type(self).encode('utf-8') @classmethod def from_ical(cls, ical): @@ -236,7 +236,6 @@ class vDDDLists(object): """A list of vDDDTypes values. - """ def __init__(self, dt_list): if not hasattr(dt_list, '__iter__'): @@ -256,7 +255,7 @@ def to_ical(self): dts_ical = (dt.to_ical() for dt in self.dts) - return ",".join(dts_ical) + return b",".join(dts_ical) @staticmethod def from_ical(ical, timezone=None): @@ -271,10 +270,9 @@ """A combined Datetime, Date or Duration parser/generator. Their format cannot be confused, and often values can be of either types. So this is practical. - """ def __init__(self, dt): - if type(dt) not in (datetime, date, timedelta, time): + if not isinstance(dt, (datetime, date, timedelta, time)): raise ValueError('You must use datetime, date, timedelta or time') if isinstance(dt, datetime): self.params = Parameters(dict(value='DATE-TIME')) @@ -284,9 +282,10 @@ self.params = Parameters(dict(value='TIME')) if (isinstance(dt, datetime) or isinstance(dt, time))\ - and getattr(dt, 'tzinfo', False): + and getattr(dt, 'tzinfo', False): tzinfo = dt.tzinfo - if tzinfo is not pytz.utc and not isinstance(tzinfo, tzutc): + if tzinfo is not pytz.utc and\ + (tzutc is None or not isinstance(tzinfo, tzutc)): # set the timezone as a parameter to the property tzid = tzid_from_dt(dt) if tzid: @@ -324,7 +323,6 @@ class vDate(object): """Render and generates iCalendar date format. - """ def __init__(self, dt): if not isinstance(dt, date): @@ -333,7 +331,8 @@ self.params = Parameters(dict(value='DATE')) def to_ical(self): - return "%04d%02d%02d" % (self.dt.year, self.dt.month, self.dt.day) + s = "%04d%02d%02d" % (self.dt.year, self.dt.month, self.dt.day) + return s.encode('utf-8') @staticmethod def from_ical(ical): @@ -358,7 +357,6 @@ tzinfo component, if present. Otherwise an timezone-naive object is created. Be aware that there are certain limitations with timezone naive DATE-TIME components in the icalendar standard. - """ def __init__(self, dt): self.dt = dt @@ -380,7 +378,7 @@ s += "Z" elif tzid: self.params.update({'TZID': tzid}) - return s + return s.encode('utf-8') @staticmethod def from_ical(ical, timezone=None): @@ -405,7 +403,7 @@ elif not ical[15:]: return datetime(*timetuple) elif ical[15:16] == 'Z': - return datetime(tzinfo=pytz.utc, *timetuple) + return pytz.utc.localize(datetime(*timetuple)) else: raise ValueError(ical) except: @@ -415,7 +413,6 @@ class vDuration(object): """Subclass of timedelta that renders itself in the iCalendar DURATION format. - """ def __init__(self, td): @@ -442,9 +439,12 @@ if seconds: timepart += "%dS" % seconds if self.td.days == 0 and timepart: - return "%sP%s" % (sign, timepart) + return (compat.unicode_type(sign).encode('utf-8') + b'P' + + compat.unicode_type(timepart).encode('utf-8')) else: - return "%sP%dD%s" % (sign, abs(self.td.days), timepart) + return (compat.unicode_type(sign).encode('utf-8') + b'P' + + compat.unicode_type(abs(self.td.days)).encode('utf-8') + + b'D' + compat.unicode_type(timepart).encode('utf-8')) @staticmethod def from_ical(ical): @@ -467,7 +467,6 @@ class vPeriod(object): """A precise period of time. - """ def __init__(self, per): start, end_or_duration = per @@ -515,10 +514,10 @@ def to_ical(self): if self.by_duration: - return '%s/%s' % (vDatetime(self.start).to_ical(), - vDuration(self.duration).to_ical()) - return '%s/%s' % (vDatetime(self.start).to_ical(), - vDatetime(self.end).to_ical()) + return (vDatetime(self.start).to_ical() + b'/' + + vDuration(self.duration).to_ical()) + return (vDatetime(self.start).to_ical() + b'/' + + vDatetime(self.end).to_ical()) @staticmethod def from_ical(ical): @@ -538,9 +537,8 @@ return 'vPeriod(%r)' % p -class vWeekday(unicode): +class vWeekday(compat.unicode_type): """This returns an unquoted weekday abbrevation. - """ week_days = CaselessDict({ "SU": 0, "MO": 1, "TU": 2, "WE": 3, "TH": 4, "FR": 5, "SA": 6, @@ -573,9 +571,8 @@ raise ValueError('Expected weekday abbrevation, got: %s' % ical) -class vFrequency(unicode): +class vFrequency(compat.unicode_type): """A simple class that catches illegal values. - """ frequencies = CaselessDict({ @@ -609,7 +606,6 @@ class vRecur(CaselessDict): """Recurrence definition. - """ frequencies = ["SECONDLY", "MINUTELY", "HOURLY", "DAILY", "WEEKLY", @@ -639,7 +635,7 @@ }) def __init__(self, *args, **kwargs): - CaselessDict.__init__(self, *args, **kwargs) + super(vRecur, self).__init__(*args, **kwargs) self.params = Parameters() def to_ical(self): @@ -648,9 +644,13 @@ typ = self.types[key] if not isinstance(vals, SEQUENCE_TYPES): vals = [vals] - vals = ','.join(typ(val).to_ical() for val in vals) - result.append('%s=%s' % (key, vals)) - return ';'.join(result) + vals = b','.join(typ(val).to_ical() for val in vals) + + # CaselessDict keys are always unicode + key = key.encode(DEFAULT_ENCODING) + result.append(key + b'=' + vals) + + return b';'.join(result) @classmethod def parse_type(cls, key, values): @@ -672,9 +672,8 @@ raise ValueError('Error in recurrence rule: %s' % ical) -class vText(unicode): +class vText(compat.unicode_type): """Simple text. - """ def __new__(cls, value, encoding=DEFAULT_ENCODING): @@ -698,7 +697,6 @@ class vTime(object): """Render and generates iCalendar time format. - """ def __init__(self, *args): @@ -723,9 +721,8 @@ raise ValueError('Expected time, got: %s' % ical) -class vUri(unicode): +class vUri(compat.unicode_type): """Uniform resource identifier is basically just an unquoted string. - """ def __new__(cls, value, encoding=DEFAULT_ENCODING): @@ -747,7 +744,6 @@ class vGeo(object): """A special type that is only indirectly defined in the rfc. - """ def __init__(self, geo): @@ -776,7 +772,6 @@ class vUTCOffset(object): """Renders itself as a utc offset. - """ def __init__(self, td): @@ -787,9 +782,9 @@ def to_ical(self): - if self.td<timedelta(0): + if self.td < timedelta(0): sign = '-%s' - td = timedelta(0)-self.td # get timedelta relative to 0 + td = timedelta(0) - self.td # get timedelta relative to 0 else: # Google Calendar rejects '0000' but accepts '+0000' sign = '+%s' @@ -826,11 +821,10 @@ return offset -class vInline(unicode): +class vInline(compat.unicode_type): """This is an especially dumb class that just holds raw unparsed text and has parameters. Conversion of inline values are handled by the Component class, so no further processing is needed. - """ def __new__(cls, value, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) @@ -852,13 +846,12 @@ The value and parameter names don't overlap. So one factory is enough for both kinds. - """ def __init__(self, *args, **kwargs): "Set keys to upper for initial dict" - CaselessDict.__init__(self, *args, **kwargs) - self.all_types = [ + super(TypesFactory, self).__init__(*args, **kwargs) + self.all_types = ( vBinary, vBoolean, vCalAddress, @@ -878,8 +871,8 @@ vTime, vUTCOffset, vUri, - vWeekday, - ] + vWeekday + ) self['binary'] = vBinary self['boolean'] = vBoolean self['cal-address'] = vCalAddress
View file
icalendar-3.4.tar.gz/src/icalendar/tests/__init__.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/__init__.py
Changed
@@ -2,3 +2,4 @@ import unittest if not hasattr(unittest.TestCase, 'assertIsNotNone'): import unittest2 as unittest +unittest # pep 8
View file
icalendar-3.8.2.tar.gz/src/icalendar/tests/issue_112_missing_tzinfo_on_exdate.ics
Added
@@ -0,0 +1,48 @@ +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:PUBLISH +X-WR-CALNAME:Market East +X-WR-TIMEZONE:America/New_York +X-WR-CALDESC: +BEGIN:VTIMEZONE +TZID:America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE + +BEGIN:VEVENT +DTSTART;TZID=America/New_York:20130907T120000 +DTEND;TZID=America/New_York:20130907T170000 +RRULE:FREQ=WEEKLY;BYDAY=FR,SA;UNTIL=20131025T035959Z +EXDATE;TZID=America/New_York:20131012T120000 +EXDATE;TZID=America/New_York:20131011T120000 +DTSTAMP:20131021T025552Z +UID:ak30b02u7858q1oo6ji9dm4mgg@google.com +CREATED:20130903T181453Z +DESCRIPTION:The Fieldhouse and Hard Rock Cafe are working with PhillyRising + to provide live entertainment on Friday and Saturday afternoons throughout + the Summer. +LAST-MODIFIED:20131015T210927Z +LOCATION:12th and Market Streets (weather permitting) +SEQUENCE:0 +STATUS:CONFIRMED +SUMMARY:Market East Live! +TRANSP:OPAQUE +END:VEVENT + +END:VCALENDAR
View file
icalendar-3.8.2.tar.gz/src/icalendar/tests/issue_53_parsing_failure.ics
Changed
(renamed from src/icalendar/tests/case_meetup.ics)
View file
icalendar-3.4.tar.gz/src/icalendar/tests/recurrence.ics -> icalendar-3.8.2.tar.gz/src/icalendar/tests/recurrence.ics
Changed
@@ -9,4 +9,16 @@ EXDATE:19960402T010000Z,19960403T010000Z,19960404T010000Z SUMMARY:A recurring event with exdates END:VEVENT +BEGIN:VEVENT +DTSTART;TZID=Europe/Vienna:20120327T100000 +DTEND;TZID=Europe/Vienna:20120327T180000 +RRULE:FREQ=WEEKLY;UNTIL=20120703T080000Z;BYDAY=TU +EXDATE;TZID=Europe/Vienna:20120529T100000 +EXDATE;TZID=Europe/Vienna:20120403T100000 +EXDATE;TZID=Europe/Vienna:20120410T100000 +EXDATE;TZID=Europe/Vienna:20120501T100000 +EXDATE;TZID=Europe/Vienna:20120417T100000 +DTSTAMP:20130716T120638Z +SUMMARY:A Recurring event with multiple exdates, one per line. +END:VEVENT END:VCALENDAR
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_encoding.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_encoding.py
Changed
@@ -1,13 +1,12 @@ # -*- coding: utf-8 -*- +from icalendar.tests import unittest -import icalendar -import pytz import datetime +import icalendar import os +import pytz -from . import unittest - class TestEncoding(unittest.TestCase): def test_create_from_ical(self): @@ -15,18 +14,20 @@ data = open(os.path.join(directory, 'encoding.ics'), 'rb').read() cal = icalendar.Calendar.from_ical(data) - self.assertEqual(cal['prodid'].to_ical(), - "-//Plönë.org//NONSGML plone.app.event//EN") - self.assertEqual(cal['X-WR-CALDESC'].to_ical(), - "test non ascii: äöü ÄÖÜ €") + self.assertEqual(cal['prodid'].to_ical().decode('utf-8'), + u"-//Plönë.org//NONSGML plone.app.event//EN") + self.assertEqual(cal['X-WR-CALDESC'].to_ical().decode('utf-8'), + u"test non ascii: äöü ÄÖÜ €") event = cal.walk('VEVENT')[0] - self.assertEqual(event['SUMMARY'].to_ical(), - 'Non-ASCII Test: ÄÖÜ äöü €') - self.assertEqual(event['DESCRIPTION'].to_ical(), - 'icalendar should be able to handle non-ascii: €äüöÄÜÖ.') - self.assertEqual(event['LOCATION'].to_ical(), - 'Tribstrül') + self.assertEqual(event['SUMMARY'].to_ical().decode('utf-8'), + u'Non-ASCII Test: ÄÖÜ äöü €') + self.assertEqual( + event['DESCRIPTION'].to_ical().decode('utf-8'), + u'icalendar should be able to handle non-ascii: €äüöÄÜÖ.' + ) + self.assertEqual(event['LOCATION'].to_ical().decode('utf-8'), + u'Tribstrül') def test_create_to_ical(self): cal = icalendar.Calendar() @@ -38,28 +39,42 @@ cal.add('x-wr-relcalid', u"12345") event = icalendar.Event() - event.add('dtstart', datetime.datetime(2010, 10, 10, 10, 00, 00, tzinfo=pytz.utc)) - event.add('dtend', datetime.datetime(2010, 10, 10, 12, 00, 00, tzinfo=pytz.utc)) - event.add('created', datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=pytz.utc)) + event.add( + 'dtstart', + pytz.utc.localize(datetime.datetime(2010, 10, 10, 10, 0, 0)) + ) + event.add( + 'dtend', + pytz.utc.localize(datetime.datetime(2010, 10, 10, 12, 0, 0)) + ) + event.add( + 'created', + pytz.utc.localize(datetime.datetime(2010, 10, 10, 0, 0, 0)) + ) event.add('uid', u'123456') event.add('summary', u'Non-ASCII Test: ÄÖÜ äöü €') - event.add('description', u'icalendar should be able to de/serialize non-ascii.') + event.add( + 'description', + u'icalendar should be able to de/serialize non-ascii.' + ) event.add('location', u'Tribstrül') cal.add_component(event) ical_lines = cal.to_ical().splitlines() - cmp = 'PRODID:-//Pl\xc3\xb6n\xc3\xab.org//NONSGML plone.app.event//EN' + cmp = b'PRODID:-//Pl\xc3\xb6n\xc3\xab.org//NONSGML plone.app.event//EN' self.assertTrue(cmp in ical_lines) def test_create_event_simple(self): event = icalendar.Event() - event.add("dtstart", datetime.datetime(2010, 10, 10, 0, 0, 0, tzinfo=pytz.utc)) + event.add( + "dtstart", + pytz.utc.localize(datetime.datetime(2010, 10, 10, 0, 0, 0)) + ) event.add("summary", u"åäö") out = event.to_ical() - summary = "SUMMARY:åäö" + summary = b'SUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6' self.assertTrue(summary in out.splitlines()) - def test_unicode_parameter_name(self): # Test for issue #80 cal = icalendar.Calendar() @@ -67,7 +82,9 @@ event.add(u'DESCRIPTION', u'äöüßÄÖÜ') cal.add_component(event) c = cal.to_ical() - self.assertEqual(c, - 'BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDESCRIPTION:'\ - + '\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f\xc3\x84\xc3\x96\xc3\x9c\r\n'\ - + 'END:VEVENT\r\nEND:VCALENDAR\r\n') + self.assertEqual( + c, + b'BEGIN:VCALENDAR\r\nBEGIN:VEVENT\r\nDESCRIPTION:' + + b'\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f\xc3\x84\xc3\x96\xc3\x9c\r\n' + + b'END:VEVENT\r\nEND:VCALENDAR\r\n' + )
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_fixed_issues.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_fixed_issues.py
Changed
@@ -1,33 +1,34 @@ # -*- coding: utf-8 -*- -from . import unittest -import icalendar +from icalendar.parser_tools import to_unicode +from icalendar.tests import unittest + import datetime +import icalendar import os import pytz class TestIssues(unittest.TestCase): - def test_issue_53(self): """Issue #53 - Parsing failure on some descriptions? https://github.com/collective/icalendar/issues/53 """ directory = os.path.dirname(__file__) - ics = open(os.path.join(directory, 'case_meetup.ics'), 'rb') + ics = open(os.path.join(directory, 'issue_53_parsing_failure.ics'), + 'rb') cal = icalendar.Calendar.from_ical(ics.read()) ics.close() event = cal.walk('VEVENT')[0] desc = event.get('DESCRIPTION') - self.assertTrue('July 12 at 6:30 PM' in desc.to_ical()) + self.assertTrue(b'July 12 at 6:30 PM' in desc.to_ical()) timezones = cal.walk('VTIMEZONE') self.assertEqual(len(timezones), 1) tz = timezones[0] - self.assertEqual(tz['tzid'].to_ical(), "America/New_York") - + self.assertEqual(tz['tzid'].to_ical(), b"America/New_York") def test_issue_55(self): """Issue #55 - Parse error on utc-offset with seconds value @@ -45,12 +46,14 @@ END:VTIMEZONE""" tz = icalendar.Timezone.from_ical(ical_str) - self.assertEqual(tz.to_ical(), - 'BEGIN:VTIMEZONE\r\nTZID:America/Los Angeles\r\nBEGIN:STANDARD\r\n' - 'DTSTART:18831118T120702\r\nRDATE:18831118T120702\r\nTZNAME:PST' - '\r\nTZOFFSETFROM:-075258\r\nTZOFFSETTO:-0800\r\nEND:STANDARD\r\n' - 'END:VTIMEZONE\r\n') - + self.assertEqual( + tz.to_ical(), + b'BEGIN:VTIMEZONE\r\nTZID:America/Los Angeles\r\n' + b'BEGIN:STANDARD\r\n' + b'DTSTART:18831118T120702\r\nRDATE:18831118T120702\r\nTZNAME:PST' + b'\r\nTZOFFSETFROM:-075258\r\nTZOFFSETTO:-0800\r\n' + b'END:STANDARD\r\n' + b'END:VTIMEZONE\r\n') def test_issue_58(self): """Issue #58 - TZID on UTC DATE-TIMEs @@ -64,11 +67,12 @@ event = icalendar.Event() dt = pytz.utc.localize(datetime.datetime(2012, 7, 16, 0, 0, 0)) event.add('dtstart', dt) - self.assertEqual(event.to_ical(), - "BEGIN:VEVENT\r\n" - "DTSTART;VALUE=DATE-TIME:20120716T000000Z\r\n" - "END:VEVENT\r\n") - + self.assertEqual( + event.to_ical(), + b"BEGIN:VEVENT\r\n" + b"DTSTART;VALUE=DATE-TIME:20120716T000000Z\r\n" + b"END:VEVENT\r\n" + ) def test_issue_64(self): """Issue #64 - Event.to_ical() fails for unicode strings @@ -79,17 +83,21 @@ event = icalendar.Event() event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0)) event.add("summary", u"abcdef") - self.assertEqual(event.to_ical(), - "BEGIN:VEVENT\r\nSUMMARY:abcdef\r\nDTSTART;VALUE=DATE-TIME:" - "20120903T000000\r\nEND:VEVENT\r\n") + self.assertEqual( + event.to_ical(), + b"BEGIN:VEVENT\r\nSUMMARY:abcdef\r\nDTSTART;VALUE=DATE-TIME:" + b"20120903T000000\r\nEND:VEVENT\r\n" + ) # Unicode characters event = icalendar.Event() event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0)) event.add("summary", u"åäö") - self.assertEqual(event.to_ical(), - "BEGIN:VEVENT\r\nSUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6\r\n" - "DTSTART;VALUE=DATE-TIME:20120903T000000\r\nEND:VEVENT\r\n") + self.assertEqual( + event.to_ical(), + b"BEGIN:VEVENT\r\nSUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6\r\n" + b"DTSTART;VALUE=DATE-TIME:20120903T000000\r\nEND:VEVENT\r\n" + ) def test_issue_70(self): """Issue #70 - e.decode("RRULE") causes Attribute Error @@ -111,10 +119,10 @@ cal = icalendar.Calendar.from_ical(ical_str) recur = cal.decoded("RRULE") self.assertIsInstance(recur, icalendar.vRecur) - self.assertEqual(recur.to_ical(), - u'FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1') - - + self.assertEqual( + recur.to_ical(), + b'FREQ=WEEKLY;UNTIL=20070619T225959;INTERVAL=1' + ) def test_issue_82(self): """Issue #82 - vBinary __repr__ called rather than to_ical from @@ -124,15 +132,15 @@ b = icalendar.vBinary('text') b.params['FMTTYPE'] = 'text/plain' - self.assertEqual(b.to_ical(), 'dGV4dA==') + self.assertEqual(b.to_ical(), b'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" + self.assertEqual( + e.to_ical(), + b"BEGIN:VEVENT\r\nATTACH;ENCODING=BASE64;FMTTYPE=text/plain;" + b"VALUE=BINARY:dGV4dA==\r\nEND:VEVENT\r\n" ) - def test_issue_100(self): """Issue #100 - Transformed doctests into unittests, Test fixes and cleanup. @@ -142,7 +150,6 @@ ical_content = "BEGIN:VEVENT\r\nSUMMARY;LANGUAGE=ru:te\r\nEND:VEVENT" icalendar.Event.from_ical(ical_content).to_ical() - def test_issue_101(self): """Issue #101 - icalender is choking on umlauts in ORGANIZER @@ -172,3 +179,140 @@ cal = icalendar.Calendar.from_ical(ical_str) org_cn = cal.walk('VEVENT')[0]['ORGANIZER'].params['CN'] self.assertEqual(org_cn, u'acme, ädmin') + + def test_issue_104__ignore_exceptions(self): + """ + Issue #104 - line parsing error in a VEVENT + (which has ignore_exceptions). Should mark the event broken + but not raise an exception. + https://github.com/collective/icalendar/issues/104 + """ + ical_str = """ +BEGIN:VEVENT +DTSTART:20140401T000000Z +DTEND:20140401T010000Z +DTSTAMP:20140401T000000Z +SUMMARY:Broken Eevnt +CLASS:PUBLIC +STATUS:CONFIRMED +TRANSP:OPAQUE +X +END:VEVENT""" + event = icalendar.Calendar.from_ical(ical_str) + self.assertTrue(isinstance(event, icalendar.Event)) + self.assertTrue(event.is_broken) + + def test_issue_104__no_ignore_exceptions(self): + """ + Issue #104 - line parsing error in a VCALENDAR + (which doesn't have ignore_exceptions). Should raise an exception. + """ + ical_str = """BEGIN:VCALENDAR +VERSION:2.0 +METHOD:PUBLISH +BEGIN:VEVENT +DTSTART:20140401T000000Z +DTEND:20140401T010000Z +DTSTAMP:20140401T000000Z +SUMMARY:Broken Eevnt +CLASS:PUBLIC +STATUS:CONFIRMED +TRANSP:OPAQUE +END:VEVENT +X +END:VCALENDAR""" + with self.assertRaises(ValueError): + icalendar.Calendar.from_ical(ical_str) + + def test_issue_112(self): + """Issue #112 - No timezone info on EXDATE + https://github.com/collective/icalendar/issues/112 + """ + directory = os.path.dirname(__file__) + path = os.path.join(directory, + 'issue_112_missing_tzinfo_on_exdate.ics') + with open(path, 'rb') as ics: + cal = icalendar.Calendar.from_ical(ics.read()) + event = cal.walk('VEVENT')[0] + + event_ical = to_unicode(event.to_ical()) # Py3 str type doesn't + # support buffer API + # General timezone aware dates in ical string + self.assertTrue('DTSTART;TZID=America/New_York:20130907T120000' + in event_ical) + self.assertTrue('DTEND;TZID=America/New_York:20130907T170000' + in event_ical) + # Specific timezone aware exdates in ical string + self.assertTrue('EXDATE;TZID=America/New_York:20131012T120000' + in event_ical) + self.assertTrue('EXDATE;TZID=America/New_York:20131011T120000' + in event_ical) + + self.assertEqual(event['exdate'][0].dts[0].dt.tzname(), 'EDT') + + def test_issue_116(self): + """Issue #116/#117 - How to add 'X-APPLE-STRUCTURED-LOCATION' + https://github.com/collective/icalendar/issues/116 + https://github.com/collective/icalendar/issues/117 + """ + event = icalendar.Event() + event.add( + "X-APPLE-STRUCTURED-LOCATION", + "geo:-33.868900,151.207000", + parameters={ + "VALUE": "URI", + "X-ADDRESS": "367 George Street Sydney CBD NSW 2000", + "X-APPLE-RADIUS": "72", + "X-TITLE": "367 George Street" + } + ) + self.assertEqual( + event.to_ical(), + b'BEGIN:VEVENT\r\nX-APPLE-STRUCTURED-LOCATION;VALUE=URI;' + b'X-ADDRESS="367 George Street Sydney \r\n CBD NSW 2000";' + b'X-APPLE-RADIUS=72;X-TITLE="367 George Street":' + b'geo:-33.868900\r\n \\,151.207000\r\nEND:VEVENT\r\n' + ) + + # roundtrip + self.assertEqual( + event.to_ical(), + icalendar.Event.from_ical(event.to_ical()).to_ical() + ) + + def test_issue_142(self): + """Issue #142 - Multivalued parameters + This is needed for VCard 3.0. + https://github.com/collective/icalendar/pull/142 + """ + from icalendar.parser import Contentline, Parameters + + ctl = Contentline.from_ical("TEL;TYPE=HOME,VOICE:000000000") + + self.assertEqual( + ctl.parts(), + (u'TEL', Parameters({'TYPE': ['HOME', 'VOICE']}), u'000000000'), + ) + + def test_issue_143(self): + """Issue #143 - Allow dots in property names. + Another vCard related issue. + https://github.com/collective/icalendar/pull/143 + """ + from icalendar.parser import Contentline, Parameters + + ctl = Contentline.from_ical("ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany") # nopep8 + self.assertEqual( + ctl.parts(), + (u'ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR', + Parameters(), + u';;This is the Adress 08; Some City;;12345;Germany'), + ) + + ctl2 = Contentline.from_ical("ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL:") # nopep8 + self.assertEqual( + ctl2.parts(), + (u'ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL', + Parameters(), + u''), + )
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_icalendar.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_icalendar.py
Changed
@@ -1,15 +1,5 @@ # coding: utf-8 -from . import unittest -import doctest -import os -from .. import ( - cal, - caselessdict, - parser, - prop, -) - -OPTIONFLAGS = doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS +from icalendar.tests import unittest class IcalendarTestCase (unittest.TestCase): @@ -20,9 +10,9 @@ c.append(Contentline(''.join('123456789 ' * 10))) self.assertEqual( c.to_ical(), - 'BEGIN:VEVENT\r\n123456789 123456789 123456789 123456789 ' - '123456789 123456789 123456789 1234\r\n 56789 123456789 ' - '123456789 \r\n' + b'BEGIN:VEVENT\r\n123456789 123456789 123456789 123456789 ' + b'123456789 123456789 123456789 1234\r\n 56789 123456789 ' + b'123456789 \r\n' ) # from doctests @@ -45,15 +35,15 @@ self.assertEqual( Contentline('Si meliora dies, ut vina, poemata reddit').to_ical(), - 'Si meliora dies, ut vina, poemata reddit' + b'Si meliora dies, ut vina, poemata reddit' ) # A long line gets folded c = Contentline(''.join(['123456789 '] * 10)).to_ical() self.assertEqual( c, - ('123456789 123456789 123456789 123456789 123456789 123456789 ' - '123456789 1234\r\n 56789 123456789 123456789 ') + (b'123456789 123456789 123456789 123456789 123456789 123456789 ' + b'123456789 1234\r\n 56789 123456789 123456789 ') ) # A folded line gets unfolded @@ -70,22 +60,24 @@ # N or a LATIN CAPITAL LETTER N, that is "\n" or "\N". # Newlines are not allwoed in content lines - self.assertRaises(AssertionError, Contentline, '1234\r\n\r\n1234') + self.assertRaises(AssertionError, Contentline, b'1234\r\n\r\n1234') self.assertEqual( Contentline('1234\\n\\n1234').to_ical(), - '1234\\n\\n1234' + b'1234\\n\\n1234' ) # We do not fold within a UTF-8 character - c = Contentline('This line has a UTF-8 character where it should be ' - 'folded. Make sure it g\xc3\xabts folded before that ' - 'character.') - self.assertIn('\xc3\xab', c.to_ical()) + c = Contentline(b'This line has a UTF-8 character where it should be ' + b'folded. Make sure it g\xc3\xabts folded before that ' + b'character.') + + self.assertIn(b'\xc3\xab', c.to_ical()) # Another test of the above - c = Contentline('x' * 73 + '\xc3\xab' + '\\n ' + 'y' * 10) - self.assertEqual(c.to_ical().count('\xc3'), 1) + c = Contentline(b'x' * 73 + b'\xc3\xab' + b'\\n ' + b'y' * 10) + + self.assertEqual(c.to_ical().count(b'\xc3'), 1) # Don't fail if we fold a line that is exactly X times 74 characters # long @@ -112,7 +104,7 @@ 'MAILTO:maxm@example.com') ) self.assertEqual( - c.to_ical(), + c.to_ical().decode('utf-8'), 'ATTENDEE;CN=Max Rasmussen;ROLE=REQ-PARTICIPANT:' 'MAILTO:maxm@example.com' ) @@ -162,8 +154,8 @@ self.assertEqual(name, 'ATTENDEE') self.assertEqual(vals, 'MAILTO:maxm@example.com') self.assertEqual( - params.items(), - [('ROLE', 'REQ-PARTICIPANT'), ('CN', 'Max Rasmussen')] + sorted(params.items()), + sorted([('ROLE', 'REQ-PARTICIPANT'), ('CN', 'Max Rasmussen')]) ) # And the traditional failure @@ -226,8 +218,12 @@ u'Vestibulum conval\r\n lis imperdiet dui posuere.') ) + # I don't really get this test + # at least just but bytes in there + # porting it to "run" under python 2 & 3 makes it not much better with self.assertRaises(AssertionError): - foldline('привет', limit=3) + foldline(u'привет'.encode('utf-8'), limit=3) + self.assertEqual(foldline(u'foobar', limit=4), u'foo\r\n bar') self.assertEqual( foldline(u'Lorem ipsum dolor sit amet, consectetur adipiscing elit' @@ -255,23 +251,3 @@ from ..parser import q_join self.assertEqual(q_join(['Max', 'Moller', 'Rasmussen, Max']), 'Max,Moller,"Rasmussen, Max"') - - -def load_tests(loader=None, tests=None, pattern=None): - suite = unittest.TestSuite() - suite.addTest(doctest.DocTestSuite(caselessdict)) - suite.addTest(doctest.DocTestSuite(parser)) - suite.addTest(doctest.DocTestSuite(prop)) - suite.addTest(doctest.DocTestSuite(cal)) - current_dir = os.path.dirname(__file__) - for docfile in ['example.rst', 'groupscheduled.rst', - 'small.rst', 'multiple.rst', 'recurrence.rst']: - filename = os.path.abspath(os.path.join(current_dir, docfile)) - suite.addTest( - doctest.DocFileSuite( - docfile, - optionflags=OPTIONFLAGS, - globs={'__file__': filename} - ) - ) - return suite
View file
icalendar-3.8.2.tar.gz/src/icalendar/tests/test_multiple.py
Added
@@ -0,0 +1,28 @@ +from icalendar import Calendar +from icalendar.prop import vText +from icalendar.tests import unittest + +import os + + +class TestMultiple(unittest.TestCase): + """A example with multiple VCALENDAR components""" + + def test_multiple(self): + + directory = os.path.dirname(__file__) + cals = Calendar.from_ical( + open(os.path.join(directory, 'multiple.ics'), 'rb').read(), + multiple=True + ) + + self.assertEqual(len(cals), 2) + self.assertSequenceEqual([comp.name for comp in cals[0].walk()], + ['VCALENDAR', 'VEVENT']) + self.assertSequenceEqual([comp.name for comp in cals[1].walk()], + ['VCALENDAR', 'VEVENT', 'VEVENT']) + + self.assertEqual( + cals[0]['prodid'], + vText('-//Mozilla.org/NONSGML Mozilla Calendar V1.0//EN') + )
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_property_params.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_property_params.py
Changed
@@ -1,8 +1,12 @@ # coding: utf-8 +from icalendar import Calendar +from icalendar import Event +from icalendar import Parameters +from icalendar import vCalAddress +from icalendar.tests import unittest -from . import unittest - -from .. import vCalAddress, Calendar, Event, Parameters +import icalendar +import re class TestPropertyParams(unittest.TestCase): @@ -17,8 +21,8 @@ ical.add('organizer', cal_address) 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""" + exp_str = b"""BEGIN:VCALENDAR\r\nORGANIZER;CN="Doe, John":"""\ + b"""mailto:john.doe@example.org\r\nEND:VCALENDAR\r\n""" self.assertEqual(ical_str, exp_str) @@ -33,23 +37,25 @@ vevent = Event() vevent['ORGANIZER'] = cal_address self.assertEqual( - vevent.to_ical(), - 'BEGIN:VEVENT\r\n' - 'ORGANIZER;CN="Джон Доу":mailto:john.doe@example.org\r\n' - 'END:VEVENT\r\n' + vevent.to_ical().decode('utf-8'), + u'BEGIN:VEVENT\r\n' + u'ORGANIZER;CN="Джон Доу":mailto:john.doe@example.org\r\n' + u'END:VEVENT\r\n' ) - self.assertEqual(vevent['ORGANIZER'].params['CN'], 'Джон Доу') + + self.assertEqual(vevent['ORGANIZER'].params['CN'], + 'Джон Доу') def test_quoting(self): # not double-quoted - self._test_quoting(u"Aramis", 'Aramis') + self._test_quoting(u"Aramis", u'Aramis') # if a space is present - enclose in double quotes - self._test_quoting(u"Aramis Alameda", '"Aramis Alameda"') + self._test_quoting(u"Aramis Alameda", u'"Aramis Alameda"') # a single quote in parameter value - double quote the value - self._test_quoting("Aramis d'Alameda", '"Aramis d\'Alameda"') + self._test_quoting(u"Aramis d'Alameda", u'"Aramis d\'Alameda"') # double quote is replaced with single quote - self._test_quoting("Aramis d\"Alameda", '"Aramis d\'Alameda"') - self._test_quoting(u"Арамис д'Аламеда", '"Арамис д\'Аламеда"') + self._test_quoting(u"Aramis d\"Alameda", u'"Aramis d\'Alameda"') + self._test_quoting(u"Арамис д'Аламеда", u'"Арамис д\'Аламеда"') def _test_quoting(self, cn_param, cn_quoted): """ @@ -62,16 +68,16 @@ vevent.add('ATTENDEE', attendee) self.assertEqual( vevent.to_ical(), - 'BEGIN:VEVENT\r\nATTENDEE;CN=%s:test@mail.com\r\nEND:VEVENT\r\n' - % cn_quoted + b'BEGIN:VEVENT\r\nATTENDEE;CN=' + cn_quoted.encode('utf-8') + + b':test@mail.com\r\nEND:VEVENT\r\n' ) def test_escaping(self): # verify that escaped non safe chars are decoded correctly - NON_SAFE_CHARS = ur',\;:' + NON_SAFE_CHARS = u',\\;:' for char in NON_SAFE_CHARS: - cn_escaped = ur"Society\%s 2014" % char - cn_decoded = ur"Society%s 2014" % char + cn_escaped = u"Society\\%s 2014" % char + cn_decoded = u"Society%s 2014" % char vevent = Event.from_ical( u'BEGIN:VEVENT\r\n' u'ORGANIZER;CN=%s:that\r\n' @@ -90,18 +96,18 @@ r'that, that; %th%%at%\ that:' ) self.assertEqual( - vevent['ORGANIZER'].to_ical(), - r'это, то; that\ %th%%at%:' + vevent['ORGANIZER'].to_ical().decode('utf-8'), + u'это, то; that\\ %th%%at%:' ) def test_parameters_class(self): # Simple parameter:value pair p = Parameters(parameter1='Value1') - self.assertEqual(p.to_ical(), 'PARAMETER1=Value1') + self.assertEqual(p.to_ical(), b'PARAMETER1=Value1') # keys are converted to upper - self.assertEqual(p.keys(), ['PARAMETER1']) + self.assertEqual(list(p.keys()), ['PARAMETER1']) # Parameters are case insensitive self.assertEqual(p['parameter1'], 'Value1') @@ -109,22 +115,22 @@ # Parameter with list of values must be seperated by comma p = Parameters({'parameter1': ['Value1', 'Value2']}) - self.assertEqual(p.to_ical(), 'PARAMETER1=Value1,Value2') + self.assertEqual(p.to_ical(), b'PARAMETER1=Value1,Value2') # Multiple parameters must be seperated by a semicolon p = Parameters({'RSVP': 'TRUE', 'ROLE': 'REQ-PARTICIPANT'}) - self.assertEqual(p.to_ical(), 'ROLE=REQ-PARTICIPANT;RSVP=TRUE') + self.assertEqual(p.to_ical(), b'ROLE=REQ-PARTICIPANT;RSVP=TRUE') # Parameter values containing ',;:' must be double quoted p = Parameters({'ALTREP': 'http://www.wiz.org'}) - self.assertEqual(p.to_ical(), 'ALTREP="http://www.wiz.org"') + self.assertEqual(p.to_ical(), b'ALTREP="http://www.wiz.org"') # list items must be quoted seperately p = Parameters({'MEMBER': ['MAILTO:projectA@host.com', 'MAILTO:projectB@host.com']}) self.assertEqual( p.to_ical(), - 'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"' + b'MEMBER="MAILTO:projectA@host.com","MAILTO:projectB@host.com"' ) # Now the whole sheebang @@ -133,8 +139,8 @@ 'ALTREP': ['http://www.wiz.org', 'value4']}) self.assertEqual( p.to_ical(), - ('ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;' - 'PARAMETER2=Value2,Value3') + (b'ALTREP="http://www.wiz.org",value4;PARAMETER1=Value1;' + b'PARAMETER2=Value2,Value3') ) # We can also parse parameter strings @@ -164,3 +170,47 @@ 'ALTREP': ['http://www.wiz.org', 'value4'], 'PARAMETER2': ['Value2', 'Value3']}) ) + + def test_parse_and_access_property_params(self): + """Parse an ics string and access some property parameters then. + This is a follow-up of a question recieved per email. + + """ + ics = """BEGIN:VCALENDAR +VERSION:2.0 +PRODID://RESEARCH IN MOTION//BIS 3.0 +METHOD:REQUEST +BEGIN:VEVENT +SEQUENCE:2 +X-RIM-REVISION:0 +SUMMARY:Test meeting from BB +X-MICROSOFT-CDO-ALLDAYEVENT:TRUE +CLASS:PUBLIC +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandXS":MAILTO:rembrand@xs4all.nl +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandDX":MAILTO:rembrand@daxlab.com +ATTENDEE;PARTSTAT=NEEDS-ACTION;RSVP=TRUE;CN="RembrandSB":MAILTO:rembspam@xs4all.nl +UID:XRIMCAL-628059586-522954492-9750559 +DTSTART;VALUE=DATE:20120814 +DTEND;VALUE=DATE:20120815 +DESCRIPTION:Test meeting from BB +DTSTAMP:20120813T151458Z +ORGANIZER:mailto:rembrand@daxlab.com +END:VEVENT +END:VCALENDAR""" + + cal = icalendar.Calendar.from_ical(ics) + event = cal.walk("VEVENT")[0] + event['attendee'][0] + self.assertEqual(event['attendee'][0].to_ical(), + b'MAILTO:rembrand@xs4all.nl') + self.assertEqual(event['attendee'][0].params.to_ical(), + b'CN=RembrandXS;PARTSTAT=NEEDS-ACTION;RSVP=TRUE') + self.assertEqual(event['attendee'][0].params['cn'], u'RembrandXS') + + def test_repr(self): + """Test correct class representation. + """ + it = Parameters(parameter1='Value1') + self.assertTrue( + re.match("Parameters\({u?'PARAMETER1': 'Value1'}\)", str(it)) + )
View file
icalendar-3.8.2.tar.gz/src/icalendar/tests/test_recurrence.py
Added
@@ -0,0 +1,62 @@ +# -*- coding: utf-8 -*- +from icalendar.caselessdict import CaselessDict +from icalendar.tests import unittest + +import datetime +import icalendar +import os +import pytz + + +class TestRecurrence(unittest.TestCase): + + def setUp(self): + directory = os.path.dirname(__file__) + self.cal = icalendar.Calendar.from_ical( + open(os.path.join(directory, 'recurrence.ics'), 'rb').read() + ) + + def test_recurrence_exdates_one_line(self): + first_event = self.cal.walk('vevent')[0] + + self.assertIsInstance(first_event, CaselessDict) + self.assertEqual( + first_event['rrule'], {'COUNT': [100], 'FREQ': ['DAILY']} + ) + + self.assertEqual( + first_event['exdate'].to_ical(), + b'19960402T010000Z,19960403T010000Z,19960404T010000Z' + ) + + self.assertEqual( + first_event['exdate'].dts[0].dt, + pytz.utc.localize(datetime.datetime(1996, 4, 2, 1, 0)) + ) + + self.assertEqual( + first_event['exdate'].dts[1].dt, + pytz.utc.localize(datetime.datetime(1996, 4, 3, 1, 0)) + ) + + self.assertEqual( + first_event['exdate'].dts[2].dt, + pytz.utc.localize(datetime.datetime(1996, 4, 4, 1, 0)) + ) + + def test_recurrence_exdates_multiple_lines(self): + event = self.cal.walk('vevent')[1] + + exdate = event['exdate'] + + # TODO: DOCUMENT BETTER! + # In this case we have multiple EXDATE definitions, one per line. + # Icalendar makes a list out of this instead of zipping it into one + # vDDDLists object. Actually, this feels correct for me, as it also + # allows to define different timezones per exdate line - but client + # code has to handle this as list and not blindly expecting to be able + # to call event['EXDATE'].to_ical() on it: + self.assertEqual(isinstance(exdate, list), True) # multiple EXDATE + self.assertEqual(exdate[0].to_ical(), b'20120529T100000') + + # TODO: test for embedded timezone information!
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_time.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_time.py
Changed
@@ -1,8 +1,9 @@ -import icalendar +from icalendar.tests import unittest + import datetime +import icalendar import os -from . import unittest class TestTime(unittest.TestCase): @@ -24,5 +25,5 @@ def test_create_to_ical(self): cal = icalendar.Calendar() cal.add('X-SOMETIME', datetime.time(17, 20, 10)) - self.assertTrue('X-SOMETIME;VALUE=TIME:172010' in + self.assertTrue(b'X-SOMETIME;VALUE=TIME:172010' in cal.to_ical().splitlines())
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_timezoned.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_timezoned.py
Changed
@@ -1,33 +1,51 @@ # -*- coding: utf-8 -*- -import icalendar -import pytz +from icalendar.tests import unittest + import datetime import dateutil.parser +import icalendar import os - -from . import unittest +import pytz class TestTimezoned(unittest.TestCase): def test_create_from_ical(self): directory = os.path.dirname(__file__) - cal = icalendar.Calendar.from_ical(open(os.path.join(directory, 'timezoned.ics'), 'rb').read()) + cal = icalendar.Calendar.from_ical( + open(os.path.join(directory, 'timezoned.ics'), 'rb').read() + ) - self.assertEqual(cal['prodid'].to_ical(), "-//Plone.org//NONSGML plone.app.event//EN") + self.assertEqual( + cal['prodid'].to_ical(), + b"-//Plone.org//NONSGML plone.app.event//EN" + ) timezones = cal.walk('VTIMEZONE') self.assertEqual(len(timezones), 1) tz = timezones[0] - self.assertEqual(tz['tzid'].to_ical(), "Europe/Vienna") + self.assertEqual(tz['tzid'].to_ical(), b"Europe/Vienna") std = tz.walk('STANDARD')[0] - self.assertEqual(std.decoded('TZOFFSETFROM'), datetime.timedelta(0, 7200)) + self.assertEqual( + std.decoded('TZOFFSETFROM'), + datetime.timedelta(0, 7200) + ) ev1 = cal.walk('VEVENT')[0] - self.assertEqual(ev1.decoded('DTSTART'), datetime.datetime(2012, 02, 13, 10, 0, 0, tzinfo=pytz.timezone('Europe/Vienna'))) - self.assertEqual(ev1.decoded('DTSTAMP'), datetime.datetime(2010, 10, 10, 9, 10, 10, tzinfo=pytz.utc)) + self.assertEqual( + ev1.decoded('DTSTART'), + pytz.timezone('Europe/Vienna').localize( + datetime.datetime(2012, 2, 13, 10, 0, 0) + ) + ) + self.assertEqual( + ev1.decoded('DTSTAMP'), + pytz.utc.localize( + datetime.datetime(2010, 10, 10, 9, 10, 10) + ) + ) def test_create_to_ical(self): cal = icalendar.Calendar() @@ -63,12 +81,22 @@ event = icalendar.Event() tz = pytz.timezone("Europe/Vienna") - event.add('dtstart', datetime.datetime(2012, 02, 13, 10, 00, 00, tzinfo=tz)) - event.add('dtend', datetime.datetime(2012, 02, 17, 18, 00, 00, tzinfo=tz)) - event.add('dtstamp', datetime.datetime(2010, 10, 10, 10, 10, 10, tzinfo=tz)) - event.add('created', datetime.datetime(2010, 10, 10, 10, 10, 10, tzinfo=tz)) + event.add( + 'dtstart', + tz.localize(datetime.datetime(2012, 2, 13, 10, 00, 00))) + event.add( + 'dtend', + tz.localize(datetime.datetime(2012, 2, 17, 18, 00, 00))) + event.add( + 'dtstamp', + tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) + event.add( + 'created', + tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) event.add('uid', u'123456') - event.add('last-modified', datetime.datetime(2010, 10, 10, 10, 10, 10, tzinfo=tz)) + event.add( + 'last-modified', + tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) event.add('summary', u'artsprint 2012') # event.add('rrule', u'FREQ=YEARLY;INTERVAL=1;COUNT=10') event.add('description', u'sprinting at the artsprint') @@ -82,15 +110,16 @@ event.add('url', u'http://plone.org') cal.add_component(event) - test_out = '|'.join(cal.to_ical().splitlines()) + test_out = b'|'.join(cal.to_ical().splitlines()) + test_out = test_out.decode('utf-8') - vtimezone_lines = "BEGIN:VTIMEZONE|TZID:Europe/Vienna|X-LIC-LOCATION:"\ - + "Europe/Vienna|BEGIN:STANDARD|DTSTART;VALUE=DATE-TIME:19701025T03"\ - + "0000|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10|RRULE:FREQ=YEARLY;B"\ - + "YDAY=-1SU;BYMONTH=3|TZNAME:CET|TZOFFSETFROM:+0200|TZOFFSETTO:+01"\ - + "00|END:STANDARD|BEGIN:DAYLIGHT|DTSTART;VALUE=DATE-TIME:19700329T"\ - + "020000|TZNAME:CEST|TZOFFSETFROM:+0100|TZOFFSETTO:+0200|END:DAYLI"\ - + "GHT|END:VTIMEZONE" + vtimezone_lines = "BEGIN:VTIMEZONE|TZID:Europe/Vienna|X-LIC-LOCATION:" + "Europe/Vienna|BEGIN:STANDARD|DTSTART;VALUE=DATE-TIME:19701025T03" + "0000|RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10|RRULE:FREQ=YEARLY;B" + "YDAY=-1SU;BYMONTH=3|TZNAME:CET|TZOFFSETFROM:+0200|TZOFFSETTO:+01" + "00|END:STANDARD|BEGIN:DAYLIGHT|DTSTART;VALUE=DATE-TIME:19700329T" + "020000|TZNAME:CEST|TZOFFSETFROM:+0100|TZOFFSETTO:+0200|END:DAYLI" + "GHT|END:VTIMEZONE" self.assertTrue(vtimezone_lines in test_out) test_str = "DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20120213T100000" @@ -98,13 +127,12 @@ self.assertTrue("ATTENDEE:sepp" in test_out) # ical standard expects DTSTAMP and CREATED in UTC - self.assertTrue("DTSTAMP;VALUE=DATE-TIME:20101010T091010Z" in test_out) - self.assertTrue("CREATED;VALUE=DATE-TIME:20101010T091010Z" in test_out) + self.assertTrue("DTSTAMP;VALUE=DATE-TIME:20101010T081010Z" in test_out) + self.assertTrue("CREATED;VALUE=DATE-TIME:20101010T081010Z" in test_out) def test_tzinfo_dateutil(self): # Test for issues #77, #63 # references: #73,7430b66862346fe3a6a100ab25e35a8711446717 - date = dateutil.parser.parse('2012-08-30T22:41:00Z') date2 = dateutil.parser.parse('2012-08-30T22:41:00 +02:00') self.assertTrue(date.tzinfo.__module__ == 'dateutil.tz') @@ -112,6 +140,6 @@ # make sure, it's parsed properly and doesn't throw an error self.assertTrue(icalendar.vDDDTypes(date).to_ical() - == '20120830T224100Z') + == b'20120830T224100Z') self.assertTrue(icalendar.vDDDTypes(date2).to_ical() - == '20120830T224100') + == b'20120830T224100')
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_unit_cal.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_unit_cal.py
Changed
@@ -1,7 +1,10 @@ -from . import unittest +from datetime import datetime +from datetime import timedelta +from icalendar.tests import unittest + import icalendar -from datetime import datetime, timedelta import pytz +import re class TestCalComponent(unittest.TestCase): @@ -17,44 +20,73 @@ # Every key defines a property.A property can consist of either a # single item. This can be set with a single value... c['prodid'] = '-//max m//icalendar.mxm.dk/' - self.assertEqual(c, - Calendar({'PRODID': '-//max m//icalendar.mxm.dk/'})) + self.assertEqual( + c, + Calendar({'PRODID': '-//max m//icalendar.mxm.dk/'}) + ) # or with a list c['ATTENDEE'] = ['Max M', 'Rasmussen'] - self.assertEqual(c, + self.assertEqual( + c, Calendar({'ATTENDEE': ['Max M', 'Rasmussen'], - 'PRODID': '-//max m//icalendar.mxm.dk/'})) + 'PRODID': '-//max m//icalendar.mxm.dk/'}) + ) + + ### ADD MULTIPLE VALUES TO A PROPERTY # if you use the add method you don't have to considder if a value is # a list or not. c = Component() c.name = 'VEVENT' + + # add multiple values at once + c.add('attendee', + ['test@test.com', 'test2@test.com']) + + # or add one per line c.add('attendee', 'maxm@mxm.dk') c.add('attendee', 'test@example.dk') - self.assertEqual(c, - Event({'ATTENDEE': [prop.vCalAddress('maxm@mxm.dk'), - prop.vCalAddress('test@example.dk')]})) + + # add again multiple values at once to very concatenaton of lists + c.add('attendee', + ['test3@test.com', 'test4@test.com']) + + self.assertEqual( + c, + Event({'ATTENDEE': [ + prop.vCalAddress('test@test.com'), + prop.vCalAddress('test2@test.com'), + prop.vCalAddress('maxm@mxm.dk'), + prop.vCalAddress('test@example.dk'), + prop.vCalAddress('test3@test.com'), + prop.vCalAddress('test4@test.com') + ]}) + ) + + ### # You can get the values back directly ... c.add('prodid', '-//my product//') self.assertEqual(c['prodid'], prop.vText(u'-//my product//')) # ... or decoded to a python type - self.assertEqual(c.decoded('prodid'), '-//my product//') + self.assertEqual(c.decoded('prodid'), b'-//my product//') # With default values for non existing properties self.assertEqual(c.decoded('version', 'No Version'), 'No Version') c.add('rdate', [datetime(2013, 3, 28), datetime(2013, 3, 27)]) - self.assertEqual(type(c.decoded('rdate')), prop.vDDDLists) + self.assertTrue(isinstance(c.decoded('rdate'), prop.vDDDLists)) # The component can render itself in the RFC 2445 format. c = Component() c.name = 'VCALENDAR' c.add('attendee', 'Max M') - self.assertEqual(c.to_ical(), - 'BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n') + self.assertEqual( + c.to_ical(), + b'BEGIN:VCALENDAR\r\nATTENDEE:Max M\r\nEND:VCALENDAR\r\n' + ) # Components can be nested, so You can add a subcompont. Eg a calendar # holds events. @@ -62,15 +94,19 @@ e.name = 'VEVENT' e.add('dtend', '20000102T000000', encode=0) e.add('dtstart', '20000101T000000', encode=0) - self.assertEqual(e.to_ical(), - 'BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n' - + 'DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r' - + '\nEND:VEVENT\r\n') + self.assertEqual( + e.to_ical(), + b'BEGIN:VEVENT\r\nDTEND:20000102T000000\r\n' + + b'DTSTART:20000101T000000\r\nSUMMARY:A brief history of time\r' + + b'\nEND:VEVENT\r\n' + ) c.add_component(e) - self.assertEqual(c.subcomponents, + self.assertEqual( + c.subcomponents, [Event({'DTEND': '20000102T000000', 'DTSTART': '20000101T000000', - 'SUMMARY': 'A brief history of time'})]) + 'SUMMARY': 'A brief history of time'})] + ) # We can walk over nested componentes with the walk method. self.assertEqual([i.name for i in c.walk()], ['VCALENDAR', 'VEVENT']) @@ -79,57 +115,75 @@ # them on their name. self.assertEqual([i.name for i in c.walk('VEVENT')], ['VEVENT']) - self.assertEqual([i['dtstart'] for i in c.walk('VEVENT')], - ['20000101T000000']) + self.assertEqual( + [i['dtstart'] for i in c.walk('VEVENT')], + ['20000101T000000'] + ) # We can enumerate property items recursively with the property_items # method. - self.assertEqual(c.property_items(), - [('BEGIN', 'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')), - ('BEGIN', 'VEVENT'), ('DTEND', '20000102T000000'), + self.assertEqual( + c.property_items(), + [('BEGIN', b'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')), + ('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'), ('DTSTART', '20000101T000000'), - ('SUMMARY', 'A brief history of time'), ('END', 'VEVENT'), - ('END', 'VCALENDAR')]) + ('SUMMARY', 'A brief history of time'), ('END', b'VEVENT'), + ('END', b'VCALENDAR')] + ) # We can also enumerate property items just under the component. - self.assertEqual(c.property_items(recursive=False), - [('BEGIN', 'VCALENDAR'), + self.assertEqual( + c.property_items(recursive=False), + [('BEGIN', b'VCALENDAR'), ('ATTENDEE', prop.vCalAddress('Max M')), - ('END', 'VCALENDAR')]) + ('END', b'VCALENDAR')] + ) sc = c.subcomponents[0] - self.assertEqual(sc.property_items(recursive=False), - [('BEGIN', 'VEVENT'), ('DTEND', '20000102T000000'), + self.assertEqual( + sc.property_items(recursive=False), + [('BEGIN', b'VEVENT'), ('DTEND', '20000102T000000'), ('DTSTART', '20000101T000000'), - ('SUMMARY', 'A brief history of time'), ('END', 'VEVENT')]) + ('SUMMARY', 'A brief history of time'), ('END', b'VEVENT')] + ) # Text fields which span multiple mulitple lines require proper # indenting c = Calendar() 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') + self.assertEqual( + c.to_ical(), + b'BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two' + + b'\r\nEND:VCALENDAR\r\n' + ) # INLINE properties have their values on one property line. Note the # double quoting of the value with a colon in it. c = Calendar() c['resources'] = 'Chair, Table, "Room: 42"' - self.assertEqual(c, - Calendar({'RESOURCES': 'Chair, Table, "Room: 42"'})) + self.assertEqual( + c, + Calendar({'RESOURCES': 'Chair, Table, "Room: 42"'}) + ) - self.assertEqual(c.to_ical(), - 'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n' - + 'END:VCALENDAR\r\n') + self.assertEqual( + c.to_ical(), + b'BEGIN:VCALENDAR\r\nRESOURCES:Chair\\, Table\\, "Room: 42"\r\n' + + b'END:VCALENDAR\r\n' + ) # The inline values must be handled by the get_inline() and # set_inline() methods. - self.assertEqual(c.get_inline('resources', decode=0), - ['Chair', 'Table', 'Room: 42']) + self.assertEqual( + c.get_inline('resources', decode=0), + [u'Chair', u'Table', u'Room: 42'] + ) # These can also be decoded - self.assertEqual(c.get_inline('resources', decode=1), - [u'Chair', u'Table', u'Room: 42']) + self.assertEqual( + c.get_inline('resources', decode=1), + [b'Chair', b'Table', b'Room: 42'] + ) # You can set them directly ... c.set_inline('resources', ['A', 'List', 'of', 'some, recources'], @@ -137,14 +191,18 @@ self.assertEqual(c['resources'], 'A,List,of,"some, recources"') # ... and back again - self.assertEqual(c.get_inline('resources', decode=0), - ['A', 'List', 'of', 'some, recources']) + self.assertEqual( + c.get_inline('resources', decode=0), + ['A', 'List', 'of', 'some, recources'] + ) c['freebusy'] = '19970308T160000Z/PT3H,19970308T200000Z/PT1H,'\ + '19970308T230000Z/19970309T000000Z' - self.assertEqual(c.get_inline('freebusy', decode=0), + self.assertEqual( + c.get_inline('freebusy', decode=0), ['19970308T160000Z/PT3H', '19970308T200000Z/PT1H', - '19970308T230000Z/19970309T000000Z']) + '19970308T230000Z/19970309T000000Z'] + ) freebusy = c.get_inline('freebusy', decode=1) self.assertTrue(isinstance(freebusy[0][0], datetime)) @@ -152,25 +210,25 @@ def test_cal_Component_add(self): # Test the for timezone correctness: dtstart should preserve it's - # timezone, crated, dtstamp and last-modified must be in UTC. + # timezone, created, 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"))) + vienna = pytz.timezone("Europe/Vienna") + comp.add('dtstart', vienna.localize(datetime(2010, 10, 10, 10, 0, 0))) 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('dtstamp', vienna.localize(datetime(2010, 10, 10, 14, 0, 0))) + comp.add('last-modified', pytz.utc.localize( + datetime(2010, 10, 10, 16, 0, 0))) lines = comp.to_ical().splitlines() self.assertTrue( - "DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20101010T100000" + b"DTSTART;TZID=Europe/Vienna;VALUE=DATE-TIME:20101010T100000" in lines) - self.assertTrue("CREATED;VALUE=DATE-TIME:20101010T120000Z" in lines) - self.assertTrue("DTSTAMP;VALUE=DATE-TIME:20101010T130000Z" in lines) + self.assertTrue(b"CREATED;VALUE=DATE-TIME:20101010T120000Z" in lines) + self.assertTrue(b"DTSTAMP;VALUE=DATE-TIME:20101010T120000Z" in lines) self.assertTrue( - "LAST-MODIFIED;VALUE=DATE-TIME:20101010T160000Z" in lines) + b"LAST-MODIFIED;VALUE=DATE-TIME:20101010T160000Z" in lines + ) def test_cal_Component_add_no_reencode(self): """Already encoded values should not be re-encoded. @@ -185,20 +243,108 @@ self.assertEqual(comp['ATTACH'], [u'me', 'you', binary]) + def test_cal_Component_add_property_parameter(self): + # Test the for timezone correctness: dtstart should preserve it's + # timezone, crated, dtstamp and last-modified must be in UTC. + Component = icalendar.cal.Component + comp = Component() + comp.add('X-TEST-PROP', 'tryout.', + parameters={'prop1': 'val1', 'prop2': 'val2'}) + lines = comp.to_ical().splitlines() + self.assertTrue(b"X-TEST-PROP;PROP1=val1;PROP2=val2:tryout." 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 + # Check for proper handling of TZID parameter of datetime properties Component = icalendar.cal.Component - componentStr = 'BEGIN:VEVENT\nRECURRENCE-ID;TZID=America/Denver:'\ - + '20120404T073000\nEND:VEVENT' - component = Component.from_ical(componentStr) - self.assertEqual( - str(component['RECURRENCE-ID'].dt.tzinfo.zone), "America/Denver") + for component_name, property_name in ( + ('VEVENT', 'DTSTART'), + ('VEVENT', 'DTEND'), + ('VEVENT', 'RECURRENCE-ID'), + ('VTODO', 'DUE') + ): + component_str = 'BEGIN:' + component_name + '\n' + component_str += property_name + ';TZID=America/Denver:' + component_str += '20120404T073000\nEND:' + component_name + component = Component.from_ical(component_str) + self.assertEqual(str(component[property_name].dt.tzinfo.zone), + "America/Denver") + + component_str = 'BEGIN:' + component_name + '\n' + component_str += property_name + ':' + component_str += '20120404T073000\nEND:' + component_name + component = Component.from_ical(component_str) + self.assertEqual(component[property_name].dt.tzinfo, + None) + + def test_cal_Component_to_ical_property_order(self): + Component = icalendar.cal.Component + component_str = [b'BEGIN:VEVENT', + b'DTSTART:19970714T170000Z', + b'DTEND:19970715T035959Z', + b'SUMMARY:Bastille Day Party', + b'END:VEVENT'] + component = Component.from_ical(b'\r\n'.join(component_str)) + + sorted_str = component.to_ical().splitlines() + assert sorted_str != component_str + assert set(sorted_str) == set(component_str) + + preserved_str = component.to_ical(sorted=False).splitlines() + assert preserved_str == component_str + + def test_cal_Component_to_ical_parameter_order(self): + Component = icalendar.cal.Component + component_str = [b'BEGIN:VEVENT', + b'X-FOOBAR;C=one;A=two;B=three:helloworld.', + b'END:VEVENT'] + component = Component.from_ical(b'\r\n'.join(component_str)) + + sorted_str = component.to_ical().splitlines() + assert sorted_str[0] == component_str[0] + assert sorted_str[1] == b'X-FOOBAR;A=two;B=three;C=one:helloworld.' + assert sorted_str[2] == component_str[2] + + preserved_str = component.to_ical(sorted=False).splitlines() + assert preserved_str == component_str + + def test_repr(self): + """Test correct class representation. + """ + from icalendar.cal import Component, Calendar, Event + + component = Component() + component['key1'] = 'value1' - componentStr = 'BEGIN:VEVENT\nRECURRENCE-ID:20120404T073000\n'\ - + 'END:VEVENT' - component = Component.from_ical(componentStr) - self.assertEqual(component['RECURRENCE-ID'].dt.tzinfo, None) + self.assertTrue( + re.match("Component\({u?'KEY1': 'value1'}\)", str(component)) + ) + + calendar = Calendar() + calendar['key1'] = 'value1' + + self.assertTrue( + re.match("VCALENDAR\({u?'KEY1': 'value1'}\)", str(calendar)) + ) + + event = Event() + event['key1'] = 'value1' + + self.assertTrue( + re.match("VEVENT\({u?'KEY1': 'value1'}\)", str(event)) + ) + + # Representation of nested Components + nested = Component(key1='VALUE1') + nested.add_component(component) + calendar.add_component(event) + nested.add_component(calendar) + + self.assertTrue( + re.match( + "Component\({u?'KEY1': 'VALUE1'}, Component\({u?'KEY1': 'value1'}\), VCALENDAR\({u?'KEY1': 'value1'}, VEVENT\({u?'KEY1': 'value1'}\)\)\)", # nopep8 + str(nested) + ) + ) class TestCal(unittest.TestCase): @@ -208,8 +354,10 @@ factory = ComponentFactory() component = factory['VEVENT'] event = component(dtstart='19700101') - self.assertEqual(event.to_ical(), - 'BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n') + self.assertEqual( + event.to_ical(), + b'BEGIN:VEVENT\r\nDTSTART:19700101\r\nEND:VEVENT\r\n' + ) self.assertEqual( factory.get('VCALENDAR', icalendar.cal.Component), @@ -227,16 +375,17 @@ event = icalendar.cal.Event() event['summary'] = 'Python meeting about calendaring' event['uid'] = '42' - event.set('dtstart', datetime(2005, 4, 4, 8, 0, 0)) + event.add('dtstart', datetime(2005, 4, 4, 8, 0, 0)) cal.add_component(event) self.assertEqual( cal.subcomponents[0].to_ical(), - 'BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n'\ - + 'DTSTART;VALUE=DATE-TIME:20050404T080000\r\nUID:42\r\n'\ - + 'END:VEVENT\r\n') + b'BEGIN:VEVENT\r\nSUMMARY:Python meeting about calendaring\r\n' + + b'DTSTART;VALUE=DATE-TIME:20050404T080000\r\nUID:42\r\n' + + b'END:VEVENT\r\n') # Write to disc - import tempfile, os + import tempfile + import os directory = tempfile.mkdtemp() open(os.path.join(directory, 'test.ics'), 'wb').write(cal.to_ical()) @@ -267,4 +416,4 @@ self.assertEqual( [e['DESCRIPTION'].to_ical() for e in icalendar.cal.Calendar.from_ical(s).walk('VEVENT')], - ['Perfectly OK event']) + [b'Perfectly OK event'])
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_unit_caselessdict.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_unit_caselessdict.py
Changed
@@ -1,4 +1,5 @@ -from . import unittest +from icalendar.tests import unittest + import icalendar @@ -10,46 +11,60 @@ keys = ['DTEND', 'DTSTAMP', 'DTSTART', 'UID', 'SUMMARY', 'LOCATION'] out = canonsort_keys(keys) - self.assertEqual(out, - ['DTEND', 'DTSTAMP', 'DTSTART', 'LOCATION', 'SUMMARY', 'UID']) + self.assertEqual( + out, + ['DTEND', 'DTSTAMP', 'DTSTART', 'LOCATION', 'SUMMARY', 'UID'] + ) out = canonsort_keys(keys, ('SUMMARY', 'DTSTART', 'DTEND', )) - self.assertEqual(out, - ['SUMMARY', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'UID']) + self.assertEqual( + out, + ['SUMMARY', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'UID'] + ) out = canonsort_keys(keys, ('UID', 'DTSTART', 'DTEND', )) - self.assertEqual(out, - ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']) - - out = canonsort_keys(keys, - ('UID', 'DTSTART', 'DTEND', 'RRULE', 'EXDATE')) - self.assertEqual(out, - ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY']) - + self.assertEqual( + out, + ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY'] + ) + + out = canonsort_keys( + keys, + ('UID', 'DTSTART', 'DTEND', 'RRULE', 'EXDATE') + ) + self.assertEqual( + out, + ['UID', 'DTSTART', 'DTEND', 'DTSTAMP', 'LOCATION', 'SUMMARY'] + ) def test_caselessdict_canonsort_items(self): canonsort_items = icalendar.caselessdict.canonsort_items - d = dict(i=7, c='at', a=3.5, l=(2,3), e=[4,5], n=13, d={'x': 'y'}, + d = dict(i=7, c='at', a=3.5, l=(2, 3), e=[4, 5], n=13, d={'x': 'y'}, r=1.0) out = canonsort_items(d) - self.assertEqual(out, + self.assertEqual( + out, [('a', 3.5), ('c', 'at'), ('d', {'x': 'y'}), ('e', [4, 5]), - ('i', 7), ('l', (2, 3)), ('n', 13), ('r', 1.0)]) + ('i', 7), ('l', (2, 3)), ('n', 13), ('r', 1.0)] + ) out = canonsort_items(d, ('i', 'c', 'a')) - self.assertTrue(out, + self.assertTrue( + out, [('i', 7), ('c', 'at'), ('a', 3.5), ('d', {'x': 'y'}), - ('e', [4, 5]), ('l', (2, 3)), ('n', 13), ('r', 1.0)]) - + ('e', [4, 5]), ('l', (2, 3)), ('n', 13), ('r', 1.0)] + ) def test_CaselessDict(self): CaselessDict = icalendar.caselessdict.CaselessDict ncd = CaselessDict(key1='val1', key2='val2') - self.assertEqual(ncd, - CaselessDict({'KEY2': 'val2', 'KEY1': 'val1'})) + self.assertEqual( + ncd, + CaselessDict({'KEY2': 'val2', 'KEY1': 'val1'}) + ) self.assertEqual(ncd['key1'], 'val1') self.assertEqual(ncd['KEY1'], 'val1') @@ -66,11 +81,10 @@ self.assertTrue('key4' in ncd) del ncd['key4'] - self.assertFalse(ncd.has_key('key4')) + self.assertFalse('key4' in ncd) - ncd.update({'key5':'val5', 'KEY6':'val6', 'KEY5':'val7'}) + ncd.update({'key5': 'val5', 'KEY6': 'val6', 'KEY5': 'val7'}) self.assertEqual(ncd['key6'], 'val6') - keys = ncd.keys() - keys.sort() + keys = sorted(ncd.keys()) self.assertEqual(keys, ['KEY1', 'KEY2', 'KEY3', 'KEY5', 'KEY6'])
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_unit_parser_tools.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_unit_parser_tools.py
Changed
@@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- -from . import unittest -from ..parser_tools import to_unicode -from ..parser_tools import data_encode +from icalendar.parser_tools import data_encode +from icalendar.parser_tools import to_unicode +from icalendar.tests import unittest class TestParserTools(unittest.TestCase): @@ -11,19 +11,19 @@ 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(b'\xc6\xb5'), u'\u01b5') self.assertEqual(to_unicode(u'\xc6\xb5'.encode('iso-8859-1')), u'\u01b5') - self.assertEqual(to_unicode('\xc6\xb5', encoding='ascii'), u'\u01b5') - with self.assertRaises(AssertionError): - to_unicode(1) - with self.assertRaises(AssertionError): - to_unicode(None) + self.assertEqual(to_unicode(b'\xc6\xb5', encoding='ascii'), u'\u01b5') + self.assertEqual(to_unicode(1), 1) + self.assertEqual(to_unicode(None), None) def test_parser_tools_data_encode(self): - data1 = {u'k1': u'v1', 'k2': 'v2', u'k3': u'v3', - 'li1': ['it1', u'it2', {'k4': u'v4', u'k5': 'v5'}, 123]} - res = {'k3': 'v3', 'k2': 'v2', 'k1': 'v1', - 'li1': ['it1', 'it2', {'k5': 'v5', 'k4': 'v4'}, 123]} + data1 = { + u'k1': u'v1', 'k2': 'v2', u'k3': u'v3', + 'li1': ['it1', u'it2', {'k4': u'v4', u'k5': 'v5'}, 123] + } + res = {b'k3': b'v3', b'k2': b'v2', b'k1': b'v1', + b'li1': [b'it1', b'it2', {b'k5': b'v5', b'k4': b'v4'}, 123]} self.assertEqual(data_encode(data1), res)
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_unit_prop.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_unit_prop.py
Changed
@@ -1,5 +1,10 @@ -from datetime import datetime, date, timedelta, time -from . import unittest +# -*- coding: utf-8 -*- +from datetime import date +from datetime import datetime +from datetime import time +from datetime import timedelta +from icalendar.parser import Parameters +from icalendar.tests import unittest import pytz @@ -9,33 +14,33 @@ def test_prop_vBinary(self): from ..prop import vBinary - txt = 'This is gibberish' - txt_ical = 'VGhpcyBpcyBnaWJiZXJpc2g=' + txt = b'This is gibberish' + txt_ical = b'VGhpcyBpcyBnaWJiZXJpc2g=' self.assertEqual(vBinary(txt).to_ical(), txt_ical) self.assertEqual(vBinary.from_ical(txt_ical), txt) # The roundtrip test - txt = 'Binary data \x13 \x56' - txt_ical = 'QmluYXJ5IGRhdGEgEyBW' + txt = b'Binary data \x13 \x56' + txt_ical = b'QmluYXJ5IGRhdGEgEyBW' self.assertEqual(vBinary(txt).to_ical(), txt_ical) self.assertEqual(vBinary.from_ical(txt_ical), txt) + self.assertIsInstance(vBinary('txt').params, Parameters) self.assertEqual( - str(vBinary('txt').params), - "Parameters({'VALUE': 'BINARY', 'ENCODING': 'BASE64'})" + vBinary('txt').params, {'VALUE': 'BINARY', 'ENCODING': 'BASE64'} ) # Long data should not have line breaks, as that would interfere - txt = 'a' * 99 - txt_ical = 'YWFh' * 33 + txt = b'a' * 99 + txt_ical = b'YWFh' * 33 self.assertEqual(vBinary(txt).to_ical(), txt_ical) self.assertEqual(vBinary.from_ical(txt_ical), txt) def test_prop_vBoolean(self): from ..prop import vBoolean - self.assertEqual(vBoolean(True).to_ical(), 'TRUE') - self.assertEqual(vBoolean(0).to_ical(), 'FALSE') + self.assertEqual(vBoolean(True).to_ical(), b'TRUE') + self.assertEqual(vBoolean(0).to_ical(), b'FALSE') # The roundtrip test self.assertEqual(vBoolean.from_ical(vBoolean(True).to_ical()), True) @@ -43,23 +48,24 @@ def test_prop_vCalAddress(self): from ..prop import vCalAddress - txt = 'MAILTO:maxm@mxm.dk' + txt = b'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.assertIsInstance(a.params, Parameters) + self.assertEqual(a.params, {'CN': 'Max M'}) self.assertEqual(vCalAddress.from_ical(txt), 'MAILTO:maxm@mxm.dk') def test_prop_vFloat(self): from ..prop import vFloat - self.assertEqual(vFloat(1.0).to_ical(), '1.0') + self.assertEqual(vFloat(1.0).to_ical(), b'1.0') self.assertEqual(vFloat.from_ical('42'), 42.0) - self.assertEqual(vFloat(42).to_ical(), '42.0') + self.assertEqual(vFloat(42).to_ical(), b'42.0') def test_prop_vInt(self): from ..prop import vInt - self.assertEqual(vInt(42).to_ical(), '42') + self.assertEqual(vInt(42).to_ical(), b'42') self.assertEqual(vInt.from_ical('13'), 13) self.assertRaises(ValueError, vInt.from_ical, '1s3') @@ -79,13 +85,13 @@ self.assertEqual(str(dt_list[2]), '1996-04-04 01:00:00+00:00') dt_list = vDDDLists([]) - self.assertEqual(dt_list.to_ical(), '') + self.assertEqual(dt_list.to_ical(), b'') dt_list = vDDDLists([datetime(2000, 1, 1)]) - self.assertEqual(dt_list.to_ical(), '20000101T000000') + self.assertEqual(dt_list.to_ical(), b'20000101T000000') dt_list = vDDDLists([datetime(2000, 1, 1), datetime(2000, 11, 11)]) - self.assertEqual(dt_list.to_ical(), '20000101T000000,20001111T000000') + self.assertEqual(dt_list.to_ical(), b'20000101T000000,20001111T000000') def test_prop_vDDDTypes(self): from ..prop import vDDDTypes @@ -94,7 +100,7 @@ datetime)) self.assertEqual(vDDDTypes.from_ical('20010101T123000Z'), - datetime(2001, 1, 1, 12, 30, tzinfo=pytz.utc)) + pytz.utc.localize(datetime(2001, 1, 1, 12, 30))) self.assertTrue(isinstance(vDDDTypes.from_ical('20010101'), date)) @@ -108,8 +114,8 @@ def test_prop_vDate(self): from ..prop import vDate - self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), '20010101') - self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), '18990101') + self.assertEqual(vDate(date(2001, 1, 1)).to_ical(), b'20010101') + self.assertEqual(vDate(date(1899, 1, 1)).to_ical(), b'18990101') self.assertEqual(vDate.from_ical('20010102'), date(2001, 1, 2)) @@ -119,16 +125,16 @@ from ..prop import vDatetime dt = datetime(2001, 1, 1, 12, 30, 0) - self.assertEqual(vDatetime(dt).to_ical(), '20010101T123000') + self.assertEqual(vDatetime(dt).to_ical(), b'20010101T123000') self.assertEqual(vDatetime.from_ical('20000101T120000'), datetime(2000, 1, 1, 12, 0)) - dutc = datetime(2001, 1, 1, 12, 30, 0, tzinfo=pytz.utc) - self.assertEqual(vDatetime(dutc).to_ical(), '20010101T123000Z') + dutc = pytz.utc.localize(datetime(2001, 1, 1, 12, 30, 0)) + self.assertEqual(vDatetime(dutc).to_ical(), b'20010101T123000Z') - dutc = datetime(1899, 1, 1, 12, 30, 0, tzinfo=pytz.utc) - self.assertEqual(vDatetime(dutc).to_ical(), '18990101T123000Z') + dutc = pytz.utc.localize(datetime(1899, 1, 1, 12, 30, 0)) + self.assertEqual(vDatetime(dutc).to_ical(), b'18990101T123000Z') self.assertEqual(vDatetime.from_ical('20010101T000000'), datetime(2001, 1, 1, 0, 0)) @@ -136,7 +142,7 @@ self.assertRaises(ValueError, vDatetime.from_ical, '20010101T000000A') utc = vDatetime.from_ical('20010101T000000Z') - self.assertEqual(vDatetime(utc).to_ical(), '20010101T000000Z') + self.assertEqual(vDatetime(utc).to_ical(), b'20010101T000000Z') # 1 minute before transition to DST dat = vDatetime.from_ical('20120311T015959', 'America/Denver') @@ -149,26 +155,29 @@ '20120311030000 -0600') dat = vDatetime.from_ical('20101010T000000', 'Europe/Vienna') - self.assertEqual(vDatetime(dat).to_ical(), '20101010T000000') + self.assertEqual(vDatetime(dat).to_ical(), b'20101010T000000') def test_prop_vDuration(self): from ..prop import vDuration - self.assertEqual(vDuration(timedelta(11)).to_ical(), 'P11D') - self.assertEqual(vDuration(timedelta(-14)).to_ical(), '-P14D') - self.assertEqual(vDuration(timedelta(1, 7384)).to_ical(), 'P1DT2H3M4S') - self.assertEqual(vDuration(timedelta(1, 7380)).to_ical(), 'P1DT2H3M') - self.assertEqual(vDuration(timedelta(1, 7200)).to_ical(), 'P1DT2H') - self.assertEqual(vDuration(timedelta(0, 7200)).to_ical(), 'PT2H') - self.assertEqual(vDuration(timedelta(0, 7384)).to_ical(), 'PT2H3M4S') - self.assertEqual(vDuration(timedelta(0, 184)).to_ical(), 'PT3M4S') - self.assertEqual(vDuration(timedelta(0, 22)).to_ical(), 'PT22S') - self.assertEqual(vDuration(timedelta(0, 3622)).to_ical(), 'PT1H0M22S') + self.assertEqual(vDuration(timedelta(11)).to_ical(), b'P11D') + self.assertEqual(vDuration(timedelta(-14)).to_ical(), b'-P14D') + self.assertEqual( + vDuration(timedelta(1, 7384)).to_ical(), + b'P1DT2H3M4S' + ) + self.assertEqual(vDuration(timedelta(1, 7380)).to_ical(), b'P1DT2H3M') + self.assertEqual(vDuration(timedelta(1, 7200)).to_ical(), b'P1DT2H') + self.assertEqual(vDuration(timedelta(0, 7200)).to_ical(), b'PT2H') + self.assertEqual(vDuration(timedelta(0, 7384)).to_ical(), b'PT2H3M4S') + self.assertEqual(vDuration(timedelta(0, 184)).to_ical(), b'PT3M4S') + self.assertEqual(vDuration(timedelta(0, 22)).to_ical(), b'PT22S') + self.assertEqual(vDuration(timedelta(0, 3622)).to_ical(), b'PT1H0M22S') self.assertEqual(vDuration(timedelta(days=1, hours=5)).to_ical(), - 'P1DT5H') - self.assertEqual(vDuration(timedelta(hours=-5)).to_ical(), '-PT5H') + b'P1DT5H') + self.assertEqual(vDuration(timedelta(hours=-5)).to_ical(), b'-PT5H') self.assertEqual(vDuration(timedelta(days=-1, hours=-5)).to_ical(), - '-P1DT5H') + b'-P1DT5H') # How does the parsing work? self.assertEqual(vDuration.from_ical('PT1H0M22S'), timedelta(0, 3622)) @@ -185,10 +194,10 @@ # One day in exact datetimes per = (datetime(2000, 1, 1), datetime(2000, 1, 2)) self.assertEqual(vPeriod(per).to_ical(), - '20000101T000000/20000102T000000') + b'20000101T000000/20000102T000000') per = (datetime(2000, 1, 1), timedelta(days=31)) - self.assertEqual(vPeriod(per).to_ical(), '20000101T000000/P31D') + self.assertEqual(vPeriod(per).to_ical(), b'20000101T000000/P31D') # Roundtrip p = vPeriod.from_ical('20000101T000000/20000102T000000') @@ -197,7 +206,7 @@ (datetime(2000, 1, 1, 0, 0), datetime(2000, 1, 2, 0, 0)) ) self.assertEqual(vPeriod(p).to_ical(), - '20000101T000000/20000102T000000') + b'20000101T000000/20000102T000000') self.assertEqual(vPeriod.from_ical('20000101T000000/P31D'), (datetime(2000, 1, 1, 0, 0), timedelta(31))) @@ -205,7 +214,7 @@ # Roundtrip with absolute time p = vPeriod.from_ical('20000101T000000Z/20000102T000000Z') self.assertEqual(vPeriod(p).to_ical(), - '20000101T000000Z/20000102T000000Z') + b'20000101T000000Z/20000102T000000Z') # And an error self.assertRaises(ValueError, @@ -213,34 +222,34 @@ # Timezoned dk = pytz.timezone('Europe/Copenhagen') - start = datetime(2000, 1, 1, tzinfo=dk) - end = datetime(2000, 1, 2, tzinfo=dk) + start = dk.localize(datetime(2000, 1, 1)) + end = dk.localize(datetime(2000, 1, 2)) per = (start, end) self.assertEqual(vPeriod(per).to_ical(), - '20000101T000000/20000102T000000') + b'20000101T000000/20000102T000000') self.assertEqual(vPeriod(per).params['TZID'], 'Europe/Copenhagen') - p = vPeriod((datetime(2000, 1, 1, tzinfo=dk), timedelta(days=31))) - self.assertEqual(p.to_ical(), '20000101T000000/P31D') + p = vPeriod((dk.localize(datetime(2000, 1, 1)), timedelta(days=31))) + self.assertEqual(p.to_ical(), b'20000101T000000/P31D') def test_prop_vWeekday(self): from ..prop import vWeekday - self.assertEqual(vWeekday('mo').to_ical(), 'MO') + self.assertEqual(vWeekday('mo').to_ical(), b'MO') self.assertRaises(ValueError, vWeekday, 'erwer') self.assertEqual(vWeekday.from_ical('mo'), 'MO') self.assertEqual(vWeekday.from_ical('+3mo'), '+3MO') self.assertRaises(ValueError, vWeekday.from_ical, 'Saturday') - self.assertEqual(vWeekday('+mo').to_ical(), '+MO') - self.assertEqual(vWeekday('+3mo').to_ical(), '+3MO') - self.assertEqual(vWeekday('-tu').to_ical(), '-TU') + self.assertEqual(vWeekday('+mo').to_ical(), b'+MO') + self.assertEqual(vWeekday('+3mo').to_ical(), b'+3MO') + self.assertEqual(vWeekday('-tu').to_ical(), b'-TU') def test_prop_vFrequency(self): from ..prop import vFrequency self.assertRaises(ValueError, vFrequency, 'bad test') - self.assertEqual(vFrequency('daily').to_ical(), 'DAILY') + self.assertEqual(vFrequency('daily').to_ical(), b'DAILY') self.assertEqual(vFrequency('daily').from_ical('MONTHLY'), 'MONTHLY') def test_prop_vRecur(self): @@ -258,7 +267,7 @@ }) self.assertEqual( vRecur(r).to_ical(), - 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' + b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' ) r = vRecur(FREQ='yearly', INTERVAL=2) @@ -270,22 +279,25 @@ }) self.assertEqual( r.to_ical(), - 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' + b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' ) r = vRecur(freq='DAILY', count=10) r['bysecond'] = [0, 15, 30, 45] self.assertEqual(r.to_ical(), - 'FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45') + b'FREQ=DAILY;COUNT=10;BYSECOND=0,15,30,45') r = vRecur(freq='DAILY', until=datetime(2005, 1, 1, 12, 0, 0)) - self.assertEqual(r.to_ical(), 'FREQ=DAILY;UNTIL=20050101T120000') + self.assertEqual(r.to_ical(), b'FREQ=DAILY;UNTIL=20050101T120000') # How do we fare with regards to parsing? r = vRecur.from_ical('FREQ=DAILY;INTERVAL=2;COUNT=10') self.assertEqual(r, {'COUNT': [10], 'FREQ': ['DAILY'], 'INTERVAL': [2]}) - self.assertEqual(vRecur(r).to_ical(), 'FREQ=DAILY;COUNT=10;INTERVAL=2') + self.assertEqual( + vRecur(r).to_ical(), + b'FREQ=DAILY;COUNT=10;INTERVAL=2' + ) r = vRecur.from_ical('FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=-SU;' 'BYHOUR=8,9;BYMINUTE=30') @@ -297,19 +309,20 @@ self.assertEqual( vRecur(r).to_ical(), - 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;BYMONTH=1' + b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=-SU;' + b'BYMONTH=1' ) # Some examples from the spec r = vRecur.from_ical('FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1') self.assertEqual(vRecur(r).to_ical(), - 'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1') + b'FREQ=MONTHLY;BYDAY=MO,TU,WE,TH,FR;BYSETPOS=-1') p = 'FREQ=YEARLY;INTERVAL=2;BYMONTH=1;BYDAY=SU;BYHOUR=8,9;BYMINUTE=30' r = vRecur.from_ical(p) self.assertEqual( vRecur(r).to_ical(), - 'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' + b'FREQ=YEARLY;INTERVAL=2;BYMINUTE=30;BYHOUR=8,9;BYDAY=SU;BYMONTH=1' ) # and some errors @@ -318,21 +331,21 @@ def test_prop_vText(self): from ..prop import vText - self.assertEqual(vText(u'Simple text').to_ical(), 'Simple text') + self.assertEqual(vText(u'Simple text').to_ical(), b'Simple text') # Escaped text t = vText('Text ; with escaped, chars') - self.assertEqual(t.to_ical(), 'Text \\; with escaped\\, chars') + self.assertEqual(t.to_ical(), b'Text \\; with escaped\\, chars') # Escaped newlines - self.assertEqual(vText('Text with escaped\N chars').to_ical(), - 'Text with escaped\\n chars') + self.assertEqual(vText('Text with escaped\\N chars').to_ical(), + b'Text with escaped\\n chars') # If you pass a unicode object, it will be utf-8 encoded. As this is # the (only) standard that RFC 2445 support. t = vText(u'international chars \xe4\xf6\xfc') self.assertEqual(t.to_ical(), - 'international chars \xc3\xa4\xc3\xb6\xc3\xbc') + b'international chars \xc3\xa4\xc3\xb6\xc3\xbc') # and parsing? self.assertEqual(vText.from_ical('Text \\; with escaped\\, chars'), @@ -343,7 +356,8 @@ # We are forgiving to utf-8 encoding errors: # We intentionally use a string with unexpected encoding - self.assertEqual(vText.from_ical('Ol\xe9'), u'Ol\ufffd') + # + self.assertEqual(vText.from_ical(b'Ol\xe9'), u'Ol\ufffd') # Notice how accented E character, encoded with latin-1, got replaced # with the official U+FFFD REPLACEMENT CHARACTER. @@ -361,7 +375,7 @@ from ..prop import vUri self.assertEqual(vUri('http://www.example.com/').to_ical(), - 'http://www.example.com/') + b'http://www.example.com/') self.assertEqual(vUri.from_ical('http://www.example.com/'), 'http://www.example.com/') @@ -429,8 +443,8 @@ t2 = vInline('other text') t2.params['cn'] = 'Test Osterone' - self.assertEqual(str(t2.params), - "Parameters({'CN': 'Test Osterone'})") + self.assertIsInstance(t2.params, Parameters) + self.assertEqual(t2.params, {'CN': 'Test Osterone'}) def test_prop_TypesFactory(self): from ..prop import TypesFactory @@ -439,7 +453,7 @@ factory = TypesFactory() datetime_parser = factory['date-time'] self.assertEqual(datetime_parser(datetime(2001, 1, 1)).to_ical(), - '20010101T000000') + b'20010101T000000') # A typical use is when the parser tries to find a content type and use # text as the default @@ -451,14 +465,13 @@ # It can also be used to directly encode property and parameter values self.assertEqual( factory.to_ical('comment', u'by Rasmussen, Max M\xfcller'), - 'by Rasmussen\\, Max M\xc3\xbcller' + b'by Rasmussen\\, Max M\xc3\xbcller' ) - self.assertEqual(factory.to_ical('priority', 1), '1') + self.assertEqual(factory.to_ical('priority', 1), b'1') self.assertEqual(factory.to_ical('cn', u'Rasmussen, Max M\xfcller'), - 'Rasmussen\\, Max M\xc3\xbcller') - + b'Rasmussen\\, Max M\xc3\xbcller') self.assertEqual( - factory.from_ical('cn', 'Rasmussen\\, Max M\xc3\xb8ller'), + factory.from_ical('cn', b'Rasmussen\\, Max M\xc3\xb8ller'), u'Rasmussen, Max M\xf8ller' ) @@ -479,6 +492,6 @@ ical = vevent.to_ical() self.assertTrue( - 'RDATE;TZID=Europe/Vienna:20130101T000000,20130102T000000' in ical + b'RDATE;TZID=Europe/Vienna:20130101T000000,20130102T000000' in ical ) - self.assertTrue('EXDATE;TZID=Europe/Vienna:20130103T000000' in ical) + self.assertTrue(b'EXDATE;TZID=Europe/Vienna:20130103T000000' in ical)
View file
icalendar-3.4.tar.gz/src/icalendar/tests/test_unit_tools.py -> icalendar-3.8.2.tar.gz/src/icalendar/tests/test_unit_tools.py
Changed
@@ -1,4 +1,4 @@ -from . import unittest +from icalendar.tests import unittest from icalendar.tools import UIDGenerator @@ -9,19 +9,20 @@ # Automatic semi-random uid g = UIDGenerator() uid = g.uid() + txt = uid.to_ical() length = 15 + 1 + 16 + 1 + 11 self.assertTrue(len(txt) == length) - self.assertTrue('@example.com' in txt) + self.assertTrue(b'@example.com' in txt) # You should at least insert your own hostname to be more compliant uid = g.uid('Example.ORG') txt = uid.to_ical() self.assertTrue(len(txt) == length) - self.assertTrue('@Example.ORG' in txt) + self.assertTrue(b'@Example.ORG' in txt) # You can also insert a path or similar uid = g.uid('Example.ORG', '/path/to/content') txt = uid.to_ical() self.assertTrue(len(txt) == length) - self.assertTrue('-/path/to/content@Example.ORG' in txt) + self.assertTrue(b'-/path/to/content@Example.ORG' in txt)
View file
icalendar-3.4.tar.gz/src/icalendar/tools.py -> icalendar-3.8.2.tar.gz/src/icalendar/tools.py
Changed
@@ -1,14 +1,11 @@ -from __future__ import absolute_import -import random -from string import ( - ascii_letters, - digits, -) from datetime import datetime -from .prop import ( - vText, - vDatetime, -) +from icalendar.parser_tools import to_unicode +from icalendar.prop import vDatetime +from icalendar.prop import vText +from string import ascii_letters +from string import digits + +import random class UIDGenerator(object): @@ -28,7 +25,9 @@ Like: 20050105T225746Z-HKtJMqUgdO0jDUwm@example.com """ + host_name = to_unicode(host_name) unique = unique or self.rnd_string() - return vText('%s-%s@%s' % (vDatetime(datetime.today()).to_ical(), + today = to_unicode(vDatetime(datetime.today()).to_ical()) + return vText('%s-%s@%s' % (today, unique, host_name))
View file
icalendar-3.4.tar.gz/tox.ini -> icalendar-3.8.2.tar.gz/tox.ini
Changed
@@ -1,12 +1,13 @@ +# to run for a specific environment, use ``tox -e ENVNAME`` [tox] -envlist = py26,py27 +envlist = py26,py27,py33 [testenv] deps = - icalendar [test] + pytest coverage + icalendar [test] commands = - coverage erase - coverage run --source=icalendar --omit=*tests* {envbindir}/unit2 discover icalendar [] - coverage report --omit=*tests* - coverage html --omit=*tests* + coverage run --source=src/icalendar --omit=*/tests/* --module pytest src/icalendar + coverage report + coverage html
View file
python-icalendar.dsc
Changed
@@ -2,7 +2,7 @@ Source: python-icalendar Binary: python-icalendar Architecture: all -Version: 3.4-1 +Version: 3.8.2 Maintainer: Paul Klos <kolab@klos2day.nl> Homepage: http://codespeak.net/icalendar/ Standards-Version: 3.9.3 @@ -10,5 +10,5 @@ Package-List: python-icalendar deb python optional Files: - 00000000000000000000000000000000 0 python-icalendar-3.4.tar.gz - 00000000000000000000000000000000 0 python-icalendar-3.4.debian.tar.gz + 00000000000000000000000000000000 0 python-icalendar-3.8.2.tar.gz + 00000000000000000000000000000000 0 python-icalendar-3.8.2.debian.tar.gz
Locations
Projects
Search
Status Monitor
Help
Open Build Service
OBS Manuals
API Documentation
OBS Portal
Reporting a Bug
Contact
Mailing List
Forums
Chat (IRC)
Twitter
Open Build Service (OBS)
is an
openSUSE project
.