Repository: py-vobject/vobject (language: python)

Issue:
Added linters and pre-commit hooks
⚠️ Python compatibility 3.7 and above only.

**Checks** :
- [x]  42 testcases ran successfully. (skipped=1)
- [x]  No function names are modified to snake_case.

**Changes** :
- Added pre-commit hooks along with `isort, black, flake8` linters.
- Hooks run automatically during git commit.
- Fixed linting errors and added `TODO` and `noqa` for other cases.
- Replaced `xrange` with `range` function in _win32tz.py_

<hr/>

🙏🏼 Changes are mostly double quotes, whitespace, indentation and sort order. Apologies, as its too many changes in single PR.


Discussion / hints:

Fix patch:
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/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<group> {name!s})\.)?(?P<name> {name!s}) # name group
   (?P<params> ;?(?: {param!s} )* )               # params group (may be empty)
 : (?P<value> .* )$                             # 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 @@
 </span>
 """
 
-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('<span class="summary">' + summary + '</span>:' + CRLF)
+                out('<span class="summary">' + summary + "</span>:" + 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('<abbr class="dtstart", title="{0!s}">{1!s}</abbr>\r\n'
-                    .format(dtstart.strftime(machine),
-                            dtstart.strftime(timeformat)))
+                out(
+                    '<abbr class="dtstart", title="{0!s}">{1!s}</abbr>\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('- <abbr class="dtend", title="{0!s}">{1!s}</abbr>\r\n'
-                        .format(dtend.strftime(machine),
-                                human.strftime(timeformat)))
+                    out(
+                        '- <abbr class="dtend", title="{0!s}">{1!s}</abbr>\r\n'.format(
+                            dtend.strftime(machine), human.strftime(timeformat)
+                        )
+                    )
 
             # LOCATION
             location = event.getChildValue("location")
             if location:
-                out('at <span class="location">' + location + '</span>' + CRLF)
+                out('at <span class="location">' + location + "</span>" + CRLF)
 
             description = event.getChildValue("description")
             if description:
-                out('<div class="description">' + description + '</div>' + CRLF)
+                out('<div class="description">' + description + "</div>" + CRLF)
 
             if url:
                 level -= 1
-                out('</a>' + CRLF)
+                out("</a>" + CRLF)
 
             level -= 1
-            out('</span>' + CRLF)  # close vevent
+            out("</span>" + 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 "<VTIMEZONE | {0}>".format(getattr(self, 'tzid', 'No TZID'))
+        return "<VTIMEZONE | {0}>".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<https://moin.conectiva.com.br/DateUtil>}.
     """
+
     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()


Test patch:
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),
         #    """<span class="vevent">
         #           <a class="url" href="http://microformats.org/code/hcalendar/creator">
@@ -203,13 +190,14 @@ def test_ical_to_hcal():
         #           <div class="description">The greatest thing ever!</div>
         #        </span>
         #    """
-        #)
+        # )
 
 
 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),
-            "<class 'vobject.icalendar.VCalendar2_0'>"
-        )
+        behavior = base.getBehavior("VCALENDAR")
+        self.assertEqual(str(behavior), "<class 'vobject.icalendar.VCalendar2_0'>")
         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"))),
-            "<RDATE{'VALUE': ['DATE']}[datetime.date(1997, 3, 4), datetime.date(1997, 5, 4), datetime.date(1997, 7, 4), datetime.date(1997, 9, 4)]>"
+            "<RDATE{'VALUE': ['DATE']}[datetime.date(1997, 3, 4), datetime.date(1997, 5, 4), "
+            "datetime.date(1997, 7, 4), datetime.date(1997, 9, 4)]>",
         )
         self.assertEqual(
-            str(parseRDate(textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H"))),
-            "<RDATE{'VALUE': ['PERIOD']}[(datetime.datetime(1996, 4, 3, 2, 0, tzinfo=tzutc()), datetime.datetime(1996, 4, 3, 4, 0, tzinfo=tzutc())), (datetime.datetime(1996, 4, 4, 1, 0, tzinfo=tzutc()), " + ("datetime.timedelta(0, 10800)" if sys.version_info < (3,7) else "datetime.timedelta(seconds=10800)") + ")]>"
+            str(
+                parseRDate(
+                    textLineToContentLine("RDATE;VALUE=PERIOD:19960403T020000Z/19960403T040000Z,19960404T010000Z/PT3H")
+                )
+            ),
+            "<RDATE{'VALUE': ['PERIOD']}[(datetime.datetime(1996, 4, 3, 2, 0, tzinfo=tzutc()), datetime."
+            "datetime(1996, 4, 3, 4, 0, tzinfo=tzutc())), (datetime.datetime(1996, 4, 4, 1, 0, tzinfo=tzutc()), "
+            + ("datetime.timedelta(0, 10800)" if sys.version_info < (3, 7) else "datetime.timedelta(seconds=10800)")
+            + ")]>",
         )
 
     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),
-            "<SILLYPROFILE| [<MORESTUFF{}this line is not folded, but in practice probably ought to be, as it is exceptionally long, and moreover demonstratively stupid>, <SILLYNAME{}name>, <STUFF{}foldedline>]>"
-        )
-        self.assertEqual(
-            str(silly.stuff),
-            "<STUFF{}foldedline>"
+            "<SILLYPROFILE| [<MORESTUFF{}this line is not folded, but in practice probably ought to be, as it is"
+            " exceptionally long, and moreover demonstratively stupid>, <SILLYNAME{}name>, <STUFF{}foldedline>]>",
         )
+        self.assertEqual(str(silly.stuff), "<STUFF{}foldedline>")
 
     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),
-            "<TRIGGER{}-1 day, 0:00:00>"
-        )
+        self.assertEqual(str(c.vevent.valarm.trigger), "<TRIGGER{}-1 day, 0:00:00>")
 
-        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),
-            "<RRULE{}FREQ=Weekly;COUNT=10>"
-        )
+        self.assertEqual(str(vevent.rrule), "<RRULE{}FREQ=Weekly;COUNT=10>")
 
     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),
-            '<X-BAD-UNDERSCORE{}TRUE>'
-        )
+        self.assertEqual(str(newcal.vevent.x_bad_underscore), "<X-BAD-UNDERSCORE{}TRUE>")
 
     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| []>"
-        )
+        vcard = base.newFromBehavior("vcard", "3.0")
+        self.assertEqual(str(vcard), "<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,
         #    "<Address: Haight Street 512;\nEscape, Test\nNovosibirsk,  80214\nGnuland>"
-        #)
-        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),
-            "<VTIMEZONE | <TZID{}US/Pacific>>"
-        )
-        santiago = icalendar.TimezoneComponent(tzs.get('Santiago'))
-        self.assertEqual(
-            str(santiago),
-            "<VTIMEZONE | <TZID{}Santiago>>"
-        )
+        pacific = icalendar.TimezoneComponent(tzs.get("US/Pacific"))
+        self.assertEqual(str(pacific), "<VTIMEZONE | <TZID{}US/Pacific>>")
+        santiago = icalendar.TimezoneComponent(tzs.get("Santiago"))
+        self.assertEqual(str(santiago), "<VTIMEZONE | <TZID{}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()