diff --git a/PKG-INFO b/PKG-INFO
index f3e4cabb95c8f079b9c335b8b33f78a3a2517a0d..f96a4c0c19866892c7f608dac72a119f5d827d0d 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,7 +1,7 @@
 Metadata-Version: 2.1
 Name: dict2xml
-Version: 1.6.1
-Summary: small script to output xml as a string from a python dictionary
+Version: 1.7.0
+Summary: Small utility to convert a python dictionary into an XML string
 Home-page: http://github.com/delfick/python-dict2xml
 Author: Stephen Moore
 Author-email: stephen@delfick.com
@@ -32,20 +32,21 @@ Description: dict2xml
         
         .. code-block:: python
         
-          from dict2xml import dict2xml as xmlify
+          from dict2xml import dict2xml
+        
           data = {
-              'a' : 1
-            , 'b' : [2, 3]
-            , 'c' : {
-                'd' : [
-                    {'p' : 9}
-                  , {'o' : 10}
-                  ]
-                , 'e': 7
-                }
+            'a': 1,
+            'b': [2, 3],
+            'c': {
+              'd': [
+                {'p': 9},
+                {'o': 10}
+              ],
+              'e': 7
             }
+          }
         
-          print xmlify(data, wrap="all", indent="  ")
+          print dict2xml(data, wrap="all", indent="  ")
         
         Output
         ------
@@ -97,10 +98,16 @@ Description: dict2xml
         Changelog
         ---------
         
-        1.6.1
+        1.7.0 - 16 April, 2020
+            * Use collections.abc to avoid deprecation warning. Thanks @mangin.
+            * This library no longer supports Python2 and is only supported for
+              Python3.6+. Note that the library should still work in Python3.5 as I
+              have not used f-strings, but the framework I use for the tests is only 3.6+.
+        
+        1.6.1 - August 27, 2019
             * Include readme and LICENSE in the package
         
-        1.6
+        1.6 - April 27, 2018
             * No code changes
             * changed the licence to MIT
             * Added more metadata to pypi
diff --git a/README.rst b/README.rst
index 7bda705c05775354848833f87c4939e7f21ea03e..a3b645131026854f09983d9d59a3c2485ccc82c4 100644
--- a/README.rst
+++ b/README.rst
@@ -24,20 +24,21 @@ example
 
 .. code-block:: python
 
-  from dict2xml import dict2xml as xmlify
+  from dict2xml import dict2xml
+
   data = {
-      'a' : 1
-    , 'b' : [2, 3]
-    , 'c' : {
-        'd' : [
-            {'p' : 9}
-          , {'o' : 10}
-          ]
-        , 'e': 7
-        }
+    'a': 1,
+    'b': [2, 3],
+    'c': {
+      'd': [
+        {'p': 9},
+        {'o': 10}
+      ],
+      'e': 7
     }
+  }
 
-  print xmlify(data, wrap="all", indent="  ")
+  print dict2xml(data, wrap="all", indent="  ")
 
 Output
 ------
@@ -89,10 +90,16 @@ Limitations
 Changelog
 ---------
 
-1.6.1
+1.7.0 - 16 April, 2020
+    * Use collections.abc to avoid deprecation warning. Thanks @mangin.
+    * This library no longer supports Python2 and is only supported for
+      Python3.6+. Note that the library should still work in Python3.5 as I
+      have not used f-strings, but the framework I use for the tests is only 3.6+.
+
+1.6.1 - August 27, 2019
     * Include readme and LICENSE in the package
 
-1.6
+1.6 - April 27, 2018
     * No code changes
     * changed the licence to MIT
     * Added more metadata to pypi
diff --git a/dict2xml.egg-info/PKG-INFO b/dict2xml.egg-info/PKG-INFO
index f3e4cabb95c8f079b9c335b8b33f78a3a2517a0d..f96a4c0c19866892c7f608dac72a119f5d827d0d 100644
--- a/dict2xml.egg-info/PKG-INFO
+++ b/dict2xml.egg-info/PKG-INFO
@@ -1,7 +1,7 @@
 Metadata-Version: 2.1
 Name: dict2xml
-Version: 1.6.1
-Summary: small script to output xml as a string from a python dictionary
+Version: 1.7.0
+Summary: Small utility to convert a python dictionary into an XML string
 Home-page: http://github.com/delfick/python-dict2xml
 Author: Stephen Moore
 Author-email: stephen@delfick.com
@@ -32,20 +32,21 @@ Description: dict2xml
         
         .. code-block:: python
         
