Portable metaclass between python2 and python3

I am trying to get a python2 program running in python3, it has the following meta class definition. Which works well on Py2. What is the “best” way to have this compatible with py2 and py3?

It does not work in unit test, where it:

try:
    raise Actor.DoesNotExist
except Actor.DoesNotExist:
    pass

Renouncement:

AttributeError: type object 'Actor' has no attribute 'DoesNotExist'

Defining a base metaclass:

class MetaDocument(type):
    def __new__(meta,name,bases,dct):

        class DoesNotExist(BaseException):
            pass

        class MultipleDocumentsReturned(BaseException):
            pass
        dct['DoesNotExist'] = DoesNotExist
        dct['MultipleDocumentsReturned'] = MultipleDocumentsReturned
        class_type = type.__new__(meta, name, bases, dct)
        if not class_type in document_classes:
            if name == 'Document' and bases == (object,):
                pass
            else:
                document_classes.append(class_type)
        return class_type

class Document(object):
    __metaclass__ = MetaDocument
+4
source share
2 answers

You can use the metaclass MetaDocument()as a factory to create a class that replaces your class Documentby reusing the class attributes:

class Document(object):
    # various and sundry methods and attributes

body = vars(Document).copy()
body.pop('__dict__', None)
body.pop('__weakref__', None)

Document = MetaDocument(Document.__name__, Document.__bases__, body)

This does not require you to build the third argument, the body of the class, manually.

You can turn this into a class decorator:

def with_metaclass(mcls):
    def decorator(cls):
        body = vars(cls).copy()
        # clean out class body
        body.pop('__dict__', None)
        body.pop('__weakref__', None)
        return mcls(cls.__name__, cls.__bases__, body)
    return decorator

then use as:

@with_metaclass(MetaDocument)
class Document(object):
    # various and sundry methods and attributes

six library :

@six.add_metaclass(MetaDocument)
class Document(object):

@six.add_metaclass() decorator __slots__, , , ; .

six six.with_metaclass() factory:

class Document(six.with_metaclass(MetaDocument)):

MRO.

+7

six .

class Document(six.with_metaclass(MetaDocument, object)):
    # class definition, without the __metaclass__

,

>>> Document.__mro__
(<class 'test.Document'>, <type 'object'>)

>>> Document.__mro__
(<class 'test.Document'>, <class 'test.NewBase'>, <type 'object'>)

with_metaclass .

+1

Source: https://habr.com/ru/post/1531799/


All Articles