Reputation: 483
I'm debugging a python script (python isn't my go to language), and this is the first time I'm working with metaclass's in python. I'm getting a metaclass conflict error when I run the code as it is below.
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
In attempting to solve it I commented out the metaclass declaration in MySQLMSoftware
, thinking that it's redundant as it inherits from a class with the same metaclass declaration, but this leads to:
TypeError: Error when calling the metaclass bases
module.__init__() takes at most 2 arguments (3 given)
Any insight, guidance or direction would be greatly appreciated. I've been reading about python's metaclass implementation and the issue isn't popping out so far.
MSoftware.py
from abc import ABCMeta, abstractmethod
class MSoftware(object) :
__metaclass__ = ABCMeta
def __init__(self,name,spark=None,mysql=None):
self.name = name
...
MySQLMSoftware.py
from mouse import Mouse, MSoftware
from abc import ABCMeta, abstractmethod
class MySQLMSoftware(MSoftware): # Traceback always goes here
__metaclass__ = ABCMeta
MAX_ROWS = 30000000
def __init__(self,name,years,spark=None,mysql=None):
MSoftware.__init__(self,name,spark,mysql)
self.years = years
...
TTime.py
from mouse.Mouse import Mouse
from mouse.MySQLMSoftware import MySQLMSoftware
class TTime(MySQLMSoftware) :
DATABASE = "Database"
NAME = "table"
YEARS = [2014,2016]
def __init__(self,spark=None,mysql=None):
MySQLMSoftware.__init__(self,TTime.NAME,TTime.YEARS,spark,mysql)
...
main.py
import sys
from mouse.Mouse import Mouse
from mouse.TTime import TTime
...
Upvotes: 3
Views: 9726
Reputation: 104682
The issue has to do with your imports, and confusion between identically named classes and modules. The MSoftware
you're importing in MySQLMSoftware.py
is the module implemented in MSoftware.py
, not the class of the same name within that module. To get the latter (without changing the imports), you'd need to use MSoftware.MSoftware
. There may be a similar issue with the Mouse
class and module (which is even worse, since the top level package is named mouse
too).
Try changing the line:
from mouse import Mouse, MSoftware
To these two lines:
from mouse.Mouse import Mouse
from mouse.MSoftware import MSoftware
This will fix the metaclass conflict issue, and make the metaclass declaration in the MySQLMSoftware
class unnecessary.
I'd like to note that the root cause of the issue (from a higher level perspective) is poor module design. You have several modules that each appear to contain a single class with the same name as them. That's a common project layout in other languages (like Java), but it's not necessary or desirable in Python. There's never a good reason for mouse.Mouse.Mouse
to be a thing.
Probably you should combine most (or maybe all) of your package's modules into a smaller number of files, probably starting by putting most of the stuff in mouse/__init__.py
(or a top level mouse.py
if you combine them all and don't need the package any more). This way you won't have quite so many redundant imports.
Upvotes: 3
Reputation: 309821
The problem is that when selecting a metaclass, python picks the most inherited version. However, you have two conflicting metaclasses in play (ABCMeta
and whatever MSoftware
has).
I think that the python3.x docs are slightly more correct than the python2.x docs in this regard:
The appropriate metaclass for a class definition is determined as follows:
- if no bases and no explicit metaclass are given, then type() is used
- if an explicit metaclass is given and it is not an instance of type(), > then it is used directly as the metaclass
- if an instance of
type()
is given as the explicit metaclass, or bases are defined, then the most derived metaclass is used
One solution is to create a new metaclass that is the mixture of the two:
class NewMeta(type(MSoftware), ABCMeta):
pass
In this case, since all of the metaclasses are "instances" of MSoftware
's metaclass, NewMeta
will be selected because it is the most derived metaclass and things should work (provided that MSoftware
's metaclass can be used in cooperative multi-inheritance).
Here's an example where I create the NewMeta
dynamically to overcome this issue with some dummy classes:
class Meta1(type):
pass
class Meta2(type):
pass
class Class1(object):
__metaclass__ = Meta1
# TypeError!
# class Class2(Class1):
# __metaclass__ = Meta2
class Class2(Class1):
__metaclass__ = type('Class2Meta', (type(Class1), Meta2), {})
Upvotes: 5