diff --git a/examples/demo/demo.py b/examples/demo/demo.py
index 69f6cdc..0b1bd00 100644
--- a/examples/demo/demo.py
+++ b/examples/demo/demo.py
@@ -16,17 +16,15 @@ from wsme.types import File
 
 import bottle
 
-from six import u
-
 import logging
 
 
 class Person(object):
     id = int
-    firstname = unicode
-    lastname = unicode
+    firstname = str
+    lastname = str
 
-    hobbies = [unicode]
+    hobbies = [str]
 
     def __repr__(self):
         return "Person(%s, %s %s, %s)" % (
@@ -47,7 +45,7 @@ class DemoRoot(WSRoot):
     def echofile(self, afile):
         return afile
 
-    @expose(unicode)
+    @expose(str)
     def helloworld(self):
         return u"Здраво, свете (<- Hello World in Serbian !)"
 
@@ -55,25 +53,25 @@ class DemoRoot(WSRoot):
     def getperson(self):
         p = Person()
         p.id = 12
-        p.firstname = u'Ross'
-        p.lastname = u'Geler'
+        p.firstname = 'Ross'
+        p.lastname = 'Geler'
         p.hobbies = []
-        print p
+        print(p)
         return p
 
     @expose([Person])
     def listpersons(self):
         p = Person()
         p.id = 12
-        p.firstname = u('Ross')
-        p.lastname = u('Geler')
+        p.firstname = 'Ross'
+        p.lastname = 'Geler'
         r = [p]
         p = Person()
         p.id = 13
-        p.firstname = u('Rachel')
-        p.lastname = u('Green')
+        p.firstname = 'Rachel'
+        p.lastname = 'Green'
         r.append(p)
-        print r
+        print(r)
         return r
 
     @expose(Person)
@@ -84,7 +82,7 @@ class DemoRoot(WSRoot):
     @expose([Person])
     @validate([Person])
     def setpersons(self, persons):
-        print persons
+        print(persons)
         return persons
 
 
diff --git a/requirements.txt b/requirements.txt
index ffb3942..cf4d31b 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,3 @@
-six>=1.9.0
 WebOb>=1.8.0
 simplegeneric
 pytz
diff --git a/tests/pecantest/test/controllers/ws.py b/tests/pecantest/test/controllers/ws.py
index b35a0c6..049e380 100644
--- a/tests/pecantest/test/controllers/ws.py
+++ b/tests/pecantest/test/controllers/ws.py
@@ -6,8 +6,6 @@ from wsme.types import Base, text, wsattr
 import wsme
 import wsmeext.pecan
 
-import six
-
 
 class Author(Base):
     id = int
@@ -78,7 +76,7 @@ class AuthorsController(RestController):
 
     books = BooksController()
 
-    @wsmeext.pecan.wsexpose([Author], [six.text_type], [Criterion])
+    @wsmeext.pecan.wsexpose([Author], [str], [Criterion])
     def get_all(self, q=None, r=None):
         if q:
             return [
diff --git a/tests/pecantest/test/tests/test_ws.py b/tests/pecantest/test/tests/test_ws.py
index ae06650..3b5617e 100644
--- a/tests/pecantest/test/tests/test_ws.py
+++ b/tests/pecantest/test/tests/test_ws.py
@@ -1,8 +1,8 @@
-from six.moves import http_client
-from test.tests import FunctionalTest
+from http import client as http_client
 import json
+
 import pecan
-import six
+from test.tests import FunctionalTest
 
 
 used_status_codes = [400, 401, 403, 404, 500]
@@ -240,8 +240,5 @@ class TestWS(FunctionalTest):
         assert book['author']['id'] == 1
 
     def test_no_content_type_if_no_return_type(self):
-        if six.PY3:
-            self.skipTest(
-                "This test does not work in Python 3 until https://review.openstack.org/#/c/48439/ is merged")
         res = self.app.delete('/authors/4')
         assert "Content-Type" not in res.headers, res.headers['Content-Type']
diff --git a/wsme/api.py b/wsme/api.py
index b2ddf6e..b4d728a 100644
--- a/wsme/api.py
+++ b/wsme/api.py
@@ -1,8 +1,7 @@
-import traceback
 import functools
 import inspect
 import logging
-import six
+import traceback
 
 import wsme.exc
 import wsme.types
@@ -26,10 +25,7 @@ def wrapfunc(f):
 
 def getargspec(f):
     f = getattr(f, '_wsme_original_func', f)
-    if six.PY2:
-        return inspect.getargspec(f)
-    else:
-        return inspect.getfullargspec(f)
+    return inspect.getfullargspec(f)
 
 
 class FunctionArgument(object):
@@ -220,7 +216,7 @@ def format_exception(excinfo, debug=False):
     code = getattr(error, 'code', None)
     if code and utils.is_valid_code(code) and utils.is_client_error(code):
         faultstring = (error.faultstring if hasattr(error, 'faultstring')
-                       else six.text_type(error))
+                       else str(error))
         faultcode = getattr(error, 'faultcode', 'Client')
         r = dict(faultcode=faultcode,
                  faultstring=faultstring)
@@ -228,7 +224,7 @@ def format_exception(excinfo, debug=False):
         r['debuginfo'] = None
         return r
     else:
-        faultstring = six.text_type(error)
+        faultstring = str(error)
         debuginfo = "\n".join(traceback.format_exception(*excinfo))
 
         log.error('Server-side error: "%s". Detail: \n%s' % (
diff --git a/wsme/exc.py b/wsme/exc.py
index e70db65..f698f65 100644
--- a/wsme/exc.py
+++ b/wsme/exc.py
@@ -1,5 +1,3 @@
-import six
-
 from wsme.utils import _
 
 
@@ -14,10 +12,10 @@ class ClientSideError(RuntimeError):
     def faultstring(self):
         if self.msg is None:
             return str(self)
-        elif isinstance(self.msg, six.text_type):
+        elif isinstance(self.msg, str):
             return self.msg
         else:
-            return six.u(self.msg)
+            return str(self.msg)
 
 
 class InvalidInput(ClientSideError):
@@ -28,9 +26,9 @@ class InvalidInput(ClientSideError):
 
     @property
     def faultstring(self):
-        return _(six.u(
-            "Invalid input for field/attribute %s. Value: '%s'. %s")
-        ) % (self.fieldname, self.value, self.msg)
+        return _("Invalid input for field/attribute %s. Value: '%s'. %s") % (
+            self.fieldname, self.value, self.msg,
+        )
 
 
 class MissingArgument(ClientSideError):
@@ -40,8 +38,9 @@ class MissingArgument(ClientSideError):
 
     @property
     def faultstring(self):
-        return _(six.u('Missing argument: "%s"%s')) % (
-            self.argname, self.msg and ": " + self.msg or "")
+        return _('Missing argument: "%s"%s') % (
+            self.argname, self.msg and ": " + self.msg or "",
+        )
 
 
 class UnknownArgument(ClientSideError):
@@ -51,8 +50,9 @@ class UnknownArgument(ClientSideError):
 
     @property
     def faultstring(self):
-        return _(six.u('Unknown argument: "%s"%s')) % (
-            self.argname, self.msg and ": " + self.msg or "")
+        return _('Unknown argument: "%s"%s') % (
+            self.argname, self.msg and ": " + self.msg or "",
+        )
 
 
 class UnknownFunction(ClientSideError):
@@ -62,7 +62,7 @@ class UnknownFunction(ClientSideError):
 
     @property
     def faultstring(self):
-        return _(six.u("Unknown function name: %s")) % (self.name)
+        return _("Unknown function name: %s") % (self.name)
 
 
 class UnknownAttribute(ClientSideError):
diff --git a/wsme/rest/args.py b/wsme/rest/args.py
index 9dc16c7..855b51a 100644
--- a/wsme/rest/args.py
+++ b/wsme/rest/args.py
@@ -11,8 +11,6 @@ from wsme.types import UserType, ArrayType, DictType, File
 from wsme.utils import parse_isodate, parse_isotime, parse_isodatetime
 import wsme.runtime
 
-from six import moves
-
 ARRAY_MAX_SIZE = 1000
 
 
@@ -116,7 +114,7 @@ def array_from_params(datatype, params, path, hit_paths):
                 if len(value) < len(attrvalues):
                     value[-1:] = [
                         datatype.item_type()
-                        for i in moves.range(len(attrvalues) - len(value))
+                        for i in range(len(attrvalues) - len(value))
                     ]
                 for i, attrvalue in enumerate(attrvalues):
                     setattr(
diff --git a/wsme/rest/json.py b/wsme/rest/json.py
index cf8727c..f91dd1e 100644
--- a/wsme/rest/json.py
+++ b/wsme/rest/json.py
@@ -6,7 +6,6 @@ import decimal
 import json
 
 from simplegeneric import generic
-import six
 
 import wsme.exc
 import wsme.types
@@ -180,12 +179,12 @@ def dict_fromjson(datatype, value):
         for item in value.items()))
 
 
-@fromjson.when_object(six.binary_type)
+@fromjson.when_object(bytes)
 def str_fromjson(datatype, value):
-    if (isinstance(value, six.string_types) or
-            isinstance(value, six.integer_types) or
+    if (isinstance(value, str) or
+            isinstance(value, int) or
             isinstance(value, float)):
-        return six.text_type(value).encode('utf8')
+        return str(value).encode('utf8')
 
 
 @fromjson.when_object(wsme.types.text)
@@ -195,7 +194,7 @@ def text_fromjson(datatype, value):
     return value
 
 
-@fromjson.when_object(*six.integer_types + (float,))
+@fromjson.when_object(int, float)
 def numeric_fromjson(datatype, value):
     """Convert string object to built-in types int, long or float."""
     if value is None:
@@ -208,7 +207,7 @@ def bool_fromjson(datatype, value):
     """Convert to bool, restricting strings to just unambiguous values."""
     if value is None:
         return None
-    if isinstance(value, six.integer_types + (bool,)):
+    if isinstance(value, (int, bool,)):
         return bool(value)
     if value in ENUM_TRUE:
         return True
@@ -248,7 +247,7 @@ def datetime_fromjson(datatype, value):
 def parse(s, datatypes, bodyarg, encoding='utf8'):
     jload = json.load
     if not hasattr(s, 'read'):
-        if six.PY3 and isinstance(s, six.binary_type):
+        if isinstance(s, bytes):
             s = s.decode(encoding)
         jload = json.loads
     try:
diff --git a/wsme/rest/xml.py b/wsme/rest/xml.py
index 286afa7..60c8796 100644
--- a/wsme/rest/xml.py
+++ b/wsme/rest/xml.py
@@ -2,8 +2,6 @@ from __future__ import absolute_import
 
 import datetime
 
-import six
-
 import xml.etree.ElementTree as et
 
 from simplegeneric import generic
@@ -69,7 +67,7 @@ def toxml(datatype, key, value):
                     el.append(toxml(attrdef.datatype, attrdef.name,
                                     attrvalue))
         else:
-            el.text = six.text_type(value)
+            el.text = str(value)
     return el
 
 
@@ -213,7 +211,7 @@ def dict_fromxml(datatype, element):
 def unicode_fromxml(datatype, element):
     if element.get('nil') == 'true':
         return None
-    return wsme.types.text(element.text) if element.text else six.u('')
+    return wsme.types.text(element.text) if element.text else ''
 
 
 @fromxml.when_object(datetime.date)
diff --git a/wsme/root.py b/wsme/root.py
index ed89148..1d03fba 100644
--- a/wsme/root.py
+++ b/wsme/root.py
@@ -2,9 +2,6 @@ import logging
 import sys
 import weakref
 
-from six import u, b
-import six
-
 import webob
 
 from wsme.exc import ClientSideError, UnknownFunction
@@ -15,7 +12,7 @@ import wsme.types
 
 log = logging.getLogger(__name__)
 
-html_body = u("""
+html_body = """
 <html>
 <head>
   <style type='text/css'>
@@ -26,7 +23,7 @@ html_body = u("""
 %(content)s
 </body>
 </html>
-""")
+"""
 
 
 def default_prepare_response_body(request, results):
@@ -34,12 +31,12 @@ def default_prepare_response_body(request, results):
     sep = None
     for value in results:
         if sep is None:
-            if isinstance(value, six.text_type):
-                sep = u('\n')
-                r = u('')
+            if isinstance(value, str):
+                sep = '\n'
+                r = ''
             else:
-                sep = b('\n')
-                r = b('')
+                sep = b'\n'
+                r = b''
         else:
             r += sep
         r += value
@@ -177,9 +174,9 @@ class WSRoot(object):
                 context.path = protocol.extract_path(context)
 
             if context.path is None:
-                raise ClientSideError(u(
+                raise ClientSideError(
                     'The %s protocol was unable to extract a function '
-                    'path from the request') % protocol.name)
+                    'path from the request' % protocol.name)
 
             context.func, context.funcdef, args = \
                 self._lookup_function(context.path)
@@ -224,9 +221,9 @@ class WSRoot(object):
         routepath, func = self.find_route(path)
         if routepath:
             content = func()
-            if isinstance(content, six.text_type):
+            if isinstance(content, str):
                 res.text = content
-            elif isinstance(content, six.binary_type):
+            elif isinstance(content, bytes):
                 res.body = content
             res.content_type = func._cfg['content-type']
             return res
@@ -253,7 +250,7 @@ class WSRoot(object):
             res.status = error_status
             res.content_type = 'text/plain'
             try:
-                res.text = u(msg)
+                res.text = str(msg)
             except TypeError:
                 res.text = msg
             log.error(msg)
@@ -277,7 +274,7 @@ class WSRoot(object):
                 self._do_call(protocol, context)
                 for context in protocol.iter_calls(request)))
 
-            if isinstance(body, six.text_type):
+            if isinstance(body, str):
                 res.text = body
             else:
                 res.body = body
@@ -365,6 +362,6 @@ class WSRoot(object):
                 "error :\n%s" % e)
             return html_body % dict(
                 css='',
-                content=u('<pre>%s</pre>') %
-                    content.replace(b('>'), b('&gt;'))
-                           .replace(b('<'), b('&lt;')))
+                content='<pre>%s</pre>' %
+                    content.replace(b'>', b'&gt;')
+                           .replace(b'<', b'&lt;'))
diff --git a/wsme/tests/protocol.py b/wsme/tests/protocol.py
index 1c41f8c..4515978 100644
--- a/wsme/tests/protocol.py
+++ b/wsme/tests/protocol.py
@@ -1,12 +1,9 @@
 # coding=utf-8
 
-import unittest
-import warnings
 import datetime
 import decimal
-import six
-
-from six import u, b
+import unittest
+import warnings
 
 from webtest import TestApp
 
@@ -17,7 +14,7 @@ import wsme.utils
 
 warnings.filterwarnings('ignore', module='webob.dec')
 
-binarysample = b('\x00\xff\x43')
+binarysample = b'\x00\xff\x43'
 
 try:
     1 / 0
@@ -88,11 +85,11 @@ class NestedOuterApi(object):
 class ReturnTypes(object):
     @expose(wsme.types.bytes)
     def getbytes(self):
-        return b("astring")
+        return b"astring"
 
     @expose(wsme.types.text)
     def gettext(self):
-        return u('\xe3\x81\xae')
+        return '\xe3\x81\xae'
 
     @expose(int)
     def getint(self):
@@ -137,7 +134,7 @@ class ReturnTypes(object):
 
     @expose([wsme.types.bytes])
     def getbytesarray(self):
-        return [b("A"), b("B"), b("C")]
+        return [b"A", b"B", b"C"]
 
     @expose([NestedOuter])
     def getnestedarray(self):
@@ -145,7 +142,7 @@ class ReturnTypes(object):
 
     @expose({wsme.types.bytes: NestedOuter})
     def getnesteddict(self):
-        return {b('a'): NestedOuter(), b('b'): NestedOuter()}
+        return {b'a': NestedOuter(), b'b': NestedOuter()}
 
     @expose(NestedOuter)
     def getobjectarrayattribute(self):
@@ -164,7 +161,7 @@ class ReturnTypes(object):
 
     @expose(myenumtype)
     def getenum(self):
-        return b('v2')
+        return b'v2'
 
     @expose(NamedAttrsObject)
     def getnamedattrsobj(self):
@@ -254,7 +251,7 @@ class ArgTypes(object):
     @validate(wsme.types.binary)
     def setbinary(self, value):
         print(repr(value))
-        self.assertEqual(type(value), six.binary_type)
+        self.assertEqual(type(value), bytes)
         return value
 
     @expose([wsme.types.bytes])
@@ -393,9 +390,9 @@ class ProtocolTestCase(unittest.TestCase):
     protocol_options = {}
 
     def assertTypedEquals(self, a, b, convert):
-        if isinstance(a, six.string_types):
+        if isinstance(a, str):
             a = convert(a)
-        if isinstance(b, six.string_types):
+        if isinstance(b, str):
             b = convert(b)
         self.assertEqual(a, b)
 
@@ -433,7 +430,7 @@ class ProtocolTestCase(unittest.TestCase):
         except CallException as e:
             self.assertEqual(e.faultcode, 'Client')
             self.assertEqual(e.faultstring.lower(),
-                             u('unknown function name: invalid_function'))
+                             'unknown function name: invalid_function')
 
     def test_serverside_error(self):
         try:
@@ -462,11 +459,11 @@ class ProtocolTestCase(unittest.TestCase):
 
     def test_return_bytes(self):
         r = self.call('returntypes/getbytes', _rt=wsme.types.bytes)
-        self.assertEqual(r, b('astring'))
+        self.assertEqual(r, b'astring')
 
     def test_return_text(self):
         r = self.call('returntypes/gettext', _rt=wsme.types.text)
-        self.assertEqual(r, u('\xe3\x81\xae'))
+        self.assertEqual(r, '\xe3\x81\xae')
 
     def test_return_int(self):
         r = self.call('returntypes/getint')
@@ -509,8 +506,8 @@ class ProtocolTestCase(unittest.TestCase):
         self.assertEqual(r, {'inner': {'aint': 0}})
 
     def test_return_bytesarray(self):
-        r = self.call('returntypes/getbytesarray', _rt=[six.binary_type])
-        self.assertEqual(r, [b('A'), b('B'), b('C')])
+        r = self.call('returntypes/getbytesarray', _rt=[bytes])
+        self.assertEqual(r, [b'A', b'B', b'C'])
 
     def test_return_nestedarray(self):
         r = self.call('returntypes/getnestedarray', _rt=[NestedOuter])
@@ -520,8 +517,8 @@ class ProtocolTestCase(unittest.TestCase):
         r = self.call('returntypes/getnesteddict',
                       _rt={wsme.types.bytes: NestedOuter})
         self.assertEqual(r, {
-            b('a'): {'inner': {'aint': 0}},
-            b('b'): {'inner': {'aint': 0}}
+            b'a': {'inner': {'aint': 0}},
+            b'b': {'inner': {'aint': 0}}
         })
 
     def test_return_objectarrayattribute(self):
@@ -543,23 +540,23 @@ class ProtocolTestCase(unittest.TestCase):
 
     def test_return_enum(self):
         r = self.call('returntypes/getenum', _rt=myenumtype)
-        self.assertEqual(r, b('v2'), r)
+        self.assertEqual(r, b'v2', r)
 
     def test_return_namedattrsobj(self):
         r = self.call('returntypes/getnamedattrsobj', _rt=NamedAttrsObject)
         self.assertEqual(r, {'attr.1': 5, 'attr.2': 6})
 
     def test_setbytes(self):
-        assert self.call('argtypes/setbytes', value=b('astring'),
-                         _rt=wsme.types.bytes) == b('astring')
+        assert self.call('argtypes/setbytes', value=b'astring',
+                         _rt=wsme.types.bytes) == b'astring'
 
     def test_settext(self):
-        assert self.call('argtypes/settext', value=u('\xe3\x81\xae'),
-                         _rt=wsme.types.text) == u('\xe3\x81\xae')
+        assert self.call('argtypes/settext', value='\xe3\x81\xae',
+                         _rt=wsme.types.text) == '\xe3\x81\xae'
 
     def test_settext_empty(self):
-        assert self.call('argtypes/settext', value=u(''),
-                         _rt=wsme.types.text) == u('')
+        assert self.call('argtypes/settext', value='',
+                         _rt=wsme.types.text) == ''
 
     def test_settext_none(self):
         self.assertEqual(
@@ -629,14 +626,14 @@ class ProtocolTestCase(unittest.TestCase):
         self.assertEqual(r, value)
 
     def test_setbytesarray(self):
-        value = [b("1"), b("2"), b("three")]
+        value = [b"1", b"2", b"three"]
         r = self.call('argtypes/setbytesarray',
                       value=(value, [wsme.types.bytes]),
                       _rt=[wsme.types.bytes])
         self.assertEqual(r, value)
 
     def test_settextarray(self):
-        value = [u("1")]
+        value = [u"1"]
         r = self.call('argtypes/settextarray',
                       value=(value, [wsme.types.text]),
                       _rt=[wsme.types.text])
@@ -664,17 +661,17 @@ class ProtocolTestCase(unittest.TestCase):
 
     def test_setnesteddict(self):
         value = {
-            b('o1'): {'inner': {'aint': 54}},
-            b('o2'): {'inner': {'aint': 55}},
+            b'o1': {'inner': {'aint': 54}},
+            b'o2': {'inner': {'aint': 55}},
         }
         r = self.call('argtypes/setnesteddict',
-                      value=(value, {six.binary_type: NestedOuter}),
-                      _rt={six.binary_type: NestedOuter})
+                      value=(value, {bytes: NestedOuter}),
+                      _rt={bytes: NestedOuter})
         print(r)
         self.assertEqual(r, value)
 
     def test_setenum(self):
-        value = b('v1')
+        value = b'v1'
         r = self.call('argtypes/setenum', value=value,
                       _rt=myenumtype)
         self.assertEqual(r, value)
@@ -697,7 +694,7 @@ class ProtocolTestCase(unittest.TestCase):
             assert "No error raised"
         except CallException as e:
             self.assertEqual(e.faultcode, 'Client')
-            self.assertEqual(e.faultstring, u('Missing argument: "value"'))
+            self.assertEqual(e.faultstring, 'Missing argument: "value"')
 
     def test_misc_multiply(self):
         self.assertEqual(self.call('misc/multiply', a=5, b=2, _rt=int), 10)
diff --git a/wsme/tests/test_api.py b/wsme/tests/test_api.py
index 36bc2eb..e0f5a65 100644
--- a/wsme/tests/test_api.py
+++ b/wsme/tests/test_api.py
@@ -1,11 +1,6 @@
 # encoding=utf8
 
-from six import b
-
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
+import unittest
 import webtest
 
 from wsme import WSRoot, expose, validate
@@ -205,7 +200,7 @@ class TestController(unittest.TestCase):
         assert res.status_int == 406
         print(res.body)
         assert res.body.find(
-            b("None of the following protocols can handle this request")) != -1
+            b"None of the following protocols can handle this request") != -1
 
     def test_return_content_type_guess(self):
         class DummierProto(DummyProtocol):
@@ -259,19 +254,19 @@ class TestController(unittest.TestCase):
             'Accept': 'application/json'
         })
 
-        self.assertEqual(res.body, b('10'))
+        self.assertEqual(res.body, b'10')
 
         res = app.get('/mul_float?a=1.2&b=2.9', headers={
             'Accept': 'application/json'
         })
 
-        self.assertEqual(res.body, b('3.48'))
+        self.assertEqual(res.body, b'3.48')
 
         res = app.get('/mul_string?a=hello&b=2', headers={
             'Accept': 'application/json'
         })
 
-        self.assertEqual(res.body, b('"hellohello"'))
+        self.assertEqual(res.body, b'"hellohello"')
 
     def test_wsattr_mandatory(self):
         class ComplexType(object):
diff --git a/wsme/tests/test_exc.py b/wsme/tests/test_exc.py
index ba903fb..2406279 100644
--- a/wsme/tests/test_exc.py
+++ b/wsme/tests/test_exc.py
@@ -2,25 +2,24 @@
 
 from wsme.exc import (ClientSideError, InvalidInput, MissingArgument,
                       UnknownArgument)
-from six import u
 
 
 def test_clientside_error():
     e = ClientSideError("Test")
 
-    assert e.faultstring == u("Test")
+    assert e.faultstring == "Test"
 
 
 def test_unicode_clientside_error():
-    e = ClientSideError(u("\u30d5\u30a1\u30b7\u30ea"))
+    e = ClientSideError("\u30d5\u30a1\u30b7\u30ea")
 
-    assert e.faultstring == u("\u30d5\u30a1\u30b7\u30ea")
+    assert e.faultstring == "\u30d5\u30a1\u30b7\u30ea"
 
 
 def test_invalidinput():
     e = InvalidInput('field', 'badvalue', "error message")
 
-    assert e.faultstring == u(
+    assert e.faultstring == (
         "Invalid input for field/attribute field. Value: 'badvalue'. "
         "error message"
     ), e.faultstring
@@ -30,11 +29,11 @@ def test_missingargument():
     e = MissingArgument('argname', "error message")
 
     assert e.faultstring == \
-        u('Missing argument: "argname": error message'), e.faultstring
+        ('Missing argument: "argname": error message'), e.faultstring
 
 
 def test_unknownargument():
     e = UnknownArgument('argname', "error message")
 
     assert e.faultstring == \
-        u('Unknown argument: "argname": error message'), e.faultstring
+        ('Unknown argument: "argname": error message'), e.faultstring
diff --git a/wsme/tests/test_restjson.py b/wsme/tests/test_restjson.py
index ca80888..fa3afb6 100644
--- a/wsme/tests/test_restjson.py
+++ b/wsme/tests/test_restjson.py
@@ -2,6 +2,7 @@ import base64
 import datetime
 import decimal
 import json
+from urllib.parse import urlencode
 
 from wsme.exc import ClientSideError, InvalidInput
 from wsme.types import isarray, isdict, isusertype, register_type
@@ -11,14 +12,6 @@ from wsme.rest.json import fromjson, tojson, parse
 import wsme.tests.protocol
 from wsme.utils import parse_isodatetime, parse_isotime, parse_isodate
 
-import six
-from six import b, u
-
-if six.PY3:
-    from urllib.parse import urlencode
-else:
-    from urllib import urlencode  # noqa
-
 
 def prepare_value(value, datatype):
     if isinstance(datatype, list):
@@ -35,10 +28,7 @@ def prepare_value(value, datatype):
     if datatype == decimal.Decimal:
         return str(value)
     if datatype == wsme.types.binary:
-        if six.PY3:
-            return base64.encodebytes(value).decode('ascii')
-        else:
-            return base64.encodestring(value).decode('ascii')
+        return base64.encodebytes(value).decode('ascii')
     if datatype == wsme.types.bytes:
         return value.decode('ascii')
     return value
@@ -49,10 +39,7 @@ def prepare_result(value, datatype):
     if value is None:
         return None
     if datatype == wsme.types.binary:
-        if six.PY3:
-            return base64.decodebytes(value.encode('ascii'))
-        else:
-            return base64.decodestring(value.encode('ascii'))
+        return base64.decodebytes(value.encode('ascii'))
     if isusertype(datatype):
         datatype = datatype.basetype
     if isinstance(datatype, list):
@@ -119,21 +106,21 @@ class MiniCrud(object):
     @validate(Obj)
     def create(self, data):
         print(repr(data))
-        return CRUDResult(data, u('create'))
+        return CRUDResult(data, 'create')
 
     @expose(CRUDResult, method='GET', ignore_extra_args=True)
     @validate(Obj)
     def read(self, ref):
         print(repr(ref))
         if ref.id == 1:
-            ref.name = u('test')
-        return CRUDResult(ref, u('read'))
+            ref.name = 'test'
+        return CRUDResult(ref, 'read')
 
     @expose(CRUDResult, method='POST')
     @validate(Obj)
     def update(self, data):
         print(repr(data))
-        return CRUDResult(data, u('update'))
+        return CRUDResult(data, 'update')
 
     @expose(CRUDResult, wsme.types.text, body=Obj)
     def update_with_body(self, msg, data):
@@ -145,8 +132,8 @@ class MiniCrud(object):
     def delete(self, ref):
         print(repr(ref))
         if ref.id == 1:
-            ref.name = u('test')
-        return CRUDResult(ref, u('delete'))
+            ref.name = 'test'
+        return CRUDResult(ref, 'delete')
 
 
 wsme.tests.protocol.WSTestRoot.crud = MiniCrud()
@@ -397,7 +384,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
             assert e.msg == "'%s' is not a legal date value" % jdate
 
     def test_valid_str_to_builtin_fromjson(self):
-        types = six.integer_types + (bool, float)
+        types = (int, bool, float)
         value = '2'
         for t in types:
             for ba in True, False:
@@ -430,7 +417,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
                 self.assertIsInstance(i['a'], float)
 
     def test_invalid_str_to_buitin_fromjson(self):
-        types = six.integer_types + (float, bool)
+        types = (int, float, bool)
         value = '2a'
         for t in types:
             for ba in True, False:
@@ -577,7 +564,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
 
     def test_bytes_tojson(self):
         assert tojson(wsme.types.bytes, None) is None
-        assert tojson(wsme.types.bytes, b('ascii')) == u('ascii')
+        assert tojson(wsme.types.bytes, b'ascii') == 'ascii'
 
     def test_encode_sample_params(self):
         r = wsme.rest.json.encode_sample_params(
@@ -596,7 +583,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         assert r[1] == '''2'''
 
     def test_PUT(self):
-        data = {"id": 1, "name": u("test")}
+        data = {"id": 1, "name": "test"}
         content = json.dumps(dict(data=data))
         headers = {
             'Content-Type': 'application/json',
@@ -610,7 +597,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
         assert result['message'] == "create"
 
     def test_GET(self):
@@ -625,7 +612,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
 
     def test_GET_complex_accept(self):
         headers = {
@@ -639,7 +626,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
 
     def test_GET_complex_choose_xml(self):
         headers = {
@@ -661,10 +648,11 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
             headers=headers,
             status=406)
         print("Received:", res.body)
-        assert res.body == b("Unacceptable Accept type: "
-                             "text/html, application/xml;q=0.9 not in "
-                             "['application/json', 'text/javascript', "
-                             "'application/javascript', 'text/xml']")
+        assert res.body == (
+            b"Unacceptable Accept type: "
+            b"text/html, application/xml;q=0.9 not in "
+            b"['application/json', 'text/javascript', "
+            b"'application/javascript', 'text/xml']")
 
     def test_GET_bad_simple_accept(self):
         headers = {
@@ -675,9 +663,10 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
             headers=headers,
             status=406)
         print("Received:", res.body)
-        assert res.body == b("Unacceptable Accept type: text/plain not in "
-                             "['application/json', 'text/javascript', "
-                             "'application/javascript', 'text/xml']")
+        assert res.body == (
+            b"Unacceptable Accept type: text/plain not in "
+            b"['application/json', 'text/javascript', "
+            b"'application/javascript', 'text/xml']")
 
     def test_POST(self):
         headers = {
@@ -685,14 +674,14 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         }
         res = self.app.post(
             '/crud',
-            json.dumps(dict(data=dict(id=1, name=u('test')))),
+            json.dumps(dict(data=dict(id=1, name='test'))),
             headers=headers,
             expect_errors=False)
         print("Received:", res.body)
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
         assert result['message'] == "update"
 
     def test_POST_bad_content_type(self):
@@ -701,13 +690,14 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         }
         res = self.app.post(
             '/crud',
-            json.dumps(dict(data=dict(id=1, name=u('test')))),
+            json.dumps(dict(data=dict(id=1, name='test'))),
             headers=headers,
             status=415)
         print("Received:", res.body)
-        assert res.body == b("Unacceptable Content-Type: text/plain not in "
-                             "['application/json', 'text/javascript', "
-                             "'application/javascript', 'text/xml']")
+        assert res.body == (
+            b"Unacceptable Content-Type: text/plain not in "
+            b"['application/json', 'text/javascript', "
+            b"'application/javascript', 'text/xml']")
 
     def test_DELETE(self):
         res = self.app.delete(
@@ -717,7 +707,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
         assert result['message'] == "delete"
 
     def test_extra_arguments(self):
@@ -732,7 +722,7 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
         assert result['message'] == "read"
 
     def test_unexpected_extra_arg(self):
@@ -768,12 +758,12 @@ class TestRestJson(wsme.tests.protocol.RestOnlyProtocolTestCase):
         }
         res = self.app.post(
             '/crud/update_with_body?msg=hello',
-            json.dumps(dict(id=1, name=u('test'))),
+            json.dumps(dict(id=1, name='test')),
             headers=headers,
             expect_errors=False)
         print("Received:", res.body)
         result = json.loads(res.text)
         print(result)
         assert result['data']['id'] == 1
-        assert result['data']['name'] == u("test")
+        assert result['data']['name'] == "test"
         assert result['message'] == "hello"
diff --git a/wsme/tests/test_restxml.py b/wsme/tests/test_restxml.py
index 50d3d98..7bea454 100644
--- a/wsme/tests/test_restxml.py
+++ b/wsme/tests/test_restxml.py
@@ -1,9 +1,6 @@
-import decimal
-import datetime
 import base64
-
-from six import u, b
-import six
+import datetime
+import decimal
 
 from wsme.rest.xml import fromxml, toxml
 import wsme.tests.protocol
@@ -30,16 +27,13 @@ def dumpxml(key, obj, datatype=None):
             node.append(dumpxml('key', item[0], key_type))
             node.append(dumpxml('value', item[1], value_type))
     elif datatype == wsme.types.binary:
-        if six.PY3:
-            el.text = base64.encodebytes(obj).decode('ascii')
-        else:
-            el.text = base64.encodestring(obj).decode('ascii')
+        el.text = base64.encodebytes(obj).decode('ascii')
     elif isinstance(obj, wsme.types.bytes):
         el.text = obj.decode('ascii')
     elif isinstance(obj, wsme.types.text):
         el.text = obj
     elif type(obj) in (int, float, bool, decimal.Decimal):
-        el.text = six.text_type(obj)
+        el.text = str(obj)
     elif type(obj) in (datetime.date, datetime.time, datetime.datetime):
         el.text = obj.isoformat()
     elif isinstance(obj, type(None)):
@@ -99,10 +93,7 @@ def loadxml(el, datatype):
         return d
     else:
         if datatype == wsme.types.binary:
-            if six.PY3:
-                return base64.decodebytes(el.text.encode('ascii'))
-            else:
-                return base64.decodestring(el.text.encode('ascii'))
+            return base64.decodebytes(el.text.encode('ascii'))
         if isusertype(datatype):
             datatype = datatype.basetype
         if datatype == datetime.date:
@@ -112,7 +103,7 @@ def loadxml(el, datatype):
         if datatype == datetime.datetime:
             return parse_isodatetime(el.text)
         if datatype == wsme.types.text:
-            return datatype(el.text if el.text else u(''))
+            return datatype(el.text if el.text else '')
         if datatype == bool:
             return el.text.lower() != 'false'
         if datatype is None:
@@ -168,28 +159,28 @@ class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
 
         value = MyType()
         value.aint = 5
-        value.atext = u('test')
+        value.atext = 'test'
 
         language, sample = wsme.rest.xml.encode_sample_value(
             MyType, value, True)
         print(language, sample)
 
         assert language == 'xml'
-        assert sample == b("""<value>
+        assert sample == b"""<value>
   <aint>5</aint>
   <atext>test</atext>
-</value>""")
+</value>"""
 
     def test_encode_sample_params(self):
         lang, content = wsme.rest.xml.encode_sample_params(
             [('a', int, 2)], True)
         assert lang == 'xml', lang
-        assert content == b('<parameters>\n  <a>2</a>\n</parameters>'), content
+        assert content == b'<parameters>\n  <a>2</a>\n</parameters>', content
 
     def test_encode_sample_result(self):
         lang, content = wsme.rest.xml.encode_sample_result(int, 2, True)
         assert lang == 'xml', lang
-        assert content == b('<result>2</result>'), content
+        assert content == b'<result>2</result>', content
 
     def test_nil_fromxml(self):
         for dt in (
@@ -204,7 +195,7 @@ class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
                 [int], {int: str}, bool,
                 datetime.date, datetime.time, datetime.datetime):
             x = et.tostring(toxml(dt, 'value', None))
-            assert x == b('<value nil="true" />'), x
+            assert x == b'<value nil="true" />', x
 
     def test_unset_attrs(self):
         class AType(object):
@@ -213,4 +204,4 @@ class TestRestXML(wsme.tests.protocol.RestOnlyProtocolTestCase):
         wsme.types.register_type(AType)
 
         x = et.tostring(toxml(AType, 'value', AType()))
-        assert x == b('<value />'), x
+        assert x == b'<value />', x
diff --git a/wsme/tests/test_root.py b/wsme/tests/test_root.py
index 8ccb0c1..e7530cb 100644
--- a/wsme/tests/test_root.py
+++ b/wsme/tests/test_root.py
@@ -7,7 +7,6 @@ import wsme.protocol
 import wsme.rest.protocol
 from wsme.root import default_prepare_response_body
 
-from six import b, u
 from webob import Request
 
 
@@ -21,10 +20,10 @@ class TestRoot(unittest.TestCase):
         txn.abort()
 
     def test_default_prepare_response_body(self):
-        default_prepare_response_body(None, [b('a')]) == b('a')
-        default_prepare_response_body(None, [b('a'), b('b')]) == b('a\nb')
-        default_prepare_response_body(None, [u('a')]) == u('a')
-        default_prepare_response_body(None, [u('a'), u('b')]) == u('a\nb')
+        default_prepare_response_body(None, [b'a']) == b'a'
+        default_prepare_response_body(None, [b'a', b'b']) == b'a\nb'
+        default_prepare_response_body(None, ['a']) == 'a'
+        default_prepare_response_body(None, ['a', 'b']) == 'a\nb'
 
     def test_protocol_selection_error(self):
         class P(wsme.protocol.Protocol):
diff --git a/wsme/tests/test_types.py b/wsme/tests/test_types.py
index 81e6891..f5f5abe 100644
--- a/wsme/tests/test_types.py
+++ b/wsme/tests/test_types.py
@@ -1,9 +1,5 @@
 import re
-try:
-    import unittest2 as unittest
-except ImportError:
-    import unittest
-import six
+import unittest
 
 from wsme import exc
 from wsme import types
@@ -37,8 +33,8 @@ class TestTypes(unittest.TestCase):
     def test_flat_type(self):
         class Flat(object):
             aint = int
-            abytes = six.binary_type
-            atext = six.text_type
+            abytes = bytes
+            atext = str
             afloat = float
 
         types.register_type(Flat)
@@ -228,12 +224,12 @@ Value: 'v3'. Value should be one of: v., v.",
 
         obj = SType()
 
-        obj.atext = six.b('somebytes')
-        assert obj.atext == six.u('somebytes')
+        obj.atext = b'somebytes'
+        assert obj.atext == 'somebytes'
         assert isinstance(obj.atext, types.text)
 
-        obj.abytes = six.u('sometext')
-        assert obj.abytes == six.b('sometext')
+        obj.abytes = 'sometext'
+        assert obj.abytes == b'sometext'
         assert isinstance(obj.abytes, types.bytes)
 
     def test_named_attribute(self):
@@ -303,8 +299,8 @@ Value: 'v3'. Value should be one of: v., v.",
 
     def test_validate_int(self):
         self.assertEqual(types.validate_value(int, 1), 1)
+        self.assertEqual(types.validate_value(int, b'1'), 1)
         self.assertEqual(types.validate_value(int, '1'), 1)
-        self.assertEqual(types.validate_value(int, six.u('1')), 1)
         self.assertRaises(ValueError, types.validate_value, int, 1.1)
 
     def test_validate_integer_type(self):
@@ -440,10 +436,10 @@ Value: 'v3'. Value should be one of: v., v.",
 
     def test_base_init(self):
         class C1(types.Base):
-            s = six.text_type
+            s = str
 
-        c = C1(s=six.u('test'))
-        assert c.s == six.u('test')
+        c = C1(s='test')
+        assert c.s == 'test'
 
     def test_array_eq(self):
         ell = [types.ArrayType(str)]
@@ -464,20 +460,14 @@ Value: 'v3'. Value should be one of: v., v.",
     def test_binary_to_base(self):
         import base64
         assert types.binary.tobasetype(None) is None
-        if six.PY3:
-            expected = base64.encodebytes(six.b('abcdef'))
-        else:
-            expected = base64.encodestring(six.b('abcdef'))
-        assert types.binary.tobasetype(six.b('abcdef')) == expected
+        expected = base64.encodebytes(b'abcdef')
+        assert types.binary.tobasetype(b'abcdef') == expected
 
     def test_binary_from_base(self):
         import base64
         assert types.binary.frombasetype(None) is None
-        if six.PY3:
-            encoded = base64.encodebytes(six.b('abcdef'))
-        else:
-            encoded = base64.encodestring(six.b('abcdef'))
-        assert types.binary.frombasetype(encoded) == six.b('abcdef')
+        encoded = base64.encodebytes(b'abcdef')
+        assert types.binary.frombasetype(encoded) == b'abcdef'
 
     def test_wsattr_weakref_datatype(self):
         # If the datatype inside the wsattr ends up a weakref, it
@@ -557,8 +547,8 @@ Value: 'v3'. Value should be one of: v., v.",
         class buffer:
             def read(self):
                 return 'from-file'
-        f = types.File(content=six.b('from-content'))
-        assert f.file.read() == six.b('from-content')
+        f = types.File(content=b'from-content')
+        assert f.file.read() == b'from-content'
 
     def test_unregister(self):
         class TempType(object):
diff --git a/wsme/types.py b/wsme/types.py
index e67f623..a2dcdf0 100644
--- a/wsme/types.py
+++ b/wsme/types.py
@@ -2,10 +2,10 @@ import base64
 import datetime
 import decimal
 import inspect
+import io
 import logging
 import netaddr
 import re
-import six
 import sys
 import uuid
 import weakref
@@ -14,15 +14,15 @@ from wsme import exc
 
 log = logging.getLogger(__name__)
 
-#: The 'str' (python 2) or 'bytes' (python 3) type.
+#: Byte string.
 #: Its use should be restricted to
 #: pure ascii strings as the protocols will generally not be
 #: be able to send non-unicode strings.
 #: To transmit binary strings, use the :class:`binary` type
-bytes = six.binary_type
+bytes = bytes
 
 #: Unicode string.
-text = six.text_type
+text = str
 
 
 class ArrayType(object):
@@ -128,18 +128,12 @@ class BinaryType(UserType):
     def tobasetype(self, value):
         if value is None:
             return None
-        if six.PY3:
-            return base64.encodebytes(value)
-        else:
-            return base64.encodestring(value)
+        return base64.encodebytes(value)
 
     def frombasetype(self, value):
         if value is None:
             return None
-        if six.PY3:
-            return base64.decodebytes(value)
-        else:
-            return base64.decodestring(value)
+        return base64.decodebytes(value)
 
 
 #: The binary almost-native type
@@ -194,13 +188,13 @@ class StringType(UserType):
         Name = StringType(min_length=1, pattern='^[a-zA-Z ]*$')
 
     """
-    basetype = six.string_types
+    basetype = str
     name = "string"
 
     def __init__(self, min_length=None, max_length=None, pattern=None):
         self.min_length = min_length
         self.max_length = max_length
-        if isinstance(pattern, six.string_types):
+        if isinstance(pattern, str):
             self.pattern = re.compile(pattern)
         else:
             self.pattern = pattern
@@ -231,7 +225,7 @@ class IPv4AddressType(UserType):
     """
     A simple IPv4 type.
     """
-    basetype = six.string_types
+    basetype = str
     name = "ipv4address"
 
     @staticmethod
@@ -251,7 +245,7 @@ class IPv6AddressType(UserType):
 
     This type represents IPv6 addresses in the short format.
     """
-    basetype = six.string_types
+    basetype = str
     name = "ipv6address"
 
     @staticmethod
@@ -273,13 +267,13 @@ class UuidType(UserType):
     having dashes. For example, '6a0a707c-45ef-4758-b533-e55adddba8ce'
     and '6a0a707c45ef4758b533e55adddba8ce' are distinguished as valid.
     """
-    basetype = six.string_types
+    basetype = str
     name = "uuid"
 
     @staticmethod
     def validate(value):
         try:
-            return six.text_type((uuid.UUID(value)))
+            return str(uuid.UUID(value))
         except (TypeError, ValueError, AttributeError):
             error = 'Value should be UUID format'
             raise ValueError(error)
@@ -305,13 +299,13 @@ class Enum(UserType):
         self.values = set(values)
         name = kw.pop('name', None)
         if name is None:
-            name = "Enum(%s)" % ', '.join((six.text_type(v) for v in values))
+            name = "Enum(%s)" % ', '.join((str(v) for v in values))
         self.name = name
 
     def validate(self, value):
         if value not in self.values:
             raise ValueError("Value should be one of: %s" %
-                             ', '.join(map(six.text_type, self.values)))
+                             ', '.join(map(str, self.values)))
         return value
 
     def tobasetype(self, value):
@@ -341,13 +335,12 @@ Unset = UnsetType()
 HostRequest = object()
 
 
-pod_types = six.integer_types + (
-    bytes, text, float, bool)
+pod_types = (int, bytes, str, float, bool)
 dt_types = (datetime.date, datetime.time, datetime.datetime)
 extra_types = (binary, decimal.Decimal)
 native_types = pod_types + dt_types + extra_types
 # The types for which we allow promotion to certain numbers.
-_promotable_types = six.integer_types + (text, bytes)
+_promotable_types = (int, str, bytes)
 
 
 def iscomplex(datatype):
@@ -380,7 +373,7 @@ def validate_value(datatype, value):
     # Do type promotion/conversion and data validation for builtin
     # types.
     v_type = type(value)
-    if datatype in six.integer_types:
+    if datatype in (int,):
         if v_type in _promotable_types:
             try:
                 # Try to turn the value into an int
@@ -396,9 +389,9 @@ def validate_value(datatype, value):
             # An error is raised at the end of the function
             # when the types don't match.
             pass
-    elif datatype is text and isinstance(value, bytes):
+    elif datatype is str and isinstance(value, bytes):
         value = value.decode()
-    elif datatype is bytes and isinstance(value, text):
+    elif datatype is bytes and isinstance(value, str):
         value = value.encode()
 
     if not isinstance(value, datatype):
@@ -496,7 +489,7 @@ class wsattr(object):
         try:
             value = validate_value(self.datatype, value)
         except (ValueError, TypeError) as e:
-            raise exc.InvalidInput(self.name, value, six.text_type(e))
+            raise exc.InvalidInput(self.name, value, str(e))
         dataholder = self._get_dataholder(instance)
         if value is Unset:
             if hasattr(dataholder, self.key):
@@ -730,7 +723,7 @@ class Registry(object):
                 return ct
 
     def resolve_type(self, type_):
-        if isinstance(type_, six.string_types):
+        if isinstance(type_, str):
             return self.lookup(type_)
         if isinstance(type_, list):
             type_ = ArrayType(type_[0])
@@ -769,7 +762,7 @@ class BaseMeta(type):
             cls.__registry__.register(cls)
 
 
-class Base(six.with_metaclass(BaseMeta)):
+class Base(metaclass=BaseMeta):
     """Base type for complex types"""
     def __init__(self, **kw):
         for key, value in kw.items():
@@ -784,10 +777,10 @@ class File(Base):
     input, File can be loaded from a form file field.
     """
     #: The file name
-    filename = wsattr(text)
+    filename = wsattr(str)
 
     #: Mime type of the content
-    contenttype = wsattr(text)
+    contenttype = wsattr(str)
 
     def _get_content(self):
         if self._content is None and self._file:
@@ -812,14 +805,14 @@ class File(Base):
             if fieldstorage.file:
                 self._file = fieldstorage.file
                 self.filename = fieldstorage.filename
-                self.contenttype = text(fieldstorage.type)
+                self.contenttype = str(fieldstorage.type)
             else:
                 self._content = fieldstorage.value
 
     @property
     def file(self):
         if self._file is None and self._content:
-            self._file = six.BytesIO(self._content)
+            self._file = io.BytesIO(self._content)
         return self._file
 
 
diff --git a/wsme/utils.py b/wsme/utils.py
index e52b0ef..d63d8b1 100644
--- a/wsme/utils.py
+++ b/wsme/utils.py
@@ -1,8 +1,9 @@
-import decimal
+import builtins
 import datetime
+import decimal
+from http import client as http_client
 import pytz
 import re
-from six.moves import builtins, http_client
 
 try:
     import dateutil.parser
diff --git a/wsmeext/pecan.py b/wsmeext/pecan.py
index 0d1c3b6..560329d 100644
--- a/wsmeext/pecan.py
+++ b/wsmeext/pecan.py
@@ -2,7 +2,6 @@ from __future__ import absolute_import
 
 import functools
 import inspect
-import six
 import sys
 
 import pecan
@@ -135,10 +134,7 @@ def wsexpose(*args, **kwargs):
             pecan_text_xml_decorate(callfunction)
         if 'json' in funcdef.rest_content_types:
             pecan_json_decorate(callfunction)
-        if six.PY2:
-            argspec = inspect.getargspec(f)
-        else:
-            argspec = inspect.getfullargspec(f)
+        argspec = inspect.getfullargspec(f)
         pecan.util._cfg(callfunction)['argspec'] = argspec
         callfunction._wsme_definition = funcdef
         return callfunction
diff --git a/wsmeext/sphinxext.py b/wsmeext/sphinxext.py
index 3881a90..f2dde7b 100644
--- a/wsmeext/sphinxext.py
+++ b/wsmeext/sphinxext.py
@@ -4,7 +4,6 @@ import sys
 
 from docutils.parsers.rst import Directive
 from docutils.parsers.rst import directives
-import six
 from sphinx import addnodes
 from sphinx.ext import autodoc
 from sphinx.domains.python import PyClasslike, PyClassmember
@@ -36,9 +35,9 @@ def datatypename(datatype):
 
 def make_sample_object(datatype):
     if datatype is wsme.types.bytes:
-        return six.b('samplestring')
+        return b'samplestring'
     if datatype is wsme.types.text:
-        return u'sample unicode'
+        return 'sample unicode'
     if datatype is int:
         return 5
     sample_obj = getattr(datatype, 'sample', datatype)()
@@ -252,7 +251,7 @@ class TypeDocumenter(autodoc.ClassDocumenter):
                     ])
                     content.extend(
                         u' ' * 8 + line
-                        for line in six.text_type(sample).split('\n'))
+                        for line in str(sample).split('\n'))
             for line in content:
                 self.add_line(line, u'<wsmeext.sphinxext')
 
@@ -455,7 +454,7 @@ def document_function(funcdef, docstrings=None, protocols=['restjson']):
             ])
             codesamples.extend((
                 u' ' * 12 + line
-                for line in six.text_type(sample).split('\n')
+                for line in str(sample).split('\n')
             ))
 
         if funcdef.return_type:
@@ -475,7 +474,7 @@ def document_function(funcdef, docstrings=None, protocols=['restjson']):
                 ])
                 codesamples.extend((
                     u' ' * 12 + line
-                    for line in six.text_type(sample).split('\n')
+                    for line in str(sample).split('\n')
                 ))
 
     docstrings[0:0] = [codesamples]