11# coding: utf-8
22
3- from __future__ import division , unicode_literals
4-
3+ from __future__ import absolute_import , division , unicode_literals
54
65from datetime import timedelta
76
7+ import six
8+
9+
810if not hasattr (timedelta , 'total_seconds' ):
911 def total_seconds (delta ):
1012 """
@@ -15,3 +17,61 @@ def total_seconds(delta):
1517else :
1618 def total_seconds (delta ):
1719 return delta .total_seconds ()
20+
21+
22+ def with_metaclass (meta , * bases , ** with_metaclass_kwargs ):
23+ """Extends the behavior of six.with_metaclass.
24+
25+ The normal usage (expanded to include temporaries, to make the illustration
26+ easier) is:
27+
28+ .. code-block:: python
29+
30+ temporary_class = six.with_metaclass(meta, *bases)
31+ temporary_metaclass = type(temporary_class)
32+
33+ class Subclass(temporary_class):
34+ ...
35+
36+ SubclassMeta = type(Subclass)
37+
38+ In this example:
39+
40+ - ``temporary_class`` is a class with ``(object,)`` as its bases.
41+ - ``temporary_metaclass`` is a metaclass with ``(meta,)`` as its bases.
42+ - ``Subclass`` is a class with ``bases`` as its bases.
43+ - ``SubclassMeta`` is ``meta``.
44+
45+ ``six.with_metaclass()`` is defined in such a way that it can make sure
46+ that ``Subclass`` has the correct metaclass and bases, while only using
47+ syntax which is common to both Python 2 and Python 3.
48+ ``temporary_metaclass()`` returns an instance of ``meta``, rather than an
49+ instance of itself / a subclass of ``temporary_class``, which is how
50+ ``SubclassMeta`` ends up being ``meta``, and how the temporaries don't
51+ appear anywhere in the final subclass.
52+
53+ There are two problems with the current (as of six==1.10.0) implementation
54+ of ``six.with_metaclass()``, which this function solves.
55+
56+ ``six.with_metaclass()`` does not define ``__prepare__()`` on the temporary
57+ metaclass. This means that ``meta.__prepare__()`` gets called directly,
58+ with bases set to ``(object,)``. If it needed to actually receive
59+ ``bases``, then errors might occur. For example, this was a problem when
60+ used with ``enum.EnumMeta`` in Python 3.6. Here we make sure that
61+ ``__prepare__()`` is defined on the temporary metaclass, and pass ``bases``
62+ to ``meta.__prepare__()``.
63+
64+ Since ``temporary_class`` doesn't have the correct bases, in theory this
65+ could cause other problems, besides the previous one, in certain edge
66+ cases. To make sure that doesn't become a problem, we make sure that
67+ ``temporary_class`` has ``bases`` as its bases, just like the final class.
68+ """
69+ temporary_class = six .with_metaclass (meta , * bases , ** with_metaclass_kwargs )
70+ temporary_metaclass = type (temporary_class )
71+
72+ class TemporaryMetaSubclass (temporary_metaclass ):
73+ @classmethod
74+ def __prepare__ (cls , name , this_bases , ** kwds ): # pylint:disable=unused-argument
75+ return meta .__prepare__ (name , bases , ** kwds )
76+
77+ return type .__new__ (TemporaryMetaSubclass , str ('temporary_class' ), bases , {})
0 commit comments