From 2deaa94b38f63804f45142089b5911dd4e4230e6 Mon Sep 17 00:00:00 2001
From: rsb-23 <57601627+rsb-23@users.noreply.github.com>
Date: Wed, 27 Mar 2024 15:05:58 +0530
Subject: [PATCH 1/2] Linter fixes
---
.pre-commit-config.yaml | 22 +
requirements-dev.txt | 2 +
setup.cfg | 9 +
setup.py | 61 +--
tests.py | 664 +++++++++++-------------
vobject/__init__.py | 6 +-
vobject/base.py | 271 +++++-----
vobject/behavior.py | 13 +-
vobject/change_tz.py | 27 +-
vobject/hcalendar.py | 49 +-
vobject/icalendar.py | 1081 +++++++++++++++++++++------------------
vobject/ics_diff.py | 50 +-
vobject/vcard.py | 159 +++---
vobject/win32tz.py | 70 ++-
14 files changed, 1286 insertions(+), 1198 deletions(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 setup.cfg
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..25ee2cd
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,22 @@
+repos:
+ - repo: https://github.com/PyCQA/isort
+ rev: 5.13.2
+ hooks:
+ - id: isort
+
+ - repo: https://github.com/psf/black
+ rev: 24.2.0
+ hooks:
+ - id: black
+ args: [-l, "120", -C]
+ - repo: https://github.com/pre-commit/pre-commit-hooks
+ rev: v4.5.0
+ hooks:
+ - id: check-yaml
+ - id: end-of-file-fixer
+ - id: no-commit-to-branch
+ args: [-b, main, -b, master]
+ - repo: https://github.com/PyCQA/flake8
+ rev: 7.0.0
+ hooks:
+ - id: flake8
diff --git a/requirements-dev.txt b/requirements-dev.txt
index a0350ad..28af6eb 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -2,3 +2,5 @@ coverage
python-dateutil >= 2.4.0
setuptools
wheel
+pre-commit
+black
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..66e5a12
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,9 @@
+[flake8]
+max-line-length = 120
+ignore = E203, E266, E501, W503
+exclude = .git, __pycache__, venv
+per-file-ignores = */__init__.py: F401
+
+[isort]
+profile = black
+multi_line_output = 3
diff --git a/setup.py b/setup.py
index e9e5a76..76bd4f3 100755
--- a/setup.py
+++ b/setup.py
@@ -23,37 +23,32 @@
- http://vobject.skyhouseconsulting.com/history.html
"""
-from setuptools import setup, find_packages
-
-doclines = (__doc__ or '').splitlines()
-
-setup(name = "vobject",
- version = "0.9.7",
- author = "Jeffrey Harris",
- author_email = "jeffrey@osafoundation.org",
- maintainer = "David Arnold",
- maintainer_email="davida@pobox.com",
- license = "Apache",
- zip_safe = True,
- url = "http://py-vobject.github.io/vobject/",
- download_url = 'https://github.com/py-vobject/vobject/tarball/0.9.7',
- bugtrack_url = "https://github.com/py-vobject/vobject/issues",
- entry_points = {
- 'console_scripts': [
- 'ics_diff = vobject.ics_diff:main',
- 'change_tz = vobject.change_tz:main'
- ]
- },
- include_package_data = True,
- install_requires = ['python-dateutil >= 2.4.0', 'six'],
- platforms = ["any"],
- packages = find_packages(),
- description = "A full-featured Python package for parsing and creating "
- "iCalendar and vCard files",
- long_description = "\n".join(doclines[2:]),
- keywords = ['vobject', 'icalendar', 'vcard', 'ics', 'vcs', 'hcalendar'],
- test_suite="tests",
- classifiers = """
+from setuptools import find_packages, setup
+
+doclines = (__doc__ or "").splitlines()
+
+setup(
+ name="vobject",
+ version="0.9.7",
+ author="Jeffrey Harris",
+ author_email="jeffrey@osafoundation.org",
+ maintainer="David Arnold",
+ maintainer_email="davida@pobox.com",
+ license="Apache",
+ zip_safe=True,
+ url="http://py-vobject.github.io/vobject/",
+ download_url="https://github.com/py-vobject/vobject/tarball/0.9.7",
+ bugtrack_url="https://github.com/py-vobject/vobject/issues",
+ entry_points={"console_scripts": ["ics_diff = vobject.ics_diff:main", "change_tz = vobject.change_tz:main"]},
+ include_package_data=True,
+ install_requires=["python-dateutil >= 2.4.0", "six"],
+ platforms=["any"],
+ packages=find_packages(),
+ description="A full-featured Python package for parsing and creating " "iCalendar and vCard files",
+ long_description="\n".join(doclines[2:]),
+ keywords=["vobject", "icalendar", "vcard", "ics", "vcs", "hcalendar"],
+ test_suite="tests",
+ classifiers="""
Development Status :: 5 - Production/Stable
Environment :: Console
Intended Audience :: Developers
@@ -63,5 +58,5 @@
Programming Language :: Python
Programming Language :: Python :: 2.7
Programming Language :: Python :: 3
- Topic :: Text Processing""".strip().splitlines()
- )
+ Topic :: Text Processing""".strip().splitlines(),
+)
diff --git a/tests.py b/tests.py
index 7db519e..de9ae03 100644
--- a/tests.py
+++ b/tests.py
@@ -3,29 +3,32 @@
from __future__ import print_function
import datetime
-import dateutil
+import json
import re
import sys
import unittest
-import json
+import dateutil
+from dateutil.rrule import MONTHLY, WEEKLY, rrule, rruleset
from dateutil.tz import tzutc
-from dateutil.rrule import rrule, rruleset, WEEKLY, MONTHLY
-
-from vobject import base, iCalendar
-from vobject import icalendar
-
-from vobject.base import __behaviorRegistry as behavior_registry
-from vobject.base import ContentLine, parseLine, ParseError
-from vobject.base import readComponents, textLineToContentLine
+from vobject import base, iCalendar, icalendar
+from vobject.base import ContentLine, ParseError
+from vobject.base import __behaviorRegistry as BehaviorRegistry
+from vobject.base import parseLine, readComponents, textLineToContentLine
from vobject.change_tz import change_tz
-
-from vobject.icalendar import MultiDateBehavior, PeriodBehavior, \
- RecurringComponent, utc
-from vobject.icalendar import parseDtstart, stringToTextValues, \
- stringToPeriod, timedeltaToString
-
+from vobject.icalendar import (
+ MultiDateBehavior,
+ PeriodBehavior,
+ RecurringComponent,
+ parseDtstart,
+ stringToPeriod,
+ stringToTextValues,
+ timedeltaToString,
+ utc,
+)
+
+behavior_registry = BehaviorRegistry
two_hours = datetime.timedelta(hours=2)
@@ -33,14 +36,14 @@ def get_test_file(path):
"""
Helper function to open and read test files.
"""
- filepath = "test_files/{}".format(path)
+ filepath = f"test_files/{path}"
if sys.version_info[0] < 3:
# On python 2, this library operates on bytes.
- f = open(filepath, 'r')
+ f = open(filepath, "r")
else:
# On python 3, it operates on unicode. We need to specify an encoding
# for systems for which the preferred encoding isn't utf-8 (e.g windows)
- f = open(filepath, 'r', encoding='utf-8')
+ f = open(filepath, "r", encoding="utf-8")
text = f.read()
f.close()
return text
@@ -50,6 +53,7 @@ class TestCalendarSerializing(unittest.TestCase):
"""
Test creating an iCalendar file
"""
+
max_diff = None
def test_scratchbuild(self):
@@ -57,25 +61,21 @@ def test_scratchbuild(self):
CreateCalendar 2.0 format from scratch
"""
test_cal = get_test_file("simple_2_0_test.ics")
- cal = base.newFromBehavior('vcalendar', '2.0')
- cal.add('vevent')
- cal.vevent.add('dtstart').value = datetime.datetime(2006, 5, 9)
- cal.vevent.add('description').value = "Test event"
- cal.vevent.add('created').value = \
- datetime.datetime(2006, 1, 1, 10,
- tzinfo=dateutil.tz.tzical(
- "test_files/timezones.ics").get('US/Pacific'))
- cal.vevent.add('uid').value = "Not very random UID"
- cal.vevent.add('dtstamp').value = datetime.datetime(2017, 6, 26, 0, tzinfo=tzutc())
-
- cal.vevent.add('attendee').value = 'mailto:froelich@example.com'
- cal.vevent.attendee.params['CN'] = ['Fröhlich']
+ cal = base.newFromBehavior("vcalendar", "2.0")
+ cal.add("vevent")
+ cal.vevent.add("dtstart").value = datetime.datetime(2006, 5, 9)
+ cal.vevent.add("description").value = "Test event"
+ cal.vevent.add("created").value = datetime.datetime(
+ 2006, 1, 1, 10, tzinfo=dateutil.tz.tzical("test_files/timezones.ics").get("US/Pacific")
+ )
+ cal.vevent.add("uid").value = "Not very random UID"
+ cal.vevent.add("dtstamp").value = datetime.datetime(2017, 6, 26, 0, tzinfo=tzutc())
+
+ cal.vevent.add("attendee").value = "mailto:froelich@example.com"
+ cal.vevent.attendee.params["CN"] = ["Fröhlich"]
# Note we're normalizing line endings, because no one got time for that.
- self.assertEqual(
- cal.serialize().replace('\r\n', '\n'),
- test_cal.replace('\r\n', '\n')
- )
+ self.assertEqual(cal.serialize().replace("\r\n", "\n"), test_cal.replace("\r\n", "\n"))
def test_unicode(self):
"""
@@ -86,20 +86,14 @@ def test_unicode(self):
vevent2 = base.readOne(vevent.serialize())
self.assertEqual(str(vevent), str(vevent2))
- self.assertEqual(
- vevent.summary.value,
- 'The title こんにちはキティ'
- )
+ self.assertEqual(vevent.summary.value, "The title こんにちはキティ")
if sys.version_info[0] < 3:
- test_cal = test_cal.decode('utf-8')
+ test_cal = test_cal.decode("utf-8")
vevent = base.readOne(test_cal).vevent
vevent2 = base.readOne(vevent.serialize())
self.assertEqual(str(vevent), str(vevent2))
- self.assertEqual(
- vevent.summary.value,
- u'The title こんにちはキティ'
- )
+ self.assertEqual(vevent.summary.value, "The title こんにちはキティ")
def test_wrapping(self):
"""
@@ -108,36 +102,27 @@ def test_wrapping(self):
test_journal = get_test_file("journal.ics")
vobj = base.readOne(test_journal)
vjournal = base.readOne(vobj.serialize())
- self.assertTrue('Joe, Lisa, and Bob' in vjournal.description.value)
- self.assertTrue('Tuesday.\n2.' in vjournal.description.value)
+ self.assertTrue("Joe, Lisa, and Bob" in vjournal.description.value)
+ self.assertTrue("Tuesday.\n2." in vjournal.description.value)
def test_multiline(self):
"""
Multi-text serialization test
"""
- category = base.newFromBehavior('categories')
- category.value = ['Random category']
- self.assertEqual(
- category.serialize().strip(),
- "CATEGORIES:Random category"
- )
+ category = base.newFromBehavior("categories")
+ category.value = ["Random category"]
+ self.assertEqual(category.serialize().strip(), "CATEGORIES:Random category")
- category.value.append('Other category')
- self.assertEqual(
- category.serialize().strip(),
- "CATEGORIES:Random category,Other category"
- )
+ category.value.append("Other category")
+ self.assertEqual(category.serialize().strip(), "CATEGORIES:Random category,Other category")
def test_semicolon_separated(self):
"""
Semi-colon separated multi-text serialization test
"""
- request_status = base.newFromBehavior('request-status')
- request_status.value = ['5.1', 'Service unavailable']
- self.assertEqual(
- request_status.serialize().strip(),
- "REQUEST-STATUS:5.1;Service unavailable"
- )
+ request_status = base.newFromBehavior("request-status")
+ request_status.value = ["5.1", "Service unavailable"]
+ self.assertEqual(request_status.serialize().strip(), "REQUEST-STATUS:5.1;Service unavailable")
@staticmethod
def test_unicode_multiline():
@@ -145,12 +130,14 @@ def test_unicode_multiline():
Test multiline unicode characters
"""
cal = iCalendar()
- cal.add('method').value = 'REQUEST'
- cal.add('vevent')
- cal.vevent.add('created').value = datetime.datetime.now()
- cal.vevent.add('summary').value = 'Классное событие'
- cal.vevent.add('description').value = ('Классное событие Классное событие Классное событие Классное событие '
- 'Классное событие Классsdssdное событие')
+ cal.add("method").value = "REQUEST"
+ cal.add("vevent")
+ cal.vevent.add("created").value = datetime.datetime.now()
+ cal.vevent.add("summary").value = "Классное событие"
+ cal.vevent.add("description").value = (
+ "Классное событие Классное событие Классное событие Классное событие "
+ "Классное событие Классsdssdное событие"
+ )
# json tries to encode as utf-8 and it would break if some chars could not be encoded
json.dumps(cal.serialize())
@@ -185,7 +172,7 @@ def test_ical_to_hcal():
event2.add('dtend').value = event2.dtstart.value + datetime.timedelta(days = 6)
hcal = cal.serialize()
"""
- #self.assertEqual(
+ # self.assertEqual(
# str(hcal),
# """
#
@@ -203,13 +190,14 @@ def test_ical_to_hcal():
# The greatest thing ever!
#
# """
- #)
+ # )
class TestBehaviors(unittest.TestCase):
"""
Test Behaviors
"""
+
def test_general_behavior(self):
"""
Tests for behavior registry, getting and creating a behavior.
@@ -217,32 +205,70 @@ def test_general_behavior(self):
# Check expected behavior registry.
self.assertEqual(
sorted(behavior_registry.keys()),
- ['', 'ACTION', 'ADR', 'AVAILABLE', 'BUSYTYPE', 'CALSCALE',
- 'CATEGORIES', 'CLASS', 'COMMENT', 'COMPLETED', 'CONTACT',
- 'CREATED', 'DAYLIGHT', 'DESCRIPTION', 'DTEND', 'DTSTAMP',
- 'DTSTART', 'DUE', 'DURATION', 'EXDATE', 'EXRULE', 'FN', 'FREEBUSY',
- 'LABEL', 'LAST-MODIFIED', 'LOCATION', 'METHOD', 'N', 'ORG',
- 'PHOTO', 'PRODID', 'RDATE', 'RECURRENCE-ID', 'RELATED-TO',
- 'REQUEST-STATUS', 'RESOURCES', 'RRULE', 'STANDARD', 'STATUS',
- 'SUMMARY', 'TRANSP', 'TRIGGER', 'UID', 'VALARM', 'VAVAILABILITY',
- 'VCALENDAR', 'VCARD', 'VEVENT', 'VFREEBUSY', 'VJOURNAL',
- 'VTIMEZONE', 'VTODO']
+ [
+ "",
+ "ACTION",
+ "ADR",
+ "AVAILABLE",
+ "BUSYTYPE",
+ "CALSCALE",
+ "CATEGORIES",
+ "CLASS",
+ "COMMENT",
+ "COMPLETED",
+ "CONTACT",
+ "CREATED",
+ "DAYLIGHT",
+ "DESCRIPTION",
+ "DTEND",
+ "DTSTAMP",
+ "DTSTART",
+ "DUE",
+ "DURATION",
+ "EXDATE",
+ "EXRULE",
+ "FN",
+ "FREEBUSY",
+ "LABEL",
+ "LAST-MODIFIED",
+ "LOCATION",
+ "METHOD",
+ "N",
+ "ORG",
+ "PHOTO",
+ "PRODID",
+ "RDATE",
+ "RECURRENCE-ID",
+ "RELATED-TO",
+ "REQUEST-STATUS",
+ "RESOURCES",
+ "RRULE",
+ "STANDARD",
+ "STATUS",
+ "SUMMARY",
+ "TRANSP",
+ "TRIGGER",
+ "UID",
+ "VALARM",
+ "VAVAILABILITY",
+ "VCALENDAR",
+ "VCARD",
+ "VEVENT",
+ "VFREEBUSY",
+ "VJOURNAL",
+ "VTIMEZONE",
+ "VTODO",
+ ],
)
# test get_behavior
- behavior = base.getBehavior('VCALENDAR')
- self.assertEqual(
- str(behavior),
- ""
- )
+ behavior = base.getBehavior("VCALENDAR")
+ self.assertEqual(str(behavior), "")
self.assertTrue(behavior.isComponent)
- self.assertEqual(
- base.getBehavior("invalid_name"),
- None
- )
+ self.assertEqual(base.getBehavior("invalid_name"), None)
# test for ContentLine (not a component)
- non_component_behavior = base.getBehavior('RDATE')
+ non_component_behavior = base.getBehavior("RDATE")
self.assertFalse(non_component_behavior.isComponent)
def test_MultiDateBehavior(self):
@@ -252,62 +278,62 @@ def test_MultiDateBehavior(self):
parseRDate = MultiDateBehavior.transformToNative
self.assertEqual(
str(parseRDate(textLineToContentLine("RDATE;VALUE=DATE:19970304,19970504,19970704,19970904"))),
- ""
+ "",
)
self.assertEqual(
- str(parseRDate(textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H"))),
- ""
+ str(
+ parseRDate(
+ textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H")
+ )
+ ),
+ "",
)
def test_periodBehavior(self):
"""
Test PeriodBehavior
"""
- line = ContentLine('test', [], '', isNative=True)
+ line = ContentLine("test", [], "", isNative=True)
line.behavior = PeriodBehavior
line.value = [(datetime.datetime(2006, 2, 16, 10), two_hours)]
+ self.assertEqual(line.transformFromNative().value, "20060216T100000/PT2H")
self.assertEqual(
- line.transformFromNative().value,
- '20060216T100000/PT2H'
- )
- self.assertEqual(
- line.transformToNative().value,
- [(datetime.datetime(2006, 2, 16, 10, 0),
- datetime.timedelta(0, 7200))]
+ line.transformToNative().value, [(datetime.datetime(2006, 2, 16, 10, 0), datetime.timedelta(0, 7200))]
)
line.value.append((datetime.datetime(2006, 5, 16, 10), two_hours))
- self.assertEqual(
- line.serialize().strip(),
- 'TEST:20060216T100000/PT2H,20060516T100000/PT2H'
- )
+ self.assertEqual(line.serialize().strip(), "TEST:20060216T100000/PT2H,20060516T100000/PT2H")
class TestVTodo(unittest.TestCase):
"""
VTodo Tests
"""
+
def test_vtodo(self):
"""
Test VTodo
"""
vtodo = get_test_file("vtodo.ics")
obj = base.readOne(vtodo)
- obj.vtodo.add('completed')
- obj.vtodo.completed.value = datetime.datetime(2015,5,5,13,30)
- self.assertEqual(obj.vtodo.completed.serialize()[0:23],
- 'COMPLETED:20150505T1330')
+ obj.vtodo.add("completed")
+ obj.vtodo.completed.value = datetime.datetime(2015, 5, 5, 13, 30)
+ self.assertEqual(obj.vtodo.completed.serialize()[:23], "COMPLETED:20150505T1330")
obj = base.readOne(obj.serialize())
- self.assertEqual(obj.vtodo.completed.value,
- datetime.datetime(2015,5,5,13,30))
+ self.assertEqual(obj.vtodo.completed.value, datetime.datetime(2015, 5, 5, 13, 30))
class TestVobject(unittest.TestCase):
"""
VObject Tests
"""
+
max_diff = None
@classmethod
@@ -331,26 +357,33 @@ def test_parseLine(self):
"""
Test line parsing
"""
- self.assertEqual(parseLine("BLAH:"), ('BLAH', [], '', None))
+ self.assertEqual(parseLine("BLAH:"), ("BLAH", [], "", None))
self.assertEqual(
parseLine("RDATE:VALUE=DATE:19970304,19970504,19970704,19970904"),
- ('RDATE', [], 'VALUE=DATE:19970304,19970504,19970704,19970904', None)
+ ("RDATE", [], "VALUE=DATE:19970304,19970504,19970704,19970904", None),
)
self.assertEqual(
- parseLine('DESCRIPTION;ALTREP="http://www.wiz.org":The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA'),
- ('DESCRIPTION', [['ALTREP', 'http://www.wiz.org']], 'The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA', None)
+ parseLine(
+ 'DESCRIPTION;ALTREP="http://www.wiz.org":The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA'
+ ),
+ (
+ "DESCRIPTION",
+ [["ALTREP", "http://www.wiz.org"]],
+ "The Fall 98 Wild Wizards Conference - - Las Vegas, NV, USA",
+ None,
+ ),
)
self.assertEqual(
parseLine("EMAIL;PREF;INTERNET:john@nowhere.com"),
- ('EMAIL', [['PREF'], ['INTERNET']], 'john@nowhere.com', None)
+ ("EMAIL", [["PREF"], ["INTERNET"]], "john@nowhere.com", None),
)
self.assertEqual(
parseLine('EMAIL;TYPE="blah",hah;INTERNET="DIGI",DERIDOO:john@nowhere.com'),
- ('EMAIL', [['TYPE', 'blah', 'hah'], ['INTERNET', 'DIGI', 'DERIDOO']], 'john@nowhere.com', None)
+ ("EMAIL", [["TYPE", "blah", "hah"], ["INTERNET", "DIGI", "DERIDOO"]], "john@nowhere.com", None),
)
self.assertEqual(
- parseLine('item1.ADR;type=HOME;type=pref:;;Reeperbahn 116;Hamburg;;20359;'),
- ('ADR', [['type', 'HOME'], ['type', 'pref']], ';;Reeperbahn 116;Hamburg;;20359;', 'item1')
+ parseLine("item1.ADR;type=HOME;type=pref:;;Reeperbahn 116;Hamburg;;20359;"),
+ ("ADR", [["type", "HOME"], ["type", "pref"]], ";;Reeperbahn 116;Hamburg;;20359;", "item1"),
)
self.assertRaises(ParseError, parseLine, ":")
@@ -359,6 +392,7 @@ class TestGeneralFileParsing(unittest.TestCase):
"""
General tests for parsing ics files.
"""
+
def test_readOne(self):
"""
Test reading first component of ics
@@ -367,12 +401,10 @@ def test_readOne(self):
silly = base.readOne(cal)
self.assertEqual(
str(silly),
- ", , ]>"
- )
- self.assertEqual(
- str(silly.stuff),
- ""
+ ", , ]>",
)
+ self.assertEqual(str(silly.stuff), "")
def test_importing(self):
"""
@@ -380,35 +412,16 @@ def test_importing(self):
"""
cal = get_test_file("standard_test.ics")
c = base.readOne(cal, validate=True)
- self.assertEqual(
- str(c.vevent.valarm.trigger),
- ""
- )
+ self.assertEqual(str(c.vevent.valarm.trigger), "")
- self.assertEqual(
- str(c.vevent.dtstart.value),
- "2002-10-28 14:00:00-08:00"
- )
- self.assertTrue(
- isinstance(c.vevent.dtstart.value, datetime.datetime)
- )
- self.assertEqual(
- str(c.vevent.dtend.value),
- "2002-10-28 15:00:00-08:00"
- )
- self.assertTrue(
- isinstance(c.vevent.dtend.value, datetime.datetime)
- )
- self.assertEqual(
- c.vevent.dtstamp.value,
- datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc())
- )
+ self.assertEqual(str(c.vevent.dtstart.value), "2002-10-28 14:00:00-08:00")
+ self.assertTrue(isinstance(c.vevent.dtstart.value, datetime.datetime))
+ self.assertEqual(str(c.vevent.dtend.value), "2002-10-28 15:00:00-08:00")
+ self.assertTrue(isinstance(c.vevent.dtend.value, datetime.datetime))
+ self.assertEqual(c.vevent.dtstamp.value, datetime.datetime(2002, 10, 28, 1, 17, 6, tzinfo=tzutc()))
vevent = c.vevent.transformFromNative()
- self.assertEqual(
- str(vevent.rrule),
- ""
- )
+ self.assertEqual(str(vevent.rrule), "")
def test_bad_stream(self):
"""
@@ -425,23 +438,16 @@ def test_bad_line(self):
self.assertRaises(ParseError, base.readOne, cal)
newcal = base.readOne(cal, ignoreUnreadable=True)
- self.assertEqual(
- str(newcal.vevent.x_bad_underscore),
- ''
- )
+ self.assertEqual(str(newcal.vevent.x_bad_underscore), "")
def test_parseParams(self):
"""
Test parsing parameters
"""
- self.assertEqual(
- base.parseParams(';ALTREP="http://www.wiz.org"'),
- [['ALTREP', 'http://www.wiz.org']]
- )
+ self.assertEqual(base.parseParams(';ALTREP="http://www.wiz.org"'), [["ALTREP", "http://www.wiz.org"]])
self.assertEqual(
base.parseParams(';ALTREP="http://www.wiz.org;;",Blah,Foo;NEXT=Nope;BAR'),
- [['ALTREP', 'http://www.wiz.org;;', 'Blah', 'Foo'],
- ['NEXT', 'Nope'], ['BAR']]
+ [["ALTREP", "http://www.wiz.org;;", "Blah", "Foo"], ["NEXT", "Nope"], ["BAR"]],
)
@@ -449,6 +455,7 @@ class TestVcards(unittest.TestCase):
"""
Test VCards
"""
+
@classmethod
def setUpClass(cls):
"""
@@ -462,24 +469,17 @@ def test_vcard_creation(self):
"""
Test creating a vCard
"""
- vcard = base.newFromBehavior('vcard', '3.0')
- self.assertEqual(
- str(vcard),
- ""
- )
+ vcard = base.newFromBehavior("vcard", "3.0")
+ self.assertEqual(str(vcard), "")
def test_default_behavior(self):
"""
Default behavior test.
"""
card = self.card
+ self.assertEqual(base.getBehavior("note"), None)
self.assertEqual(
- base.getBehavior('note'),
- None
- )
- self.assertEqual(
- str(card.note.value),
- "The Mayor of the great city of Goerlitz in the great country of Germany.\nNext line."
+ str(card.note.value), "The Mayor of the great city of Goerlitz in the great country of Germany.\nNext line."
)
def test_with_groups(self):
@@ -487,25 +487,12 @@ def test_with_groups(self):
vCard groups test
"""
card = self.card
- self.assertEqual(
- str(card.group),
- 'home'
- )
- self.assertEqual(
- str(card.tel.group),
- 'home'
- )
-
- card.group = card.tel.group = 'new'
- self.assertEqual(
- str(card.tel.serialize().strip()),
- 'new.TEL;TYPE=fax,voice,msg:+49 3581 123456'
- )
- self.assertEqual(
- str(card.serialize().splitlines()[0]),
- 'new.BEGIN:VCARD'
- )
+ self.assertEqual(str(card.group), "home")
+ self.assertEqual(str(card.tel.group), "home")
+ card.group = card.tel.group = "new"
+ self.assertEqual(str(card.tel.serialize().strip()), "new.TEL;TYPE=fax,voice,msg:+49 3581 123456")
+ self.assertEqual(str(card.serialize().splitlines()[0]), "new.BEGIN:VCARD")
def test_vcard_3_parsing(self):
"""
@@ -514,14 +501,11 @@ def test_vcard_3_parsing(self):
test_file = get_test_file("simple_3_0_test.ics")
card = base.readOne(test_file)
# value not rendering correctly?
- #self.assertEqual(
+ # self.assertEqual(
# card.adr.value,
# ""
- #)
- self.assertEqual(
- card.org.value,
- ["University of Novosibirsk", "Department of Octopus Parthenogenesis"]
- )
+ # )
+ self.assertEqual(card.org.value, ["University of Novosibirsk", "Department of Octopus Parthenogenesis"])
for _ in range(3):
new_card = base.readOne(card.serialize())
@@ -533,65 +517,39 @@ class TestIcalendar(unittest.TestCase):
"""
Tests for icalendar.py
"""
+
max_diff = None
+
def test_parseDTStart(self):
"""
Should take a content line and return a datetime object.
"""
self.assertEqual(
- parseDtstart(textLineToContentLine("DTSTART:20060509T000000")),
- datetime.datetime(2006, 5, 9, 0, 0)
+ parseDtstart(textLineToContentLine("DTSTART:20060509T000000")), datetime.datetime(2006, 5, 9, 0, 0)
)
def test_regexes(self):
"""
Test regex patterns
"""
+ self.assertEqual(re.findall(base.patterns["name"], "12foo-bar:yay"), ["12foo-bar", "yay"])
+ self.assertEqual(re.findall(base.patterns["safe_char"], 'a;b"*,cd'), ["a", "b", "*", "c", "d"])
+ self.assertEqual(re.findall(base.patterns["qsafe_char"], 'a;b"*,cd'), ["a", ";", "b", "*", ",", "c", "d"])
self.assertEqual(
- re.findall(base.patterns['name'], '12foo-bar:yay'),
- ['12foo-bar', 'yay']
- )
- self.assertEqual(
- re.findall(base.patterns['safe_char'], 'a;b"*,cd'),
- ['a', 'b', '*', 'c', 'd']
- )
- self.assertEqual(
- re.findall(base.patterns['qsafe_char'], 'a;b"*,cd'),
- ['a', ';', 'b', '*', ',', 'c', 'd']
- )
- self.assertEqual(
- re.findall(base.patterns['param_value'],
- '"quoted";not-quoted;start"after-illegal-quote',
- re.VERBOSE),
- ['"quoted"', '', 'not-quoted', '', 'start', '',
- 'after-illegal-quote', '']
+ re.findall(base.patterns["param_value"], '"quoted";not-quoted;start"after-illegal-quote', re.VERBOSE),
+ ['"quoted"', "", "not-quoted", "", "start", "", "after-illegal-quote", ""],
)
match = base.line_re.match('TEST;ALTREP="http://www.wiz.org":value:;"')
- self.assertEqual(
- match.group('value'),
- 'value:;"'
- )
- self.assertEqual(
- match.group('name'),
- 'TEST'
- )
- self.assertEqual(
- match.group('params'),
- ';ALTREP="http://www.wiz.org"'
- )
+ self.assertEqual(match.group("value"), 'value:;"')
+ self.assertEqual(match.group("name"), "TEST")
+ self.assertEqual(match.group("params"), ';ALTREP="http://www.wiz.org"')
def test_stringToTextValues(self):
"""
Test string lists
"""
- self.assertEqual(
- stringToTextValues(''),
- ['']
- )
- self.assertEqual(
- stringToTextValues('abcd,efgh'),
- ['abcd', 'efgh']
- )
+ self.assertEqual(stringToTextValues(""), [""])
+ self.assertEqual(stringToTextValues("abcd,efgh"), ["abcd", "efgh"])
def test_stringToPeriod(self):
"""
@@ -599,27 +557,19 @@ def test_stringToPeriod(self):
"""
self.assertEqual(
stringToPeriod("19970101T180000Z/19970102T070000Z"),
- (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()),
- datetime.datetime(1997, 1, 2, 7, 0, tzinfo=tzutc()))
+ (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()), datetime.datetime(1997, 1, 2, 7, 0, tzinfo=tzutc())),
)
self.assertEqual(
stringToPeriod("19970101T180000Z/PT1H"),
- (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()),
- datetime.timedelta(0, 3600))
+ (datetime.datetime(1997, 1, 1, 18, 0, tzinfo=tzutc()), datetime.timedelta(0, 3600)),
)
def test_timedeltaToString(self):
"""
Test timedelta strings
"""
- self.assertEqual(
- timedeltaToString(two_hours),
- 'PT2H'
- )
- self.assertEqual(
- timedeltaToString(datetime.timedelta(minutes=20)),
- 'PT20M'
- )
+ self.assertEqual(timedeltaToString(two_hours), "PT2H")
+ self.assertEqual(timedeltaToString(datetime.timedelta(minutes=20)), "PT20M")
def test_delta_to_offset(self):
"""Test deltaToOffset() function."""
@@ -641,21 +591,14 @@ def test_vtimezone_creation(self):
Test timezones
"""
tzs = dateutil.tz.tzical("test_files/timezones.ics")
- pacific = icalendar.TimezoneComponent(tzs.get('US/Pacific'))
- self.assertEqual(
- str(pacific),
- ">"
- )
- santiago = icalendar.TimezoneComponent(tzs.get('Santiago'))
- self.assertEqual(
- str(santiago),
- ">"
- )
+ pacific = icalendar.TimezoneComponent(tzs.get("US/Pacific"))
+ self.assertEqual(str(pacific), ">")
+ santiago = icalendar.TimezoneComponent(tzs.get("Santiago"))
+ self.assertEqual(str(santiago), ">")
for year in range(2001, 2010):
for month in (2, 9):
- dt = datetime.datetime(year, month, 15,
- tzinfo=tzs.get('Santiago'))
- self.assertTrue(dt.replace(tzinfo=tzs.get('Santiago')), dt)
+ dt = datetime.datetime(year, month, 15, tzinfo=tzs.get("Santiago"))
+ self.assertTrue(dt.replace(tzinfo=tzs.get("Santiago")), dt)
@staticmethod
def test_timezone_serializing():
@@ -663,21 +606,19 @@ def test_timezone_serializing():
Serializing with timezones test
"""
tzs = dateutil.tz.tzical("test_files/timezones.ics")
- pacific = tzs.get('US/Pacific')
- cal = base.Component('VCALENDAR')
+ pacific = tzs.get("US/Pacific")
+ cal = base.Component("VCALENDAR")
cal.setBehavior(icalendar.VCalendar2_0)
- ev = cal.add('vevent')
- ev.add('dtstart').value = datetime.datetime(2005, 10, 12, 9,
- tzinfo=pacific)
+ ev = cal.add("vevent")
+ ev.add("dtstart").value = datetime.datetime(2005, 10, 12, 9, tzinfo=pacific)
evruleset = rruleset()
- evruleset.rrule(rrule(WEEKLY, interval=2, byweekday=[2,4],
- until=datetime.datetime(2005, 12, 15, 9)))
- evruleset.rrule(rrule(MONTHLY, bymonthday=[-1,-5]))
+ evruleset.rrule(rrule(WEEKLY, interval=2, byweekday=[2, 4], until=datetime.datetime(2005, 12, 15, 9)))
+ evruleset.rrule(rrule(MONTHLY, bymonthday=[-1, -5]))
evruleset.exdate(datetime.datetime(2005, 10, 14, 9, tzinfo=pacific))
ev.rruleset = evruleset
- ev.add('duration').value = datetime.timedelta(hours=1)
+ ev.add("duration").value = datetime.timedelta(hours=1)
- apple = tzs.get('America/Montreal')
+ apple = tzs.get("America/Montreal")
ev.dtstart.value = datetime.datetime(2005, 10, 12, 9, tzinfo=apple)
def test_pytz_timezone_serializing(self):
@@ -695,20 +636,16 @@ def unregister_tzid(tzid):
if icalendar.getTzid(tzid, False):
icalendar.registerTzid(tzid, None)
- unregister_tzid('US/Eastern')
- eastern = pytz.timezone('US/Eastern')
- cal = base.Component('VCALENDAR')
+ unregister_tzid("US/Eastern")
+ eastern = pytz.timezone("US/Eastern")
+ cal = base.Component("VCALENDAR")
cal.setBehavior(icalendar.VCalendar2_0)
- ev = cal.add('vevent')
- ev.add('dtstart').value = eastern.localize(
- datetime.datetime(2008, 10, 12, 9))
+ ev = cal.add("vevent")
+ ev.add("dtstart").value = eastern.localize(datetime.datetime(2008, 10, 12, 9))
serialized = cal.serialize()
expected_vtimezone = get_test_file("tz_us_eastern.ics")
- self.assertIn(
- expected_vtimezone.replace('\r\n', '\n'),
- serialized.replace('\r\n', '\n')
- )
+ self.assertIn(expected_vtimezone.replace("\r\n", "\n"), serialized.replace("\r\n", "\n"))
# Exhaustively test all zones (just looking for no errors)
for tzname in pytz.all_timezones:
@@ -722,18 +659,15 @@ def test_freeBusy(self):
"""
test_cal = get_test_file("freebusy.ics")
- vfb = base.newFromBehavior('VFREEBUSY')
- vfb.add('uid').value = 'test'
- vfb.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
- vfb.add('dtstart').value = datetime.datetime(2006, 2, 16, 1, tzinfo=utc)
- vfb.add('dtend').value = vfb.dtstart.value + two_hours
- vfb.add('freebusy').value = [(vfb.dtstart.value, two_hours / 2)]
- vfb.add('freebusy').value = [(vfb.dtstart.value, vfb.dtend.value)]
+ vfb = base.newFromBehavior("VFREEBUSY")
+ vfb.add("uid").value = "test"
+ vfb.add("dtstamp").value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
+ vfb.add("dtstart").value = datetime.datetime(2006, 2, 16, 1, tzinfo=utc)
+ vfb.add("dtend").value = vfb.dtstart.value + two_hours
+ vfb.add("freebusy").value = [(vfb.dtstart.value, two_hours / 2)]
+ vfb.add("freebusy").value = [(vfb.dtstart.value, vfb.dtend.value)]
- self.assertEqual(
- vfb.serialize().replace('\r\n', '\n'),
- test_cal.replace('\r\n', '\n')
- )
+ self.assertEqual(vfb.serialize().replace("\r\n", "\n"), test_cal.replace("\r\n", "\n"))
def test_availablity(self):
"""
@@ -741,26 +675,23 @@ def test_availablity(self):
"""
test_cal = get_test_file("availablity.ics")
- vcal = base.newFromBehavior('VAVAILABILITY')
- vcal.add('uid').value = 'test'
- vcal.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
- vcal.add('dtstart').value = datetime.datetime(2006, 2, 16, 0, tzinfo=utc)
- vcal.add('dtend').value = datetime.datetime(2006, 2, 17, 0, tzinfo=utc)
- vcal.add('busytype').value = "BUSY"
+ vcal = base.newFromBehavior("VAVAILABILITY")
+ vcal.add("uid").value = "test"
+ vcal.add("dtstamp").value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
+ vcal.add("dtstart").value = datetime.datetime(2006, 2, 16, 0, tzinfo=utc)
+ vcal.add("dtend").value = datetime.datetime(2006, 2, 17, 0, tzinfo=utc)
+ vcal.add("busytype").value = "BUSY"
- av = base.newFromBehavior('AVAILABLE')
- av.add('uid').value = 'test1'
- av.add('dtstamp').value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
- av.add('dtstart').value = datetime.datetime(2006, 2, 16, 9, tzinfo=utc)
- av.add('dtend').value = datetime.datetime(2006, 2, 16, 12, tzinfo=utc)
- av.add('summary').value = "Available in the morning"
+ av = base.newFromBehavior("AVAILABLE")
+ av.add("uid").value = "test1"
+ av.add("dtstamp").value = datetime.datetime(2006, 2, 15, 0, tzinfo=utc)
+ av.add("dtstart").value = datetime.datetime(2006, 2, 16, 9, tzinfo=utc)
+ av.add("dtend").value = datetime.datetime(2006, 2, 16, 12, tzinfo=utc)
+ av.add("summary").value = "Available in the morning"
vcal.add(av)
- self.assertEqual(
- vcal.serialize().replace('\r\n', '\n'),
- test_cal.replace('\r\n', '\n')
- )
+ self.assertEqual(vcal.serialize().replace("\r\n", "\n"), test_cal.replace("\r\n", "\n"))
def test_recurrence(self):
"""
@@ -770,24 +701,15 @@ def test_recurrence(self):
test_file = get_test_file("recurrence.ics")
cal = base.readOne(test_file)
dates = list(cal.vevent.getrruleset())
- self.assertEqual(
- dates[0],
- datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc())
- )
- self.assertEqual(
- dates[1],
- datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc())
- )
- self.assertEqual(
- dates[-1],
- datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc())
- )
+ self.assertEqual(dates[0], datetime.datetime(2006, 1, 26, 23, 0, tzinfo=tzutc()))
+ self.assertEqual(dates[1], datetime.datetime(2006, 2, 23, 23, 0, tzinfo=tzutc()))
+ self.assertEqual(dates[-1], datetime.datetime(2006, 12, 28, 23, 0, tzinfo=tzutc()))
def test_recurring_component(self):
"""
Test recurring events
"""
- vevent = RecurringComponent(name='VEVENT')
+ vevent = RecurringComponent(name="VEVENT")
# init
self.assertTrue(vevent.isNative)
@@ -797,27 +719,24 @@ def test_recurring_component(self):
self.assertEqual(vevent.rruleset, None)
# Now add start and rule for recurring event
- vevent.add('dtstart').value = datetime.datetime(2005, 1, 19, 9)
- vevent.add('rrule').value =u"FREQ=WEEKLY;COUNT=2;INTERVAL=2;BYDAY=TU,TH"
+ vevent.add("dtstart").value = datetime.datetime(2005, 1, 19, 9)
+ vevent.add("rrule").value = "FREQ=WEEKLY;COUNT=2;INTERVAL=2;BYDAY=TU,TH"
self.assertEqual(
- list(vevent.rruleset),
- [datetime.datetime(2005, 1, 20, 9, 0), datetime.datetime(2005, 2, 1, 9, 0)]
+ list(vevent.rruleset), [datetime.datetime(2005, 1, 20, 9, 0), datetime.datetime(2005, 2, 1, 9, 0)]
)
self.assertEqual(
list(vevent.getrruleset(addRDate=True)),
- [datetime.datetime(2005, 1, 19, 9, 0), datetime.datetime(2005, 1, 20, 9, 0)]
+ [datetime.datetime(2005, 1, 19, 9, 0), datetime.datetime(2005, 1, 20, 9, 0)],
)
# Also note that dateutil will expand all-day events (datetime.date values)
# to datetime.datetime value with time 0 and no timezone.
- vevent.dtstart.value = datetime.date(2005,3,18)
+ vevent.dtstart.value = datetime.date(2005, 3, 18)
self.assertEqual(
- list(vevent.rruleset),
- [datetime.datetime(2005, 3, 29, 0, 0), datetime.datetime(2005, 3, 31, 0, 0)]
+ list(vevent.rruleset), [datetime.datetime(2005, 3, 29, 0, 0), datetime.datetime(2005, 3, 31, 0, 0)]
)
self.assertEqual(
- list(vevent.getrruleset(True)),
- [datetime.datetime(2005, 3, 18, 0, 0), datetime.datetime(2005, 3, 29, 0, 0)]
+ list(vevent.getrruleset(True)), [datetime.datetime(2005, 3, 18, 0, 0), datetime.datetime(2005, 3, 29, 0, 0)]
)
def test_recurrence_without_tz(self):
@@ -848,6 +767,7 @@ class TestChangeTZ(unittest.TestCase):
"""
Tests for change_tz.change_tz
"""
+
class StubCal(object):
class StubEvent(object):
class Node(object):
@@ -871,29 +791,38 @@ def test_change_tz(self):
"""
# Setup - create a stub vevent list
- old_tz = dateutil.tz.gettz('UTC') # 0:00
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
+ old_tz = dateutil.tz.gettz("UTC") # 0:00
+ new_tz = dateutil.tz.gettz("America/Chicago") # -5:00
dates = [
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=old_tz)),
- (datetime.datetime(2010, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
- datetime.datetime(2011, 1, 2, 3, 0, 0, 0, tzinfo=old_tz))]
+ (
+ datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
+ datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=old_tz),
+ ),
+ (
+ datetime.datetime(2010, 12, 31, 23, 59, 59, 0, tzinfo=old_tz),
+ datetime.datetime(2011, 1, 2, 3, 0, 0, 0, tzinfo=old_tz),
+ ),
+ ]
cal = self.StubCal(dates)
# Exercise - change the timezone
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'))
+ change_tz(cal, new_tz, dateutil.tz.gettz("UTC"))
# Test - that the tzs were converted correctly
expected_new_dates = [
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
- datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz)),
- (datetime.datetime(2010, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
- datetime.datetime(2011, 1, 1, 21, 0, 0, 0, tzinfo=new_tz))]
-
- for vevent, expected_datepair in zip(cal.vevent_list,
- expected_new_dates):
+ (
+ datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
+ datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz),
+ ),
+ (
+ datetime.datetime(2010, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
+ datetime.datetime(2011, 1, 1, 21, 0, 0, 0, tzinfo=new_tz),
+ ),
+ ]
+
+ for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates):
self.assertEqual(vevent.dtstart.value, expected_datepair[0])
self.assertEqual(vevent.dtend.value, expected_datepair[1])
@@ -904,26 +833,26 @@ def test_change_tz_utc_only(self):
"""
# Setup - create a stub vevent list
- utc_tz = dateutil.tz.gettz('UTC') # 0:00
- non_utc_tz = dateutil.tz.gettz('America/Santiago') # -4:00
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
+ utc_tz = dateutil.tz.gettz("UTC") # 0:00
+ non_utc_tz = dateutil.tz.gettz("America/Santiago") # -4:00
+ new_tz = dateutil.tz.gettz("America/Chicago") # -5:00
dates = [
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=utc_tz),
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=non_utc_tz))]
+ (
+ datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=utc_tz),
+ datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=non_utc_tz),
+ )
+ ]
cal = self.StubCal(dates)
# Exercise - change the timezone passing utc_only=True
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'), utc_only=True)
+ change_tz(cal, new_tz, dateutil.tz.gettz("UTC"), utc_only=True)
# Test - that only the utc item has changed
- expected_new_dates = [
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
- dates[0][1])]
+ expected_new_dates = [(datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz), dates[0][1])]
- for vevent, expected_datepair in zip(cal.vevent_list,
- expected_new_dates):
+ for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates):
self.assertEqual(vevent.dtstart.value, expected_datepair[0])
self.assertEqual(vevent.dtend.value, expected_datepair[1])
@@ -935,24 +864,29 @@ def test_change_tz_default(self):
"""
# Setup - create a stub vevent list
- new_tz = dateutil.tz.gettz('America/Chicago') # -5:00
+ new_tz = dateutil.tz.gettz("America/Chicago") # -5:00
dates = [
- (datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=None),
- datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=None))]
+ (
+ datetime.datetime(1999, 12, 31, 23, 59, 59, 0, tzinfo=None),
+ datetime.datetime(2000, 1, 1, 0, 0, 0, 0, tzinfo=None),
+ )
+ ]
cal = self.StubCal(dates)
# Exercise - change the timezone
- change_tz(cal, new_tz, dateutil.tz.gettz('UTC'))
+ change_tz(cal, new_tz, dateutil.tz.gettz("UTC"))
# Test - that the tzs were converted correctly
expected_new_dates = [
- (datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
- datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz))]
+ (
+ datetime.datetime(1999, 12, 31, 17, 59, 59, 0, tzinfo=new_tz),
+ datetime.datetime(1999, 12, 31, 18, 0, 0, 0, tzinfo=new_tz),
+ )
+ ]
- for vevent, expected_datepair in zip(cal.vevent_list,
- expected_new_dates):
+ for vevent, expected_datepair in zip(cal.vevent_list, expected_new_dates):
self.assertEqual(vevent.dtstart.value, expected_datepair[0])
self.assertEqual(vevent.dtend.value, expected_datepair[1])
@@ -974,5 +908,5 @@ def test_radicale_0827(self):
return
-if __name__ == '__main__':
+if __name__ == "__main__":
unittest.main()
diff --git a/vobject/__init__.py b/vobject/__init__.py
index 416dea7..fe13acd 100644
--- a/vobject/__init__.py
+++ b/vobject/__init__.py
@@ -76,13 +76,13 @@
"""
-from .base import newFromBehavior, readOne, readComponents
from . import icalendar, vcard
+from .base import newFromBehavior, readComponents, readOne
def iCalendar():
- return newFromBehavior('vcalendar', '2.0')
+ return newFromBehavior("vcalendar", "2.0")
def vCard():
- return newFromBehavior('vcard', '3.0')
+ return newFromBehavior("vcard", "3.0")
diff --git a/vobject/base.py b/vobject/base.py
index b51f308..f46ea5f 100644
--- a/vobject/base.py
+++ b/vobject/base.py
@@ -2,13 +2,14 @@
from __future__ import print_function
-import copy
import codecs
+import copy
import logging
import re
-import six
import sys
+import six
+
# ------------------------------------ Python 2/3 compatibility challenges ----
# Python 3 no longer has a basestring type, so....
try:
@@ -19,24 +20,27 @@
# One more problem ... in python2 the str operator breaks on unicode
# objects containing non-ascii characters
try:
- unicode
+ unicode # noqa # todo: check this
def str_(s):
"""
Return byte string with correct encoding
"""
- if type(s) == unicode:
- return s.encode('utf-8')
+ if type(s) is unicode: # noqa
+ return s.encode("utf-8")
else:
return str(s)
+
except NameError:
+
def str_(s):
"""
Return string
"""
return s
-if not isinstance(b'', type('')):
+
+if not isinstance(b"", type("")):
unicode_type = str
else:
unicode_type = unicode # noqa
@@ -51,7 +55,7 @@ def to_unicode(value):
if isinstance(value, unicode_type):
return value
- return value.decode('utf-8')
+ return value.decode("utf-8")
def to_basestring(s):
@@ -63,26 +67,28 @@ def to_basestring(s):
if isinstance(s, bytes):
return s
- return s.encode('utf-8')
+ return s.encode("utf-8")
+
# ------------------------------------ Logging ---------------------------------
logger = logging.getLogger(__name__)
if not logging.getLogger().handlers:
handler = logging.StreamHandler()
- formatter = logging.Formatter('%(name)s %(levelname)s %(message)s')
+ formatter = logging.Formatter("%(name)s %(levelname)s %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.ERROR) # Log errors
DEBUG = False # Don't waste time on debug calls
# ----------------------------------- Constants --------------------------------
-CR = '\r'
-LF = '\n'
+CR = "\r"
+LF = "\n"
CRLF = CR + LF
-SPACE = ' '
-TAB = '\t'
+SPACE = " "
+TAB = "\t"
SPACEORTAB = SPACE + TAB
+
# --------------------------------- Main classes -------------------------------
@@ -103,6 +109,7 @@ class VBase(object):
Current spec: 4.0 (http://tools.ietf.org/html/rfc6350)
"""
+
def __init__(self, group=None, *args, **kwds):
super(VBase, self).__init__(*args, **kwds)
self.group = group
@@ -185,7 +192,7 @@ def transformToNative(self):
return self.behavior.transformToNative(self)
except Exception as e:
# wrap errors in transformation in a ParseError
- lineNumber = getattr(self, 'lineNumber', None)
+ lineNumber = getattr(self, "lineNumber", None)
if isinstance(e, ParseError):
if lineNumber is not None:
@@ -215,7 +222,7 @@ def transformFromNative(self):
return self.behavior.transformFromNative(self)
except Exception as e:
# wrap errors in transformation in a NativeError
- lineNumber = getattr(self, 'lineNumber', None)
+ lineNumber = getattr(self, "lineNumber", None)
if isinstance(e, NativeError):
if lineNumber is not None:
e.lineNumber = lineNumber
@@ -267,7 +274,7 @@ def toVName(name, stripNum=0, upper=False):
name = name.upper()
if stripNum != 0:
name = name[:-stripNum]
- return name.replace('_', '-')
+ return name.replace("_", "-")
class ContentLine(VBase):
@@ -296,8 +303,8 @@ class ContentLine(VBase):
@ivar lineNumber:
An optional line number associated with the contentline.
"""
- def __init__(self, name, params, value, group=None, encoded=False,
- isNative=False, lineNumber=None, *args, **kwds):
+
+ def __init__(self, name, params, value, group=None, encoded=False, isNative=False, lineNumber=None, *args, **kwds):
"""
Take output from parseLine, convert params list to dictionary.
@@ -323,27 +330,31 @@ def updateTable(x):
list(map(updateTable, params))
qp = False
- if 'ENCODING' in self.params:
- if 'QUOTED-PRINTABLE' in self.params['ENCODING']:
+ if "ENCODING" in self.params:
+ if "QUOTED-PRINTABLE" in self.params["ENCODING"]:
qp = True
- self.params['ENCODING'].remove('QUOTED-PRINTABLE')
- if len(self.params['ENCODING']) == 0:
- del self.params['ENCODING']
- if 'QUOTED-PRINTABLE' in self.singletonparams:
+ self.params["ENCODING"].remove("QUOTED-PRINTABLE")
+ if len(self.params["ENCODING"]) == 0:
+ del self.params["ENCODING"]
+ if "QUOTED-PRINTABLE" in self.singletonparams:
qp = True
- self.singletonparams.remove('QUOTED-PRINTABLE')
+ self.singletonparams.remove("QUOTED-PRINTABLE")
if qp:
- if 'ENCODING' in self.params:
- self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode(self.params['ENCODING'])
+ if "ENCODING" in self.params:
+ self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode(
+ self.params["ENCODING"]
+ )
else:
- if 'CHARSET' in self.params:
- self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode(self.params['CHARSET'][0])
+ if "CHARSET" in self.params:
+ self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode(
+ self.params["CHARSET"][0]
+ )
else:
- self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode('utf-8')
+ self.value = codecs.decode(self.value.encode("utf-8"), "quoted-printable").decode("utf-8")
@classmethod
- def duplicate(clz, copyit):
- newcopy = clz('', {}, '')
+ def duplicate(cls, copyit):
+ newcopy = cls("", {}, "")
newcopy.copy(copyit)
return newcopy
@@ -372,9 +383,9 @@ def __getattr__(self, name):
which are legal in IANA tokens.
"""
try:
- if name.endswith('_param'):
+ if name.endswith("_param"):
return self.params[toVName(name, 6, True)][0]
- elif name.endswith('_paramlist'):
+ elif name.endswith("_paramlist"):
return self.params[toVName(name, 10, True)]
else:
raise AttributeError(name)
@@ -388,13 +399,13 @@ def __setattr__(self, name, value):
Underscores, legal in python variable names, are converted to dashes,
which are legal in IANA tokens.
"""
- if name.endswith('_param'):
- if type(value) == list:
+ if name.endswith("_param"):
+ if type(value) is list:
self.params[toVName(name, 6, True)] = value
else:
self.params[toVName(name, 6, True)] = [value]
- elif name.endswith('_paramlist'):
- if type(value) == list:
+ elif name.endswith("_paramlist"):
+ if type(value) is list:
self.params[toVName(name, 10, True)] = value
else:
raise VObjectError("Parameter list set to a non-list")
@@ -407,9 +418,9 @@ def __setattr__(self, name, value):
def __delattr__(self, name):
try:
- if name.endswith('_param'):
+ if name.endswith("_param"):
del self.params[toVName(name, 6, True)]
- elif name.endswith('_paramlist'):
+ elif name.endswith("_paramlist"):
del self.params[toVName(name, 10, True)]
else:
object.__delattr__(self, name)
@@ -429,22 +440,22 @@ def valueRepr(self):
def __str__(self):
try:
return "<{0}{1}{2}>".format(self.name, self.params, self.valueRepr())
- except UnicodeEncodeError as e:
- return "<{0}{1}{2}>".format(self.name, self.params, self.valueRepr().encode('utf-8'))
+ except UnicodeEncodeError:
+ return "<{0}{1}{2}>".format(self.name, self.params, self.valueRepr().encode("utf-8"))
def __repr__(self):
return self.__str__()
def __unicode__(self):
- return u"<{0}{1}{2}>".format(self.name, self.params, self.valueRepr())
+ return "<{0}{1}{2}>".format(self.name, self.params, self.valueRepr())
def prettyPrint(self, level=0, tabwidth=3):
- pre = ' ' * level * tabwidth
+ pre = " " * level * tabwidth
print(pre, self.name + ":", self.valueRepr())
if self.params:
- print(pre, "params for ", self.name + ':')
+ print(pre, "params for ", self.name + ":")
for k in self.params.keys():
- print(pre + ' ' * tabwidth, k, self.params[k])
+ print(pre + " " * tabwidth, k, self.params[k])
class Component(VBase):
@@ -465,6 +476,7 @@ class Component(VBase):
A boolean flag determining whether BEGIN: and END: lines should
be serialized.
"""
+
def __init__(self, name=None, *args, **kwds):
super(Component, self).__init__(*args, **kwds)
self.contents = {}
@@ -472,7 +484,7 @@ def __init__(self, name=None, *args, **kwds):
self.name = name.upper()
self.useBegin = True
else:
- self.name = ''
+ self.name = ""
self.useBegin = False
self.autoBehavior()
@@ -507,8 +519,7 @@ def setProfile(self, name):
if self.name or self.useBegin:
if self.name == name:
return
- raise VObjectError("This component already has a PROFILE or "
- "uses BEGIN.")
+ raise VObjectError("This component already has a PROFILE or " "uses BEGIN.")
self.name = name.upper()
def __getattr__(self, name):
@@ -520,17 +531,17 @@ def __getattr__(self, name):
"""
# if the object is being re-created by pickle, self.contents may not
# be set, don't get into an infinite loop over the issue
- if name == 'contents':
+ if name == "contents":
return object.__getattribute__(self, name)
try:
- if name.endswith('_list'):
+ if name.endswith("_list"):
return self.contents[toVName(name, 5)]
else:
return self.contents[toVName(name)][0]
except KeyError:
raise AttributeError(name)
- normal_attributes = ['contents', 'name', 'behavior', 'parentBehavior', 'group']
+ normal_attributes = ["contents", "name", "behavior", "parentBehavior", "group"]
def __setattr__(self, name, value):
"""
@@ -540,11 +551,11 @@ def __setattr__(self, name, value):
which are legal in IANA tokens.
"""
if name not in self.normal_attributes and name.lower() == name:
- if type(value) == list:
- if name.endswith('_list'):
+ if type(value) is list:
+ if name.endswith("_list"):
name = name[:-5]
self.contents[toVName(name)] = value
- elif name.endswith('_list'):
+ elif name.endswith("_list"):
raise VObjectError("Component list set to a non-list")
else:
self.contents[toVName(name)] = [value]
@@ -558,7 +569,7 @@ def __setattr__(self, name, value):
def __delattr__(self, name):
try:
if name not in self.normal_attributes and name.lower() == name:
- if name.endswith('_list'):
+ if name.endswith("_list"):
del self.contents[toVName(name, 5)]
else:
del self.contents[toVName(name)]
@@ -599,12 +610,12 @@ def add(self, objOrName, group=None):
if behavior.isComponent:
obj = Component(name)
else:
- obj = ContentLine(name, [], '', group)
+ obj = ContentLine(name, [], "", group)
obj.parentBehavior = self.behavior
obj.behavior = behavior
obj = obj.transformToNative()
except (KeyError, AttributeError):
- obj = ContentLine(objOrName, [], '', group)
+ obj = ContentLine(objOrName, [], "", group)
if obj.behavior is None and self.behavior is not None:
if isinstance(obj, ContentLine):
obj.behavior = self.behavior.defaultBehavior
@@ -689,13 +700,13 @@ def __str__(self):
if self.name:
return "<{0}| {1}>".format(self.name, self.getSortedChildren())
else:
- return u'<*unnamed*| {0}>'.format(self.getSortedChildren())
+ return "<*unnamed*| {0}>".format(self.getSortedChildren())
def __repr__(self):
return self.__str__()
def prettyPrint(self, level=0, tabwidth=3):
- pre = ' ' * level * tabwidth
+ pre = " " * level * tabwidth
print(pre, self.name)
if isinstance(self, Component):
for line in self.getChildren():
@@ -709,7 +720,7 @@ def __init__(self, msg, lineNumber=None):
self.lineNumber = lineNumber
def __str__(self):
- if hasattr(self, 'lineNumber'):
+ if hasattr(self, "lineNumber"):
return "At line {0!s}: {1!s}".format(self.lineNumber, self.msg)
else:
return repr(self.msg)
@@ -729,38 +740,42 @@ class NativeError(VObjectError):
# --------- Parsing functions and parseLine regular expressions ----------------
-patterns = {}
# Note that underscore is not legal for names, it's included because
# Lotus Notes uses it
-patterns['name'] = '[a-zA-Z0-9_-]+' # 1*(ALPHA / DIGIT / "-")
-patterns['safe_char'] = '[^";:,]'
-patterns['qsafe_char'] = '[^"]'
-
+patterns = {"name": "[a-zA-Z0-9_-]+", "safe_char": '[^";:,]', "qsafe_char": '[^"]'}
# the combined Python string replacement and regex syntax is a little confusing;
# remember that {foobar} is replaced with patterns['foobar'], so for instance
# param_value is any number of safe_chars or any number of qsaf_chars surrounded
# by double quotes.
-patterns['param_value'] = ' "{qsafe_char!s} * " | {safe_char!s} * '.format(**patterns)
-
+patterns["param_value"] = ' "{qsafe_char!s} * " | {safe_char!s} * '.format(**patterns)
# get a tuple of two elements, one will be empty, the other will have the value
-patterns['param_value_grouped'] = """
+patterns["param_value_grouped"] = (
+ """
" ( {qsafe_char!s} * )" | ( {safe_char!s} + )
-""".format(**patterns)
+""".format(
+ **patterns
+ )
+)
# get a parameter and its values, without any saved groups
-patterns['param'] = r"""
+patterns["param"] = (
+ r"""
; (?: {name!s} ) # parameter name
(?:
(?: = (?: {param_value!s} ) )? # 0 or more parameter values, multiple
(?: , (?: {param_value!s} ) )* # parameters are comma separated
)*
-""".format(**patterns)
+""".format(
+ **patterns
+ )
+)
# get a parameter, saving groups for name and value (value still needs parsing)
-patterns['params_grouped'] = r"""
+patterns["params_grouped"] = (
+ r"""
; ( {name!s} )
(?: =
@@ -769,21 +784,28 @@ class NativeError(VObjectError):
(?: , (?: {param_value!s} ) )* # parameters are comma separated
)
)?
-""".format(**patterns)
+""".format(
+ **patterns
+ )
+)
# get a full content line, break it up into group, name, parameters, and value
-patterns['line'] = r"""
+patterns["line"] = (
+ r"""
^ ((?P {name!s})\.)?(?P {name!s}) # name group
(?P ;?(?: {param!s} )* ) # params group (may be empty)
: (?P .* )$ # value group
-""".format(**patterns)
+""".format(
+ **patterns
+ )
+)
' "%(qsafe_char)s*" | %(safe_char)s* ' # what is this line?? - never assigned?
-param_values_re = re.compile(patterns['param_value_grouped'], re.VERBOSE)
-params_re = re.compile(patterns['params_grouped'], re.VERBOSE)
-line_re = re.compile(patterns['line'], re.DOTALL | re.VERBOSE)
-begin_re = re.compile('BEGIN', re.IGNORECASE)
+param_values_re = re.compile(patterns["param_value_grouped"], re.VERBOSE)
+params_re = re.compile(patterns["params_grouped"], re.VERBOSE)
+line_re = re.compile(patterns["line"], re.DOTALL | re.VERBOSE)
+begin_re = re.compile("BEGIN", re.IGNORECASE)
def parseParams(string):
@@ -796,7 +818,7 @@ def parseParams(string):
paramList = [tup[0]] # tup looks like (name, valuesString)
for pair in param_values_re.findall(tup[1]):
# pair looks like ('', value) or (value, '')
- if pair[0] != '':
+ if pair[0] != "":
paramList.append(pair[0])
else:
paramList.append(pair[1])
@@ -812,25 +834,33 @@ def parseLine(line, lineNumber=None):
if match is None:
raise ParseError("Failed to parse line: {0!s}".format(line), lineNumber)
# Underscores are replaced with dash to work around Lotus Notes
- return (match.group('name').replace('_', '-'),
- parseParams(match.group('params')),
- match.group('value'), match.group('group'))
+ return (
+ match.group("name").replace("_", "-"),
+ parseParams(match.group("params")),
+ match.group("value"),
+ match.group("group"),
+ )
+
# logical line regular expressions
-patterns['lineend'] = r'(?:\r\n|\r|\n|$)'
-patterns['wrap'] = r'{lineend!s} [\t ]'.format(**patterns)
-patterns['logicallines'] = r"""
+patterns["lineend"] = r"(?:\r\n|\r|\n|$)"
+patterns["wrap"] = r"{lineend!s} [\t ]".format(**patterns)
+patterns["logicallines"] = (
+ r"""
(
(?: [^\r\n] | {wrap!s} )*
{lineend!s}
)
-""".format(**patterns)
+""".format(
+ **patterns
+ )
+)
-patterns['wraporend'] = r'({wrap!s} | {lineend!s} )'.format(**patterns)
+patterns["wraporend"] = r"({wrap!s} | {lineend!s} )".format(**patterns)
-wrap_re = re.compile(patterns['wraporend'], re.VERBOSE)
-logical_lines_re = re.compile(patterns['logicallines'], re.VERBOSE)
+wrap_re = re.compile(patterns["wraporend"], re.VERBOSE)
+logical_lines_re = re.compile(patterns["logicallines"], re.VERBOSE)
testLines = """
Line 0 text
@@ -870,8 +900,8 @@ def getLogicalLines(fp, allowQP=True):
lineNumber = 1
for match in logical_lines_re.finditer(val):
- line, n = wrap_re.subn('', match.group())
- if line != '':
+ line, n = wrap_re.subn("", match.group())
+ if line != "":
yield line, lineNumber
lineNumber += n
@@ -883,12 +913,12 @@ def getLogicalLines(fp, allowQP=True):
lineStartNumber = 0
while True:
line = fp.readline()
- if line == '':
+ if line == "":
break
else:
line = line.rstrip(CRLF)
lineNumber += 1
- if line.rstrip() == '':
+ if line.rstrip() == "":
if logicalLine.tell() > 0:
yield logicalLine.getvalue(), lineStartNumber
lineStartNumber = lineNumber
@@ -897,7 +927,7 @@ def getLogicalLines(fp, allowQP=True):
continue
if quotedPrintable and allowQP:
- logicalLine.write('\n')
+ logicalLine.write("\n")
logicalLine.write(line)
quotedPrintable = False
elif line[0] in SPACEORTAB:
@@ -914,7 +944,7 @@ def getLogicalLines(fp, allowQP=True):
# vCard 2.1 allows parameters to be encoded without a parameter name
# False positives are unlikely, but possible.
val = logicalLine.getvalue()
- if val[-1] == '=' and val.lower().find('quoted-printable') >= 0:
+ if val[-1] == "=" and val.lower().find("quoted-printable") >= 0:
quotedPrintable = True
if logicalLine.tell() > 0:
@@ -922,8 +952,7 @@ def getLogicalLines(fp, allowQP=True):
def textLineToContentLine(text, n=None):
- return ContentLine(*parseLine(text, n), **{'encoded': True,
- 'lineNumber': n})
+ return ContentLine(*parseLine(text, n), **{"encoded": True, "lineNumber": n})
def dquoteEscape(param):
@@ -932,7 +961,7 @@ def dquoteEscape(param):
"""
if param.find('"') >= 0:
raise VObjectError("Double quotes aren't allowed in parameter values.")
- for char in ',;:':
+ for char in ",;:":
if param.find(char) >= 0:
return '"' + param + '"'
return param
@@ -948,7 +977,7 @@ def foldOneLine(outbuf, input, lineLength=75):
if len(input) < lineLength:
# Optimize for unfolded line case
try:
- outbuf.write(bytes(input, 'UTF-8'))
+ outbuf.write(bytes(input, "UTF-8"))
except Exception:
# fall back on py2 syntax
outbuf.write(input)
@@ -965,7 +994,7 @@ def foldOneLine(outbuf, input, lineLength=75):
size = len(to_basestring(s)) # calculate it's size in bytes
if counter + size > lineLength:
try:
- outbuf.write(bytes("\r\n ", 'UTF-8'))
+ outbuf.write(bytes("\r\n ", "UTF-8"))
except Exception:
# fall back on py2 syntax
outbuf.write("\r\n ")
@@ -976,13 +1005,13 @@ def foldOneLine(outbuf, input, lineLength=75):
outbuf.write(to_unicode(s))
else:
# fall back on py2 syntax
- outbuf.write(s.encode('utf-8'))
+ outbuf.write(s.encode("utf-8"))
written += size
counter += size
start += 1
try:
- outbuf.write(bytes("\r\n", 'UTF-8'))
+ outbuf.write(bytes("\r\n", "UTF-8"))
except Exception:
# fall back on py2 syntax
outbuf.write("\r\n")
@@ -996,18 +1025,16 @@ def defaultSerialize(obj, buf, lineLength):
if isinstance(obj, Component):
if obj.group is None:
- groupString = ''
+ groupString = ""
else:
- groupString = obj.group + '.'
+ groupString = obj.group + "."
if obj.useBegin:
- foldOneLine(outbuf, "{0}BEGIN:{1}".format(groupString, obj.name),
- lineLength)
+ foldOneLine(outbuf, "{0}BEGIN:{1}".format(groupString, obj.name), lineLength)
for child in obj.getSortedChildren():
# validate is recursive, we only need to validate once
child.serialize(outbuf, lineLength, validate=False)
if obj.useBegin:
- foldOneLine(outbuf, "{0}END:{1}".format(groupString, obj.name),
- lineLength)
+ foldOneLine(outbuf, "{0}END:{1}".format(groupString, obj.name), lineLength)
elif isinstance(obj, ContentLine):
startedEncoded = obj.encoded
@@ -1017,19 +1044,19 @@ def defaultSerialize(obj, buf, lineLength):
s = six.StringIO()
if obj.group is not None:
- s.write(obj.group + '.')
+ s.write(obj.group + ".")
s.write(str_(obj.name.upper()))
keys = sorted(obj.params.keys())
for key in keys:
- paramstr = ','.join(dquoteEscape(p) for p in obj.params[key])
+ paramstr = ",".join(dquoteEscape(p) for p in obj.params[key])
try:
s.write(";{0}={1}".format(key, paramstr))
except (UnicodeDecodeError, UnicodeEncodeError):
- s.write(";{0}={1}".format(key, paramstr.encode('utf-8')))
+ s.write(";{0}={1}".format(key, paramstr.encode("utf-8")))
try:
s.write(":{0}".format(obj.value))
except (UnicodeDecodeError, UnicodeEncodeError):
- s.write(":{0}".format(obj.value.encode('utf-8')))
+ s.write(":{0}".format(obj.value.encode("utf-8")))
if obj.behavior and not startedEncoded:
obj.behavior.decode(obj)
foldOneLine(outbuf, s.getvalue(), lineLength)
@@ -1072,8 +1099,7 @@ def pop(self):
return self.stack.pop()
-def readComponents(streamOrString, validate=False, transform=True,
- ignoreUnreadable=False, allowQP=False):
+def readComponents(streamOrString, validate=False, transform=True, ignoreUnreadable=False, allowQP=False):
"""
Generate one Component at a time from a stream.
"""
@@ -1095,7 +1121,7 @@ def readComponents(streamOrString, validate=False, transform=True,
msg = "Skipped line {lineNumber}, message: {msg}"
else:
msg = "Skipped a line, message: {msg}"
- logger.error(msg.format(**{'lineNumber': e.lineNumber, 'msg': str(e)}))
+ logger.error(msg.format(**{"lineNumber": e.lineNumber, "msg": str(e)}))
continue
else:
vline = textLineToContentLine(line, n)
@@ -1138,8 +1164,7 @@ def readComponents(streamOrString, validate=False, transform=True,
if stack.topName() is None:
logger.warning("Top level component was never named")
elif stack.top().useBegin:
- raise ParseError("Component {0!s} was never closed".format(
- (stack.topName())), n)
+ raise ParseError("Component {0!s} was never closed".format((stack.topName())), n)
yield stack.pop()
except ParseError as e:
@@ -1147,13 +1172,11 @@ def readComponents(streamOrString, validate=False, transform=True,
raise
-def readOne(stream, validate=False, transform=True, ignoreUnreadable=False,
- allowQP=False):
+def readOne(stream, validate=False, transform=True, ignoreUnreadable=False, allowQP=False):
"""
Return the first component from stream.
"""
- return next(readComponents(stream, validate, transform, ignoreUnreadable,
- allowQP))
+ return next(readComponents(stream, validate, transform, ignoreUnreadable, allowQP))
# --------------------------- version registry ---------------------------------
@@ -1208,7 +1231,7 @@ def newFromBehavior(name, id=None):
if behavior.isComponent:
obj = Component(name)
else:
- obj = ContentLine(name, [], '')
+ obj = ContentLine(name, [], "")
obj.behavior = behavior
obj.isNative = False
return obj
diff --git a/vobject/behavior.py b/vobject/behavior.py
index a1a292e..53739ff 100644
--- a/vobject/behavior.py
+++ b/vobject/behavior.py
@@ -1,7 +1,7 @@
from . import base
-#------------------------ Abstract class for behavior --------------------------
+# ------------------------ Abstract class for behavior --------------------------
class Behavior(object):
"""
Behavior (validation, encoding, and transformations) for vobjects.
@@ -43,9 +43,10 @@ class Behavior(object):
@cvar allowGroup:
Whether or not vCard style group prefixes are allowed.
"""
- name = ''
- description = ''
- versionString = ''
+
+ name = ""
+ description = ""
+ versionString = ""
knownChildren = {}
quotedPrintable = False
defaultBehavior = None
@@ -90,7 +91,7 @@ def validate(cls, obj, raiseException=False, complainUnrecognized=False):
if count.get(key, 0) < val[0]:
if raiseException:
m = "{0} components must contain at least {1} {2}"
- raise base.ValidateError(m .format(cls.name, val[0], key))
+ raise base.ValidateError(m.format(cls.name, val[0], key))
return False
if val[1] and count.get(key, 0) > val[1]:
if raiseException:
@@ -105,6 +106,8 @@ def validate(cls, obj, raiseException=False, complainUnrecognized=False):
@classmethod
def lineValidate(cls, line, raiseException, complainUnrecognized):
"""Examine a line's parameters and values, return True if valid."""
+ # todo: remove used param line, raiseException, complainUnrecognized
+ print(line, raiseException, complainUnrecognized)
return True
@classmethod
diff --git a/vobject/change_tz.py b/vobject/change_tz.py
index 9a3bb21..6f8c3a7 100644
--- a/vobject/change_tz.py
+++ b/vobject/change_tz.py
@@ -1,11 +1,12 @@
"""Translate an ics file's events to a different timezone."""
from optparse import OptionParser
-from vobject import icalendar, base
+
+from vobject import base, icalendar
try:
import PyICU
-except:
+except ImportError:
PyICU = None
from datetime import datetime
@@ -25,14 +26,13 @@ def change_tz(cal, new_timezone, default, utc_only=False, utc_tz=icalendar.utc):
utc_only=True
"""
- for vevent in getattr(cal, 'vevent_list', []):
- start = getattr(vevent, 'dtstart', None)
- end = getattr(vevent, 'dtend', None)
+ for vevent in getattr(cal, "vevent_list", []):
+ start = getattr(vevent, "dtstart", None)
+ end = getattr(vevent, "dtend", None)
for node in (start, end):
if node:
dt = node.value
- if (isinstance(dt, datetime) and
- (not utc_only or dt.tzinfo == utc_tz)):
+ if isinstance(dt, datetime) and (not utc_only or dt.tzinfo == utc_tz):
if dt.tzinfo is None:
dt = dt.replace(tzinfo=default)
node.value = dt.astimezone(new_timezone)
@@ -61,10 +61,10 @@ def main():
cal = base.readOne(open(ics_file))
change_tz(cal, timezone, PyICU.ICUtzinfo.default, utc_only)
- out_name = ics_file + '.converted'
+ out_name = ics_file + ".converted"
print("... Writing {0!s}".format(out_name))
- with open(out_name, 'wb') as out:
+ with open(out_name, "wb") as out:
cal.serialize(out)
print("Done")
@@ -80,10 +80,10 @@ def get_options():
parser = OptionParser(usage=usage, version=version)
parser.set_description("change_tz will convert the timezones in an ics file. ")
- parser.add_option("-u", "--only-utc", dest="utc", action="store_true",
- default=False, help="Only change UTC events.")
- parser.add_option("-l", "--list", dest="list", action="store_true",
- default=False, help="List available timezones")
+ parser.add_option(
+ "-u", "--only-utc", dest="utc", action="store_true", default=False, help="Only change UTC events."
+ )
+ parser.add_option("-l", "--list", dest="list", action="store_true", default=False, help="List available timezones")
(cmdline_options, args) = parser.parse_args()
if not args and not cmdline_options.list:
@@ -94,6 +94,7 @@ def get_options():
return cmdline_options, args
+
if __name__ == "__main__":
try:
main()
diff --git a/vobject/hcalendar.py b/vobject/hcalendar.py
index e680d90..88abc11 100644
--- a/vobject/hcalendar.py
+++ b/vobject/hcalendar.py
@@ -12,7 +12,7 @@
DTSTART:20051005
DTEND:20051008
SUMMARY:Web 2.0 Conference
-LOCATION:Argent Hotel\, San Francisco\, CA
+LOCATION:Argent Hotel, San Francisco, CA
END:VEVENT
END:VCALENDAR
@@ -28,16 +28,16 @@
"""
-import six
-
from datetime import date, datetime, timedelta
+import six
+
from .base import CRLF, registerBehavior
from .icalendar import VCalendar2_0
class HCalendar(VCalendar2_0):
- name = 'HCALENDAR'
+ name = "HCALENDAR"
@classmethod
def serialize(cls, obj, buf=None, lineLength=None, validate=True):
@@ -50,7 +50,7 @@ def serialize(cls, obj, buf=None, lineLength=None, validate=True):
tabwidth = 3
def indent():
- return ' ' * level * tabwidth
+ return " " * level * tabwidth
def out(s):
outbuf.write(indent())
@@ -72,24 +72,26 @@ def out(s):
# SUMMARY
summary = event.getChildValue("summary")
if summary:
- out('' + summary + ':' + CRLF)
+ out('' + summary + ":" + CRLF)
# DTSTART
dtstart = event.getChildValue("dtstart")
if dtstart:
- if type(dtstart) == date:
+ if type(dtstart) is date:
timeformat = "%A, %B %e"
machine = "%Y%m%d"
- elif type(dtstart) == datetime:
+ elif type(dtstart) is datetime:
timeformat = "%A, %B %e, %H:%M"
machine = "%Y%m%dT%H%M%S%z"
- #TODO: Handle non-datetime formats?
- #TODO: Spec says we should handle when dtstart isn't included
+ # TODO: Handle non-datetime formats?
+ # TODO: Spec says we should handle when dtstart isn't included
- out('{1!s}\r\n'
- .format(dtstart.strftime(machine),
- dtstart.strftime(timeformat)))
+ out(
+ '{1!s}\r\n'.format(
+ dtstart.strftime(machine), dtstart.strftime(timeformat)
+ )
+ )
# DTEND
dtend = event.getChildValue("dtend")
@@ -97,34 +99,37 @@ def out(s):
duration = event.getChildValue("duration")
if duration:
dtend = duration + dtstart
- # TODO: If lacking dtend & duration?
+ # TODO: If lacking dtend & duration?
if dtend:
human = dtend
# TODO: Human readable part could be smarter, excluding repeated data
- if type(dtend) == date:
+ if type(dtend) is date:
human = dtend - timedelta(days=1)
- out('- {1!s}\r\n'
- .format(dtend.strftime(machine),
- human.strftime(timeformat)))
+ out(
+ '- {1!s}\r\n'.format(
+ dtend.strftime(machine), human.strftime(timeformat)
+ )
+ )
# LOCATION
location = event.getChildValue("location")
if location:
- out('at ' + location + '' + CRLF)
+ out('at ' + location + "" + CRLF)
description = event.getChildValue("description")
if description:
- out('' + description + '
' + CRLF)
+ out('' + description + "
" + CRLF)
if url:
level -= 1
- out('' + CRLF)
+ out("" + CRLF)
level -= 1
- out('' + CRLF) # close vevent
+ out("" + CRLF) # close vevent
return buf or outbuf.getvalue()
+
registerBehavior(HCalendar)
diff --git a/vobject/icalendar.py b/vobject/icalendar.py
index 55e63fd..0ca21f8 100644
--- a/vobject/icalendar.py
+++ b/vobject/icalendar.py
@@ -2,52 +2,60 @@
from __future__ import print_function
+import base64
import datetime
import logging
import random # for generating a UID
import socket
import string
-import base64
+from functools import partial
-from dateutil import rrule, tz
import six
+from dateutil import rrule, tz
try:
import pytz
except ImportError:
+
class Pytz:
"""fake pytz module (pytz is not required)"""
class AmbiguousTimeError(Exception):
"""pytz error for ambiguous times
- during transition daylight->standard"""
+ during transition daylight->standard"""
class NonExistentTimeError(Exception):
"""pytz error for non-existent times
- during transition standard->daylight"""
+ during transition standard->daylight"""
pytz = Pytz # keeps quantifiedcode happy
from . import behavior
-from .base import (VObjectError, NativeError, ValidateError, ParseError,
- Component, ContentLine, logger, registerBehavior,
- backslashEscape, foldOneLine)
-
+from .base import (
+ Component,
+ ContentLine,
+ NativeError,
+ ParseError,
+ ValidateError,
+ VObjectError,
+ backslashEscape,
+ foldOneLine,
+ logger,
+ registerBehavior,
+)
# ------------------------------- Constants ------------------------------------
DATENAMES = ("rdate", "exdate")
RULENAMES = ("exrule", "rrule")
DATESANDRULES = ("exrule", "rrule", "rdate", "exdate")
-PRODID = u"-//PYVOBJECT//NONSGML Version 1//EN"
+PRODID = "-//PYVOBJECT//NONSGML Version 1//EN"
WEEKDAYS = "MO", "TU", "WE", "TH", "FR", "SA", "SU"
-FREQUENCIES = ('YEARLY', 'MONTHLY', 'WEEKLY', 'DAILY', 'HOURLY', 'MINUTELY',
- 'SECONDLY')
+FREQUENCIES = ("YEARLY", "MONTHLY", "WEEKLY", "DAILY", "HOURLY", "MINUTELY", "SECONDLY")
zeroDelta = datetime.timedelta(0)
twoHours = datetime.timedelta(hours=2)
-
# ---------------------------- TZID registry -----------------------------------
__tzidMap = {}
@@ -57,7 +65,7 @@ def toUnicode(s):
Take a string or unicode, turn it into unicode, decoding as utf-8
"""
if isinstance(s, six.binary_type):
- s = s.decode('utf-8')
+ s = s.decode("utf-8")
return s
@@ -75,7 +83,8 @@ def getTzid(tzid, smart=True):
tz = __tzidMap.get(toUnicode(tzid), None)
if smart and tzid and not tz:
try:
- from pytz import timezone, UnknownTimeZoneError
+ from pytz import UnknownTimeZoneError, timezone
+
try:
tz = timezone(tzid)
registerTzid(toUnicode(tzid), tz)
@@ -85,6 +94,7 @@ def getTzid(tzid, smart=True):
logging.error(e)
return tz
+
utc = tz.tzutc()
registerTzid("UTC", utc)
@@ -105,6 +115,7 @@ class TimezoneComponent(Component):
@ivar tzid:
The string used to refer to this timezone.
"""
+
def __init__(self, tzinfo=None, *args, **kwds):
"""
Accept an existing Component or a tzinfo class.
@@ -116,24 +127,23 @@ def __init__(self, tzinfo=None, *args, **kwds):
self.behavior = VTimezone
if tzinfo is not None:
self.tzinfo = tzinfo
- if not hasattr(self, 'name') or self.name == '':
- self.name = 'VTIMEZONE'
+ if not hasattr(self, "name") or self.name == "":
+ self.name = "VTIMEZONE"
self.useBegin = True
@classmethod
- def registerTzinfo(obj, tzinfo):
+ def registerTzinfo(cls, tzinfo):
"""
Register tzinfo if it's not already registered, return its tzid.
"""
- tzid = obj.pickTzid(tzinfo)
+ tzid = cls.pickTzid(tzinfo)
if tzid and not getTzid(tzid, False):
registerTzid(tzid, tzinfo)
return tzid
def gettzinfo(self):
# workaround for dateutil failing to parse some experimental properties
- good_lines = ('rdate', 'rrule', 'dtstart', 'tzname', 'tzoffsetfrom',
- 'tzoffsetto', 'tzid')
+ good_lines = ("rdate", "rrule", "dtstart", "tzname", "tzoffsetfrom", "tzoffsetto", "tzid")
# serialize encodes as utf-8, cStringIO will leave utf-8 alone
buffer = six.StringIO()
# allow empty VTIMEZONEs
@@ -142,13 +152,14 @@ def gettzinfo(self):
def customSerialize(obj):
if isinstance(obj, Component):
- foldOneLine(buffer, u"BEGIN:" + obj.name)
+ foldOneLine(buffer, "BEGIN:" + obj.name)
for child in obj.lines():
if child.name.lower() in good_lines:
child.serialize(buffer, 75, validate=False)
for comp in obj.components():
customSerialize(comp)
- foldOneLine(buffer, u"END:" + obj.name)
+ foldOneLine(buffer, "END:" + obj.name)
+
customSerialize(self)
buffer.seek(0) # tzical wants to read a stream
return tz.tzical(buffer).get()
@@ -168,6 +179,7 @@ def settzinfo(self, tzinfo, start=2000, end=2030):
- tzinfo classes dst method always treats times that could be in either
offset as being in the later regime
"""
+
def fromLastWeek(dt):
"""
How many weeks from the end of the month dt is, starting from 1.
@@ -181,45 +193,47 @@ def fromLastWeek(dt):
return n
# lists of dictionaries defining rules which are no longer in effect
- completed = {'daylight': [], 'standard': []}
+ completed = {"daylight": [], "standard": []}
# dictionary defining rules which are currently in effect
- working = {'daylight': None, 'standard': None}
+ working = {"daylight": None, "standard": None}
# rule may be based on nth week of the month or the nth from the last
for year in range(start, end + 1):
newyear = datetime.datetime(year, 1, 1)
- for transitionTo in 'daylight', 'standard':
+ for transitionTo in "daylight", "standard":
transition = getTransition(transitionTo, year, tzinfo)
oldrule = working[transitionTo]
if transition == newyear:
# transitionTo is in effect for the whole year
- rule = {'end' : None,
- 'start' : newyear,
- 'month' : 1,
- 'weekday' : None,
- 'hour' : None,
- 'plus' : None,
- 'minus' : None,
- 'name' : tzinfo.tzname(newyear),
- 'offset' : tzinfo.utcoffset(newyear),
- 'offsetfrom' : tzinfo.utcoffset(newyear)}
+ rule = {
+ "end": None,
+ "start": newyear,
+ "month": 1,
+ "weekday": None,
+ "hour": None,
+ "plus": None,
+ "minus": None,
+ "name": tzinfo.tzname(newyear),
+ "offset": tzinfo.utcoffset(newyear),
+ "offsetfrom": tzinfo.utcoffset(newyear),
+ }
if oldrule is None:
# transitionTo was not yet in effect
working[transitionTo] = rule
else:
# transitionTo was already in effect
- if (oldrule['offset'] != tzinfo.utcoffset(newyear)):
+ if oldrule["offset"] != tzinfo.utcoffset(newyear):
# old rule was different, it shouldn't continue
- oldrule['end'] = year - 1
+ oldrule["end"] = year - 1
completed[transitionTo].append(oldrule)
working[transitionTo] = rule
elif transition is None:
# transitionTo is not in effect
if oldrule is not None:
# transitionTo used to be in effect
- oldrule['end'] = year - 1
+ oldrule["end"] = year - 1
completed[transitionTo].append(oldrule)
working[transitionTo] = None
else:
@@ -230,43 +244,44 @@ def fromLastWeek(dt):
offset = tzinfo.utcoffset(transition)
except (pytz.AmbiguousTimeError, pytz.NonExistentTimeError):
# guaranteed that tzinfo is a pytz timezone
- is_dst = (transitionTo == "daylight")
+ is_dst = transitionTo == "daylight"
old_offset = tzinfo.utcoffset(transition - twoHours, is_dst=is_dst)
name = tzinfo.tzname(transition, is_dst=is_dst)
offset = tzinfo.utcoffset(transition, is_dst=is_dst)
- rule = {'end' : None, # None, or an integer year
- 'start' : transition, # the datetime of transition
- 'month' : transition.month,
- 'weekday' : transition.weekday(),
- 'hour' : transition.hour,
- 'name' : name,
- 'plus' : int(
- (transition.day - 1)/ 7 + 1), # nth week of the month
- 'minus' : fromLastWeek(transition), # nth from last week
- 'offset' : offset,
- 'offsetfrom' : old_offset}
+ rule = {
+ "end": None, # None, or an integer year
+ "start": transition, # the datetime of transition
+ "month": transition.month,
+ "weekday": transition.weekday(),
+ "hour": transition.hour,
+ "name": name,
+ "plus": int((transition.day - 1) / 7 + 1), # nth week of the month
+ "minus": fromLastWeek(transition), # nth from last week
+ "offset": offset,
+ "offsetfrom": old_offset,
+ }
if oldrule is None:
working[transitionTo] = rule
else:
- plusMatch = rule['plus'] == oldrule['plus']
- minusMatch = rule['minus'] == oldrule['minus']
+ plusMatch = rule["plus"] == oldrule["plus"]
+ minusMatch = rule["minus"] == oldrule["minus"]
truth = plusMatch or minusMatch
- for key in 'month', 'weekday', 'hour', 'offset':
+ for key in "month", "weekday", "hour", "offset":
truth = truth and rule[key] == oldrule[key]
if truth:
# the old rule is still true, limit to plus or minus
if not plusMatch:
- oldrule['plus'] = None
+ oldrule["plus"] = None
if not minusMatch:
- oldrule['minus'] = None
+ oldrule["minus"] = None
else:
# the new rule did not match the old
- oldrule['end'] = year - 1
+ oldrule["end"] = year - 1
completed[transitionTo].append(oldrule)
working[transitionTo] = rule
- for transitionTo in 'daylight', 'standard':
+ for transitionTo in "daylight", "standard":
if working[transitionTo] is not None:
completed[transitionTo].append(working[transitionTo])
@@ -274,56 +289,55 @@ def fromLastWeek(dt):
self.daylight = []
self.standard = []
- self.add('tzid').value = self.pickTzid(tzinfo, True)
+ self.add("tzid").value = self.pickTzid(tzinfo, True)
# old = None # unused?
- for transitionTo in 'daylight', 'standard':
+ for transitionTo in "daylight", "standard":
for rule in completed[transitionTo]:
comp = self.add(transitionTo)
- dtstart = comp.add('dtstart')
- dtstart.value = rule['start']
- if rule['name'] is not None:
- comp.add('tzname').value = rule['name']
- line = comp.add('tzoffsetto')
- line.value = deltaToOffset(rule['offset'])
- line = comp.add('tzoffsetfrom')
- line.value = deltaToOffset(rule['offsetfrom'])
-
- if rule['plus'] is not None:
- num = rule['plus']
- elif rule['minus'] is not None:
- num = -1 * rule['minus']
+ dtstart = comp.add("dtstart")
+ dtstart.value = rule["start"]
+ if rule["name"] is not None:
+ comp.add("tzname").value = rule["name"]
+ line = comp.add("tzoffsetto")
+ line.value = deltaToOffset(rule["offset"])
+ line = comp.add("tzoffsetfrom")
+ line.value = deltaToOffset(rule["offsetfrom"])
+
+ if rule["plus"] is not None:
+ num = rule["plus"]
+ elif rule["minus"] is not None:
+ num = -1 * rule["minus"]
else:
num = None
if num is not None:
- dayString = ";BYDAY=" + str(num) + WEEKDAYS[rule['weekday']]
+ dayString = ";BYDAY=" + str(num) + WEEKDAYS[rule["weekday"]]
else:
dayString = ""
- if rule['end'] is not None:
- if rule['hour'] is None:
+ if rule["end"] is not None:
+ if rule["hour"] is None:
# all year offset, with no rule
- endDate = datetime.datetime(rule['end'], 1, 1)
+ endDate = datetime.datetime(rule["end"], 1, 1)
else:
- weekday = rrule.weekday(rule['weekday'], num)
- du_rule = rrule.rrule(rrule.YEARLY,
- bymonth=rule['month'], byweekday=weekday,
- dtstart=datetime.datetime(
- rule['end'], 1, 1, rule['hour']
- )
+ weekday = rrule.weekday(rule["weekday"], num)
+ du_rule = rrule.rrule(
+ rrule.YEARLY,
+ bymonth=rule["month"],
+ byweekday=weekday,
+ dtstart=datetime.datetime(rule["end"], 1, 1, rule["hour"]),
)
endDate = du_rule[0]
- endDate = endDate.replace(tzinfo=utc) - rule['offsetfrom']
+ endDate = endDate.replace(tzinfo=utc) - rule["offsetfrom"]
endString = ";UNTIL=" + dateTimeToString(endDate)
else:
- endString = ''
- new_rule = "FREQ=YEARLY{0!s};BYMONTH={1!s}{2!s}"\
- .format(dayString, rule['month'], endString)
+ endString = ""
+ new_rule = "FREQ=YEARLY{0!s};BYMONTH={1!s}{2!s}".format(dayString, rule["month"], endString)
- comp.add('rrule').value = new_rule
+ comp.add("rrule").value = new_rule
tzinfo = property(gettzinfo, settzinfo)
# prevent Component's __setattr__ from overriding the tzinfo property
- normal_attributes = Component.normal_attributes + ['tzinfo']
+ normal_attributes = Component.normal_attributes + ["tzinfo"]
@staticmethod
def pickTzid(tzinfo, allowUTC=False):
@@ -334,15 +348,15 @@ def pickTzid(tzinfo, allowUTC=False):
# If tzinfo is UTC, we don't need a TZID
return None
# try PyICU's tzid key
- if hasattr(tzinfo, 'tzid'):
+ if hasattr(tzinfo, "tzid"):
return toUnicode(tzinfo.tzid)
# try pytz zone key
- if hasattr(tzinfo, 'zone'):
+ if hasattr(tzinfo, "zone"):
return toUnicode(tzinfo.zone)
# try tzical's tzid key
- elif hasattr(tzinfo, '_tzid'):
+ elif hasattr(tzinfo, "_tzid"):
return toUnicode(tzinfo._tzid)
else:
# return tzname for standard (non-DST) time
@@ -352,20 +366,19 @@ def pickTzid(tzinfo, allowUTC=False):
if tzinfo.dst(dt) == notDST:
return toUnicode(tzinfo.tzname(dt))
# there was no standard time in 2000!
- raise VObjectError("Unable to guess TZID for tzinfo {0!s}"
- .format(tzinfo))
+ raise VObjectError("Unable to guess TZID for tzinfo {0!s}".format(tzinfo))
def __str__(self):
- return "".format(getattr(self, 'tzid', 'No TZID'))
+ return "".format(getattr(self, "tzid", "No TZID"))
def __repr__(self):
return self.__str__()
def prettyPrint(self, level, tabwidth):
- pre = ' ' * level * tabwidth
+ pre = " " * level * tabwidth
print(pre, self.name)
print(pre, "TZID:", self.tzid)
- print('')
+ print("")
class RecurringComponent(Component):
@@ -387,6 +400,7 @@ class RecurringComponent(Component):
@ivar rruleset:
A U{rruleset}.
"""
+
def __init__(self, *args, **kwds):
super(RecurringComponent, self).__init__(*args, **kwds)
@@ -423,17 +437,17 @@ def getrruleset(self, addRDate=False):
dtstart = self.due.value
else:
# if there's no dtstart, just return None
- logging.error('failed to get dtstart with VTODO')
+ logging.error("failed to get dtstart with VTODO")
return None
except (AttributeError, KeyError):
# if there's no due, just return None
- logging.error('failed to find DUE at all.')
+ logging.error("failed to find DUE at all.")
return None
if name in DATENAMES:
- if type(line.value[0]) == datetime.datetime:
+ if type(line.value[0]) is datetime.datetime:
list(map(addfunc, line.value))
- elif type(line.value[0]) == datetime.date:
+ elif type(line.value[0]) is datetime.date:
for dt in line.value:
addfunc(datetime.datetime(dt.year, dt.month, dt.day))
else:
@@ -442,11 +456,10 @@ def getrruleset(self, addRDate=False):
elif name in RULENAMES:
# a Ruby iCalendar library escapes semi-colons in rrules,
# so also remove any backslashes
- value = line.value.replace('\\', '')
+ value = line.value.replace("\\", "")
# If dtstart has no time zone, `until`
# shouldn't get one, either:
- ignoretz = (not isinstance(dtstart, datetime.datetime) or
- dtstart.tzinfo is None)
+ ignoretz = not isinstance(dtstart, datetime.datetime) or dtstart.tzinfo is None
try:
until = rrule.rrulestr(value, ignoretz=ignoretz)._until
except ValueError:
@@ -457,16 +470,16 @@ def getrruleset(self, addRDate=False):
utc_now = datetime.datetime.now(datetime.timezone.utc)
until = rrule.rrulestr(value, dtstart=utc_now)._until
- if until is not None and isinstance(dtstart,
- datetime.datetime) and \
- (until.tzinfo != dtstart.tzinfo):
+ if (
+ until is not None
+ and isinstance(dtstart, datetime.datetime)
+ and (until.tzinfo != dtstart.tzinfo)
+ ):
# dateutil converts the UNTIL date to a datetime,
# check to see if the UNTIL parameter value was a date
- vals = dict(pair.split('=') for pair in
- value.upper().split(';'))
- if len(vals.get('UNTIL', '')) == 8:
- until = datetime.datetime.combine(until.date(),
- dtstart.time())
+ vals = dict(pair.split("=") for pair in value.upper().split(";"))
+ if len(vals.get("UNTIL", "")) == 8:
+ until = datetime.datetime.combine(until.date(), dtstart.time())
# While RFC2445 says UNTIL MUST be UTC, Chandler allows
# floating recurring events, and uses floating UNTIL
# values. Also, some odd floating UNTIL but timezoned
@@ -493,17 +506,16 @@ def getrruleset(self, addRDate=False):
if dtstart.tzinfo is None:
until = until.replace(tzinfo=None)
- value_without_until = ';'.join(
- pair for pair in value.split(';')
- if pair.split('=')[0].upper() != 'UNTIL')
- rule = rrule.rrulestr(value_without_until,
- dtstart=dtstart, ignoretz=ignoretz)
+ value_without_until = ";".join(
+ pair for pair in value.split(";") if pair.split("=")[0].upper() != "UNTIL"
+ )
+ rule = rrule.rrulestr(value_without_until, dtstart=dtstart, ignoretz=ignoretz)
rule._until = until
# add the rrule or exrule to the rruleset
addfunc(rule)
- if (name == 'rrule' or name == 'rdate') and addRDate:
+ if (name == "rrule" or name == "rdate") and addRDate:
# rlist = rruleset._rrule if name == 'rrule' else rruleset._rdate
try:
# dateutils does not work with all-day
@@ -515,7 +527,7 @@ def getrruleset(self, addRDate=False):
else:
adddtstart = dtstart
- if name == 'rrule':
+ if name == "rrule":
if rruleset._rrule[-1][0] != adddtstart:
rruleset.rdate(adddtstart)
added = True
@@ -523,7 +535,7 @@ def getrruleset(self, addRDate=False):
rruleset._rrule[-1]._count -= 1
else:
added = False
- elif name == 'rdate':
+ elif name == "rdate":
if rruleset._rdate[0] != adddtstart:
rruleset.rdate(adddtstart)
added = True
@@ -532,6 +544,7 @@ def getrruleset(self, addRDate=False):
except IndexError:
# it's conceivable that an rrule has 0 datetimes
added = False
+ print(added) # todo: remove unused vars
return rruleset
@@ -551,15 +564,15 @@ def setrruleset(self, rruleset):
untilSerialize = dateToString
else:
# make sure to convert time zones to UTC
- untilSerialize = lambda x: dateTimeToString(x, True)
+ untilSerialize = partial(dateTimeToString, convertToUTC=True)
for name in DATESANDRULES:
if name in self.contents:
del self.contents[name]
- setlist = getattr(rruleset, '_' + name)
+ setlist = getattr(rruleset, "_" + name)
if name in DATENAMES:
setlist = list(setlist) # make a copy of the list
- if name == 'rdate' and dtstart in setlist:
+ if name == "rdate" and dtstart in setlist:
setlist.remove(dtstart)
if isDate:
setlist = [dt.date() for dt in setlist]
@@ -568,28 +581,29 @@ def setrruleset(self, rruleset):
elif name in RULENAMES:
for rule in setlist:
buf = six.StringIO()
- buf.write('FREQ=')
+ buf.write("FREQ=")
buf.write(FREQUENCIES[rule._freq])
values = {}
if rule._interval != 1:
- values['INTERVAL'] = [str(rule._interval)]
+ values["INTERVAL"] = [str(rule._interval)]
if rule._wkst != 0: # wkst defaults to Monday
- values['WKST'] = [WEEKDAYS[rule._wkst]]
+ values["WKST"] = [WEEKDAYS[rule._wkst]]
if rule._bysetpos is not None:
- values['BYSETPOS'] = [str(i) for i in rule._bysetpos]
+ values["BYSETPOS"] = [str(i) for i in rule._bysetpos]
if rule._count is not None:
- values['COUNT'] = [str(rule._count)]
+ values["COUNT"] = [str(rule._count)]
elif rule._until is not None:
- values['UNTIL'] = [untilSerialize(rule._until)]
+ values["UNTIL"] = [untilSerialize(rule._until)]
days = []
- if (rule._byweekday is not None and (
- rrule.WEEKLY != rule._freq or
- len(rule._byweekday) != 1 or
- rule._dtstart.weekday() != rule._byweekday[0])):
+ if rule._byweekday is not None and (
+ rrule.WEEKLY != rule._freq
+ or len(rule._byweekday) != 1
+ or rule._dtstart.weekday() != rule._byweekday[0]
+ ):
# ignore byweekday if freq is WEEKLY and day correlates
# with dtstart because it was automatically set by dateutil
days.extend(WEEKDAYS[n] for n in rule._byweekday)
@@ -598,39 +612,45 @@ def setrruleset(self, rruleset):
days.extend(n + WEEKDAYS[day] for day, n in rule._bynweekday)
if len(days) > 0:
- values['BYDAY'] = days
+ values["BYDAY"] = days
if rule._bymonthday is not None and len(rule._bymonthday) > 0:
- if not (rule._freq <= rrule.MONTHLY and
- len(rule._bymonthday) == 1 and
- rule._bymonthday[0] == rule._dtstart.day):
+ if not (
+ rule._freq <= rrule.MONTHLY
+ and len(rule._bymonthday) == 1
+ and rule._bymonthday[0] == rule._dtstart.day
+ ):
# ignore bymonthday if it's generated by dateutil
- values['BYMONTHDAY'] = [str(n) for n in rule._bymonthday]
+ values["BYMONTHDAY"] = [str(n) for n in rule._bymonthday]
if rule._bynmonthday is not None and len(rule._bynmonthday) > 0:
- values.setdefault('BYMONTHDAY', []).extend(str(n) for n in rule._bynmonthday)
+ values.setdefault("BYMONTHDAY", []).extend(str(n) for n in rule._bynmonthday)
if rule._bymonth is not None and len(rule._bymonth) > 0:
- if (rule._byweekday is not None or
- len(rule._bynweekday or ()) > 0 or
- not (rule._freq == rrule.YEARLY and
- len(rule._bymonth) == 1 and
- rule._bymonth[0] == rule._dtstart.month)):
+ if (
+ rule._byweekday is not None
+ or len(rule._bynweekday or ()) > 0
+ or not (
+ rule._freq == rrule.YEARLY
+ and len(rule._bymonth) == 1
+ and rule._bymonth[0] == rule._dtstart.month
+ )
+ ):
# ignore bymonth if it's generated by dateutil
- values['BYMONTH'] = [str(n) for n in rule._bymonth]
+ values["BYMONTH"] = [str(n) for n in rule._bymonth]
if rule._byyearday is not None:
- values['BYYEARDAY'] = [str(n) for n in rule._byyearday]
+ values["BYYEARDAY"] = [str(n) for n in rule._byyearday]
if rule._byweekno is not None:
- values['BYWEEKNO'] = [str(n) for n in rule._byweekno]
+ values["BYWEEKNO"] = [str(n) for n in rule._byweekno]
# byhour, byminute, bysecond are always ignored for now
for key, paramvals in values.items():
- buf.write(';')
+ buf.write(";")
buf.write(key)
- buf.write('=')
- buf.write(','.join(paramvals))
+ buf.write("=")
+ buf.write(",".join(paramvals))
self.add(name).value = buf.getvalue()
@@ -640,7 +660,7 @@ def __setattr__(self, name, value):
"""
For convenience, make self.contents directly accessible.
"""
- if name == 'rruleset':
+ if name == "rruleset":
self.setrruleset(value)
else:
super(RecurringComponent, self).__setattr__(name, value)
@@ -653,7 +673,8 @@ class TextBehavior(behavior.Behavior):
TextBehavior also deals with base64 encoding if the ENCODING parameter is
explicitly set to BASE64.
"""
- base64string = 'BASE64' # vCard uses B
+
+ base64string = "BASE64" # vCard uses B
@classmethod
def decode(cls, line):
@@ -661,7 +682,7 @@ def decode(cls, line):
Remove backslash escaping from line.value.
"""
if line.encoded:
- encoding = getattr(line, 'encoding_param', None)
+ encoding = getattr(line, "encoding_param", None)
if encoding and encoding.upper() == cls.base64string:
line.value = base64.b64decode(line.value)
else:
@@ -674,9 +695,9 @@ def encode(cls, line):
Backslash escape line.value.
"""
if not line.encoded:
- encoding = getattr(line, 'encoding_param', None)
+ encoding = getattr(line, "encoding_param", None)
if encoding and encoding.upper() == cls.base64string:
- line.value = base64.b64encode(line.value.encode('utf-8')).decode('utf-8').replace('\n', '')
+ line.value = base64.b64encode(line.value.encode("utf-8")).decode("utf-8").replace("\n", "")
else:
line.value = backslashEscape(line.value)
line.encoded = True
@@ -691,6 +712,7 @@ class RecurringBehavior(VCalendarComponentBehavior):
"""
Parent Behavior for components which should be RecurringComponents.
"""
+
hasNative = True
@staticmethod
@@ -699,14 +721,14 @@ def transformToNative(obj):
Turn a recurring Component into a RecurringComponent.
"""
if not obj.isNative:
- object.__setattr__(obj, '__class__', RecurringComponent)
+ object.__setattr__(obj, "__class__", RecurringComponent)
obj.isNative = True
return obj
@staticmethod
def transformFromNative(obj):
if obj.isNative:
- object.__setattr__(obj, '__class__', Component)
+ object.__setattr__(obj, "__class__", Component)
obj.isNative = False
return obj
@@ -717,23 +739,23 @@ def generateImplicitParameters(obj):
This is just a dummy implementation, for now.
"""
- if not hasattr(obj, 'uid'):
+ if not hasattr(obj, "uid"):
rand = int(random.random() * 100000)
now = datetime.datetime.now(utc)
now = dateTimeToString(now)
host = socket.gethostname()
- obj.add(ContentLine('UID', [], "{0} - {1}@{2}".format(now, rand,
- host)))
+ obj.add(ContentLine("UID", [], "{0} - {1}@{2}".format(now, rand, host)))
- if not hasattr(obj, 'dtstamp'):
+ if not hasattr(obj, "dtstamp"):
now = datetime.datetime.now(utc)
- obj.add('dtstamp').value = now
+ obj.add("dtstamp").value = now
class DateTimeBehavior(behavior.Behavior):
"""
Parent Behavior for ContentLines containing one DATE-TIME.
"""
+
hasNative = True
@staticmethod
@@ -749,17 +771,17 @@ def transformToNative(obj):
if obj.isNative:
return obj
obj.isNative = True
- if obj.value == '':
+ if obj.value == "":
return obj
obj.value = obj.value
# we're cheating a little here, parseDtstart allows DATE
obj.value = parseDtstart(obj)
if obj.value.tzinfo is None:
- obj.params['X-VOBJ-FLOATINGTIME-ALLOWED'] = ['TRUE']
- if obj.params.get('TZID'):
+ obj.params["X-VOBJ-FLOATINGTIME-ALLOWED"] = ["TRUE"]
+ if obj.params.get("TZID"):
# Keep a copy of the original TZID around
- obj.params['X-VOBJ-ORIGINAL-TZID'] = [obj.params['TZID']]
- del obj.params['TZID']
+ obj.params["X-VOBJ-ORIGINAL-TZID"] = [obj.params["TZID"]]
+ del obj.params["TZID"]
return obj
@classmethod
@@ -773,10 +795,10 @@ def transformFromNative(cls, obj):
obj.value = dateTimeToString(obj.value, cls.forceUTC)
if not cls.forceUTC and tzid is not None:
obj.tzid_param = tzid
- if obj.params.get('X-VOBJ-ORIGINAL-TZID'):
- if not hasattr(obj, 'tzid_param'):
+ if obj.params.get("X-VOBJ-ORIGINAL-TZID"):
+ if not hasattr(obj, "tzid_param"):
obj.tzid_param = obj.x_vobj_original_tzid_param
- del obj.params['X-VOBJ-ORIGINAL-TZID']
+ del obj.params["X-VOBJ-ORIGINAL-TZID"]
return obj
@@ -785,6 +807,7 @@ class UTCDateTimeBehavior(DateTimeBehavior):
"""
A value which must be specified in UTC.
"""
+
forceUTC = True
@@ -792,6 +815,7 @@ class DateOrDateTimeBehavior(behavior.Behavior):
"""
Parent Behavior for ContentLines containing one DATE or DATE-TIME.
"""
+
hasNative = True
@staticmethod
@@ -802,14 +826,14 @@ def transformToNative(obj):
if obj.isNative:
return obj
obj.isNative = True
- if obj.value == '':
+ if obj.value == "":
return obj
obj.value = obj.value
obj.value = parseDtstart(obj, allowSignatureMismatch=True)
- if getattr(obj, 'value_param', 'DATE-TIME').upper() == 'DATE-TIME':
- if hasattr(obj, 'tzid_param'):
+ if getattr(obj, "value_param", "DATE-TIME").upper() == "DATE-TIME":
+ if hasattr(obj, "tzid_param"):
# Keep a copy of the original TZID around
- obj.params['X-VOBJ-ORIGINAL-TZID'] = [obj.tzid_param]
+ obj.params["X-VOBJ-ORIGINAL-TZID"] = [obj.tzid_param]
del obj.tzid_param
return obj
@@ -818,9 +842,9 @@ def transformFromNative(obj):
"""
Replace the date or datetime in obj.value with an ISO 8601 string.
"""
- if type(obj.value) == datetime.date:
+ if type(obj.value) is datetime.date:
obj.isNative = False
- obj.value_param = 'DATE'
+ obj.value_param = "DATE"
obj.value = dateToString(obj.value)
return obj
else:
@@ -832,6 +856,7 @@ class MultiDateBehavior(behavior.Behavior):
Parent Behavior for ContentLines containing one or more DATE, DATE-TIME, or
PERIOD.
"""
+
hasNative = True
@staticmethod
@@ -843,11 +868,11 @@ def transformToNative(obj):
if obj.isNative:
return obj
obj.isNative = True
- if obj.value == '':
+ if obj.value == "":
obj.value = []
return obj
- tzinfo = getTzid(getattr(obj, 'tzid_param', None))
- valueParam = getattr(obj, 'value_param', "DATE-TIME").upper()
+ tzinfo = getTzid(getattr(obj, "tzid_param", None))
+ valueParam = getattr(obj, "value_param", "DATE-TIME").upper()
valTexts = obj.value.split(",")
if valueParam == "DATE":
obj.value = [stringToDate(x) for x in valTexts]
@@ -863,10 +888,10 @@ def transformFromNative(obj):
Replace the date, datetime or period tuples in obj.value with
appropriate strings.
"""
- if obj.value and type(obj.value[0]) == datetime.date:
+ if obj.value and type(obj.value[0]) is datetime.date:
obj.isNative = False
- obj.value_param = 'DATE'
- obj.value = ','.join([dateToString(val) for val in obj.value])
+ obj.value_param = "DATE"
+ obj.value = ",".join([dateToString(val) for val in obj.value])
return obj
# Fixme: handle PERIOD case
else:
@@ -875,12 +900,12 @@ def transformFromNative(obj):
transformed = []
tzid = None
for val in obj.value:
- if tzid is None and type(val) == datetime.datetime:
+ if tzid is None and type(val) is datetime.datetime:
tzid = TimezoneComponent.registerTzinfo(val.tzinfo)
if tzid is not None:
obj.tzid_param = tzid
transformed.append(dateTimeToString(val))
- obj.value = ','.join(transformed)
+ obj.value = ",".join(transformed)
return obj
@@ -890,6 +915,7 @@ class MultiTextBehavior(behavior.Behavior):
After transformation, value is a list of strings.
"""
+
listSeparator = ","
@classmethod
@@ -898,8 +924,7 @@ def decode(cls, line):
Remove backslash escaping from line.value, then split on commas.
"""
if line.encoded:
- line.value = stringToTextValues(line.value,
- listSeparator=cls.listSeparator)
+ line.value = stringToTextValues(line.value, listSeparator=cls.listSeparator)
line.encoded = False
@classmethod
@@ -908,8 +933,7 @@ def encode(cls, line):
Backslash escape line.value.
"""
if not line.encoded:
- line.value = cls.listSeparator.join(backslashEscape(val)
- for val in line.value)
+ line.value = cls.listSeparator.join(backslashEscape(val) for val in line.value)
line.encoded = True
@@ -922,21 +946,22 @@ class VCalendar2_0(VCalendarComponentBehavior):
"""
vCalendar 2.0 behavior. With added VAVAILABILITY support.
"""
- name = 'VCALENDAR'
- description = 'vCalendar 2.0, also known as iCalendar.'
- versionString = '2.0'
- sortFirst = ('version', 'calscale', 'method', 'prodid', 'vtimezone')
+
+ name = "VCALENDAR"
+ description = "vCalendar 2.0, also known as iCalendar."
+ versionString = "2.0"
+ sortFirst = ("version", "calscale", "method", "prodid", "vtimezone")
knownChildren = {
- 'CALSCALE': (0, 1, None), # min, max, behaviorRegistry id
- 'METHOD': (0, 1, None),
- 'VERSION': (0, 1, None), # required, but auto-generated
- 'PRODID': (1, 1, None),
- 'VTIMEZONE': (0, None, None),
- 'VEVENT': (0, None, None),
- 'VTODO': (0, None, None),
- 'VJOURNAL': (0, None, None),
- 'VFREEBUSY': (0, None, None),
- 'VAVAILABILITY': (0, None, None),
+ "CALSCALE": (0, 1, None), # min, max, behaviorRegistry id
+ "METHOD": (0, 1, None),
+ "VERSION": (0, 1, None), # required, but auto-generated
+ "PRODID": (1, 1, None),
+ "VTIMEZONE": (0, None, None),
+ "VEVENT": (0, None, None),
+ "VTODO": (0, None, None),
+ "VJOURNAL": (0, None, None),
+ "VFREEBUSY": (0, None, None),
+ "VAVAILABILITY": (0, None, None),
}
@classmethod
@@ -950,38 +975,37 @@ def generateImplicitParameters(cls, obj):
for comp in obj.components():
if comp.behavior is not None:
comp.behavior.generateImplicitParameters(comp)
- if not hasattr(obj, 'prodid'):
- obj.add(ContentLine('PRODID', [], PRODID))
- if not hasattr(obj, 'version'):
- obj.add(ContentLine('VERSION', [], cls.versionString))
+ if not hasattr(obj, "prodid"):
+ obj.add(ContentLine("PRODID", [], PRODID))
+ if not hasattr(obj, "version"):
+ obj.add(ContentLine("VERSION", [], cls.versionString))
tzidsUsed = {}
def findTzids(obj, table):
- if isinstance(obj, ContentLine) and (obj.behavior is None or
- not obj.behavior.forceUTC):
- if getattr(obj, 'tzid_param', None):
+ if isinstance(obj, ContentLine) and (obj.behavior is None or not obj.behavior.forceUTC):
+ if getattr(obj, "tzid_param", None):
table[obj.tzid_param] = 1
else:
- if type(obj.value) == list:
- for item in obj.value:
- tzinfo = getattr(obj.value, 'tzinfo', None)
+ if type(obj.value) is list:
+ for _ in obj.value:
+ tzinfo = getattr(obj.value, "tzinfo", None)
tzid = TimezoneComponent.registerTzinfo(tzinfo)
if tzid:
table[tzid] = 1
else:
- tzinfo = getattr(obj.value, 'tzinfo', None)
+ tzinfo = getattr(obj.value, "tzinfo", None)
tzid = TimezoneComponent.registerTzinfo(tzinfo)
if tzid:
table[tzid] = 1
for child in obj.getChildren():
- if obj.name != 'VTIMEZONE':
+ if obj.name != "VTIMEZONE":
findTzids(child, table)
findTzids(obj, tzidsUsed)
- oldtzids = [toUnicode(x.tzid.value) for x in getattr(obj, 'vtimezone_list', [])]
+ oldtzids = [toUnicode(x.tzid.value) for x in getattr(obj, "vtimezone_list", [])]
for tzid in tzidsUsed.keys():
tzid = toUnicode(tzid)
- if tzid != u'UTC' and tzid not in oldtzids:
+ if tzid != "UTC" and tzid not in oldtzids:
obj.add(TimezoneComponent(tzinfo=getTzid(tzid)))
@classmethod
@@ -1006,28 +1030,38 @@ def serialize(cls, obj, buf, lineLength, validate=True):
transformed = obj
undoTransform = False
out = None
+ print(transformed, out) # todo: remove unused vars
outbuf = buf or six.StringIO()
if obj.group is None:
- groupString = ''
+ groupString = ""
else:
- groupString = obj.group + '.'
+ groupString = obj.group + "."
if obj.useBegin:
- foldOneLine(outbuf, "{0}BEGIN:{1}".format(groupString, obj.name),
- lineLength)
+ foldOneLine(outbuf, "{0}BEGIN:{1}".format(groupString, obj.name), lineLength)
try:
- first_props = [s for s in cls.sortFirst if s in obj.contents \
- and not isinstance(obj.contents[s][0], Component)]
- first_components = [s for s in cls.sortFirst if s in obj.contents \
- and isinstance(obj.contents[s][0], Component)]
+ first_props = [
+ s for s in cls.sortFirst if s in obj.contents and not isinstance(obj.contents[s][0], Component)
+ ]
+ first_components = [
+ s for s in cls.sortFirst if s in obj.contents and isinstance(obj.contents[s][0], Component)
+ ]
except Exception:
first_props = first_components = []
# first_components = []
- prop_keys = sorted(list(k for k in obj.contents.keys() if k not in first_props \
- and not isinstance(obj.contents[k][0], Component)))
- comp_keys = sorted(list(k for k in obj.contents.keys() if k not in first_components \
- and isinstance(obj.contents[k][0], Component)))
+ prop_keys = sorted(
+ list(
+ k for k in obj.contents.keys() if k not in first_props and not isinstance(obj.contents[k][0], Component)
+ )
+ )
+ comp_keys = sorted(
+ list(
+ k
+ for k in obj.contents.keys()
+ if k not in first_components and isinstance(obj.contents[k][0], Component)
+ )
+ )
sorted_keys = first_props + prop_keys + first_components + comp_keys
children = [o for k in sorted_keys for o in obj.contents[k]]
@@ -1036,12 +1070,13 @@ def serialize(cls, obj, buf, lineLength, validate=True):
# validate is recursive, we only need to validate once
child.serialize(outbuf, lineLength, validate=False)
if obj.useBegin:
- foldOneLine(outbuf, "{0}END:{1}".format(groupString, obj.name),
- lineLength)
+ foldOneLine(outbuf, "{0}END:{1}".format(groupString, obj.name), lineLength)
out = buf or outbuf.getvalue()
if undoTransform:
obj.transformToNative()
return out
+
+
registerBehavior(VCalendar2_0)
@@ -1049,26 +1084,27 @@ class VTimezone(VCalendarComponentBehavior):
"""
Timezone behavior.
"""
- name = 'VTIMEZONE'
+
+ name = "VTIMEZONE"
hasNative = True
- description = 'A grouping of component properties that defines a time zone.'
- sortFirst = ('tzid', 'last-modified', 'tzurl', 'standard', 'daylight')
+ description = "A grouping of component properties that defines a time zone."
+ sortFirst = ("tzid", "last-modified", "tzurl", "standard", "daylight")
knownChildren = {
- 'TZID': (1, 1, None), # min, max, behaviorRegistry id
- 'LAST-MODIFIED': (0, 1, None),
- 'TZURL': (0, 1, None),
- 'STANDARD': (0, None, None), # NOTE: One of Standard or
- 'DAYLIGHT': (0, None, None) # Daylight must appear
+ "TZID": (1, 1, None), # min, max, behaviorRegistry id
+ "LAST-MODIFIED": (0, 1, None),
+ "TZURL": (0, 1, None),
+ "STANDARD": (0, None, None), # NOTE: One of Standard or
+ "DAYLIGHT": (0, None, None), # Daylight must appear
}
@classmethod
def validate(cls, obj, raiseException, *args):
- if not hasattr(obj, 'tzid') or obj.tzid.value is None:
+ if not hasattr(obj, "tzid") or obj.tzid.value is None:
if raiseException:
m = "VTIMEZONE components must contain a valid TZID"
raise ValidateError(m)
return False
- if 'standard' in obj.contents or 'daylight' in obj.contents:
+ if "standard" in obj.contents or "daylight" in obj.contents:
return super(VTimezone, cls).validate(obj, raiseException, *args)
else:
if raiseException:
@@ -1080,7 +1116,7 @@ def validate(cls, obj, raiseException, *args):
@staticmethod
def transformToNative(obj):
if not obj.isNative:
- object.__setattr__(obj, '__class__', TimezoneComponent)
+ object.__setattr__(obj, "__class__", TimezoneComponent)
obj.isNative = True
obj.registerTzinfo(obj.tzinfo)
return obj
@@ -1088,6 +1124,8 @@ def transformToNative(obj):
@staticmethod
def transformFromNative(obj):
return obj
+
+
registerBehavior(VTimezone)
@@ -1101,66 +1139,69 @@ class TZID(behavior.Behavior):
do we want to escape them. Leaving them alone works for Microsoft's breakage,
and doesn't affect compliant iCalendar streams.
"""
+
+
registerBehavior(TZID)
class DaylightOrStandard(VCalendarComponentBehavior):
hasNative = False
- knownChildren = {'DTSTART': (1, 1, None), # min, max, behaviorRegistry id
- 'RRULE': (0, 1, None)}
+ knownChildren = {"DTSTART": (1, 1, None), "RRULE": (0, 1, None)} # min, max, behaviorRegistry id
+
-registerBehavior(DaylightOrStandard, 'STANDARD')
-registerBehavior(DaylightOrStandard, 'DAYLIGHT')
+registerBehavior(DaylightOrStandard, "STANDARD")
+registerBehavior(DaylightOrStandard, "DAYLIGHT")
class VEvent(RecurringBehavior):
"""
Event behavior.
"""
- name = 'VEVENT'
- sortFirst = ('uid', 'recurrence-id', 'dtstart', 'duration', 'dtend')
+
+ name = "VEVENT"
+ sortFirst = ("uid", "recurrence-id", "dtstart", "duration", "dtend")
description = 'A grouping of component properties, and possibly including \
"VALARM" calendar components, that represents a scheduled \
amount of time on a calendar.'
knownChildren = {
- 'DTSTART': (0, 1, None), # min, max, behaviorRegistry id
- 'CLASS': (0, 1, None),
- 'CREATED': (0, 1, None),
- 'DESCRIPTION': (0, 1, None),
- 'GEO': (0, 1, None),
- 'LAST-MODIFIED': (0, 1, None),
- 'LOCATION': (0, 1, None),
- 'ORGANIZER': (0, 1, None),
- 'PRIORITY': (0, 1, None),
- 'DTSTAMP': (1, 1, None), # required
- 'SEQUENCE': (0, 1, None),
- 'STATUS': (0, 1, None),
- 'SUMMARY': (0, 1, None),
- 'TRANSP': (0, 1, None),
- 'UID': (1, 1, None),
- 'URL': (0, 1, None),
- 'RECURRENCE-ID': (0, 1, None),
- 'DTEND': (0, 1, None), # NOTE: Only one of DtEnd or
- 'DURATION': (0, 1, None), # Duration can appear
- 'ATTACH': (0, None, None),
- 'ATTENDEE': (0, None, None),
- 'CATEGORIES': (0, None, None),
- 'COMMENT': (0, None, None),
- 'CONTACT': (0, None, None),
- 'EXDATE': (0, None, None),
- 'EXRULE': (0, None, None),
- 'REQUEST-STATUS': (0, None, None),
- 'RELATED-TO': (0, None, None),
- 'RESOURCES': (0, None, None),
- 'RDATE': (0, None, None),
- 'RRULE': (0, None, None),
- 'VALARM': (0, None, None)
+ "DTSTART": (0, 1, None), # min, max, behaviorRegistry id
+ "CLASS": (0, 1, None),
+ "CREATED": (0, 1, None),
+ "DESCRIPTION": (0, 1, None),
+ "GEO": (0, 1, None),
+ "LAST-MODIFIED": (0, 1, None),
+ "LOCATION": (0, 1, None),
+ "ORGANIZER": (0, 1, None),
+ "PRIORITY": (0, 1, None),
+ "DTSTAMP": (1, 1, None), # required
+ "SEQUENCE": (0, 1, None),
+ "STATUS": (0, 1, None),
+ "SUMMARY": (0, 1, None),
+ "TRANSP": (0, 1, None),
+ "UID": (1, 1, None),
+ "URL": (0, 1, None),
+ "RECURRENCE-ID": (0, 1, None),
+ "DTEND": (0, 1, None), # NOTE: Only one of DtEnd or
+ "DURATION": (0, 1, None), # Duration can appear
+ "ATTACH": (0, None, None),
+ "ATTENDEE": (0, None, None),
+ "CATEGORIES": (0, None, None),
+ "COMMENT": (0, None, None),
+ "CONTACT": (0, None, None),
+ "EXDATE": (0, None, None),
+ "EXRULE": (0, None, None),
+ "REQUEST-STATUS": (0, None, None),
+ "RELATED-TO": (0, None, None),
+ "RESOURCES": (0, None, None),
+ "RDATE": (0, None, None),
+ "RRULE": (0, None, None),
+ "VALARM": (0, None, None),
}
@classmethod
def validate(cls, obj, raiseException, *args):
- if 'dtend' in obj.contents and 'duration' in obj.contents:
+ if "dtend" in obj.contents and "duration" in obj.contents:
if raiseException:
m = "VEVENT components cannot contain both DTEND and DURATION\
components"
@@ -1169,6 +1210,7 @@ def validate(cls, obj, raiseException, *args):
else:
return super(VEvent, cls).validate(obj, raiseException, *args)
+
registerBehavior(VEvent)
@@ -1176,49 +1218,50 @@ class VTodo(RecurringBehavior):
"""
To-do behavior.
"""
- name = 'VTODO'
+
+ name = "VTODO"
description = 'A grouping of component properties and possibly "VALARM" \
calendar components that represent an action-item or \
assignment.'
knownChildren = {
- 'DTSTART': (0, 1, None), # min, max, behaviorRegistry id
- 'CLASS': (0, 1, None),
- 'COMPLETED': (0, 1, None),
- 'CREATED': (0, 1, None),
- 'DESCRIPTION': (0, 1, None),
- 'GEO': (0, 1, None),
- 'LAST-MODIFIED': (0, 1, None),
- 'LOCATION': (0, 1, None),
- 'ORGANIZER': (0, 1, None),
- 'PERCENT': (0, 1, None),
- 'PRIORITY': (0, 1, None),
- 'DTSTAMP': (1, 1, None),
- 'SEQUENCE': (0, 1, None),
- 'STATUS': (0, 1, None),
- 'SUMMARY': (0, 1, None),
- 'UID': (0, 1, None),
- 'URL': (0, 1, None),
- 'RECURRENCE-ID': (0, 1, None),
- 'DUE': (0, 1, None), # NOTE: Only one of Due or
- 'DURATION': (0, 1, None), # Duration can appear
- 'ATTACH': (0, None, None),
- 'ATTENDEE': (0, None, None),
- 'CATEGORIES': (0, None, None),
- 'COMMENT': (0, None, None),
- 'CONTACT': (0, None, None),
- 'EXDATE': (0, None, None),
- 'EXRULE': (0, None, None),
- 'REQUEST-STATUS': (0, None, None),
- 'RELATED-TO': (0, None, None),
- 'RESOURCES': (0, None, None),
- 'RDATE': (0, None, None),
- 'RRULE': (0, None, None),
- 'VALARM': (0, None, None)
+ "DTSTART": (0, 1, None), # min, max, behaviorRegistry id
+ "CLASS": (0, 1, None),
+ "COMPLETED": (0, 1, None),
+ "CREATED": (0, 1, None),
+ "DESCRIPTION": (0, 1, None),
+ "GEO": (0, 1, None),
+ "LAST-MODIFIED": (0, 1, None),
+ "LOCATION": (0, 1, None),
+ "ORGANIZER": (0, 1, None),
+ "PERCENT": (0, 1, None),
+ "PRIORITY": (0, 1, None),
+ "DTSTAMP": (1, 1, None),
+ "SEQUENCE": (0, 1, None),
+ "STATUS": (0, 1, None),
+ "SUMMARY": (0, 1, None),
+ "UID": (0, 1, None),
+ "URL": (0, 1, None),
+ "RECURRENCE-ID": (0, 1, None),
+ "DUE": (0, 1, None), # NOTE: Only one of Due or
+ "DURATION": (0, 1, None), # Duration can appear
+ "ATTACH": (0, None, None),
+ "ATTENDEE": (0, None, None),
+ "CATEGORIES": (0, None, None),
+ "COMMENT": (0, None, None),
+ "CONTACT": (0, None, None),
+ "EXDATE": (0, None, None),
+ "EXRULE": (0, None, None),
+ "REQUEST-STATUS": (0, None, None),
+ "RELATED-TO": (0, None, None),
+ "RESOURCES": (0, None, None),
+ "RDATE": (0, None, None),
+ "RRULE": (0, None, None),
+ "VALARM": (0, None, None),
}
@classmethod
def validate(cls, obj, raiseException, *args):
- if 'due' in obj.contents and 'duration' in obj.contents:
+ if "due" in obj.contents and "duration" in obj.contents:
if raiseException:
m = "VTODO components cannot contain both DUE and DURATION\
components"
@@ -1227,6 +1270,7 @@ def validate(cls, obj, raiseException, *args):
else:
return super(VTodo, cls).validate(obj, raiseException, *args)
+
registerBehavior(VTodo)
@@ -1234,33 +1278,36 @@ class VJournal(RecurringBehavior):
"""
Journal entry behavior.
"""
- name = 'VJOURNAL'
+
+ name = "VJOURNAL"
knownChildren = {
- 'DTSTART': (0, 1, None), # min, max, behaviorRegistry id
- 'CLASS': (0, 1, None),
- 'CREATED': (0, 1, None),
- 'DESCRIPTION': (0, 1, None),
- 'LAST-MODIFIED': (0, 1, None),
- 'ORGANIZER': (0, 1, None),
- 'DTSTAMP': (1, 1, None),
- 'SEQUENCE': (0, 1, None),
- 'STATUS': (0, 1, None),
- 'SUMMARY': (0, 1, None),
- 'UID': (0, 1, None),
- 'URL': (0, 1, None),
- 'RECURRENCE-ID': (0, 1, None),
- 'ATTACH': (0, None, None),
- 'ATTENDEE': (0, None, None),
- 'CATEGORIES': (0, None, None),
- 'COMMENT': (0, None, None),
- 'CONTACT': (0, None, None),
- 'EXDATE': (0, None, None),
- 'EXRULE': (0, None, None),
- 'REQUEST-STATUS': (0, None, None),
- 'RELATED-TO': (0, None, None),
- 'RDATE': (0, None, None),
- 'RRULE': (0, None, None)
+ "DTSTART": (0, 1, None), # min, max, behaviorRegistry id
+ "CLASS": (0, 1, None),
+ "CREATED": (0, 1, None),
+ "DESCRIPTION": (0, 1, None),
+ "LAST-MODIFIED": (0, 1, None),
+ "ORGANIZER": (0, 1, None),
+ "DTSTAMP": (1, 1, None),
+ "SEQUENCE": (0, 1, None),
+ "STATUS": (0, 1, None),
+ "SUMMARY": (0, 1, None),
+ "UID": (0, 1, None),
+ "URL": (0, 1, None),
+ "RECURRENCE-ID": (0, 1, None),
+ "ATTACH": (0, None, None),
+ "ATTENDEE": (0, None, None),
+ "CATEGORIES": (0, None, None),
+ "COMMENT": (0, None, None),
+ "CONTACT": (0, None, None),
+ "EXDATE": (0, None, None),
+ "EXRULE": (0, None, None),
+ "REQUEST-STATUS": (0, None, None),
+ "RELATED-TO": (0, None, None),
+ "RDATE": (0, None, None),
+ "RRULE": (0, None, None),
}
+
+
registerBehavior(VJournal)
@@ -1268,26 +1315,28 @@ class VFreeBusy(VCalendarComponentBehavior):
"""
Free/busy state behavior.
"""
- name = 'VFREEBUSY'
- description = 'A grouping of component properties that describe either a \
+
+ name = "VFREEBUSY"
+ description = "A grouping of component properties that describe either a \
request for free/busy time, describe a response to a request \
- for free/busy time or describe a published set of busy time.'
- sortFirst = ('uid', 'dtstart', 'duration', 'dtend')
+ for free/busy time or describe a published set of busy time."
+ sortFirst = ("uid", "dtstart", "duration", "dtend")
knownChildren = {
- 'DTSTART': (0, 1, None), # min, max, behaviorRegistry id
- 'CONTACT': (0, 1, None),
- 'DTEND': (0, 1, None),
- 'DURATION': (0, 1, None),
- 'ORGANIZER': (0, 1, None),
- 'DTSTAMP': (1, 1, None),
- 'UID': (0, 1, None),
- 'URL': (0, 1, None),
- 'ATTENDEE': (0, None, None),
- 'COMMENT': (0, None, None),
- 'FREEBUSY': (0, None, None),
- 'REQUEST-STATUS': (0, None, None)
+ "DTSTART": (0, 1, None), # min, max, behaviorRegistry id
+ "CONTACT": (0, 1, None),
+ "DTEND": (0, 1, None),
+ "DURATION": (0, 1, None),
+ "ORGANIZER": (0, 1, None),
+ "DTSTAMP": (1, 1, None),
+ "UID": (0, 1, None),
+ "URL": (0, 1, None),
+ "ATTENDEE": (0, None, None),
+ "COMMENT": (0, None, None),
+ "FREEBUSY": (0, None, None),
+ "REQUEST-STATUS": (0, None, None),
}
+
registerBehavior(VFreeBusy)
@@ -1295,15 +1344,16 @@ class VAlarm(VCalendarComponentBehavior):
"""
Alarm behavior.
"""
- name = 'VALARM'
- description = 'Alarms describe when and how to provide alerts about events \
- and to-dos.'
+
+ name = "VALARM"
+ description = "Alarms describe when and how to provide alerts about events \
+ and to-dos."
knownChildren = {
- 'ACTION': (1, 1, None), # min, max, behaviorRegistry id
- 'TRIGGER': (1, 1, None),
- 'DURATION': (0, 1, None),
- 'REPEAT': (0, 1, None),
- 'DESCRIPTION': (0, 1, None)
+ "ACTION": (1, 1, None), # min, max, behaviorRegistry id
+ "TRIGGER": (1, 1, None),
+ "DURATION": (0, 1, None),
+ "REPEAT": (0, 1, None),
+ "DESCRIPTION": (0, 1, None),
}
@staticmethod
@@ -1314,11 +1364,11 @@ def generateImplicitParameters(obj):
try:
obj.action
except AttributeError:
- obj.add('action').value = 'AUDIO'
+ obj.add("action").value = "AUDIO"
try:
obj.trigger
except AttributeError:
- obj.add('trigger').value = datetime.timedelta(0)
+ obj.add("trigger").value = datetime.timedelta(0)
@classmethod
def validate(cls, obj, raiseException, *args):
@@ -1335,6 +1385,7 @@ def validate(cls, obj, raiseException, *args):
"""
return True
+
registerBehavior(VAlarm)
@@ -1344,31 +1395,32 @@ class VAvailability(VCalendarComponentBehavior):
Used to represent user's available time slots.
"""
- name = 'VAVAILABILITY'
- description = 'A component used to represent a user\'s available time slots.'
- sortFirst = ('uid', 'dtstart', 'duration', 'dtend')
+
+ name = "VAVAILABILITY"
+ description = "A component used to represent a user's available time slots."
+ sortFirst = ("uid", "dtstart", "duration", "dtend")
knownChildren = {
- 'UID': (1, 1, None), # min, max, behaviorRegistry id
- 'DTSTAMP': (1, 1, None),
- 'BUSYTYPE': (0, 1, None),
- 'CREATED': (0, 1, None),
- 'DTSTART': (0, 1, None),
- 'LAST-MODIFIED': (0, 1, None),
- 'ORGANIZER': (0, 1, None),
- 'SEQUENCE': (0, 1, None),
- 'SUMMARY': (0, 1, None),
- 'URL': (0, 1, None),
- 'DTEND': (0, 1, None),
- 'DURATION': (0, 1, None),
- 'CATEGORIES': (0, None, None),
- 'COMMENT': (0, None, None),
- 'CONTACT': (0, None, None),
- 'AVAILABLE': (0, None, None),
+ "UID": (1, 1, None), # min, max, behaviorRegistry id
+ "DTSTAMP": (1, 1, None),
+ "BUSYTYPE": (0, 1, None),
+ "CREATED": (0, 1, None),
+ "DTSTART": (0, 1, None),
+ "LAST-MODIFIED": (0, 1, None),
+ "ORGANIZER": (0, 1, None),
+ "SEQUENCE": (0, 1, None),
+ "SUMMARY": (0, 1, None),
+ "URL": (0, 1, None),
+ "DTEND": (0, 1, None),
+ "DURATION": (0, 1, None),
+ "CATEGORIES": (0, None, None),
+ "COMMENT": (0, None, None),
+ "CONTACT": (0, None, None),
+ "AVAILABLE": (0, None, None),
}
@classmethod
def validate(cls, obj, raiseException, *args):
- if 'dtend' in obj.contents and 'duration' in obj.contents:
+ if "dtend" in obj.contents and "duration" in obj.contents:
if raiseException:
m = "VAVAILABILITY components cannot contain both DTEND and DURATION components"
raise ValidateError(m)
@@ -1376,6 +1428,7 @@ def validate(cls, obj, raiseException, *args):
else:
return super(VAvailability, cls).validate(obj, raiseException, *args)
+
registerBehavior(VAvailability)
@@ -1383,31 +1436,32 @@ class Available(RecurringBehavior):
"""
Event behavior.
"""
- name = 'AVAILABLE'
- sortFirst = ('uid', 'recurrence-id', 'dtstart', 'duration', 'dtend')
- description = 'Defines a period of time in which a user is normally available.'
+
+ name = "AVAILABLE"
+ sortFirst = ("uid", "recurrence-id", "dtstart", "duration", "dtend")
+ description = "Defines a period of time in which a user is normally available."
knownChildren = {
- 'DTSTAMP': (1, 1, None), # min, max, behaviorRegistry id
- 'DTSTART': (1, 1, None),
- 'UID': (1, 1, None),
- 'DTEND': (0, 1, None), # NOTE: One of DtEnd or
- 'DURATION': (0, 1, None), # Duration must appear, but not both
- 'CREATED': (0, 1, None),
- 'LAST-MODIFIED': (0, 1, None),
- 'RECURRENCE-ID': (0, 1, None),
- 'RRULE': (0, 1, None),
- 'SUMMARY': (0, 1, None),
- 'CATEGORIES': (0, None, None),
- 'COMMENT': (0, None, None),
- 'CONTACT': (0, None, None),
- 'EXDATE': (0, None, None),
- 'RDATE': (0, None, None),
+ "DTSTAMP": (1, 1, None), # min, max, behaviorRegistry id
+ "DTSTART": (1, 1, None),
+ "UID": (1, 1, None),
+ "DTEND": (0, 1, None), # NOTE: One of DtEnd or
+ "DURATION": (0, 1, None), # Duration must appear, but not both
+ "CREATED": (0, 1, None),
+ "LAST-MODIFIED": (0, 1, None),
+ "RECURRENCE-ID": (0, 1, None),
+ "RRULE": (0, 1, None),
+ "SUMMARY": (0, 1, None),
+ "CATEGORIES": (0, None, None),
+ "COMMENT": (0, None, None),
+ "CONTACT": (0, None, None),
+ "EXDATE": (0, None, None),
+ "RDATE": (0, None, None),
}
@classmethod
def validate(cls, obj, raiseException, *args):
- has_dtend = 'dtend' in obj.contents
- has_duration = 'duration' in obj.contents
+ has_dtend = "dtend" in obj.contents
+ has_duration = "duration" in obj.contents
if has_dtend and has_duration:
if raiseException:
m = "AVAILABLE components cannot contain both DTEND and DURATION\
@@ -1423,6 +1477,7 @@ def validate(cls, obj, raiseException, *args):
else:
return super(Available, cls).validate(obj, raiseException, *args)
+
registerBehavior(Available)
@@ -1430,7 +1485,8 @@ class Duration(behavior.Behavior):
"""
Behavior for Duration ContentLines. Transform to datetime.timedelta.
"""
- name = 'DURATION'
+
+ name = "DURATION"
hasNative = True
@staticmethod
@@ -1442,7 +1498,7 @@ def transformToNative(obj):
return obj
obj.isNative = True
obj.value = obj.value
- if obj.value == '':
+ if obj.value == "":
return obj
else:
deltalist = stringToDurations(obj.value)
@@ -1464,6 +1520,7 @@ def transformFromNative(obj):
obj.value = timedeltaToString(obj.value)
return obj
+
registerBehavior(Duration)
@@ -1471,8 +1528,9 @@ class Trigger(behavior.Behavior):
"""
DATE-TIME or DURATION
"""
- name = 'TRIGGER'
- description = 'This property specifies when an alarm will trigger.'
+
+ name = "TRIGGER"
+ description = "This property specifies when an alarm will trigger."
hasNative = True
forceUTC = True
@@ -1483,28 +1541,29 @@ def transformToNative(obj):
"""
if obj.isNative:
return obj
- value = getattr(obj, 'value_param', 'DURATION').upper()
- if hasattr(obj, 'value_param'):
+ value = getattr(obj, "value_param", "DURATION").upper()
+ if hasattr(obj, "value_param"):
del obj.value_param
- if obj.value == '':
+ if obj.value == "":
obj.isNative = True
return obj
- elif value == 'DURATION':
+ elif value == "DURATION":
try:
return Duration.transformToNative(obj)
except ParseError:
- logger.warning("TRIGGER not recognized as DURATION, trying "
- "DATE-TIME, because iCal sometimes exports "
- "DATE-TIMEs without setting VALUE=DATE-TIME")
+ logger.warning(
+ "TRIGGER not recognized as DURATION, trying "
+ "DATE-TIME, because iCal sometimes exports "
+ "DATE-TIMEs without setting VALUE=DATE-TIME"
+ )
try:
obj.isNative = False
dt = DateTimeBehavior.transformToNative(obj)
return dt
- except:
- msg = "TRIGGER with no VALUE not recognized as DURATION " \
- "or as DATE-TIME"
+ except Exception:
+ msg = "TRIGGER with no VALUE not recognized as DURATION " "or as DATE-TIME"
raise ParseError(msg)
- elif value == 'DATE-TIME':
+ elif value == "DATE-TIME":
# TRIGGERs with DATE-TIME values must be in UTC, we could validate
# that fact, for now we take it on faith.
return DateTimeBehavior.transformToNative(obj)
@@ -1513,14 +1572,15 @@ def transformToNative(obj):
@staticmethod
def transformFromNative(obj):
- if type(obj.value) == datetime.datetime:
- obj.value_param = 'DATE-TIME'
+ if type(obj.value) is datetime.datetime:
+ obj.value_param = "DATE-TIME"
return UTCDateTimeBehavior.transformFromNative(obj)
- elif type(obj.value) == datetime.timedelta:
+ elif type(obj.value) is datetime.timedelta:
return Duration.transformFromNative(obj)
else:
- raise NativeError("Native TRIGGER values must be timedelta or "
- "datetime")
+ raise NativeError("Native TRIGGER values must be timedelta or " "datetime")
+
+
registerBehavior(Trigger)
@@ -1528,6 +1588,7 @@ class PeriodBehavior(behavior.Behavior):
"""
A list of (date-time, timedelta) tuples.
"""
+
hasNative = True
@staticmethod
@@ -1538,10 +1599,10 @@ def transformToNative(obj):
if obj.isNative:
return obj
obj.isNative = True
- if obj.value == '':
+ if obj.value == "":
obj.value = []
return obj
- tzinfo = getTzid(getattr(obj, 'tzid_param', None))
+ tzinfo = getTzid(getattr(obj, "tzid_param", None))
obj.value = [stringToPeriod(x, tzinfo) for x in obj.value.split(",")]
return obj
@@ -1560,7 +1621,7 @@ def transformFromNative(cls, obj):
if not cls.forceUTC and tzid is not None:
obj.tzid_param = tzid
- obj.value = ','.join(transformed)
+ obj.value = ",".join(transformed)
return obj
@@ -1569,9 +1630,12 @@ class FreeBusy(PeriodBehavior):
"""
Free or busy period of time, must be specified in UTC.
"""
- name = 'FREEBUSY'
+
+ name = "FREEBUSY"
forceUTC = True
-registerBehavior(FreeBusy, 'FREEBUSY')
+
+
+registerBehavior(FreeBusy, "FREEBUSY")
class RRule(behavior.Behavior):
@@ -1579,30 +1643,42 @@ class RRule(behavior.Behavior):
Dummy behavior to avoid having RRULEs being treated as text lines (and thus
having semi-colons inaccurately escaped).
"""
-registerBehavior(RRule, 'RRULE')
-registerBehavior(RRule, 'EXRULE')
+registerBehavior(RRule, "RRULE")
+registerBehavior(RRule, "EXRULE")
+
# ------------------------ Registration of common classes ----------------------
-utcDateTimeList = ['LAST-MODIFIED', 'CREATED', 'COMPLETED', 'DTSTAMP']
+utcDateTimeList = ["LAST-MODIFIED", "CREATED", "COMPLETED", "DTSTAMP"]
list(map(lambda x: registerBehavior(UTCDateTimeBehavior, x), utcDateTimeList))
-dateTimeOrDateList = ['DTEND', 'DTSTART', 'DUE', 'RECURRENCE-ID']
-list(map(lambda x: registerBehavior(DateOrDateTimeBehavior, x),
- dateTimeOrDateList))
-
-registerBehavior(MultiDateBehavior, 'RDATE')
-registerBehavior(MultiDateBehavior, 'EXDATE')
-
-
-textList = ['CALSCALE', 'METHOD', 'PRODID', 'CLASS', 'COMMENT', 'DESCRIPTION',
- 'LOCATION', 'STATUS', 'SUMMARY', 'TRANSP', 'CONTACT', 'RELATED-TO',
- 'UID', 'ACTION', 'BUSYTYPE']
+dateTimeOrDateList = ["DTEND", "DTSTART", "DUE", "RECURRENCE-ID"]
+list(map(lambda x: registerBehavior(DateOrDateTimeBehavior, x), dateTimeOrDateList))
+
+registerBehavior(MultiDateBehavior, "RDATE")
+registerBehavior(MultiDateBehavior, "EXDATE")
+
+textList = [
+ "CALSCALE",
+ "METHOD",
+ "PRODID",
+ "CLASS",
+ "COMMENT",
+ "DESCRIPTION",
+ "LOCATION",
+ "STATUS",
+ "SUMMARY",
+ "TRANSP",
+ "CONTACT",
+ "RELATED-TO",
+ "UID",
+ "ACTION",
+ "BUSYTYPE",
+]
list(map(lambda x: registerBehavior(TextBehavior, x), textList))
-list(map(lambda x: registerBehavior(MultiTextBehavior, x), ['CATEGORIES',
- 'RESOURCES']))
-registerBehavior(SemicolonMultiTextBehavior, 'REQUEST-STATUS')
+list(map(lambda x: registerBehavior(MultiTextBehavior, x), ["CATEGORIES", "RESOURCES"]))
+registerBehavior(SemicolonMultiTextBehavior, "REQUEST-STATUS")
# ------------------------ Serializing helper functions ------------------------
@@ -1614,7 +1690,7 @@ def numToDigits(num, places):
if len(s) < places:
return ("0" * (places - len(s))) + s
elif len(s) > places:
- return s[len(s)-places:]
+ return s[len(s) - places :]
else:
return s
@@ -1633,22 +1709,22 @@ def timedeltaToString(delta):
minutes = int((delta.seconds % 3600) / 60)
seconds = int(delta.seconds % 60)
- output = ''
+ output = ""
if sign == -1:
- output += '-'
- output += 'P'
+ output += "-"
+ output += "P"
if days:
- output += '{}D'.format(days)
+ output += "{}D".format(days)
if hours or minutes or seconds:
- output += 'T'
+ output += "T"
elif not days: # Deal with zero duration
- output += 'T0S'
+ output += "T0S"
if hours:
- output += '{}H'.format(hours)
+ output += "{}H".format(hours)
if minutes:
- output += '{}M'.format(minutes)
+ output += "{}M".format(minutes)
if seconds:
- output += '{}S'.format(seconds)
+ output += "{}S".format(seconds)
return output
@@ -1657,15 +1733,15 @@ def timeToString(dateOrDateTime):
Wraps dateToString and dateTimeToString, returning the results
of either based on the type of the argument
"""
- if hasattr(dateOrDateTime, 'hour'):
+ if hasattr(dateOrDateTime, "hour"):
return dateTimeToString(dateOrDateTime)
return dateToString(dateOrDateTime)
def dateToString(date):
- year = numToDigits(date.year, 4)
+ year = numToDigits(date.year, 4)
month = numToDigits(date.month, 2)
- day = numToDigits(date.day, 2)
+ day = numToDigits(date.day, 2)
return year + month + day
@@ -1736,12 +1812,12 @@ def stringToDateTime(s, tzinfo=None):
minute = int(s[11:13])
second = int(s[13:15])
if len(s) > 15:
- if s[15] == 'Z':
- tzinfo = getTzid('UTC')
- except:
+ if s[15] == "Z":
+ tzinfo = getTzid("UTC")
+ except Exception:
raise ParseError("'{0!s}' is not a valid DATE-TIME".format(s))
year = year and year or 2000
- if tzinfo is not None and hasattr(tzinfo,'localize'): # PyTZ case
+ if tzinfo is not None and hasattr(tzinfo, "localize"): # PyTZ case
return tzinfo.localize(datetime.datetime(year, month, day, hour, minute, second))
return datetime.datetime(year, month, day, hour, minute, second, 0, tzinfo)
@@ -1751,7 +1827,7 @@ def stringToDateTime(s, tzinfo=None):
escapableCharList = '\\;,Nn"'
-def stringToTextValues(s, listSeparator=',', charList=None, strict=False):
+def stringToTextValues(s, listSeparator=",", charList=None, strict=False):
"""
Returns list of strings.
"""
@@ -1777,11 +1853,11 @@ def error(msg):
while True:
try:
charIndex, char = next(charIterator)
- except:
+ except Exception:
char = "eof"
if state == "read normal":
- if char == '\\':
+ if char == "\\":
state = "read escaped char"
elif char == listSeparator:
state = "read normal"
@@ -1797,14 +1873,14 @@ def error(msg):
elif state == "read escaped char":
if escapableChar(char):
state = "read normal"
- if char in 'nN':
- current.append('\n')
+ if char in "nN":
+ current.append("\n")
else:
current.append(char)
else:
state = "read normal"
# leave unrecognized escaped characters for later passes
- current.append('\\' + char)
+ current.append("\\" + char)
elif state == "end": # an end state
if len(current) or len(results) == 0:
@@ -1824,6 +1900,7 @@ def stringToDurations(s, strict=False):
"""
Returns list of timedelta objects.
"""
+
def makeTimedelta(sign, week, day, hour, minute, sec):
if sign == "-":
sign = -1
@@ -1834,8 +1911,7 @@ def makeTimedelta(sign, week, day, hour, minute, sec):
hour = int(hour)
minute = int(minute)
sec = int(sec)
- return sign * datetime.timedelta(weeks=week, days=day, hours=hour,
- minutes=minute, seconds=sec)
+ return sign * datetime.timedelta(weeks=week, days=day, hours=hour, minutes=minute, seconds=sec)
def error(msg):
if strict:
@@ -1859,17 +1935,17 @@ def error(msg):
while True:
try:
charIndex, char = next(charIterator)
- except:
+ except Exception:
char = "eof"
if state == "start":
- if char == '+':
+ if char == "+":
state = "start"
sign = char
- elif char == '-':
+ elif char == "-":
state = "start"
sign = char
- elif char.upper() == 'P':
+ elif char.upper() == "P":
state = "read field"
elif char == "eof":
state = "error"
@@ -1879,39 +1955,37 @@ def error(msg):
current = current + char # update this part when updating "read field"
else:
state = "error"
- error("got unexpected character {0} reading in duration: {1}"
- .format(char, s))
+ error("got unexpected character {0} reading in duration: {1}".format(char, s))
elif state == "read field":
- if (char in string.digits):
+ if char in string.digits:
state = "read field"
current = current + char # update part above when updating "read field"
- elif char.upper() == 'T':
+ elif char.upper() == "T":
state = "read field"
- elif char.upper() == 'W':
+ elif char.upper() == "W":
state = "read field"
week = current
current = ""
- elif char.upper() == 'D':
+ elif char.upper() == "D":
state = "read field"
day = current
current = ""
- elif char.upper() == 'H':
+ elif char.upper() == "H":
state = "read field"
hour = current
current = ""
- elif char.upper() == 'M':
+ elif char.upper() == "M":
state = "read field"
minute = current
current = ""
- elif char.upper() == 'S':
+ elif char.upper() == "S":
state = "read field"
sec = current
current = ""
elif char == ",":
state = "start"
- durations.append(makeTimedelta(sign, week, day, hour, minute,
- sec))
+ durations.append(makeTimedelta(sign, week, day, hour, minute, sec))
current = ""
sign = None
week = None
@@ -1926,9 +2000,8 @@ def error(msg):
error("got unexpected character reading in duration: " + s)
elif state == "end": # an end state
- if (sign or week or day or hour or minute or sec):
- durations.append(makeTimedelta(sign, week, day, hour, minute,
- sec))
+ if sign or week or day or hour or minute or sec:
+ durations.append(makeTimedelta(sign, week, day, hour, minute, sec))
return durations
elif state == "error": # an end state
@@ -1948,14 +2021,14 @@ def parseDtstart(contentline, allowSignatureMismatch=False):
parameter, so rather than failing on these (technically invalid) lines,
if allowSignatureMismatch is True, try to parse both varieties.
"""
- tzinfo = getTzid(getattr(contentline, 'tzid_param', None))
- valueParam = getattr(contentline, 'value_param', 'DATE-TIME').upper()
+ tzinfo = getTzid(getattr(contentline, "tzid_param", None))
+ valueParam = getattr(contentline, "value_param", "DATE-TIME").upper()
if valueParam == "DATE":
return stringToDate(contentline.value)
elif valueParam == "DATE-TIME":
try:
return stringToDateTime(contentline.value, tzinfo)
- except:
+ except Exception:
if allowSignatureMismatch:
return stringToDate(contentline.value)
else:
@@ -1968,15 +2041,16 @@ def stringToPeriod(s, tzinfo=None):
valEnd = values[1]
if isDuration(valEnd): # period-start = date-time "/" dur-value
delta = stringToDurations(valEnd)[0]
- return (start, delta)
+ return start, delta
else:
- return (start, stringToDateTime(valEnd, tzinfo))
+ return start, stringToDateTime(valEnd, tzinfo)
def getTransition(transitionTo, year, tzinfo):
"""
Return the datetime of the transition to/from DST, or None.
"""
+
def firstTransition(iterDates, test):
"""
Return the last date not matching test, or None if all tests matched.
@@ -2010,8 +2084,9 @@ def generateDates(year, month=None, day=None):
for hour in hours:
yield datetime.datetime(year, month, day, hour)
- assert transitionTo in ('daylight', 'standard')
- if transitionTo == 'daylight':
+ assert transitionTo in ("daylight", "standard")
+ if transitionTo == "daylight":
+
def test(dt):
try:
return tzinfo.dst(dt) != zeroDelta
@@ -2019,7 +2094,9 @@ def test(dt):
return True # entering daylight time
except pytz.AmbiguousTimeError:
return False # entering standard time
- elif transitionTo == 'standard':
+
+ elif transitionTo == "standard":
+
def test(dt):
try:
return tzinfo.dst(dt) == zeroDelta
@@ -2027,6 +2104,7 @@ def test(dt):
return False # entering daylight time
except pytz.AmbiguousTimeError:
return True # entering standard time
+
newyear = datetime.datetime(year, 1, 1)
monthDt = firstTransition(generateDates(year), test)
if monthDt is None:
@@ -2038,7 +2116,7 @@ def test(dt):
month = monthDt.month
day = firstTransition(generateDates(year, month), test).day
uncorrected = firstTransition(generateDates(year, month, day), test)
- if transitionTo == 'standard':
+ if transitionTo == "standard":
# assuming tzinfo.dst returns a new offset for the first
# possible hour, we need to add one hour for the offset change
# and another hour because firstTransition returns the hour
@@ -2065,7 +2143,7 @@ def dt_test(dt):
if not dt_test(datetime.datetime(startYear, 1, 1)):
return False
for year in range(startYear, endYear):
- for transitionTo in 'daylight', 'standard':
+ for transitionTo in "daylight", "standard":
t1 = getTransition(transitionTo, year, tzinfo1)
t2 = getTransition(transitionTo, year, tzinfo2)
if t1 != t2 or not dt_test(t1):
@@ -2074,6 +2152,7 @@ def dt_test(dt):
# ------------------- Testing and running functions ----------------------------
-if __name__ == '__main__':
+if __name__ == "__main__":
import tests
+
tests._test()
diff --git a/vobject/ics_diff.py b/vobject/ics_diff.py
index 6d48e3b..3433890 100644
--- a/vobject/ics_diff.py
+++ b/vobject/ics_diff.py
@@ -11,19 +11,19 @@
def getSortKey(component):
def getUID(component):
- return component.getChildValue('uid', '')
+ return component.getChildValue("uid", "")
# it's not quite as simple as getUID, need to account for recurrenceID and
# sequence
def getSequence(component):
- sequence = component.getChildValue('sequence', 0)
+ sequence = component.getChildValue("sequence", 0)
return "{0:05d}".format(int(sequence))
def getRecurrenceID(component):
- recurrence_id = component.getChildValue('recurrence_id', None)
+ recurrence_id = component.getChildValue("recurrence_id", None)
if recurrence_id is None:
- return '0000-00-00'
+ return "0000-00-00"
else:
return recurrence_id.isoformat()
@@ -42,9 +42,9 @@ def deleteExtraneous(component, ignore_dtstamp=False):
for comp in component.components():
deleteExtraneous(comp, ignore_dtstamp)
for line in component.lines():
- if 'X-VOBJ-ORIGINAL-TZID' in line.params:
- del line.params['X-VOBJ-ORIGINAL-TZID']
- if ignore_dtstamp and hasattr(component, 'dtstamp_list'):
+ if "X-VOBJ-ORIGINAL-TZID" in line.params:
+ del line.params["X-VOBJ-ORIGINAL-TZID"]
+ if ignore_dtstamp and hasattr(component, "dtstamp_list"):
del component.dtstamp_list
@@ -118,14 +118,12 @@ def processComponentPair(leftComp, rightComp):
for key in leftChildKeys:
rightList = rightComp.contents.get(key, [])
if isinstance(leftComp.contents[key][0], Component):
- compDifference = processComponentLists(leftComp.contents[key],
- rightList)
+ compDifference = processComponentLists(leftComp.contents[key], rightList)
if len(compDifference) > 0:
differentComponents[key] = compDifference
elif leftComp.contents[key] != rightList:
- differentContentLines.append((leftComp.contents[key],
- rightList))
+ differentContentLines.append((leftComp.contents[key], rightList))
for key in rightChildKeys:
if key not in leftChildKeys:
@@ -141,10 +139,10 @@ def processComponentPair(leftComp, rightComp):
right = newFromBehavior(leftComp.name)
# add a UID, if one existed, despite the fact that they'll always be
# the same
- uid = leftComp.getChildValue('uid')
+ uid = leftComp.getChildValue("uid")
if uid is not None:
- left.add('uid').value = uid
- right.add('uid').value = uid
+ left.add("uid").value = uid
+ right.add("uid").value = uid
for name, childPairList in differentComponents.items():
leftComponents, rightComponents = zip(*childPairList)
@@ -165,11 +163,13 @@ def processComponentPair(leftComp, rightComp):
return left, right
- vevents = processComponentLists(sortByUID(getattr(left, 'vevent_list', [])),
- sortByUID(getattr(right, 'vevent_list', [])))
+ vevents = processComponentLists(
+ sortByUID(getattr(left, "vevent_list", [])), sortByUID(getattr(right, "vevent_list", []))
+ )
- vtodos = processComponentLists(sortByUID(getattr(left, 'vtodo_list', [])),
- sortByUID(getattr(right, 'vtodo_list', [])))
+ vtodos = processComponentLists(
+ sortByUID(getattr(left, "vtodo_list", [])), sortByUID(getattr(right, "vtodo_list", []))
+ )
return vevents + vtodos
@@ -198,18 +198,25 @@ def main():
deleteExtraneous(cal2, ignore_dtstamp=ignore_dtstamp)
prettyDiff(cal1, cal2)
+
version = "0.1"
def getOptions():
- ##### Configuration options #####
+ # ----Configuration options---- #
usage = "usage: %prog [options] ics_file1 ics_file2"
parser = OptionParser(usage=usage, version=version)
parser.set_description("ics_diff will print a comparison of two iCalendar files ")
- parser.add_option("-i", "--ignore-dtstamp", dest="ignore", action="store_true",
- default=False, help="ignore DTSTAMP lines [default: False]")
+ parser.add_option(
+ "-i",
+ "--ignore-dtstamp",
+ dest="ignore",
+ action="store_true",
+ default=False,
+ help="ignore DTSTAMP lines [default: False]",
+ )
(cmdline_options, args) = parser.parse_args()
if len(args) < 2:
@@ -220,6 +227,7 @@ def getOptions():
return cmdline_options, args
+
if __name__ == "__main__":
try:
main()
diff --git a/vobject/vcard.py b/vobject/vcard.py
index 59c87aa..cb61b25 100644
--- a/vobject/vcard.py
+++ b/vobject/vcard.py
@@ -3,17 +3,14 @@
import codecs
from . import behavior
-
-from .base import ContentLine, registerBehavior, backslashEscape, basestring, str_
+from .base import ContentLine, backslashEscape, basestring, registerBehavior, str_
from .icalendar import stringToTextValues
-
# ------------------------ vCard structs ---------------------------------------
class Name(object):
- def __init__(self, family='', given='', additional='', prefix='',
- suffix=''):
+ def __init__(self, family="", given="", additional="", prefix="", suffix=""):
"""
Each name attribute can be a string or a list of strings.
"""
@@ -28,13 +25,11 @@ def toString(val):
"""
Turn a string or array value into a string.
"""
- if type(val) in (list, tuple):
- return ' '.join(val)
- return val
+ return " ".join(val) if type(val) in (list, tuple) else val
def __str__(self):
- eng_order = ('prefix', 'given', 'additional', 'family', 'suffix')
- out = ' '.join(self.toString(getattr(self, val)) for val in eng_order)
+ eng_order = ("prefix", "given", "additional", "family", "suffix")
+ out = " ".join(self.toString(getattr(self, val)) for val in eng_order)
return str_(out)
def __repr__(self):
@@ -42,18 +37,19 @@ def __repr__(self):
def __eq__(self, other):
try:
- return (self.family == other.family and
- self.given == other.given and
- self.additional == other.additional and
- self.prefix == other.prefix and
- self.suffix == other.suffix)
- except:
+ return (
+ self.family == other.family
+ and self.given == other.given
+ and self.additional == other.additional
+ and self.prefix == other.prefix
+ and self.suffix == other.suffix
+ )
+ except Exception:
return False
class Address(object):
- def __init__(self, street='', city='', region='', code='',
- country='', box='', extended=''):
+ def __init__(self, street="", city="", region="", code="", country="", box="", extended=""):
"""
Each name attribute can be a string or a list of strings.
"""
@@ -66,7 +62,7 @@ def __init__(self, street='', city='', region='', code='',
self.country = country
@staticmethod
- def toString(val, join_char='\n'):
+ def toString(val, join_char="\n"):
"""
Turn a string or array value into a string.
"""
@@ -74,17 +70,15 @@ def toString(val, join_char='\n'):
return join_char.join(val)
return val
- lines = ('box', 'extended', 'street')
- one_line = ('city', 'region', 'code')
+ lines = ("box", "extended", "street")
+ one_line = ("city", "region", "code")
def __str__(self):
- lines = '\n'.join(self.toString(getattr(self, val))
- for val in self.lines if getattr(self, val))
- one_line = tuple(self.toString(getattr(self, val), ' ')
- for val in self.one_line)
+ lines = "\n".join(self.toString(getattr(self, val)) for val in self.lines if getattr(self, val))
+ one_line = tuple(self.toString(getattr(self, val), " ") for val in self.one_line)
lines += "\n{0!s}, {1!s} {2!s}".format(*one_line)
if self.country:
- lines += '\n' + self.toString(self.country)
+ lines += "\n" + self.toString(self.country)
return lines
def __repr__(self):
@@ -92,19 +86,22 @@ def __repr__(self):
def __eq__(self, other):
try:
- return (self.box == other.box and
- self.extended == other.extended and
- self.street == other.street and
- self.city == other.city and
- self.region == other.region and
- self.code == other.code and
- self.country == other.country)
- except:
+ return (
+ self.box == other.box
+ and self.extended == other.extended
+ and self.street == other.street
+ and self.city == other.city
+ and self.region == other.region
+ and self.code == other.code
+ and self.country == other.country
+ )
+ except Exception:
return False
# ------------------------ Registered Behavior subclasses ----------------------
+
class VCardTextBehavior(behavior.Behavior):
"""
Provide backslash escape encoding/decoding for single valued properties.
@@ -112,8 +109,9 @@ class VCardTextBehavior(behavior.Behavior):
TextBehavior also deals with base64 encoding if the ENCODING parameter is
explicitly set to BASE64.
"""
+
allowGroup = True
- base64string = 'B'
+ base64string = "B"
@classmethod
def decode(cls, line):
@@ -126,10 +124,10 @@ def decode(cls, line):
ENCODING=b
"""
if line.encoded:
- if 'BASE64' in line.singletonparams:
- line.singletonparams.remove('BASE64')
+ if "BASE64" in line.singletonparams:
+ line.singletonparams.remove("BASE64")
line.encoding_param = cls.base64string
- encoding = getattr(line, 'encoding_param', None)
+ encoding = getattr(line, "encoding_param", None)
if encoding:
if isinstance(line.value, bytes):
line.value = codecs.decode(line.value, "base64")
@@ -145,10 +143,10 @@ def encode(cls, line):
Backslash escape line.value.
"""
if not line.encoded:
- encoding = getattr(line, 'encoding_param', None)
+ encoding = getattr(line, "encoding_param", None)
if encoding and encoding.upper() == cls.base64string:
if isinstance(line.value, bytes):
- line.value = codecs.encode(line.value, "base64").decode("utf-8").replace('\n', '')
+ line.value = codecs.encode(line.value, "base64").decode("utf-8").replace("\n", "")
else:
line.value = codecs.encode(line.value.encode(encoding), "base64").decode("utf-8")
else:
@@ -165,22 +163,23 @@ class VCard3_0(VCardBehavior):
"""
vCard 3.0 behavior.
"""
- name = 'VCARD'
- description = 'vCard 3.0, defined in rfc2426'
- versionString = '3.0'
+
+ name = "VCARD"
+ description = "vCard 3.0, defined in rfc2426"
+ versionString = "3.0"
isComponent = True
- sortFirst = ('version', 'prodid', 'uid')
+ sortFirst = ("version", "prodid", "uid")
knownChildren = {
- 'N': (0, 1, None), # min, max, behaviorRegistry id
- 'FN': (1, None, None),
- 'VERSION': (1, 1, None), # required, auto-generated
- 'PRODID': (0, 1, None),
- 'LABEL': (0, None, None),
- 'UID': (0, None, None),
- 'ADR': (0, None, None),
- 'ORG': (0, None, None),
- 'PHOTO': (0, None, None),
- 'CATEGORIES': (0, None, None)
+ "N": (0, 1, None), # min, max, behaviorRegistry id
+ "FN": (1, None, None),
+ "VERSION": (1, 1, None), # required, auto-generated
+ "PRODID": (0, 1, None),
+ "LABEL": (0, None, None),
+ "UID": (0, None, None),
+ "ADR": (0, None, None),
+ "ORG": (0, None, None),
+ "PHOTO": (0, None, None),
+ "CATEGORIES": (0, None, None),
}
@classmethod
@@ -191,36 +190,42 @@ def generateImplicitParameters(cls, obj):
VTIMEZONEs will need to exist whenever TZID parameters exist or when
datetimes with tzinfo exist.
"""
- if not hasattr(obj, 'version'):
- obj.add(ContentLine('VERSION', [], cls.versionString))
+ if not hasattr(obj, "version"):
+ obj.add(ContentLine("VERSION", [], cls.versionString))
+
+
registerBehavior(VCard3_0, default=True)
class FN(VCardTextBehavior):
name = "FN"
- description = 'Formatted name'
+ description = "Formatted name"
+
+
registerBehavior(FN)
class Label(VCardTextBehavior):
name = "Label"
- description = 'Formatted address'
+ description = "Formatted address"
+
+
registerBehavior(Label)
wacky_apple_photo_serialize = True
-REALLY_LARGE = 1E50
+REALLY_LARGE = 1e50
class Photo(VCardTextBehavior):
name = "Photo"
- description = 'Photograph'
+ description = "Photograph"
@classmethod
def valueRepr(cls, line):
return " (BINARY PHOTO DATA at 0x{0!s}) ".format(id(line.value))
@classmethod
- def serialize(cls, obj, buf, lineLength, validate, *args, **kwargs):
+ def serialize(cls, obj, buf, lineLength, validate, *args, **kwargs):
"""
Apple's Address Book is *really* weird with images, it expects
base64 data to have very specific whitespace. It seems Address Book
@@ -229,6 +234,8 @@ def serialize(cls, obj, buf, lineLength, validate, *args, **kwargs):
if wacky_apple_photo_serialize:
lineLength = REALLY_LARGE
VCardTextBehavior.serialize(obj, buf, lineLength, validate, *args, **kwargs)
+
+
registerBehavior(Photo)
@@ -244,8 +251,7 @@ def splitFields(string):
"""
Return a list of strings or lists from a Name or Address.
"""
- return [toListOrString(i) for i in
- stringToTextValues(string, listSeparator=';', charList=';')]
+ return [toListOrString(i) for i in stringToTextValues(string, listSeparator=";", charList=";")]
def toList(stringOrList):
@@ -266,21 +272,20 @@ def serializeFields(obj, order=None):
fields = [backslashEscape(val) for val in obj]
else:
for field in order:
- escapedValueList = [backslashEscape(val) for val in
- toList(getattr(obj, field))]
- fields.append(','.join(escapedValueList))
- return ';'.join(fields)
+ escapedValueList = [backslashEscape(val) for val in toList(getattr(obj, field))]
+ fields.append(",".join(escapedValueList))
+ return ";".join(fields)
-NAME_ORDER = ('family', 'given', 'additional', 'prefix', 'suffix')
-ADDRESS_ORDER = ('box', 'extended', 'street', 'city', 'region', 'code',
- 'country')
+NAME_ORDER = ("family", "given", "additional", "prefix", "suffix")
+ADDRESS_ORDER = ("box", "extended", "street", "city", "region", "code", "country")
class NameBehavior(VCardBehavior):
"""
A structured name.
"""
+
hasNative = True
@staticmethod
@@ -302,13 +307,16 @@ def transformFromNative(obj):
obj.isNative = False
obj.value = serializeFields(obj.value, NAME_ORDER)
return obj
-registerBehavior(NameBehavior, 'N')
+
+
+registerBehavior(NameBehavior, "N")
class AddressBehavior(VCardBehavior):
"""
A structured address.
"""
+
hasNative = True
@staticmethod
@@ -330,13 +338,16 @@ def transformFromNative(obj):
obj.isNative = False
obj.value = serializeFields(obj.value, ADDRESS_ORDER)
return obj
-registerBehavior(AddressBehavior, 'ADR')
+
+
+registerBehavior(AddressBehavior, "ADR")
class OrgBehavior(VCardBehavior):
"""
A list of organization values and sub-organization values.
"""
+
hasNative = True
@staticmethod
@@ -360,4 +371,6 @@ def transformFromNative(obj):
obj.isNative = False
obj.value = serializeFields(obj.value)
return obj
-registerBehavior(OrgBehavior, 'ORG')
+
+
+registerBehavior(OrgBehavior, "ORG")
diff --git a/vobject/win32tz.py b/vobject/win32tz.py
index 5c2db66..242a38d 100644
--- a/vobject/win32tz.py
+++ b/vobject/win32tz.py
@@ -1,23 +1,19 @@
-import _winreg
-import struct
import datetime
+import struct
+
+import _winreg
handle = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
-tzparent = _winreg.OpenKey(handle,
- "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones")
+tzparent = _winreg.OpenKey(handle, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Time Zones")
parentsize = _winreg.QueryInfoKey(tzparent)[0]
-localkey = _winreg.OpenKey(handle,
- "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation")
+localkey = _winreg.OpenKey(handle, "SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation")
WEEKS = datetime.timedelta(7)
def list_timezones():
"""Return a list of all time zones known to the system."""
- l = []
- for i in xrange(parentsize):
- l.append(_winreg.EnumKey(tzparent, i))
- return l
+ return [_winreg.EnumKey(tzparent, i) for i in range(parentsize)]
class win32tz(datetime.tzinfo):
@@ -61,12 +57,10 @@ def tzname(self, dt):
def _isdst(self, dt):
dat = self.data
- dston = pickNthWeekday(dt.year, dat.dstmonth, dat.dstdayofweek,
- dat.dsthour, dat.dstminute, dat.dstweeknumber)
- dstoff = pickNthWeekday(dt.year, dat.stdmonth, dat.stddayofweek,
- dat.stdhour, dat.stdminute, dat.stdweeknumber)
+ dston = pickNthWeekday(dt.year, dat.dstmonth, dat.dstdayofweek, dat.dsthour, dat.dstminute, dat.dstweeknumber)
+ dstoff = pickNthWeekday(dt.year, dat.stdmonth, dat.stddayofweek, dat.stdhour, dat.stdminute, dat.stdweeknumber)
if dston < dstoff:
- return (dston <= dt.replace(tzinfo=None) < dstoff)
+ return dston <= dt.replace(tzinfo=None) < dstoff
else:
return not (dstoff <= dt.replace(tzinfo=None) < dston)
@@ -76,10 +70,9 @@ def __repr__(self):
def pickNthWeekday(year, month, dayofweek, hour, minute, whichweek):
"""dayofweek == 0 means Sunday, whichweek > 4 means last instance"""
- first = datetime.datetime(year=year, month=month, hour=hour, minute=minute,
- day=1)
+ first = datetime.datetime(year=year, month=month, hour=hour, minute=minute, day=1)
weekdayone = first.replace(day=((dayofweek - first.isoweekday()) % 7 + 1))
- for n in xrange(whichweek - 1, -1, -1):
+ for n in range(whichweek - 1, -1, -1):
dt = weekdayone + n * WEEKS
if dt.month == month:
return dt
@@ -92,12 +85,12 @@ def __init__(self, path):
"""Load path, or if path is empty, load local time."""
if path:
keydict = valuesToDict(_winreg.OpenKey(tzparent, path))
- self.display = keydict['Display']
- self.dstname = keydict['Dlt']
- self.stdname = keydict['Std']
+ self.display = keydict["Display"]
+ self.dstname = keydict["Dlt"]
+ self.stdname = keydict["Std"]
- #see http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
- tup = struct.unpack('=3l16h', keydict['TZI'])
+ # see http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
+ tup = struct.unpack("=3l16h", keydict["TZI"])
self.stdoffset = -tup[0] - tup[1] # Bias + StandardBias * -1
self.dstoffset = self.stdoffset - tup[2] # + DaylightBias * -1
@@ -118,17 +111,17 @@ def __init__(self, path):
else:
keydict = valuesToDict(localkey)
- self.stdname = keydict['StandardName']
- self.dstname = keydict['DaylightName']
+ self.stdname = keydict["StandardName"]
+ self.dstname = keydict["DaylightName"]
sourcekey = _winreg.OpenKey(tzparent, self.stdname)
- self.display = valuesToDict(sourcekey)['Display']
+ self.display = valuesToDict(sourcekey)["Display"]
- self.stdoffset = -keydict['Bias'] - keydict['StandardBias']
- self.dstoffset = self.stdoffset - keydict['DaylightBias']
+ self.stdoffset = -keydict["Bias"] - keydict["StandardBias"]
+ self.dstoffset = self.stdoffset - keydict["DaylightBias"]
- #see http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
- tup = struct.unpack('=8h', keydict['StandardStart'])
+ # see http://ww_winreg.jsiinc.com/SUBA/tip0300/rh0398.htm
+ tup = struct.unpack("=8h", keydict["StandardStart"])
offset = 0
self.stdmonth = tup[1 + offset]
@@ -137,7 +130,7 @@ def __init__(self, path):
self.stdhour = tup[4 + offset]
self.stdminute = tup[5 + offset]
- tup = struct.unpack('=8h', keydict['DaylightStart'])
+ tup = struct.unpack("=8h", keydict["DaylightStart"])
self.dstmonth = tup[1 + offset]
self.dstdayofweek = tup[2 + offset] # Sunday=0
self.dstweeknumber = tup[3 + offset] # Last = 5
@@ -147,16 +140,17 @@ def __init__(self, path):
def valuesToDict(key):
"""Convert a registry key's values to a dictionary."""
- d = {}
size = _winreg.QueryInfoKey(key)[1]
- for i in xrange(size):
- d[_winreg.EnumValue(key, i)[0]] = _winreg.EnumValue(key, i)[1]
- return d
+ return {_winreg.EnumValue(key, i)[0]: _winreg.EnumValue(key, i)[1] for i in range(size)}
def _test():
- import win32tz, doctest
- doctest.testmod(win32tz, verbose=0)
+ import doctest
+
+ import win32tz
+
+ doctest.testmod(win32tz, verbose=False)
+
-if __name__ == '__main__':
+if __name__ == "__main__":
_test()
From 2630c9f46c7a3ca460029d70f455fec0b6c97826 Mon Sep 17 00:00:00 2001
From: rsb-23 <57601627+rsb-23@users.noreply.github.com>
Date: Mon, 1 Apr 2024 22:52:14 +0530
Subject: [PATCH 2/2] All configs moved to toml
- lint changes by tools
---
.pre-commit-config.yaml | 6 ++++--
CONTRIBUTING.md | 1 -
pyproject.toml | 15 +++++++++++++++
setup.cfg | 9 ---------
test_files/ms_tzid.ics | 2 +-
test_files/ruby_rrule.ics | 2 +-
6 files changed, 21 insertions(+), 14 deletions(-)
create mode 100644 pyproject.toml
delete mode 100644 setup.cfg
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 25ee2cd..b2461ea 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -5,10 +5,10 @@ repos:
- id: isort
- repo: https://github.com/psf/black
- rev: 24.2.0
+ rev: 24.3.0
hooks:
- id: black
- args: [-l, "120", -C]
+
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
@@ -16,7 +16,9 @@ repos:
- id: end-of-file-fixer
- id: no-commit-to-branch
args: [-b, main, -b, master]
+
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
+ additional_dependencies: [flake8-pyproject]
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3f2e68e..5a93491 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -34,4 +34,3 @@ releases.
With the possible exception of major releases, all contributions must
maintain the existing API's syntax and semantics.
-
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000..377dbc2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,15 @@
+[tool.black]
+target-version = ["py37", "py38", "py39", "py310", "py311"]
+line-length = 120
+skip-magic-trailing-comma = true
+
+[tool.flake8]
+max-line-length = 120
+ignore = ["E203", "E266", "E501", "W503"]
+exclude = [".git", "__pycache__", "venv"]
+per-file-ignores = ["*/__init__.py: F401"]
+
+[tool.isort]
+profile = "black"
+line_length = 120
+multi_line_output = 3
diff --git a/setup.cfg b/setup.cfg
deleted file mode 100644
index 66e5a12..0000000
--- a/setup.cfg
+++ /dev/null
@@ -1,9 +0,0 @@
-[flake8]
-max-line-length = 120
-ignore = E203, E266, E501, W503
-exclude = .git, __pycache__, venv
-per-file-ignores = */__init__.py: F401
-
-[isort]
-profile = black
-multi_line_output = 3
diff --git a/test_files/ms_tzid.ics b/test_files/ms_tzid.ics
index 0db2c5c..3af37ed 100644
--- a/test_files/ms_tzid.ics
+++ b/test_files/ms_tzid.ics
@@ -36,4 +36,4 @@ BEGIN:VEVENT
UID:CommaTest
DTSTART;TZID="Canberra, Melbourne, Sydney":20080530T150000
END:VEVENT
-END:VCALENDAR
\ No newline at end of file
+END:VCALENDAR
diff --git a/test_files/ruby_rrule.ics b/test_files/ruby_rrule.ics
index 6999513..7ce44e8 100644
--- a/test_files/ruby_rrule.ics
+++ b/test_files/ruby_rrule.ics
@@ -13,4 +13,4 @@ SUMMARY:Something
DTSTART:20030101T070000
DTSTAMP:20080529T152100
END:VEVENT
-END:VCALENDAR
\ No newline at end of file
+END:VCALENDAR