Your original code uses the fact that it is the name __metaclass__
in the class body is used as the meta class, but the 2to3
fixer only looks for straight assignments:
__metaclass__ = MetaClassName
rather than a class __metaclass__
statement or other manner of defining the name (from somemodule import MetaClassName as __metaclass__
would work in a Python 2 class body and 2to3
would miss that too).
You can fix this by moving the meta classes to separate class
definitions:
class BaseMeta(type):
def __new__(cls, classname, bases, dict):
new = type.__new__(cls, classname, bases, dict)
new.classname = classname
print ("BaseMeta::new. Called.")
return new
class Base(object):
__metaclass__ = BaseMeta
class HeirMeta(BaseMeta):
def __new__(self, *args):
new = BaseMeta.__new__(self, *args)
print ("HeirMeta::new. Called.")
return new
class Heir(Base):
__metaclass__ = HeirMeta
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
class HeirOfHeir(Heir):
pass
Heir.define(HeirOfHeir)
You'll have to do this to define metaclasses in Python 3 anyway, as the mechanism to define metaclasses was changed to determining the metaclass before the class body is run rather than during (so that a metaclass can influence that step too).
Now 2to3
will correctly detect that there is a __metaclass__
attribute on your classes and rewrite those to use the new Python 3 syntax:
stackoverflow-2.7 $ bin/python -m lib2to3 fixed.py
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
--- fixed.py (original)
+++ fixed.py (refactored)
@@ -5,8 +5,8 @@
print ("BaseMeta::new. Called.")
return new
-class Base(object):
- __metaclass__ = BaseMeta
+class Base(object, metaclass=BaseMeta):
+ pass
class HeirMeta(BaseMeta):
def __new__(self, *args):
@@ -14,9 +14,7 @@
print ("HeirMeta::new. Called.")
return new
-class Heir(Base):
- __metaclass__ = HeirMeta
-
+class Heir(Base, metaclass=HeirMeta):
@classmethod
def define(cls, nexttype):
print ("Heir::define. Called.")
RefactoringTool: Files that need to be modified:
RefactoringTool: fixed.py
and the refactored code works as expected:
stackoverflow-2.7 $ bin/python -m lib2to3 -o ../stackoverflow-3.4 -nw --no-diffs fixed.py
lib2to3.main: Output in '../stackoverflow-3.4' will mirror the input directory '' layout.
RefactoringTool: Skipping implicit fixer: buffer
RefactoringTool: Skipping implicit fixer: idioms
RefactoringTool: Skipping implicit fixer: set_literal
RefactoringTool: Skipping implicit fixer: ws_comma
RefactoringTool: Refactored fixed.py
RefactoringTool: Writing converted fixed.py to ../stackoverflow-3.4/fixed.py.
RefactoringTool: Files that were modified:
RefactoringTool: fixed.py
stackoverflow-2.7 $ cd ../stackoverflow-3.4
stackoverflow-3.4 $ bin/python -V
Python 3.4.2
stackoverflow-3.4 $ bin/python fixed.py
BaseMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
BaseMeta::new. Called.
HeirMeta::new. Called.
Heir::define. Called.