-          from dict2xml import dict2xml as xmlify
+          from dict2xml import dict2xml
+        
           data = {
-              'a' : 1
-            , 'b' : [2, 3]
-            , 'c' : {
-                'd' : [
-                    {'p' : 9}
-                  , {'o' : 10}
-                  ]
-                , 'e': 7
-                }
+            'a': 1,
+            'b': [2, 3],
+            'c': {
+              'd': [
+                {'p': 9},
+                {'o': 10}
+              ],
+              'e': 7
             }
+          }
         
-          print xmlify(data, wrap="all", indent="  ")
+          print dict2xml(data, wrap="all", indent="  ")
         
         Output
         ------
@@ -97,10 +98,16 @@ Description: dict2xml
         Changelog
         ---------
         
-        1.6.1
+        1.7.0 - 16 April, 2020
+            * Use collections.abc to avoid deprecation warning. Thanks @mangin.
+            * This library no longer supports Python2 and is only supported for
+              Python3.6+. Note that the library should still work in Python3.5 as I
+              have not used f-strings, but the framework I use for the tests is only 3.6+.
+        
+        1.6.1 - August 27, 2019
             * Include readme and LICENSE in the package
         
-        1.6
+        1.6 - April 27, 2018
             * No code changes
             * changed the licence to MIT
             * Added more metadata to pypi
diff --git a/dict2xml.egg-info/SOURCES.txt b/dict2xml.egg-info/SOURCES.txt
index 2b7eec80841baf2d5876b16cbdb82751773314fc..14af3ec2ccbce65f443f0bac7cb642152aeceba6 100644
--- a/dict2xml.egg-info/SOURCES.txt
+++ b/dict2xml.egg-info/SOURCES.txt
@@ -1,6 +1,7 @@
 LICENSE
 MANIFEST.in
 README.rst
+pyproject.toml
 readme.rst
 setup.py
 dict2xml/__init__.py
diff --git a/dict2xml.egg-info/requires.txt b/dict2xml.egg-info/requires.txt
index c97deede2c1cdb0f32a0220ec8bb283a515297a8..515c52d3520a43e9d3c4113300a81a56493c2f6f 100644
--- a/dict2xml.egg-info/requires.txt
+++ b/dict2xml.egg-info/requires.txt
@@ -1,6 +1,4 @@
-six
 
 [tests]
-fudge
-noseOfYeti>=1.7.0
-nose
+noseOfYeti==2.0.1
+pytest==5.3.1
diff --git a/dict2xml/__init__.py b/dict2xml/__init__.py
index ad8f7708224c5a27eafe163b1f87955b09b5de2e..7a4654d888f6e850845dc6c87e55c30c8f3d1b0a 100644
--- a/dict2xml/__init__.py
+++ b/dict2xml/__init__.py
@@ -1,6 +1,9 @@
 from dict2xml.logic import Converter, Node
 
+
 def dict2xml(data, *args, **kwargs):
     """Return an XML string of a Python dict object."""
     return Converter(*args, **kwargs).build(data)
 
+
+__all__ = ["dict2xml", "Converter", "Node"]
diff --git a/dict2xml/logic.py b/dict2xml/logic.py
index 1364168c6dc34cfc194b3e735ff033d2252e4254..45fb5583a0e8c7479defb169356a5dea79ff0ecf 100644
--- a/dict2xml/logic.py
+++ b/dict2xml/logic.py
@@ -1,19 +1,32 @@
+import collections.abc
 import collections
 import re
-import six
 
-
-NameStartChar = re.compile(
-    u"(:|[A-Z]|_|[a-z]|[\xC0-\xD6]|[\xD8-\xF6]|[\xF8-\u02FF]|[\u0370-\u037D]|[\u037F-\u1FFF]|[\u200C-\u200D]|[\u2070-\u218F]|[\u2C00-\u2FEF]|[\u3001-\uD7FF]|[\uF900-\uFDCF]|[\uFDF0-\uFFFD])",
-    re.UNICODE)
-NameChar = re.compile(
-    u"(\-|\.|[0-9]|\xB7|[\u0300-\u036F]|[\u203F-\u2040])",
-    re.UNICODE)
+start_ranges = "|".join(
+    "[{0}]".format(r)
+    for r in [
+        "\xC0-\xD6",
+        "\xD8-\xF6",
+        "\xF8-\u02FF",
+        "\u0370-\u037D",
+        "\u037F-\u1FFF",
+        "\u200C-\u200D",
+        "\u2070-\u218F",
+        "\u2C00-\u2FEF",
+        "\u3001-\uD7FF",
+        "\uF900-\uFDCF",
+        "\uFDF0-\uFFFD",
+    ]
+)
+
+NameStartChar = re.compile(r"(:|[A-Z]|_|[a-z]|{0})".format(start_ranges))
+NameChar = re.compile(r"(\-|\.|[0-9]|\xB7|[\u0300-\u036F]|[\u203F-\u2040])")
 
 ########################
 ###   NODE
 ########################
 
