Skip to content

Commit e65d34c

Browse files
authored
Fix some issues with six dependency (#240)
Update minimum required version of `six` to 1.9.0, so that `six.raise_from` can be safely used by all clients. Fix support for the latest version of six, 1.11.0. That release changed the temporary metaclass returned from `with_metaclass()`, such that it directly inherits from `type`, instead of inheriting from the target metaclass [1]. We depended on this detail, and the change caused .. code-block:: python TypeError('metaclass conflict: ...') to be raised when defining a class with `with_metaclass()`. We fix this by manually selecting the most derived metaclass, and including it in our temporary metaclass. Also, `__prepare__` is now defined on the temporary metaclass, in six 1.11.0 [2]. This allows us to skip our own definition of that method, when using six>=1.11.0. Fixes #228. Fixes #239. [1] <benjaminp/six#191> [2] <benjaminp/six#178>
1 parent a89c84a commit e65d34c

File tree

4 files changed

+47
-12
lines changed

4 files changed

+47
-12
lines changed

HISTORY.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ Release History
134134
when it tried to refresh any ``JWTAuth`` object.
135135
- Fixed an exception that was being raised from ``ExtendableEnumMeta.__dir__()``.
136136
- CPython 3.6 support.
137+
- Increased required minimum version of six to 1.9.0.
137138

138139
1.5.3 (2016-05-26)
139140
++++++++++++++++++

boxsdk/util/compat.py

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from datetime import timedelta
66

77
import six
8+
from six.moves import map
89

910

1011
NoneType = type(None)
@@ -62,19 +63,58 @@ class Subclass(temporary_class):
6263
``bases``, then errors might occur. For example, this was a problem when
6364
used with ``enum.EnumMeta`` in Python 3.6. Here we make sure that
6465
``__prepare__()`` is defined on the temporary metaclass, and pass ``bases``
65-
to ``meta.__prepare__()``.
66+
to ``meta.__prepare__()``. This is fixed in six>=1.11.0 by PR #178 [1].
6667
6768
Since ``temporary_class`` doesn't have the correct bases, in theory this
6869
could cause other problems, besides the previous one, in certain edge
6970
cases. To make sure that doesn't become a problem, we make sure that
7071
``temporary_class`` has ``bases`` as its bases, just like the final class.
72+
73+
[1] <https://github.com/benjaminp/six/pull/178>
7174
"""
7275
temporary_class = six.with_metaclass(meta, *bases, **with_metaclass_kwargs)
7376
temporary_metaclass = type(temporary_class)
7477

75-
class TemporaryMetaSubclass(temporary_metaclass):
76-
@classmethod
77-
def __prepare__(cls, name, this_bases, **kwds): # pylint:disable=unused-argument
78-
return meta.__prepare__(name, bases, **kwds)
78+
class TemporaryMetaSubclass(temporary_metaclass, _most_derived_metaclass(meta, bases)):
79+
80+
if '__prepare__' not in temporary_metaclass.__dict__:
81+
# six<1.11.0, __prepare__ is not defined on the temporary metaclass.
82+
83+
@classmethod
84+
def __prepare__(mcs, name, this_bases, **kwds): # pylint:disable=unused-argument,arguments-differ
85+
return meta.__prepare__(name, bases, **kwds)
7986

8087
return type.__new__(TemporaryMetaSubclass, str('temporary_class'), bases, {})
88+
89+
90+
def _most_derived_metaclass(meta, bases):
91+
"""Selects the most derived metaclass of all the given metaclasses.
92+
93+
This will be the same metaclass that is selected by
94+
95+
.. code-block:: python
96+
97+
class temporary_class(*bases, metaclass=meta): pass
98+
99+
or equivalently by
100+
101+
.. code-block:: python
102+
103+
types.prepare_class('temporary_class', bases, metaclass=meta)
104+
105+
"Most derived" means the item in {meta, type(bases[0]), type(bases[1]), ...}
106+
which is a non-strict subclass of every item in that set.
107+
108+
If no such item exists, then :exc:`TypeError` is raised.
109+
110+
:type meta: `type`
111+
:type bases: :class:`Iterable` of `type`
112+
"""
113+
most_derived_metaclass = meta
114+
for base_type in map(type, bases):
115+
if issubclass(base_type, most_derived_metaclass):
116+
most_derived_metaclass = base_type
117+
elif not issubclass(most_derived_metaclass, base_type):
118+
# Raises TypeError('metaclass conflict: ...')
119+
return type.__new__(meta, str('temporary_class'), bases, {})
120+
return most_derived_metaclass

requirements.txt

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1 @@
1-
cryptography>=0.9.2
2-
redis>=2.10.3
3-
pyjwt>=1.3.0
4-
requests>=2.4.3
5-
requests-toolbelt>=0.4.0
6-
six >= 1.4.0
71
-e .[all]

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def run_tests(self):
5656

5757
def main():
5858
base_dir = dirname(__file__)
59-
install_requires = ['requests>=2.4.3', 'six>=1.4.0', 'requests-toolbelt>=0.4.0']
59+
install_requires = ['requests>=2.4.3', 'six>=1.9.0', 'requests-toolbelt>=0.4.0']
6060
redis_requires = ['redis>=2.10.3']
6161
jwt_requires = ['pyjwt>=1.3.0', 'cryptography>=0.9.2']
6262
extra_requires = defaultdict(list)

0 commit comments

Comments
 (0)