diff --git a/.travis.yml b/.travis.yml index f8bf977..5384667 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,16 +1,11 @@ language: python python: - - "2.6" - - "2.7" - - "3.3" - - "3.4" - - "3.5" - - "3.6" - - "pypy" - - "pypy3" -matrix: - allow_failures: - - python: "pypy3" + - 2.7 + - 3.4 + - 3.5 + - 3.6 + - pypy + - pypy3 install: - pip install tox script: diff --git a/CHANGES.rst b/CHANGES.rst index 63f8efb..db4dabd 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,7 +4,9 @@ Changelog 3.12.1 (unreleased) ------------------- -- Nothing changed yet. +Breaking changes: + +- Drop support for Python 2.6 and 3.3. 3.12 (2017-11-07) diff --git a/README.rst b/README.rst index 90ba236..8047859 100644 --- a/README.rst +++ b/README.rst @@ -11,7 +11,7 @@ with Python. :Code: https://github.com/collective/icalendar :Mailing list: https://github.com/collective/icalendar/issues :Dependencies: `python-dateutil`_ and `pytz`_. -:Compatible with: Python 2.6, 2.7 and 3.3+ +:Compatible with: Python 2.7 and 3.4+ :License: `BSD`_ ---- diff --git a/bootstrap.py b/bootstrap.py index 506a75f..4732735 100644 --- a/bootstrap.py +++ b/bootstrap.py @@ -88,7 +88,7 @@ if not options.allow_site_packages: for sitepackage_path in site.getsitepackages(): sys.path[:] = [x for x in sys.path if sitepackage_path not in x] -setup_args = dict(to_dir=tmpeggs, download_delay=0) +setup_args = {'to_dir': tmpeggs, 'download_delay': 0} ez['use_setuptools'](**setup_args) import setuptools import pkg_resources diff --git a/setup.py b/setup.py index 1c037da..4a03dd2 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,6 @@ # -*- coding: utf-8 -*- import codecs import setuptools -import sys import re import ast @@ -25,12 +24,6 @@ install_requires = [ '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', @@ -40,19 +33,17 @@ setuptools.setup( classifiers=[ 'Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', - "Programming Language :: Python", - "Programming Language :: Python :: Implementation :: CPython", - "Programming Language :: Python :: Implementation :: PyPy", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: Implementation :: CPython', + 'Programming Language :: Python :: Implementation :: PyPy', ], keywords='calendar calendaring ical icalendar event todo journal ' 'recurring', diff --git a/src/icalendar/cal.py b/src/icalendar/cal.py index c0a2970..145080b 100644 --- a/src/icalendar/cal.py +++ b/src/icalendar/cal.py @@ -538,8 +538,7 @@ class Timezone(Component): if 'RRULE' in component: rrulestr = component['RRULE'].to_ical().decode('utf-8') rrule = dateutil.rrule.rrulestr(rrulestr, dtstart=dtstart) - if not set(['UNTIL', 'COUNT']).intersection( - component['RRULE'].keys()): + if not {'UNTIL', 'COUNT'}.intersection(component['RRULE'].keys()): # pytz.timezones don't know any transition dates after 2038 # either rrule._until = datetime(2038, 12, 31) diff --git a/src/icalendar/caselessdict.py b/src/icalendar/caselessdict.py index 8054403..21cc2de 100644 --- a/src/icalendar/caselessdict.py +++ b/src/icalendar/caselessdict.py @@ -2,17 +2,14 @@ from icalendar.compat import iteritems from icalendar.parser_tools import to_unicode -try: - from collections import OrderedDict -except ImportError: - from ordereddict import OrderedDict +from collections 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 [])) + canonical_map = {k: i for i, k in enumerate(canonical_order or [])} head = [k for k in keys if k in canonical_map] tail = [k for k in keys if k not in canonical_map] return sorted(head, key=lambda k: canonical_map[k]) + sorted(tail) diff --git a/src/icalendar/parser.py b/src/icalendar/parser.py index 8e3d885..318469d 100644 --- a/src/icalendar/parser.py +++ b/src/icalendar/parser.py @@ -6,6 +6,8 @@ Eg. RFC 2426 (vCard) It is stupid in the sense that it treats the content purely as strings. No type conversion is attempted. """ +from __future__ import unicode_literals + from icalendar import compat from icalendar.caselessdict import CaselessDict from icalendar.parser_tools import DEFAULT_ENCODING @@ -32,12 +34,12 @@ def unescape_char(text): assert isinstance(text, (compat.unicode_type, compat.bytes_type)) # NOTE: ORDER MATTERS! 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'\\') + return text.replace('\\N', '\\n')\ + .replace('\r\n', '\n')\ + .replace('\\n', '\n')\ + .replace('\\,', ',')\ + .replace('\\;', ';')\ + .replace('\\\\', '\\') elif isinstance(text, compat.bytes_type): return text.replace(b'\\N', b'\\n')\ .replace(b'\r\n', b'\n')\ @@ -60,7 +62,7 @@ def tzid_from_dt(dt): return tzid -def foldline(line, limit=75, fold_sep=u'\r\n '): +def foldline(line, limit=75, fold_sep='\r\n '): """Make a string folded as defined in RFC5545 Lines of text SHOULD NOT be longer than 75 octets, excluding the line break. Long content lines SHOULD be split into a multiple line @@ -70,7 +72,7 @@ def foldline(line, limit=75, fold_sep=u'\r\n '): SPACE or HTAB). """ assert isinstance(line, compat.unicode_type) - assert u'\n' not in line + assert '\n' not in line # Use a fast and simple variant for the common case that line is all ASCII. try: @@ -92,7 +94,7 @@ def foldline(line, limit=75, fold_sep=u'\r\n '): byte_count = char_byte_len ret_chars.append(char) - return u''.join(ret_chars) + return ''.join(ret_chars) ################################################################# @@ -115,7 +117,7 @@ NAME = re.compile(r'[\w.-]+') UNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7F",:;]') QUNSAFE_CHAR = re.compile('[\x00-\x08\x0a-\x1f\x7F"]') FOLD = re.compile(b'(\r?\n)+[ \t]') -uFOLD = re.compile(u'(\r?\n)+[ \t]') +uFOLD = re.compile('(\r?\n)+[ \t]') NEWLINE = re.compile(r'\r?\n') @@ -289,7 +291,7 @@ class Contentline(compat.unicode_type): """ def __new__(cls, value, strict=False, encoding=DEFAULT_ENCODING): value = to_unicode(value, encoding=encoding) - assert u'\n' not in value, ('Content line can not contain unescaped ' + assert '\n' not in value, ('Content line can not contain unescaped ' 'new line characters.') self = super(Contentline, cls).__new__(cls, value) self.strict = strict @@ -313,8 +315,8 @@ class Contentline(compat.unicode_type): values = to_unicode(values) if params: params = to_unicode(params.to_ical(sorted=sorted)) - return cls(u'%s;%s:%s' % (name, params, values)) - return cls(u'%s:%s' % (name, values)) + return cls('%s;%s:%s' % (name, params, values)) + return cls('%s:%s' % (name, values)) def parts(self): """Split the content line up into (name, parameters, values) parts. @@ -348,7 +350,7 @@ class Contentline(compat.unicode_type): return (name, params, values) except ValueError as exc: raise ValueError( - u"Content line could not be parsed into parts: '%s': %s" + "Content line could not be parsed into parts: '%s': %s" % (self, exc) ) diff --git a/src/icalendar/tests/__init__.py b/src/icalendar/tests/__init__.py index 1350d5a..e69de29 100644 --- a/src/icalendar/tests/__init__.py +++ b/src/icalendar/tests/__init__.py @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# unittest/unittest2 importer -import unittest -if not hasattr(unittest.TestCase, 'assertIsNotNone'): - import unittest2 as unittest -unittest # pep 8 diff --git a/src/icalendar/tests/hypothesis/test_fuzzing.py b/src/icalendar/tests/hypothesis/test_fuzzing.py index e32b279..35619a1 100644 --- a/src/icalendar/tests/hypothesis/test_fuzzing.py +++ b/src/icalendar/tests/hypothesis/test_fuzzing.py @@ -4,7 +4,7 @@ from hypothesis import given, settings import hypothesis.strategies as st from icalendar.parser import Contentline, Contentlines, Parameters -from icalendar.tests import unittest +import unittest def printable_characters(**kw): @@ -15,7 +15,7 @@ def printable_characters(**kw): ) key = st.text(string.ascii_letters + string.digits, min_size=1) -value = printable_characters(blacklist_characters=u'\\;:\"') +value = printable_characters(blacklist_characters='\\;:\"') class TestFuzzing(unittest.TestCase): diff --git a/src/icalendar/tests/test_encoding.py b/src/icalendar/tests/test_encoding.py index ed9fe45..1604f61 100644 --- a/src/icalendar/tests/test_encoding.py +++ b/src/icalendar/tests/test_encoding.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -from icalendar.tests import unittest +from __future__ import unicode_literals + +import unittest import datetime import icalendar @@ -16,28 +18,28 @@ class TestEncoding(unittest.TestCase): cal = icalendar.Calendar.from_ical(data) self.assertEqual(cal['prodid'].to_ical().decode('utf-8'), - u"-//Plönë.org//NONSGML plone.app.event//EN") + "-//Plönë.org//NONSGML plone.app.event//EN") self.assertEqual(cal['X-WR-CALDESC'].to_ical().decode('utf-8'), - u"test non ascii: äöü ÄÖÜ €") + "test non ascii: äöü ÄÖÜ €") event = cal.walk('VEVENT')[0] self.assertEqual(event['SUMMARY'].to_ical().decode('utf-8'), - u'Non-ASCII Test: ÄÖÜ äöü €') + 'Non-ASCII Test: ÄÖÜ äöü €') self.assertEqual( event['DESCRIPTION'].to_ical().decode('utf-8'), - u'icalendar should be able to handle non-ascii: €äüöÄÜÖ.' + 'icalendar should be able to handle non-ascii: €äüöÄÜÖ.' ) self.assertEqual(event['LOCATION'].to_ical().decode('utf-8'), - u'Tribstrül') + 'Tribstrül') def test_create_to_ical(self): cal = icalendar.Calendar() - cal.add('prodid', u"-//Plönë.org//NONSGML plone.app.event//EN") - cal.add('version', u"2.0") - cal.add('x-wr-calname', u"äöü ÄÖÜ €") - cal.add('x-wr-caldesc', u"test non ascii: äöü ÄÖÜ €") - cal.add('x-wr-relcalid', u"12345") + cal.add('prodid', "-//Plönë.org//NONSGML plone.app.event//EN") + cal.add('version', "2.0") + cal.add('x-wr-calname', "äöü ÄÖÜ €") + cal.add('x-wr-caldesc', "test non ascii: äöü ÄÖÜ €") + cal.add('x-wr-relcalid', "12345") event = icalendar.Event() event.add( @@ -52,13 +54,13 @@ class TestEncoding(unittest.TestCase): '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('uid', '123456') + event.add('summary', 'Non-ASCII Test: ÄÖÜ äöü €') event.add( 'description', - u'icalendar should be able to de/serialize non-ascii.' + 'icalendar should be able to de/serialize non-ascii.' ) - event.add('location', u'Tribstrül') + event.add('location', 'Tribstrül') cal.add_component(event) ical_lines = cal.to_ical().splitlines() @@ -71,7 +73,7 @@ class TestEncoding(unittest.TestCase): "dtstart", pytz.utc.localize(datetime.datetime(2010, 10, 10, 0, 0, 0)) ) - event.add("summary", u"åäö") + event.add("summary", "åäö") out = event.to_ical() summary = b'SUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6' self.assertTrue(summary in out.splitlines()) @@ -80,7 +82,7 @@ class TestEncoding(unittest.TestCase): # Test for issue #80 cal = icalendar.Calendar() event = icalendar.Event() - event.add(u'DESCRIPTION', u'äöüßÄÖÜ') + event.add('DESCRIPTION', 'äöüßÄÖÜ') cal.add_component(event) c = cal.to_ical() self.assertEqual( diff --git a/src/icalendar/tests/test_fixed_issues.py b/src/icalendar/tests/test_fixed_issues.py index b60a123..772a7d5 100644 --- a/src/icalendar/tests/test_fixed_issues.py +++ b/src/icalendar/tests/test_fixed_issues.py @@ -1,6 +1,8 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from icalendar.parser_tools import to_unicode -from icalendar.tests import unittest +import unittest import datetime import icalendar @@ -82,7 +84,7 @@ END:VTIMEZONE""" # Non-unicode characters event = icalendar.Event() event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0)) - event.add("summary", u"abcdef") + event.add("summary", "abcdef") self.assertEqual( event.to_ical(), b"BEGIN:VEVENT\r\nSUMMARY:abcdef\r\nDTSTART;VALUE=DATE-TIME:" @@ -92,7 +94,7 @@ END:VTIMEZONE""" # Unicode characters event = icalendar.Event() event.add("dtstart", datetime.datetime(2012, 9, 3, 0, 0, 0)) - event.add("summary", u"åäö") + event.add("summary", "åäö") self.assertEqual( event.to_ical(), b"BEGIN:VEVENT\r\nSUMMARY:\xc3\xa5\xc3\xa4\xc3\xb6\r\n" @@ -178,7 +180,7 @@ END:VCALENDAR""" cal = icalendar.Calendar.from_ical(ical_str) org_cn = cal.walk('VEVENT')[0]['ORGANIZER'].params['CN'] - self.assertEqual(org_cn, u'acme, ädmin') + self.assertEqual(org_cn, 'acme, ädmin') def test_issue_104__ignore_exceptions(self): """ @@ -295,7 +297,7 @@ END:VCALENDAR""" self.assertEqual( ctl.parts(), - (u'TEL', Parameters({'TYPE': ['HOME', 'VOICE']}), u'000000000'), + ('TEL', Parameters({'TYPE': ['HOME', 'VOICE']}), '000000000'), ) def test_issue_143(self): @@ -308,17 +310,17 @@ END:VCALENDAR""" ctl = Contentline.from_ical("ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR:;;This is the Adress 08; Some City;;12345;Germany") # nopep8 self.assertEqual( ctl.parts(), - (u'ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR', + ('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.ADR', Parameters(), - u';;This is the Adress 08; Some City;;12345;Germany'), + ';;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', + ('ITEMADRNULLTHISISTHEADRESS08158SOMECITY12345.X-ABLABEL', Parameters(), - u''), + ''), ) def test_issue_157(self): @@ -436,32 +438,32 @@ END:VCALENDAR""" def test_issue_237(self): """Issue #237 - Fail to parse timezone with non-ascii TZID""" - ical_str = [u'BEGIN:VCALENDAR', - u'BEGIN:VTIMEZONE', - u'TZID:(UTC-03:00) Brasília', - u'BEGIN:STANDARD', - u'TZNAME:Brasília standard', - u'DTSTART:16010101T235959', - u'TZOFFSETFROM:-0200', - u'TZOFFSETTO:-0300', - u'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2', - u'END:STANDARD', - u'BEGIN:DAYLIGHT', - u'TZNAME:Brasília daylight', - u'DTSTART:16010101T235959', - u'TZOFFSETFROM:-0300', - u'TZOFFSETTO:-0200', - u'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10', - u'END:DAYLIGHT', - u'END:VTIMEZONE', - u'BEGIN:VEVENT', - u'DTSTART;TZID=\"(UTC-03:00) Brasília\":20170511T133000', - u'DTEND;TZID=\"(UTC-03:00) Brasília\":20170511T140000', - u'END:VEVENT', - u'END:VCALENDAR', + ical_str = ['BEGIN:VCALENDAR', + 'BEGIN:VTIMEZONE', + 'TZID:(UTC-03:00) Brasília', + 'BEGIN:STANDARD', + 'TZNAME:Brasília standard', + 'DTSTART:16010101T235959', + 'TZOFFSETFROM:-0200', + 'TZOFFSETTO:-0300', + 'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=3SA;BYMONTH=2', + 'END:STANDARD', + 'BEGIN:DAYLIGHT', + 'TZNAME:Brasília daylight', + 'DTSTART:16010101T235959', + 'TZOFFSETFROM:-0300', + 'TZOFFSETTO:-0200', + 'RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=2SA;BYMONTH=10', + 'END:DAYLIGHT', + 'END:VTIMEZONE', + 'BEGIN:VEVENT', + 'DTSTART;TZID=\"(UTC-03:00) Brasília\":20170511T133000', + 'DTEND;TZID=\"(UTC-03:00) Brasília\":20170511T140000', + 'END:VEVENT', + 'END:VCALENDAR', ] - cal = icalendar.Calendar.from_ical(u'\r\n'.join(ical_str)) + cal = icalendar.Calendar.from_ical('\r\n'.join(ical_str)) self.assertEqual(cal.errors, []) dtstart = cal.walk(name='VEVENT')[0].decoded("DTSTART") @@ -469,10 +471,10 @@ END:VCALENDAR""" self.assertEqual(dtstart, expected) try: - expected_zone = str(u'(UTC-03:00) Brasília') - expected_tzname = str(u'Brasília standard') + expected_zone = str('(UTC-03:00) Brasília') + expected_tzname = str('Brasília standard') except UnicodeEncodeError: - expected_zone = u'(UTC-03:00) Brasília'.encode('ascii', 'replace') - expected_tzname = u'Brasília standard'.encode('ascii', 'replace') + expected_zone = '(UTC-03:00) Brasília'.encode('ascii', 'replace') + expected_tzname = 'Brasília standard'.encode('ascii', 'replace') self.assertEqual(dtstart.tzinfo.zone, expected_zone) self.assertEqual(dtstart.tzname(), expected_tzname) diff --git a/src/icalendar/tests/test_icalendar.py b/src/icalendar/tests/test_icalendar.py index 47343e7..24e5968 100644 --- a/src/icalendar/tests/test_icalendar.py +++ b/src/icalendar/tests/test_icalendar.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + import icalendar import os import textwrap -from icalendar.tests import unittest +import unittest class IcalendarTestCase (unittest.TestCase): @@ -143,14 +145,14 @@ class IcalendarTestCase (unittest.TestCase): parts = ('SUMMARY', Parameters(), vText('INternational char æ ø å')) self.assertEqual( Contentline.from_parts(*parts), - u'SUMMARY:INternational char æ ø å' + 'SUMMARY:INternational char æ ø å' ) # A value can also be unicode - parts = ('SUMMARY', Parameters(), vText(u'INternational char æ ø å')) + parts = ('SUMMARY', Parameters(), vText('INternational char æ ø å')) self.assertEqual( Contentline.from_parts(*parts), - u'SUMMARY:INternational char æ ø å' + 'SUMMARY:INternational char æ ø å' ) # Traversing could look like this. @@ -226,7 +228,7 @@ class IcalendarTestCase (unittest.TestCase): 'X-APPLE-RADIUS': '328.7978217977285', 'X-ADDRESS': 'Kaiserliche Hofburg, 1010 Wien', 'X-APPLE-REFERENCEFRAME': '1', - 'X-TITLE': u'HELDENPLATZ', + 'X-TITLE': 'HELDENPLATZ', 'X-APPLE-MAPKIT-HANDLE': 'CAESXQEZGR3QZXJYZWLJAA==', 'VALUE': 'URI', @@ -238,30 +240,30 @@ class IcalendarTestCase (unittest.TestCase): def test_fold_line(self): from ..parser import foldline - self.assertEqual(foldline(u'foo'), u'foo') + self.assertEqual(foldline('foo'), 'foo') self.assertEqual( - foldline(u"Lorem ipsum dolor sit amet, consectetur adipiscing " - u"elit. Vestibulum convallis imperdiet dui posuere."), - (u'Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' - u'Vestibulum conval\r\n lis imperdiet dui posuere.') + foldline("Lorem ipsum dolor sit amet, consectetur adipiscing " + "elit. Vestibulum convallis imperdiet dui posuere."), + ('Lorem ipsum dolor sit amet, consectetur adipiscing elit. ' + '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(u'привет'.encode('utf-8'), limit=3) + foldline('привет'.encode('utf-8'), limit=3) - self.assertEqual(foldline(u'foobar', limit=4), u'foo\r\n bar') + self.assertEqual(foldline('foobar', limit=4), 'foo\r\n bar') self.assertEqual( - foldline(u'Lorem ipsum dolor sit amet, consectetur adipiscing elit' - u'. Vestibulum convallis imperdiet dui posuere.'), - (u'Lorem ipsum dolor sit amet, consectetur adipiscing elit.' - u' Vestibulum conval\r\n lis imperdiet dui posuere.') + foldline('Lorem ipsum dolor sit amet, consectetur adipiscing elit' + '. Vestibulum convallis imperdiet dui posuere.'), + ('Lorem ipsum dolor sit amet, consectetur adipiscing elit.' + ' Vestibulum conval\r\n lis imperdiet dui posuere.') ) self.assertEqual( - foldline(u'DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ'), - u'DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭ\r\n ЮЯ' + foldline('DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ'), + 'DESCRIPTION:АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭ\r\n ЮЯ' ) def test_value_double_quoting(self): @@ -307,7 +309,7 @@ class TestEncoding(unittest.TestCase): for event in cal.walk('vevent'): self.assertEqual(len(event.errors), 1, 'Not the right amount of errors.') error = event.errors[0][1] - self.assertTrue(error.startswith(u'Content line could not be parsed into parts')) + self.assertTrue(error.startswith('Content line could not be parsed into parts')) def test_apple_xlocation(self): """ diff --git a/src/icalendar/tests/test_multiple.py b/src/icalendar/tests/test_multiple.py index 07dfe26..e9605cc 100644 --- a/src/icalendar/tests/test_multiple.py +++ b/src/icalendar/tests/test_multiple.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- from icalendar import Calendar from icalendar.prop import vText -from icalendar.tests import unittest +import unittest import os diff --git a/src/icalendar/tests/test_property_params.py b/src/icalendar/tests/test_property_params.py index a94ed27..bed2c6b 100644 --- a/src/icalendar/tests/test_property_params.py +++ b/src/icalendar/tests/test_property_params.py @@ -1,9 +1,11 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from icalendar import Calendar from icalendar import Event from icalendar import Parameters from icalendar import vCalAddress -from icalendar.tests import unittest +import unittest import icalendar import re @@ -38,9 +40,9 @@ class TestPropertyParams(unittest.TestCase): vevent['ORGANIZER'] = cal_address self.assertEqual( 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' + 'BEGIN:VEVENT\r\n' + 'ORGANIZER;CN="Джон Доу":mailto:john.doe@example.org\r\n' + 'END:VEVENT\r\n' ) self.assertEqual(vevent['ORGANIZER'].params['CN'], @@ -48,14 +50,14 @@ class TestPropertyParams(unittest.TestCase): def test_quoting(self): # not double-quoted - self._test_quoting(u"Aramis", u'Aramis') + self._test_quoting("Aramis", 'Aramis') # if a space is present - enclose in double quotes - self._test_quoting(u"Aramis Alameda", u'"Aramis Alameda"') + self._test_quoting("Aramis Alameda", '"Aramis Alameda"') # a single quote in parameter value - double quote the value - self._test_quoting(u"Aramis d'Alameda", u'"Aramis d\'Alameda"') + self._test_quoting("Aramis d'Alameda", '"Aramis d\'Alameda"') # double quote is replaced with single quote - self._test_quoting(u"Aramis d\"Alameda", u'"Aramis d\'Alameda"') - self._test_quoting(u"Арамис д'Аламеда", u'"Арамис д\'Аламеда"') + self._test_quoting("Aramis d\"Alameda", '"Aramis d\'Alameda"') + self._test_quoting("Арамис д'Аламеда", '"Арамис д\'Аламеда"') def _test_quoting(self, cn_param, cn_quoted): """ @@ -74,14 +76,14 @@ class TestPropertyParams(unittest.TestCase): def test_escaping(self): # verify that escaped non safe chars are decoded correctly - NON_SAFE_CHARS = u',\\;:' + NON_SAFE_CHARS = ',\\;:' for char in NON_SAFE_CHARS: - cn_escaped = u"Society\\%s 2014" % char - cn_decoded = u"Society%s 2014" % char + cn_escaped = "Society\\%s 2014" % char + cn_decoded = "Society%s 2014" % char vevent = Event.from_ical( - u'BEGIN:VEVENT\r\n' - u'ORGANIZER;CN=%s:that\r\n' - u'END:VEVENT\r\n' % cn_escaped + 'BEGIN:VEVENT\r\n' + 'ORGANIZER;CN=%s:that\r\n' + 'END:VEVENT\r\n' % cn_escaped ) self.assertEqual(vevent['ORGANIZER'].params['CN'], cn_decoded) @@ -97,7 +99,7 @@ class TestPropertyParams(unittest.TestCase): ) self.assertEqual( vevent['ORGANIZER'].to_ical().decode('utf-8'), - u'это, то; that\\ %th%%at%:' + 'это, то; that\\ %th%%at%:' ) def test_parameters_class(self): @@ -205,12 +207,12 @@ END:VCALENDAR""" 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') + self.assertEqual(event['attendee'][0].params['cn'], 'RembrandXS') def test_repr(self): """Test correct class representation. """ it = Parameters(parameter1='Value1') self.assertTrue( - re.match(r"Parameters\({u?'PARAMETER1': 'Value1'}\)", str(it)) + re.match(r"Parameters\({u?'PARAMETER1': u?'Value1'}\)", str(it)) ) diff --git a/src/icalendar/tests/test_recurrence.py b/src/icalendar/tests/test_recurrence.py index 6f0c617..fc8b1be 100644 --- a/src/icalendar/tests/test_recurrence.py +++ b/src/icalendar/tests/test_recurrence.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- from icalendar.caselessdict import CaselessDict -from icalendar.tests import unittest +import unittest import datetime import icalendar diff --git a/src/icalendar/tests/test_time.py b/src/icalendar/tests/test_time.py index 0eb632f..12e91e3 100644 --- a/src/icalendar/tests/test_time.py +++ b/src/icalendar/tests/test_time.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from icalendar.tests import unittest +import unittest import datetime import icalendar diff --git a/src/icalendar/tests/test_timezoned.py b/src/icalendar/tests/test_timezoned.py index 068c116..c4eaee4 100644 --- a/src/icalendar/tests/test_timezoned.py +++ b/src/icalendar/tests/test_timezoned.py @@ -1,5 +1,7 @@ # -*- coding: utf-8 -*- -from icalendar.tests import unittest +from __future__ import unicode_literals + +import unittest import datetime import dateutil.parser @@ -50,12 +52,12 @@ class TestTimezoned(unittest.TestCase): def test_create_to_ical(self): cal = icalendar.Calendar() - cal.add('prodid', u"-//Plone.org//NONSGML plone.app.event//EN") - cal.add('version', u"2.0") - cal.add('x-wr-calname', u"test create calendar") - cal.add('x-wr-caldesc', u"icalendar tests") - cal.add('x-wr-relcalid', u"12345") - cal.add('x-wr-timezone', u"Europe/Vienna") + cal.add('prodid', "-//Plone.org//NONSGML plone.app.event//EN") + cal.add('version', "2.0") + cal.add('x-wr-calname', "test create calendar") + cal.add('x-wr-caldesc', "icalendar tests") + cal.add('x-wr-relcalid', "12345") + cal.add('x-wr-timezone', "Europe/Vienna") tzc = icalendar.Timezone() tzc.add('tzid', 'Europe/Vienna') @@ -93,21 +95,21 @@ class TestTimezoned(unittest.TestCase): event.add( 'created', tz.localize(datetime.datetime(2010, 10, 10, 10, 10, 10))) - event.add('uid', u'123456') + event.add('uid', '123456') 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') - event.add('location', u'aka bild, wien') - event.add('categories', u'first subject') - event.add('categories', u'second subject') - event.add('attendee', u'häns') - event.add('attendee', u'franz') - event.add('attendee', u'sepp') - event.add('contact', u'Max Mustermann, 1010 Wien') - event.add('url', u'http://plone.org') + event.add('summary', 'artsprint 2012') + # event.add('rrule', 'FREQ=YEARLY;INTERVAL=1;COUNT=10') + event.add('description', 'sprinting at the artsprint') + event.add('location', 'aka bild, wien') + event.add('categories', 'first subject') + event.add('categories', 'second subject') + event.add('attendee', 'häns') + event.add('attendee', 'franz') + event.add('attendee', 'sepp') + event.add('contact', 'Max Mustermann, 1010 Wien') + event.add('url', 'http://plone.org') cal.add_component(event) test_out = b'|'.join(cal.to_ical().splitlines()) diff --git a/src/icalendar/tests/test_unit_cal.py b/src/icalendar/tests/test_unit_cal.py index 5da47c0..9ad901c 100644 --- a/src/icalendar/tests/test_unit_cal.py +++ b/src/icalendar/tests/test_unit_cal.py @@ -1,7 +1,9 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from datetime import datetime from datetime import timedelta -from icalendar.tests import unittest +import unittest import icalendar import pytz @@ -74,7 +76,7 @@ class TestCalComponent(unittest.TestCase): # You can get the values back directly ... c.add('prodid', '-//my product//') - self.assertEqual(c['prodid'], prop.vText(u'-//my product//')) + self.assertEqual(c['prodid'], prop.vText('-//my product//')) # ... or decoded to a python type self.assertEqual(c.decoded('prodid'), b'-//my product//') @@ -156,7 +158,7 @@ class TestCalComponent(unittest.TestCase): # Text fields which span multiple mulitple lines require proper # indenting c = Calendar() - c['description'] = u'Paragraph one\n\nParagraph two' + c['description'] = 'Paragraph one\n\nParagraph two' self.assertEqual( c.to_ical(), b'BEGIN:VCALENDAR\r\nDESCRIPTION:Paragraph one\\n\\nParagraph two' @@ -182,7 +184,7 @@ class TestCalComponent(unittest.TestCase): # set_inline() methods. self.assertEqual( c.get_inline('resources', decode=0), - [u'Chair', u'Table', u'Room: 42'] + ['Chair', 'Table', 'Room: 42'] ) # These can also be decoded @@ -247,7 +249,7 @@ class TestCalComponent(unittest.TestCase): binary = prop.vBinary('us') comp.add('ATTACH', binary) - self.assertEqual(comp['ATTACH'], [u'me', 'you', binary]) + self.assertEqual(comp['ATTACH'], ['me', 'you', binary]) def test_cal_Component_add_property_parameter(self): # Test the for timezone correctness: dtstart should preserve it's @@ -322,21 +324,21 @@ class TestCalComponent(unittest.TestCase): component['key1'] = 'value1' self.assertTrue( - re.match(r"Component\({u?'KEY1': 'value1'}\)", str(component)) + re.match(r"Component\({u?'KEY1': u?'value1'}\)", str(component)) ) calendar = Calendar() calendar['key1'] = 'value1' self.assertTrue( - re.match(r"VCALENDAR\({u?'KEY1': 'value1'}\)", str(calendar)) + re.match(r"VCALENDAR\({u?'KEY1': u?'value1'}\)", str(calendar)) ) event = Event() event['key1'] = 'value1' self.assertTrue( - re.match(r"VEVENT\({u?'KEY1': 'value1'}\)", str(event)) + re.match(r"VEVENT\({u?'KEY1': u?'value1'}\)", str(event)) ) # Representation of nested Components @@ -347,7 +349,10 @@ class TestCalComponent(unittest.TestCase): self.assertTrue( re.match( - r"Component\({u?'KEY1': 'VALUE1'}, Component\({u?'KEY1': 'value1'}\), VCALENDAR\({u?'KEY1': 'value1'}, VEVENT\({u?'KEY1': 'value1'}\)\)\)", # nopep8 + r"Component\({u?'KEY1': u?'VALUE1'}, " + r"Component\({u?'KEY1': u?'value1'}\), " + r"VCALENDAR\({u?'KEY1': u?'value1'}, " + r"VEVENT\({u?'KEY1': u?'value1'}\)\)\)", str(nested) ) ) diff --git a/src/icalendar/tests/test_unit_caselessdict.py b/src/icalendar/tests/test_unit_caselessdict.py index ee51223..30675ad 100644 --- a/src/icalendar/tests/test_unit_caselessdict.py +++ b/src/icalendar/tests/test_unit_caselessdict.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from icalendar.tests import unittest +import unittest import icalendar @@ -41,9 +41,9 @@ class TestCaselessdict(unittest.TestCase): 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'}, r=1.0 - ) + d = { + '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( diff --git a/src/icalendar/tests/test_unit_parser_tools.py b/src/icalendar/tests/test_unit_parser_tools.py index ff9de9c..451b1e8 100644 --- a/src/icalendar/tests/test_unit_parser_tools.py +++ b/src/icalendar/tests/test_unit_parser_tools.py @@ -1,28 +1,30 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + from icalendar.parser_tools import data_encode from icalendar.parser_tools import to_unicode -from icalendar.tests import unittest +import unittest class TestParserTools(unittest.TestCase): def test_parser_tools_to_unicode(self): - 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(b'\xc6\xb5'), u'\u01b5') - self.assertEqual(to_unicode(u'\xc6\xb5'.encode('iso-8859-1')), - u'\u01b5') - self.assertEqual(to_unicode(b'\xc6\xb5', encoding='ascii'), u'\u01b5') + self.assertEqual(to_unicode(b'spam'), 'spam') + self.assertEqual(to_unicode('spam'), 'spam') + self.assertEqual(to_unicode('spam'.encode('utf-8')), 'spam') + self.assertEqual(to_unicode(b'\xc6\xb5'), '\u01b5') + self.assertEqual(to_unicode('\xc6\xb5'.encode('iso-8859-1')), + '\u01b5') + self.assertEqual(to_unicode(b'\xc6\xb5', encoding='ascii'), '\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] + 'k1': 'v1', 'k2': 'v2', 'k3': 'v3', + 'li1': ['it1', 'it2', {'k4': 'v4', '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]} diff --git a/src/icalendar/tests/test_unit_prop.py b/src/icalendar/tests/test_unit_prop.py index 67f2067..cc9ea53 100644 --- a/src/icalendar/tests/test_unit_prop.py +++ b/src/icalendar/tests/test_unit_prop.py @@ -1,10 +1,12 @@ # -*- coding: utf-8 -*- +from __future__ import unicode_literals + 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 unittest from icalendar.prop import vDatetime from icalendar.windows_to_olson import WINDOWS_TO_OLSON @@ -333,7 +335,7 @@ class TestProp(unittest.TestCase): def test_prop_vText(self): from ..prop import vText - self.assertEqual(vText(u'Simple text').to_ical(), b'Simple text') + self.assertEqual(vText('Simple text').to_ical(), b'Simple text') # Escaped text t = vText('Text ; with escaped, chars') @@ -345,13 +347,13 @@ class TestProp(unittest.TestCase): # 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') + t = vText('international chars \xe4\xf6\xfc') self.assertEqual(t.to_ical(), b'international chars \xc3\xa4\xc3\xb6\xc3\xbc') # and parsing? self.assertEqual(vText.from_ical('Text \\; with escaped\\, chars'), - u'Text ; with escaped, chars') + 'Text ; with escaped, chars') t = vText.from_ical('A string with\\; some\\\\ characters in\\it') self.assertEqual(t, "A string with; some\\ characters in\\it") @@ -359,7 +361,7 @@ class TestProp(unittest.TestCase): # We are forgiving to utf-8 encoding errors: # We intentionally use a string with unexpected encoding # - self.assertEqual(vText.from_ical(b'Ol\xe9'), u'Ol\ufffd') + self.assertEqual(vText.from_ical(b'Ol\xe9'), 'Ol\ufffd') # Notice how accented E character, encoded with latin-1, got replaced # with the official U+FFFD REPLACEMENT CHARACTER. @@ -466,15 +468,15 @@ class TestProp(unittest.TestCase): # It can also be used to directly encode property and parameter values self.assertEqual( - factory.to_ical('comment', u'by Rasmussen, Max M\xfcller'), + factory.to_ical('comment', 'by Rasmussen, Max M\xfcller'), b'by Rasmussen\\, Max M\xc3\xbcller' ) self.assertEqual(factory.to_ical('priority', 1), b'1') - self.assertEqual(factory.to_ical('cn', u'Rasmussen, Max M\xfcller'), + self.assertEqual(factory.to_ical('cn', 'Rasmussen, Max M\xfcller'), b'Rasmussen\\, Max M\xc3\xbcller') self.assertEqual( factory.from_ical('cn', b'Rasmussen\\, Max M\xc3\xb8ller'), - u'Rasmussen, Max M\xf8ller' + 'Rasmussen, Max M\xf8ller' ) diff --git a/src/icalendar/tests/test_unit_tools.py b/src/icalendar/tests/test_unit_tools.py index aa3b611..b957091 100644 --- a/src/icalendar/tests/test_unit_tools.py +++ b/src/icalendar/tests/test_unit_tools.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from icalendar.tests import unittest +import unittest from icalendar.tools import UIDGenerator diff --git a/tox.ini b/tox.ini index 82ea668..3f286f0 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,15 @@ # to run for a specific environment, use ``tox -e ENVNAME`` [tox] -envlist = py26,py27,py33,py34,py35,py36 +envlist = py27,py34,py35,py36,pypy,pypy3 [testenv] deps = pytest coverage - py{27,33,34,35,36}: hypothesis>=3.0 + py{27,34,35,36}: hypothesis>=3.0 .[test] commands = coverage run --source=src/icalendar --omit=*/tests/* --module pytest [] - py{27,33,34,35,36}: coverage run --append --source=src/icalendar --omit=*/tests/* --module pytest [] src/icalendar/tests/hypothesis/ + py{27,34,35,36}: coverage run --append --source=src/icalendar --omit=*/tests/* --module pytest [] src/icalendar/tests/hypothesis/ coverage report coverage html