+
 class Node(object):
     """
         Represents each tag in the tree
@@ -32,7 +45,7 @@ class Node(object):
     """
 
     # A mapping of characters to treat as escapable entities and their replacements
-    entities = [('&', '&amp;'), ('<', '&lt;'), ('>', '&gt;')]
+    entities = [("&", "&amp;"), ("<", "&lt;"), (">", "&gt;")]
 
     def __init__(self, wrap="", tag="", data=None, iterables_repeat_wrap=True):
         self.tag = self.sanitize_element(tag)
@@ -41,7 +54,7 @@ class Node(object):
         self.type = self.determine_type()
         self.iterables_repeat_wrap = iterables_repeat_wrap
 
-        if self.type == 'flat' and isinstance(self.data, six.string_types):
+        if self.type == "flat" and isinstance(self.data, str):
             # Make sure we deal with entities
             for entity, replacement in self.entities:
                 self.data = self.data.replace(entity, replacement)
@@ -52,8 +65,8 @@ class Node(object):
         wrap = self.wrap
         end, start = "", ""
         if wrap:
-            end = "</%s>" % wrap
-            start = "<%s>" % wrap
+            end = "</{0}>".format(wrap)
+            start = "<{0}>".format(wrap)
 
         # Convert the data attached in this node into a value and children
         value, children = self.convert()
@@ -70,13 +83,13 @@ class Node(object):
                     result = []
                     for c in children:
                         content = c.serialize(indenter)
-                        if c.type == 'flat':
+                        if c.type == "flat":
                             # Child with value, it already is surrounded by the tag
                             result.append(content)
                         else:
                             # Child with children of it's own, they need to be wrapped by start and end
                             content = indenter([content], True)
-                            result.append(''.join((start, content, end)))
+                            result.append("".join((start, content, end)))
 
                     # We already have what we want, return the indented result
                     return indenter(result, False)
@@ -84,12 +97,12 @@ class Node(object):
                     result = []
                     for c in children:
                         result.append(c.serialize(indenter))
-                    return ''.join([start, indenter(result, True), end])
+                    return "".join([start, indenter(result, True), end])
 
         # If here, either:
         #  * Have a value
         #  * Or this node is not an iterable
-        return ''.join((start, value, content, end))
+        return "".join((start, value, content, end))
 
     def determine_type(self):
         """
@@ -100,14 +113,14 @@ class Node(object):
             * flat : A string or something that isn't iterable or a mapping
         """
         data = self.data
-        if isinstance(data, six.string_types) or isinstance(data, six.text_type):
-            return 'flat'
-        elif isinstance(data, collections.Mapping):
-            return 'mapping'
-        elif isinstance(data, collections.Iterable):
-            return 'iterable'
+        if isinstance(data, str):
+            return "flat"
+        elif isinstance(data, collections.abc.Mapping):
+            return "mapping"
+        elif isinstance(data, collections.abc.Iterable):
+            return "iterable"
         else:
-            return 'flat'
+            return "flat"
 
     def convert(self):
         """
@@ -122,23 +135,27 @@ class Node(object):
         data = self.data
         children = []
 
-        if typ == 'mapping':
+        if typ == "mapping":
             sorted_data = data
             if not isinstance(data, collections.OrderedDict):
                 sorted_data = sorted(data)
 
             for key in sorted_data:
                 item = data[key]
-                children.append(Node(key, "", item, iterables_repeat_wrap=self.iterables_repeat_wrap))
+                children.append(
+                    Node(key, "", item, iterables_repeat_wrap=self.iterables_repeat_wrap)
+                )
 
-        elif typ == 'iterable':
+        elif typ == "iterable":
             for item in data:
-                children.append(Node("", self.wrap, item, iterables_repeat_wrap=self.iterables_repeat_wrap))
+                children.append(
+                    Node("", self.wrap, item, iterables_repeat_wrap=self.iterables_repeat_wrap,)
+                )
 
         else:
-            val = six.text_type(data)
+            val = str(data)
             if self.tag:
-                val = "<%s>%s</%s>" % (self.tag, val, self.tag)
+                val = "<{0}>{1}</{2}>".format(self.tag, val, self.tag)
 
         return val, children
 
@@ -155,23 +172,26 @@ class Node(object):
 
             :ref: http://www.w3.org/TR/REC-xml/#NT-NameChar
         """
