diff --git a/CHANGELOG.rst b/CHANGELOG.rst index fa4825a6b..0aeaf9843 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -1,6 +1,10 @@ Changelog ========= +1.8.0 (master) +----------------- +* Removed dependency on six. + 1.7.0 (2021-02-11) ------------------ diff --git a/setup.py b/setup.py index 720f63714..85ab468a7 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,6 @@ # For pkg_resources. >=1.0 so pip resolves it to a version cryptography # will tolerate; see #2599: 'setuptools>=1.0', - 'six>=1.9.0', # needed for python_2_unicode_compatible ] testing_requires = [ diff --git a/src/josepy/b64.py b/src/josepy/b64.py index 1be2f4c1c..b9060a006 100644 --- a/src/josepy/b64.py +++ b/src/josepy/b64.py @@ -12,8 +12,6 @@ """ import base64 -import six - def b64encode(data): """JOSE Base64 encode. @@ -27,8 +25,8 @@ def b64encode(data): :raises TypeError: if ``data`` is of incorrect type """ - if not isinstance(data, six.binary_type): - raise TypeError('argument should be {0}'.format(six.binary_type)) + if not isinstance(data, bytes): + raise TypeError('argument should be bytes') return base64.urlsafe_b64encode(data).rstrip(b'=') @@ -46,13 +44,13 @@ def b64decode(data): :raises ValueError: if input is unicode with non-ASCII characters """ - if isinstance(data, six.string_types): + if isinstance(data, str): try: data = data.encode('ascii') except UnicodeEncodeError: raise ValueError( 'unicode argument should contain only ASCII characters') - elif not isinstance(data, six.binary_type): + elif not isinstance(data, bytes): raise TypeError('argument should be a str or unicode') return base64.urlsafe_b64decode(data + b'=' * (4 - (len(data) % 4))) diff --git a/src/josepy/b64_test.py b/src/josepy/b64_test.py index 117cde083..ff3e1d096 100644 --- a/src/josepy/b64_test.py +++ b/src/josepy/b64_test.py @@ -1,7 +1,6 @@ """Tests for josepy.b64.""" import unittest -import six # https://en.wikipedia.org/wiki/Base64#Examples B64_PADDING_EXAMPLES = { @@ -14,8 +13,8 @@ B64_URL_UNSAFE_EXAMPLES = { - six.int2byte(251) + six.int2byte(239): b'--8', - six.int2byte(255) * 2: b'__8', + bytes((251, 239)): b'--8', + bytes((255,)) * 2: b'__8', } @@ -31,11 +30,11 @@ def test_empty(self): self.assertEqual(self._call(b''), b'') def test_unsafe_url(self): - for text, b64 in six.iteritems(B64_URL_UNSAFE_EXAMPLES): + for text, b64 in B64_URL_UNSAFE_EXAMPLES.items(): self.assertEqual(self._call(text), b64) def test_different_paddings(self): - for text, (b64, _) in six.iteritems(B64_PADDING_EXAMPLES): + for text, (b64, _) in B64_PADDING_EXAMPLES.items(): self.assertEqual(self._call(text), b64) def test_unicode_fails_with_type_error(self): @@ -51,15 +50,15 @@ def _call(cls, data): return b64decode(data) def test_unsafe_url(self): - for text, b64 in six.iteritems(B64_URL_UNSAFE_EXAMPLES): + for text, b64 in B64_URL_UNSAFE_EXAMPLES.items(): self.assertEqual(self._call(b64), text) def test_input_without_padding(self): - for text, (b64, _) in six.iteritems(B64_PADDING_EXAMPLES): + for text, (b64, _) in B64_PADDING_EXAMPLES.items(): self.assertEqual(self._call(b64), text) def test_input_with_padding(self): - for text, (b64, pad) in six.iteritems(B64_PADDING_EXAMPLES): + for text, (b64, pad) in B64_PADDING_EXAMPLES.items(): self.assertEqual(self._call(b64 + pad), text) def test_unicode_with_ascii(self): diff --git a/src/josepy/interfaces.py b/src/josepy/interfaces.py index 636c80838..21a2d9b69 100644 --- a/src/josepy/interfaces.py +++ b/src/josepy/interfaces.py @@ -2,7 +2,6 @@ import abc import json -import six from josepy import errors, util @@ -15,8 +14,7 @@ # pylint: disable=too-few-public-methods -@six.add_metaclass(abc.ABCMeta) -class JSONDeSerializable(object): +class JSONDeSerializable(object, metaclass=abc.ABCMeta): # pylint: disable=too-few-public-methods """Interface for (de)serializable JSON objects. @@ -139,7 +137,7 @@ def to_json(self): def _serialize(obj): if isinstance(obj, JSONDeSerializable): return _serialize(obj.to_partial_json()) - if isinstance(obj, six.string_types): # strings are Sequence + if isinstance(obj, str): # strings are Sequence return obj elif isinstance(obj, list): return [_serialize(subobj) for subobj in obj] @@ -149,7 +147,7 @@ def _serialize(obj): return tuple(_serialize(subobj) for subobj in obj) elif isinstance(obj, Mapping): return dict((_serialize(key), _serialize(value)) - for key, value in six.iteritems(obj)) + for key, value in obj.items()) else: return obj diff --git a/src/josepy/json_util.py b/src/josepy/json_util.py index 818a52c58..931dfde49 100644 --- a/src/josepy/json_util.py +++ b/src/josepy/json_util.py @@ -11,7 +11,6 @@ import logging import OpenSSL -import six from josepy import b64, errors, interfaces, util @@ -106,7 +105,7 @@ def default_decoder(cls, value): elif isinstance(value, dict): return util.frozendict( dict((cls.default_decoder(key), cls.default_decoder(value)) - for key, value in six.iteritems(value))) + for key, value in value.items())) else: # integer or string return value @@ -164,21 +163,23 @@ def __new__(mcs, name, bases, dikt): for base in bases: fields.update(getattr(base, '_fields', {})) # Do not reorder, this class might override fields from base classes! - for key, value in tuple(six.iteritems(dikt)): - # not six.iterkeys() (in-place edit!) + # We create a tuple based on dikt.items() here because the loop + # modifies dikt. + for key, value in tuple(dikt.items()): if isinstance(value, Field): fields[key] = dikt.pop(key) dikt['_orig_slots'] = dikt.get('__slots__', ()) dikt['__slots__'] = tuple( - list(dikt['_orig_slots']) + list(six.iterkeys(fields))) + list(dikt['_orig_slots']) + list(fields.keys())) dikt['_fields'] = fields return abc.ABCMeta.__new__(mcs, name, bases, dikt) -@six.add_metaclass(JSONObjectWithFieldsMeta) -class JSONObjectWithFields(util.ImmutableMap, interfaces.JSONDeSerializable): +class JSONObjectWithFields(util.ImmutableMap, + interfaces.JSONDeSerializable, + metaclass=JSONObjectWithFieldsMeta): # pylint: disable=too-few-public-methods """JSON object with fields. @@ -210,7 +211,7 @@ def bar(value): def _defaults(cls): """Get default fields values.""" return dict([(slot, field.default) for slot, field - in six.iteritems(cls._fields)]) + in cls._fields.items()]) def __init__(self, **kwargs): # pylint: disable=star-args @@ -237,7 +238,7 @@ def fields_to_partial_json(self): """Serialize fields to JSON.""" jobj = {} omitted = set() - for slot, field in six.iteritems(self._fields): + for slot, field in self._fields.items(): value = getattr(self, slot) if field.omit(value): @@ -257,7 +258,7 @@ def to_partial_json(self): @classmethod def _check_required(cls, jobj): missing = set() - for _, field in six.iteritems(cls._fields): + for _, field in cls._fields.items(): if not field.omitempty and field.json_name not in jobj: missing.add(field.json_name) @@ -271,7 +272,7 @@ def fields_from_json(cls, jobj): """Deserialize fields from JSON.""" cls._check_required(jobj) fields = {} - for slot, field in six.iteritems(cls._fields): + for slot, field in cls._fields.items(): if field.json_name not in jobj and field.omitempty: fields[slot] = field.default else: @@ -311,10 +312,9 @@ def decode_b64jose(data, size=None, minimum=False): :rtype: bytes """ - error_cls = TypeError if six.PY2 else binascii.Error try: decoded = b64.b64decode(data.encode()) - except error_cls as error: + except binascii.Error as error: raise errors.DeserializationError(error) if size is not None and ((not minimum and len(decoded) != size) or @@ -350,10 +350,9 @@ def decode_hex16(value, size=None, minimum=False): if size is not None and ((not minimum and len(value) != size * 2) or (minimum and len(value) < size * 2)): raise errors.DeserializationError() - error_cls = TypeError if six.PY2 else binascii.Error try: return binascii.unhexlify(value) - except error_cls as error: + except binascii.Error as error: raise errors.DeserializationError(error) @@ -433,7 +432,7 @@ def register(cls, type_cls, typ=None): @classmethod def get_type_cls(cls, jobj): """Get the registered class for ``jobj``.""" - if cls in six.itervalues(cls.TYPES): + if cls in cls.TYPES.values(): if cls.type_field_name not in jobj: raise errors.DeserializationError( "Missing type field ({0})".format(cls.type_field_name)) diff --git a/src/josepy/json_util_test.py b/src/josepy/json_util_test.py index 4c6855960..b09f348e4 100644 --- a/src/josepy/json_util_test.py +++ b/src/josepy/json_util_test.py @@ -3,7 +3,6 @@ import unittest import mock -import six from josepy import errors, interfaces, test_util, util @@ -90,8 +89,7 @@ def setUp(self): # pylint: disable=invalid-name,missing-docstring,too-few-public-methods # pylint: disable=blacklisted-name - @six.add_metaclass(JSONObjectWithFieldsMeta) - class A(object): + class A(object, metaclass=JSONObjectWithFieldsMeta): __slots__ = ('bar',) baz = self.field @@ -246,13 +244,13 @@ def setUp(self): def test_encode_b64jose(self): from josepy.json_util import encode_b64jose encoded = encode_b64jose(b'x') - self.assertTrue(isinstance(encoded, six.string_types)) + self.assertTrue(isinstance(encoded, str)) self.assertEqual(u'eA', encoded) def test_decode_b64jose(self): from josepy.json_util import decode_b64jose decoded = decode_b64jose(u'eA') - self.assertTrue(isinstance(decoded, six.binary_type)) + self.assertTrue(isinstance(decoded, bytes)) self.assertEqual(b'x', decoded) def test_decode_b64jose_padding_error(self): @@ -278,13 +276,13 @@ def test_encode_hex16(self): from josepy.json_util import encode_hex16 encoded = encode_hex16(b'foo') self.assertEqual(u'666f6f', encoded) - self.assertTrue(isinstance(encoded, six.string_types)) + self.assertTrue(isinstance(encoded, str)) def test_decode_hex16(self): from josepy.json_util import decode_hex16 decoded = decode_hex16(u'666f6f') self.assertEqual(b'foo', decoded) - self.assertTrue(isinstance(decoded, six.binary_type)) + self.assertTrue(isinstance(decoded, bytes)) def test_decode_hex16_minimum_size(self): from josepy.json_util import decode_hex16 diff --git a/src/josepy/jwk.py b/src/josepy/jwk.py index 3fe2b05fa..36fe0b327 100644 --- a/src/josepy/jwk.py +++ b/src/josepy/jwk.py @@ -5,7 +5,6 @@ import logging import cryptography.exceptions -import six from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives import hashes # type: ignore from cryptography.hazmat.primitives import serialization @@ -48,7 +47,7 @@ def thumbprint(self, hash_function=hashes.SHA256): """ digest = hashes.Hash(hash_function(), backend=default_backend()) digest.update(json.dumps( - dict((k, v) for k, v in six.iteritems(self.to_json()) + dict((k, v) for k, v in self.to_json().items() if k in self.required), **self._thumbprint_json_dumps_params).encode()) return digest.finalize() @@ -114,7 +113,7 @@ def load(cls, data, password=None, backend=None): key, cls.cryptography_key_types): raise errors.Error('Unable to deserialize {0} into {1}'.format( key.__class__, cls.__class__)) - for jwk_cls in six.itervalues(cls.TYPES): + for jwk_cls in cls.TYPES.values(): if isinstance(key, jwk_cls.cryptography_key_types): return jwk_cls(key=key) raise errors.Error('Unsupported algorithm: {0}'.format(key.__class__)) @@ -251,7 +250,7 @@ def fields_to_partial_json(self): 'qi': private.iqmp, } return dict((key, self._encode_param(value)) - for key, value in six.iteritems(params)) + for key, value in params.items()) @JWK.register @@ -348,7 +347,7 @@ def fields_to_partial_json(self): 'Supplied key is neither of type EllipticCurvePublicKey nor EllipticCurvePrivateKey') params['x'] = public.x params['y'] = public.y - params = {key: self._encode_param(value) for key, value in six.iteritems(params)} + params = {key: self._encode_param(value) for key, value in params.items()} params['crv'] = self._curve_name_to_crv(public.curve.name) return params diff --git a/src/josepy/jws.py b/src/josepy/jws.py index 64323458d..7552419d0 100644 --- a/src/josepy/jws.py +++ b/src/josepy/jws.py @@ -4,7 +4,6 @@ import sys import OpenSSL -import six from josepy import b64, errors, json_util, jwa, jwk, util @@ -76,7 +75,7 @@ class Header(json_util.JSONObjectWithFields): def not_omitted(self): """Fields that would not be omitted in the JSON object.""" return dict((name, getattr(self, name)) - for name, field in six.iteritems(self._fields) + for name, field in self._fields.items() if not field.omit(getattr(self, name))) def __add__(self, other): @@ -357,9 +356,9 @@ def sign(cls, args): protect=set(args.protect)) if args.compact: - six.print_(sig.to_compact().decode('utf-8')) + print(sig.to_compact().decode('utf-8')) else: # JSON - six.print_(sig.json_dumps_pretty()) + print(sig.json_dumps_pretty()) @classmethod def verify(cls, args): @@ -370,7 +369,7 @@ def verify(cls, args): try: sig = JWS.json_loads(sys.stdin.read()) except errors.Error as error: - six.print_(error) + print(error) return -1 if args.key is not None: diff --git a/src/josepy/util.py b/src/josepy/util.py index 82a6bc82e..782f7ef5d 100644 --- a/src/josepy/util.py +++ b/src/josepy/util.py @@ -5,7 +5,6 @@ from collections import Hashable, Mapping import OpenSSL -import six from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec, rsa @@ -211,7 +210,7 @@ def __setattr__(self, name, value): def __repr__(self): return '{0}({1})'.format(self.__class__.__name__, ', '.join( '{0}={1!r}'.format(key, value) - for key, value in six.iteritems(self))) + for key, value in self.items())) class frozendict(Mapping, Hashable): # type: ignore @@ -229,7 +228,7 @@ def __init__(self, *args, **kwargs): # TODO: support generators/iterators object.__setattr__(self, '_items', items) - object.__setattr__(self, '_keys', tuple(sorted(six.iterkeys(items)))) + object.__setattr__(self, '_keys', tuple(sorted(items.keys()))) def __getitem__(self, key): return self._items[key] diff --git a/src/josepy/util_test.py b/src/josepy/util_test.py index 2f479d585..1272ea9bc 100644 --- a/src/josepy/util_test.py +++ b/src/josepy/util_test.py @@ -2,7 +2,6 @@ import functools import unittest -import six from josepy import test_util @@ -219,7 +218,7 @@ def test_init_dict(self): def test_init_other_raises_type_error(self): from josepy.util import frozendict # specifically fail for generators... - self.assertRaises(TypeError, frozendict, six.iteritems({'a': 'b'})) + self.assertRaises(TypeError, frozendict, {'a': 'b'}.items()) def test_len(self): self.assertEqual(2, len(self.fdict))