six: metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Some time in the last 12 hours, our build system using gsutil started failing and it appears to be related to six 1.11.0, which was just released.

$ gsutil
...
 File "myproject/venv/lib/python2.7/site-packages/apitools/base/protorpclite/messages.py", line 1168, in <module>
    class Field(six.with_metaclass(_FieldMeta, object)):
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

gsutil declares a dependency on six>=1.9.0 (ugh) so it pulls in the just released 1.11.0. Rolling back to six 1.10.0 and gsutil runs alright. I’m not really involved in Python or gsutil, so maybe gsutil needs to fix something, no idea, but it seems like a breakage that’s not called out in the release notes or maybe something to fix.

cc @jdemeyer @benjaminp Possibly introduced in https://github.com/benjaminp/six/pull/191

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 17
  • Comments: 28 (13 by maintainers)

Commits related to this issue

Most upvoted comments

With so many different projects impacted by #191, often its best to revert the commit in question and cut a point release to mitigate the issue. Reverts are easier to get in. Later, you can write and discuss a new implementation with the maintainers and if there really is a breaking change, draft a changelog note about it.

I have a fix for the __metaclass__ issue at #211.

I have the same issue with some Google libraries:

  File "/Users/florenthemmi/dev/env/lib/python2.7/site-packages/apache_beam/internal/gcp/json_value.py", line 23, in <module>
    from apitools.base.py import extra_types
  File "/Users/florenthemmi/dev/env/lib/python2.7/site-packages/apitools/base/py/__init__.py", line 21, in <module>
    from apitools.base.py.base_api import *
  File "/Users/florenthemmi/dev/env/lib/python2.7/site-packages/apitools/base/py/base_api.py", line 31, in <module>
    from apitools.base.protorpclite import message_types
  File "/Users/florenthemmi/dev/env/lib/python2.7/site-packages/apitools/base/protorpclite/message_types.py", line 25, in <module>
    from apitools.base.protorpclite import messages
  File "/Users/florenthemmi/dev/env/lib/python2.7/site-packages/apitools/base/protorpclite/messages.py", line 1165, in <module>
    class Field(six.with_metaclass(_FieldMeta, object)):
TypeError: Error when calling the metaclass bases
    metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases

Looking at the 3rd party package code that triggers the error, it boils down to this:

import six


class Meta(type):
    pass


class A(six.with_metaclass(Meta)):
    __metaclass__ = Meta

Removing the __metaclass__ prevents the TypeError.

Got the same issue using a Django package.

File "/usr/local/lib/python2.7/site-packages/autocomplete_light/shortcuts.py", line 8, in <module>
   from .forms import *
 File "/usr/local/lib/python2.7/site-packages/autocomplete_light/forms.py", line 438, in <module>
   class ModelForm(six.with_metaclass(*bases)):
 File "/usr/local/lib/python2.7/site-packages/autocomplete_light/forms.py", line 283, in __new__
   attrs)
 File "/usr/local/lib/python2.7/site-packages/django/forms/models.py", line 208, in __new__
   new_class = super(ModelFormMetaclass, mcs).__new__(mcs, name, bases, attrs)
 File "/usr/local/lib/python2.7/site-packages/django/forms/forms.py", line 41, in __new__
   new_class = super(DeclarativeFieldsMetaclass, mcs).__new__(mcs, name, bases, attrs)
 File "/usr/local/lib/python2.7/site-packages/django/forms/widgets.py", line 153, in __new__
   new_class = super(MediaDefiningClass, mcs).__new__(mcs, name, bases, attrs)
TypeError: Error when calling the metaclass bases
   metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases
class PywbemLoggers(six.with_metaclass(MetaPywbemLoggers)):
    __metaclass__ = MetaPywbemLoggers

As discussed above, this is wrong: you should not combine six.with_metaclass and __metaclass__. However, as many people seem to do this, it’s becoming clear that a fix is needed.

The issue in google-apitools is the same:

# TODO(rafek): Prevent additional field subclasses.
class Field(six.with_metaclass(_FieldMeta, object)):
    """Definition for message field."""

    __initialized = False  # pylint:disable=invalid-name
    __variant_to_type = {}  # pylint:disable=invalid-name

    # TODO(craigcitro): Remove this alias.
    #
    # We add an alias here for backwards compatibility; note that in
    # python3, this attribute will silently be ignored.
    __metaclass__ = _FieldMeta