-        if wrap and isinstance(wrap, six.string_types):
-            if wrap.lower().startswith('xml'):
-                wrap = '_' + wrap
-            return ''.join(
-                ['_' if not NameStartChar.match(wrap) else ''] + \
-                ['_' if not (NameStartChar.match(c) or NameChar.match(c)) else c
-                 for c in wrap])
+        if wrap and isinstance(wrap, str):
+            if wrap.lower().startswith("xml"):
+                wrap = "_" + wrap
+            return "".join(
+                ["_" if not NameStartChar.match(wrap) else ""]
+                + ["_" if not (NameStartChar.match(c) or NameChar.match(c)) else c for c in wrap]
+            )
         else:
             return wrap
 
+
 ########################
 ###   CONVERTER
 ########################
 
+
 class Converter(object):
     """Logic for creating a Node tree and serialising that tree into a string"""
-    def __init__(self, wrap=None, indent='  ', newlines=True):
+
+    def __init__(self, wrap=None, indent="  ", newlines=True):
         """
             wrap: The tag that the everything else will be contained within
             indent: The string that is multiplied at the start of each new line, to represent each level of nesting
@@ -198,7 +218,7 @@ class Converter(object):
             def eachline(nodes):
                 """Yield each line in each node"""
                 for node in nodes:
-                    for line in node.split('\n'):
+                    for line in node.split("\n"):
                         yield line
 
             def ret(nodes, wrapped):
@@ -210,17 +230,18 @@ class Converter(object):
                         and indent each line in the child by one indent unit
                 """
                 if wrapped:
-                    seperator = "\n%s" % indent
-                    surrounding = "\n%s%%s\n" % indent
+                    seperator = "\n{0}".format(indent)
+                    surrounding = "\n{0}{{0}}\n".format(indent)
                 else:
                     seperator = "\n"
-                    surrounding = "%s"
-                return surrounding % seperator.join(eachline(nodes))
+                    surrounding = "{0}"
+                return surrounding.format(seperator.join(eachline(nodes)))
 
         return ret
 
     def build(self, data, iterables_repeat_wrap=True):
         """Create a Node tree from the data and return it as a serialized xml string"""
         indenter = self._make_indenter()
-        return Node(wrap=self.wrap, data=data, iterables_repeat_wrap=iterables_repeat_wrap).serialize(indenter)
-
+        return Node(
+            wrap=self.wrap, data=data, iterables_repeat_wrap=iterables_repeat_wrap
+        ).serialize(indenter)
diff --git a/pyproject.toml b/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..4b418113f17512a23d02dead542521657fdb1cf2
--- /dev/null
+++ b/pyproject.toml
@@ -0,0 +1,11 @@
+[tool.black]
+line-length = 100
+include = '\.py$'
+exclude = '''
+/(
+     \.git
+   | \.tox
+   | dist
+   | tools
+)/
+'''
diff --git a/setup.py b/setup.py
index 17a4afca4aa6fba9156dbd8186d9f0bed9262ab6..ce78a53cdbaa1aee86d3ef23c73f4cf84af26acb 100644
--- a/setup.py
+++ b/setup.py
@@ -1,23 +1,20 @@
 from setuptools import setup
 
+# fmt: off
+
 # Setup the project
 setup(
       name = "dict2xml"
-    , version = '1.6.1'
+    , version = '1.7.0'
     , packages = ['dict2xml']
 
     , extras_require =
       { 'tests' :
-        [ 'fudge'
-        , 'noseOfYeti>=1.7.0'
-        , 'nose'
+        [ "noseOfYeti==2.0.1"
+        , "pytest==5.3.1"
         ]
       }
 
-    , install_requires =
-      [ "six"
-      ]
-
     , classifiers =
       [ "Development Status :: 5 - Production/Stable"
       , "License :: OSI Approved :: MIT License"
@@ -33,8 +30,9 @@ setup(
     , url = "http://github.com/delfick/python-dict2xml"
     , author = "Stephen Moore"
     , author_email = "stephen@delfick.com"
-    , description = "small script to output xml as a string from a python dictionary"
+    , description = "Small utility to convert a python dictionary into an XML string"
     , long_description = open("README.rst").read()
     , license = "MIT"
     )
 
+# fmt: on