Implement PEP 3115 -- new metaclass syntax and semantics.
The compiler package hasn't been updated yet; test_compiler.py fails. Otherwise all tests seem to be passing now. There are no occurrences of __metaclass__ left in the standard library. Docs have not been updated.
This commit is contained in:
parent
ef17c16b36
commit
52cc1d838f
|
@ -119,7 +119,7 @@ exprlist: expr (',' expr)* [',']
|
|||
testlist: test (',' test)* [',']
|
||||
dictsetmaker: (test ':' test (',' test ':' test)* [',']) | (test (',' test)* [','])
|
||||
|
||||
classdef: 'class' NAME ['(' [testlist] ')'] ':' suite
|
||||
classdef: 'class' NAME ['(' [arglist] ')'] ':' suite
|
||||
|
||||
arglist: (argument ',')* (argument [',']| '*' test [',' '**' test] | '**' test)
|
||||
argument: test [gen_for] | test '=' test # Really [keyword '='] test
|
||||
|
|
|
@ -82,6 +82,9 @@ struct _stmt {
|
|||
struct {
|
||||
identifier name;
|
||||
asdl_seq *bases;
|
||||
asdl_seq *keywords;
|
||||
expr_ty starargs;
|
||||
expr_ty kwargs;
|
||||
asdl_seq *body;
|
||||
} ClassDef;
|
||||
|
||||
|
@ -380,8 +383,9 @@ mod_ty _Py_Suite(asdl_seq * body, PyArena *arena);
|
|||
stmt_ty _Py_FunctionDef(identifier name, arguments_ty args, asdl_seq * body,
|
||||
asdl_seq * decorators, expr_ty returns, int lineno, int
|
||||
col_offset, PyArena *arena);
|
||||
#define ClassDef(a0, a1, a2, a3, a4, a5) _Py_ClassDef(a0, a1, a2, a3, a4, a5)
|
||||
stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int
|
||||
#define ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8) _Py_ClassDef(a0, a1, a2, a3, a4, a5, a6, a7, a8)
|
||||
stmt_ty _Py_ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords,
|
||||
expr_ty starargs, expr_ty kwargs, asdl_seq * body, int
|
||||
lineno, int col_offset, PyArena *arena);
|
||||
#define Return(a0, a1, a2, a3) _Py_Return(a0, a1, a2, a3)
|
||||
stmt_ty _Py_Return(expr_ty value, int lineno, int col_offset, PyArena *arena);
|
||||
|
|
|
@ -59,8 +59,9 @@ extern "C" {
|
|||
#define BINARY_OR 66
|
||||
#define INPLACE_POWER 67
|
||||
#define GET_ITER 68
|
||||
|
||||
#define STORE_LOCALS 69
|
||||
#define PRINT_EXPR 70
|
||||
#define LOAD_BUILD_CLASS 71
|
||||
|
||||
#define INPLACE_LSHIFT 75
|
||||
#define INPLACE_RSHIFT 76
|
||||
|
@ -69,14 +70,13 @@ extern "C" {
|
|||
#define INPLACE_OR 79
|
||||
#define BREAK_LOOP 80
|
||||
#define WITH_CLEANUP 81
|
||||
#define LOAD_LOCALS 82
|
||||
|
||||
#define RETURN_VALUE 83
|
||||
#define IMPORT_STAR 84
|
||||
#define MAKE_BYTES 85
|
||||
#define YIELD_VALUE 86
|
||||
#define POP_BLOCK 87
|
||||
#define END_FINALLY 88
|
||||
#define BUILD_CLASS 89
|
||||
|
||||
#define HAVE_ARGUMENT 90 /* Opcodes from here have an argument: */
|
||||
|
||||
|
@ -120,10 +120,10 @@ extern "C" {
|
|||
#define RAISE_VARARGS 130 /* Number of raise arguments (1, 2 or 3) */
|
||||
/* CALL_FUNCTION_XXX opcodes defined below depend on this definition */
|
||||
#define CALL_FUNCTION 131 /* #args + (#kwargs<<8) */
|
||||
#define MAKE_FUNCTION 132 /* #defaults */
|
||||
#define MAKE_FUNCTION 132 /* #defaults + #kwdefaults<<8 + #annotations<<16 */
|
||||
#define BUILD_SLICE 133 /* Number of items */
|
||||
|
||||
#define MAKE_CLOSURE 134 /* #free vars */
|
||||
#define MAKE_CLOSURE 134 /* same as MAKE_FUNCTION */
|
||||
#define LOAD_CLOSURE 135 /* Load free variable from closure */
|
||||
#define LOAD_DEREF 136 /* Load and dereference from closure cell */
|
||||
#define STORE_DEREF 137 /* Store into cell */
|
||||
|
|
|
@ -42,18 +42,16 @@ if sys.byteorder == "little":
|
|||
|
||||
LittleEndianStructure = Structure
|
||||
|
||||
class BigEndianStructure(Structure):
|
||||
class BigEndianStructure(Structure, metaclass=_swapped_meta):
|
||||
"""Structure with big endian byte order"""
|
||||
__metaclass__ = _swapped_meta
|
||||
_swappedbytes_ = None
|
||||
|
||||
elif sys.byteorder == "big":
|
||||
_OTHER_ENDIAN = "__ctype_le__"
|
||||
|
||||
BigEndianStructure = Structure
|
||||
class LittleEndianStructure(Structure):
|
||||
class LittleEndianStructure(Structure, metaclass=_swapped_meta):
|
||||
"""Structure with little endian byte order"""
|
||||
__metaclass__ = _swapped_meta
|
||||
_swappedbytes_ = None
|
||||
|
||||
else:
|
||||
|
|
|
@ -98,8 +98,10 @@ def_op('BINARY_XOR', 65)
|
|||
def_op('BINARY_OR', 66)
|
||||
def_op('INPLACE_POWER', 67)
|
||||
def_op('GET_ITER', 68)
|
||||
def_op('STORE_LOCALS', 69)
|
||||
|
||||
def_op('PRINT_EXPR', 70)
|
||||
def_op('LOAD_BUILD_CLASS', 71)
|
||||
|
||||
def_op('INPLACE_LSHIFT', 75)
|
||||
def_op('INPLACE_RSHIFT', 76)
|
||||
|
@ -108,14 +110,13 @@ def_op('INPLACE_XOR', 78)
|
|||
def_op('INPLACE_OR', 79)
|
||||
def_op('BREAK_LOOP', 80)
|
||||
def_op('WITH_CLEANUP', 81)
|
||||
def_op('LOAD_LOCALS', 82)
|
||||
|
||||
def_op('RETURN_VALUE', 83)
|
||||
def_op('IMPORT_STAR', 84)
|
||||
def_op('MAKE_BYTES', 85)
|
||||
def_op('YIELD_VALUE', 86)
|
||||
def_op('POP_BLOCK', 87)
|
||||
def_op('END_FINALLY', 88)
|
||||
def_op('BUILD_CLASS', 89)
|
||||
|
||||
HAVE_ARGUMENT = 90 # Opcodes from here have an argument:
|
||||
|
||||
|
|
|
@ -119,9 +119,8 @@ class _TemplateMetaclass(type):
|
|||
cls.pattern = _re.compile(pattern, _re.IGNORECASE | _re.VERBOSE)
|
||||
|
||||
|
||||
class Template:
|
||||
class Template(metaclass=_TemplateMetaclass):
|
||||
"""A string class for supporting $-substitutions."""
|
||||
__metaclass__ = _TemplateMetaclass
|
||||
|
||||
delimiter = '$'
|
||||
idpattern = r'[_a-z][_a-z0-9]*'
|
||||
|
|
|
@ -7,11 +7,10 @@ class Y(object):
|
|||
class type_with_modifiable_dict(Y, type):
|
||||
pass
|
||||
|
||||
class MyClass(object):
|
||||
class MyClass(object, metaclass=type_with_modifiable_dict):
|
||||
"""This class has its __dict__ attribute completely exposed:
|
||||
user code can read, reassign and even delete it.
|
||||
"""
|
||||
__metaclass__ = type_with_modifiable_dict
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
@ -6,8 +6,8 @@ import gc
|
|||
def leak():
|
||||
class T(type):
|
||||
pass
|
||||
class U(type):
|
||||
__metaclass__ = T
|
||||
class U(type, metaclass=T):
|
||||
pass
|
||||
U.__class__ = U
|
||||
del U
|
||||
gc.collect(); gc.collect(); gc.collect()
|
||||
|
|
|
@ -88,8 +88,8 @@ class initarg(C):
|
|||
class metaclass(type):
|
||||
pass
|
||||
|
||||
class use_metaclass(object):
|
||||
__metaclass__ = metaclass
|
||||
class use_metaclass(object, metaclass=metaclass):
|
||||
pass
|
||||
|
||||
# DATA0 .. DATA2 are the pickles we expect under the various protocols, for
|
||||
# the object returned by create_data().
|
||||
|
|
|
@ -144,13 +144,19 @@ def run_tests():
|
|||
(eval_tests, eval_results, "eval")):
|
||||
for i, o in itertools.izip(input, output):
|
||||
ast_tree = compile(i, "?", kind, 0x400)
|
||||
if to_tuple(ast_tree) != o:
|
||||
print("i=", i)
|
||||
print("o=", o)
|
||||
print("kind=", kind)
|
||||
print("tree=", ast_tree)
|
||||
print("tuple=", to_tuple(ast_tree))
|
||||
assert to_tuple(ast_tree) == o
|
||||
test_order(ast_tree, (0, 0))
|
||||
|
||||
#### EVERYTHING BELOW IS GENERATED #####
|
||||
exec_results = [
|
||||
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, [], None, None, [], []), [('Pass', (1, 9))], [], None)]),
|
||||
('Module', [('ClassDef', (1, 0), 'C', [], [('Pass', (1, 8))])]),
|
||||
('Module', [('ClassDef', (1, 0), 'C', [], [], None, None, [('Pass', (1, 8))])]),
|
||||
('Module', [('FunctionDef', (1, 0), 'f', ('arguments', [], None, None, [], None, None, [], []), [('Return', (1, 8), ('Num', (1, 15), 1))], [], None)]),
|
||||
('Module', [('Delete', (1, 0), [('Name', (1, 4), 'v', ('Del',))])]),
|
||||
('Module', [('Assign', (1, 0), [('Name', (1, 0), 'v', ('Store',))], ('Num', (1, 4), 1))]),
|
||||
|
|
|
@ -191,8 +191,8 @@ class TestCopy(unittest.TestCase):
|
|||
# type.
|
||||
class Meta(type):
|
||||
pass
|
||||
class C:
|
||||
__metaclass__ = Meta
|
||||
class C(metaclass=Meta):
|
||||
pass
|
||||
self.assertEqual(copy.deepcopy(C), C)
|
||||
|
||||
def test_deepcopy_deepcopy(self):
|
||||
|
|
|
@ -616,9 +616,8 @@ def pylists():
|
|||
vereq(a[100:200], (100,200))
|
||||
|
||||
def metaclass():
|
||||
if verbose: print("Testing __metaclass__...")
|
||||
class C:
|
||||
__metaclass__ = type
|
||||
if verbose: print("Testing metaclass...")
|
||||
class C(metaclass=type):
|
||||
def __init__(self):
|
||||
self.__state = 0
|
||||
def getstate(self):
|
||||
|
@ -629,9 +628,10 @@ def metaclass():
|
|||
vereq(a.getstate(), 0)
|
||||
a.setstate(10)
|
||||
vereq(a.getstate(), 10)
|
||||
class D:
|
||||
class __metaclass__(type):
|
||||
class _metaclass(type):
|
||||
def myself(cls): return cls
|
||||
class D(metaclass=_metaclass):
|
||||
pass
|
||||
vereq(D.myself(), D)
|
||||
d = D()
|
||||
verify(d.__class__ is D)
|
||||
|
@ -639,8 +639,8 @@ def metaclass():
|
|||
def __new__(cls, name, bases, dict):
|
||||
dict['__spam__'] = 1
|
||||
return type.__new__(cls, name, bases, dict)
|
||||
class C:
|
||||
__metaclass__ = M1
|
||||
class C(metaclass=M1):
|
||||
pass
|
||||
vereq(C.__spam__, 1)
|
||||
c = C()
|
||||
vereq(c.__spam__, 1)
|
||||
|
@ -663,8 +663,7 @@ def metaclass():
|
|||
continue
|
||||
setattr(it, key, self.dict[key].__get__(it, self))
|
||||
return it
|
||||
class C:
|
||||
__metaclass__ = M2
|
||||
class C(metaclass=M2):
|
||||
def spam(self):
|
||||
return 42
|
||||
vereq(C.name, 'C')
|
||||
|
@ -690,8 +689,7 @@ def metaclass():
|
|||
name = "__super"
|
||||
setattr(cls, name, super(cls))
|
||||
return cls
|
||||
class A:
|
||||
__metaclass__ = autosuper
|
||||
class A(metaclass=autosuper):
|
||||
def meth(self):
|
||||
return "A"
|
||||
class B(A):
|
||||
|
@ -729,8 +727,7 @@ def metaclass():
|
|||
dict[key] = property(get, set)
|
||||
return super(autoproperty, metaclass).__new__(metaclass,
|
||||
name, bases, dict)
|
||||
class A:
|
||||
__metaclass__ = autoproperty
|
||||
class A(metaclass=autoproperty):
|
||||
def _get_x(self):
|
||||
return -self.__x
|
||||
def _set_x(self, x):
|
||||
|
@ -744,8 +741,7 @@ def metaclass():
|
|||
class multimetaclass(autoproperty, autosuper):
|
||||
# Merge of multiple cooperating metaclasses
|
||||
pass
|
||||
class A:
|
||||
__metaclass__ = multimetaclass
|
||||
class A(metaclass=multimetaclass):
|
||||
def _get_x(self):
|
||||
return "A"
|
||||
class B(A):
|
||||
|
@ -764,8 +760,8 @@ def metaclass():
|
|||
counter = 0
|
||||
def __init__(self, *args):
|
||||
T.counter += 1
|
||||
class C:
|
||||
__metaclass__ = T
|
||||
class C(metaclass=T):
|
||||
pass
|
||||
vereq(T.counter, 1)
|
||||
a = C()
|
||||
vereq(type(a), C)
|
||||
|
@ -1273,8 +1269,8 @@ def dynamics():
|
|||
# Test comparison of classes with dynamic metaclasses
|
||||
class dynamicmetaclass(type):
|
||||
pass
|
||||
class someclass:
|
||||
__metaclass__ = dynamicmetaclass
|
||||
class someclass(metaclass=dynamicmetaclass):
|
||||
pass
|
||||
verify(someclass != object)
|
||||
|
||||
def errors():
|
||||
|
@ -1505,36 +1501,39 @@ def altmro():
|
|||
L = type.mro(cls)
|
||||
L.reverse()
|
||||
return L
|
||||
class X(D,B,C,A):
|
||||
__metaclass__ = PerverseMetaType
|
||||
class X(D,B,C,A, metaclass=PerverseMetaType):
|
||||
pass
|
||||
vereq(X.__mro__, (object, A, C, B, D, X))
|
||||
vereq(X().f(), "A")
|
||||
|
||||
try:
|
||||
class X(object):
|
||||
class __metaclass__(type):
|
||||
class _metaclass(type):
|
||||
def mro(self):
|
||||
return [self, dict, object]
|
||||
class X(object, metaclass=_metaclass):
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
raise TestFailed, "devious mro() return not caught"
|
||||
|
||||
try:
|
||||
class X(object):
|
||||
class __metaclass__(type):
|
||||
class _metaclass(type):
|
||||
def mro(self):
|
||||
return [1]
|
||||
class X(object, metaclass=_metaclass):
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
raise TestFailed, "non-class mro() return not caught"
|
||||
|
||||
try:
|
||||
class X(object):
|
||||
class __metaclass__(type):
|
||||
class _metaclass(type):
|
||||
def mro(self):
|
||||
return 1
|
||||
class X(object, metaclass=_metaclass):
|
||||
pass
|
||||
except TypeError:
|
||||
pass
|
||||
else:
|
||||
|
@ -3575,11 +3574,11 @@ def test_mutable_bases_with_failing_mro():
|
|||
class E(D):
|
||||
pass
|
||||
|
||||
class F(D):
|
||||
__metaclass__ = WorkOnce
|
||||
class F(D, metaclass=WorkOnce):
|
||||
pass
|
||||
|
||||
class G(D):
|
||||
__metaclass__ = WorkAlways
|
||||
class G(D, metaclass=WorkAlways):
|
||||
pass
|
||||
|
||||
# Immediate subclasses have their mro's adjusted in alphabetical
|
||||
# order, so E's will get adjusted before adjusting F's fails. We
|
||||
|
@ -3690,15 +3689,15 @@ def subclass_right_op():
|
|||
|
||||
def dict_type_with_metaclass():
|
||||
if verbose:
|
||||
print("Testing type of __dict__ when __metaclass__ set...")
|
||||
print("Testing type of __dict__ when metaclass set...")
|
||||
|
||||
class B(object):
|
||||
pass
|
||||
class M(type):
|
||||
pass
|
||||
class C:
|
||||
class C(metaclass=M):
|
||||
# In 2.3a1, C.__dict__ was a real dict rather than a dict proxy
|
||||
__metaclass__ = M
|
||||
pass
|
||||
veris(type(C.__dict__), type(B.__dict__))
|
||||
|
||||
def meth_class_get():
|
||||
|
|
|
@ -0,0 +1,218 @@
|
|||
doctests = """
|
||||
|
||||
Basic class construction.
|
||||
|
||||
>>> class C:
|
||||
... def meth(self): print("Hello")
|
||||
...
|
||||
>>> C.__class__ is type
|
||||
True
|
||||
>>> a = C()
|
||||
>>> a.__class__ is C
|
||||
True
|
||||
>>> a.meth()
|
||||
Hello
|
||||
>>>
|
||||
|
||||
Use *args notation for the bases.
|
||||
|
||||
>>> class A: pass
|
||||
>>> class B: pass
|
||||
>>> bases = (A, B)
|
||||
>>> class C(*bases): pass
|
||||
>>> C.__bases__ == bases
|
||||
True
|
||||
>>>
|
||||
|
||||
Use a trivial metaclass.
|
||||
|
||||
>>> class M(type):
|
||||
... pass
|
||||
...
|
||||
>>> class C(metaclass=M):
|
||||
... def meth(self): print("Hello")
|
||||
...
|
||||
>>> C.__class__ is M
|
||||
True
|
||||
>>> a = C()
|
||||
>>> a.__class__ is C
|
||||
True
|
||||
>>> a.meth()
|
||||
Hello
|
||||
>>>
|
||||
|
||||
Use **kwds notation for the metaclass keyword.
|
||||
|
||||
>>> kwds = {'metaclass': M}
|
||||
>>> class C(**kwds): pass
|
||||
...
|
||||
>>> C.__class__ is M
|
||||
True
|
||||
>>> a = C()
|
||||
>>> a.__class__ is C
|
||||
True
|
||||
>>>
|
||||
|
||||
Use a metaclass with a __prepare__ static method.
|
||||
|
||||
>>> class M(type):
|
||||
... @staticmethod
|
||||
... def __prepare__(*args, **kwds):
|
||||
... print("Prepare called:", args, kwds)
|
||||
... return dict()
|
||||
... def __new__(cls, name, bases, namespace, **kwds):
|
||||
... print("New called:", kwds)
|
||||
... return type.__new__(cls, name, bases, namespace)
|
||||
...
|
||||
>>> class C(metaclass=M):
|
||||
... def meth(self): print("Hello")
|
||||
...
|
||||
Prepare called: ('C', ()) {}
|
||||
New called: {}
|
||||
>>>
|
||||
|
||||
Also pass another keyword.
|
||||
|
||||
>>> class C(object, metaclass=M, other="haha"):
|
||||
... pass
|
||||
...
|
||||
Prepare called: ('C', (<type 'object'>,)) {'other': 'haha'}
|
||||
New called: {'other': 'haha'}
|
||||
>>> C.__class__ is M
|
||||
True
|
||||
>>> C.__bases__ == (object,)
|
||||
True
|
||||
>>> a = C()
|
||||
>>> a.__class__ is C
|
||||
True
|
||||
>>>
|
||||
|
||||
Check that build_class doesn't mutate the kwds dict.
|
||||
|
||||
>>> kwds = {'metaclass': type}
|
||||
>>> class C(**kwds): pass
|
||||
...
|
||||
>>> kwds == {'metaclass': type}
|
||||
True
|
||||
>>>
|
||||
|
||||
Use various combinations of explicit keywords and **kwds.
|
||||
|
||||
>>> bases = (object,)
|
||||
>>> kwds = {'metaclass': M, 'other': 'haha'}
|
||||
>>> class C(*bases, **kwds): pass
|
||||
...
|
||||
Prepare called: ('C', (<type 'object'>,)) {'other': 'haha'}
|
||||
New called: {'other': 'haha'}
|
||||
>>> C.__class__ is M
|
||||
True
|
||||
>>> C.__bases__ == (object,)
|
||||
True
|
||||
>>> class B: pass
|
||||
>>> kwds = {'other': 'haha'}
|
||||
>>> class C(B, metaclass=M, *bases, **kwds): pass
|
||||
...
|
||||
Prepare called: ('C', (<class 'test.test_metaclass.B'>, <type 'object'>)) {'other': 'haha'}
|
||||
New called: {'other': 'haha'}
|
||||
>>> C.__class__ is M
|
||||
True
|
||||
>>> C.__bases__ == (B, object)
|
||||
True
|
||||
>>>
|
||||
|
||||
Check for duplicate keywords.
|
||||
|
||||
>>> class C(metaclass=type, metaclass=type): pass
|
||||
...
|
||||
Traceback (most recent call last):
|
||||
[...]
|
||||
TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
|
||||
>>>
|
||||
|
||||
Another way.
|
||||
|
||||
>>> kwds = {'metaclass': type}
|
||||
>>> class C(metaclass=type, **kwds): pass
|
||||
...
|
||||
Traceback (most recent call last):
|
||||
[...]
|
||||
TypeError: __build_class__() got multiple values for keyword argument 'metaclass'
|
||||
>>>
|
||||
|
||||
Use a __prepare__ method that returns an instrumented dict.
|
||||
|
||||
>>> class LoggingDict(dict):
|
||||
... def __setitem__(self, key, value):
|
||||
... print("d[%r] = %r" % (key, value))
|
||||
... dict.__setitem__(self, key, value)
|
||||
...
|
||||
>>> class Meta(type):
|
||||
... @staticmethod
|
||||
... def __prepare__(name, bases):
|
||||
... return LoggingDict()
|
||||
...
|
||||
>>> class C(metaclass=Meta):
|
||||
... foo = 2+2
|
||||
... foo = 42
|
||||
... bar = 123
|
||||
...
|
||||
d['__module__'] = 'test.test_metaclass'
|
||||
d['foo'] = 4
|
||||
d['foo'] = 42
|
||||
d['bar'] = 123
|
||||
>>>
|
||||
|
||||
Use a metaclass that doesn't derive from type.
|
||||
|
||||
>>> def meta(name, bases, namespace, **kwds):
|
||||
... print("meta:", name, bases)
|
||||
... print("ns:", sorted(namespace.items()))
|
||||
... print("kw:", sorted(kwds.items()))
|
||||
... return namespace
|
||||
...
|
||||
>>> class C(metaclass=meta):
|
||||
... a = 42
|
||||
... b = 24
|
||||
...
|
||||
meta: C ()
|
||||
ns: [('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
|
||||
kw: []
|
||||
>>> type(C) is dict
|
||||
True
|
||||
>>> print(sorted(C.items()))
|
||||
[('__module__', 'test.test_metaclass'), ('a', 42), ('b', 24)]
|
||||
>>>
|
||||
|
||||
And again, with a __prepare__ attribute.
|
||||
|
||||
>>> def prepare(name, bases, **kwds):
|
||||
... print("prepare:", name, bases, sorted(kwds.items()))
|
||||
... return LoggingDict()
|
||||
...
|
||||
>>> meta.__prepare__ = prepare
|
||||
>>> class C(metaclass=meta, other="booh"):
|
||||
... a = 1
|
||||
... a = 2
|
||||
... b = 3
|
||||
...
|
||||
prepare: C () [('other', 'booh')]
|
||||
d['__module__'] = 'test.test_metaclass'
|
||||
d['a'] = 1
|
||||
d['a'] = 2
|
||||
d['b'] = 3
|
||||
meta: C ()
|
||||
ns: [('__module__', 'test.test_metaclass'), ('a', 2), ('b', 3)]
|
||||
kw: [('other', 'booh')]
|
||||
>>>
|
||||
|
||||
"""
|
||||
|
||||
__test__ = {'doctests' : doctests}
|
||||
|
||||
def test_main(verbose=False):
|
||||
from test import test_support
|
||||
from test import test_metaclass
|
||||
test_support.run_doctest(test_metaclass, verbose)
|
||||
|
||||
if __name__ == "__main__":
|
||||
test_main(verbose=True)
|
|
@ -84,9 +84,6 @@ if sys.version_info[:2] < (2, 2):
|
|||
# Test framework core
|
||||
##############################################################################
|
||||
|
||||
# All classes defined herein are 'new-style' classes, allowing use of 'super()'
|
||||
__metaclass__ = type
|
||||
|
||||
def _strclass(cls):
|
||||
return "%s.%s" % (cls.__module__, cls.__name__)
|
||||
|
||||
|
|
|
@ -5991,6 +5991,10 @@ PyMODINIT_FUNC init_bsddb(void)
|
|||
* from both DBError and KeyError, since the API only supports
|
||||
* using one base class. */
|
||||
PyDict_SetItemString(d, "KeyError", PyExc_KeyError);
|
||||
{
|
||||
PyObject *builtin_mod = PyImport_ImportModule("__builtin__");
|
||||
PyDict_SetItemString(d, "__builtins__", builtin_mod);
|
||||
}
|
||||
PyRun_String("class DBNotFoundError(DBError, KeyError): pass\n"
|
||||
"class DBKeyEmptyError(DBError, KeyError): pass",
|
||||
Py_file_input, d, d);
|
||||
|
|
|
@ -11,7 +11,12 @@ module Python version "$Revision$"
|
|||
|
||||
stmt = FunctionDef(identifier name, arguments args,
|
||||
stmt* body, expr* decorators, expr? returns)
|
||||
| ClassDef(identifier name, expr* bases, stmt* body)
|
||||
| ClassDef(identifier name,
|
||||
expr* bases,
|
||||
keyword* keywords,
|
||||
expr? starargs,
|
||||
expr? kwargs,
|
||||
stmt* body)
|
||||
| Return(expr? value)
|
||||
|
||||
| Delete(expr* targets)
|
||||
|
|
|
@ -49,6 +49,9 @@ static PyTypeObject *ClassDef_type;
|
|||
static char *ClassDef_fields[]={
|
||||
"name",
|
||||
"bases",
|
||||
"keywords",
|
||||
"starargs",
|
||||
"kwargs",
|
||||
"body",
|
||||
};
|
||||
static PyTypeObject *Return_type;
|
||||
|
@ -477,7 +480,7 @@ static int init_types(void)
|
|||
FunctionDef_type = make_type("FunctionDef", stmt_type,
|
||||
FunctionDef_fields, 5);
|
||||
if (!FunctionDef_type) return 0;
|
||||
ClassDef_type = make_type("ClassDef", stmt_type, ClassDef_fields, 3);
|
||||
ClassDef_type = make_type("ClassDef", stmt_type, ClassDef_fields, 6);
|
||||
if (!ClassDef_type) return 0;
|
||||
Return_type = make_type("Return", stmt_type, Return_fields, 1);
|
||||
if (!Return_type) return 0;
|
||||
|
@ -835,8 +838,9 @@ FunctionDef(identifier name, arguments_ty args, asdl_seq * body, asdl_seq *
|
|||
}
|
||||
|
||||
stmt_ty
|
||||
ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int lineno, int
|
||||
col_offset, PyArena *arena)
|
||||
ClassDef(identifier name, asdl_seq * bases, asdl_seq * keywords, expr_ty
|
||||
starargs, expr_ty kwargs, asdl_seq * body, int lineno, int col_offset,
|
||||
PyArena *arena)
|
||||
{
|
||||
stmt_ty p;
|
||||
if (!name) {
|
||||
|
@ -850,6 +854,9 @@ ClassDef(identifier name, asdl_seq * bases, asdl_seq * body, int lineno, int
|
|||
p->kind = ClassDef_kind;
|
||||
p->v.ClassDef.name = name;
|
||||
p->v.ClassDef.bases = bases;
|
||||
p->v.ClassDef.keywords = keywords;
|
||||
p->v.ClassDef.starargs = starargs;
|
||||
p->v.ClassDef.kwargs = kwargs;
|
||||
p->v.ClassDef.body = body;
|
||||
p->lineno = lineno;
|
||||
p->col_offset = col_offset;
|
||||
|
@ -1974,6 +1981,21 @@ ast2obj_stmt(void* _o)
|
|||
if (PyObject_SetAttrString(result, "bases", value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_list(o->v.ClassDef.keywords, ast2obj_keyword);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttrString(result, "keywords", value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_expr(o->v.ClassDef.starargs);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttrString(result, "starargs", value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_expr(o->v.ClassDef.kwargs);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttrString(result, "kwargs", value) == -1)
|
||||
goto failed;
|
||||
Py_DECREF(value);
|
||||
value = ast2obj_list(o->v.ClassDef.body, ast2obj_stmt);
|
||||
if (!value) goto failed;
|
||||
if (PyObject_SetAttrString(result, "body", value) == -1)
|
||||
|
|
57
Python/ast.c
57
Python/ast.c
|
@ -2092,28 +2092,6 @@ ast_for_testlist_gexp(struct compiling *c, const node* n)
|
|||
return ast_for_testlist(c, n);
|
||||
}
|
||||
|
||||
/* like ast_for_testlist() but returns a sequence */
|
||||
static asdl_seq*
|
||||
ast_for_class_bases(struct compiling *c, const node* n)
|
||||
{
|
||||
/* testlist: test (',' test)* [','] */
|
||||
assert(NCH(n) > 0);
|
||||
REQ(n, testlist);
|
||||
if (NCH(n) == 1) {
|
||||
expr_ty base;
|
||||
asdl_seq *bases = asdl_seq_new(1, c->c_arena);
|
||||
if (!bases)
|
||||
return NULL;
|
||||
base = ast_for_expr(c, CHILD(n, 0));
|
||||
if (!base)
|
||||
return NULL;
|
||||
asdl_seq_SET(bases, 0, base);
|
||||
return bases;
|
||||
}
|
||||
|
||||
return seq_for_testlist(c, n);
|
||||
}
|
||||
|
||||
static stmt_ty
|
||||
ast_for_expr_stmt(struct compiling *c, const node *n)
|
||||
{
|
||||
|
@ -3032,8 +3010,9 @@ ast_for_with_stmt(struct compiling *c, const node *n)
|
|||
static stmt_ty
|
||||
ast_for_classdef(struct compiling *c, const node *n)
|
||||
{
|
||||
/* classdef: 'class' NAME ['(' testlist ')'] ':' suite */
|
||||
asdl_seq *bases, *s;
|
||||
/* classdef: 'class' NAME ['(' arglist ')'] ':' suite */
|
||||
asdl_seq *s;
|
||||
expr_ty call, dummy;
|
||||
|
||||
REQ(n, classdef);
|
||||
|
||||
|
@ -3042,32 +3021,36 @@ ast_for_classdef(struct compiling *c, const node *n)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (NCH(n) == 4) {
|
||||
if (NCH(n) == 4) { /* class NAME ':' suite */
|
||||
s = ast_for_suite(c, CHILD(n, 3));
|
||||
if (!s)
|
||||
return NULL;
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n),
|
||||
n->n_col_offset, c->c_arena);
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, NULL, NULL, NULL, s,
|
||||
LINENO(n), n->n_col_offset, c->c_arena);
|
||||
}
|
||||
/* check for empty base list */
|
||||
if (TYPE(CHILD(n,3)) == RPAR) {
|
||||
|
||||
if (TYPE(CHILD(n, 3)) == RPAR) { /* class NAME '(' ')' ':' suite */
|
||||
s = ast_for_suite(c, CHILD(n,5));
|
||||
if (!s)
|
||||
return NULL;
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, s, LINENO(n),
|
||||
n->n_col_offset, c->c_arena);
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), NULL, NULL, NULL, NULL, s,
|
||||
LINENO(n), n->n_col_offset, c->c_arena);
|
||||
}
|
||||
|
||||
/* else handle the base class list */
|
||||
bases = ast_for_class_bases(c, CHILD(n, 3));
|
||||
if (!bases)
|
||||
/* class NAME '(' arglist ')' ':' suite */
|
||||
/* build up a fake Call node so we can extract its pieces */
|
||||
dummy = Name(NEW_IDENTIFIER(CHILD(n, 1)), Load, LINENO(n), n->n_col_offset, c->c_arena);
|
||||
call = ast_for_call(c, CHILD(n, 3), dummy);
|
||||
if (!call)
|
||||
return NULL;
|
||||
|
||||
s = ast_for_suite(c, CHILD(n, 6));
|
||||
if (!s)
|
||||
return NULL;
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)), bases, s, LINENO(n),
|
||||
n->n_col_offset, c->c_arena);
|
||||
|
||||
return ClassDef(NEW_IDENTIFIER(CHILD(n, 1)),
|
||||
call->v.Call.args, call->v.Call.keywords,
|
||||
call->v.Call.starargs, call->v.Call.kwargs, s,
|
||||
LINENO(n), n->n_col_offset, c->c_arena);
|
||||
}
|
||||
|
||||
static stmt_ty
|
||||
|
|
|
@ -30,6 +30,113 @@ static PyObject *filterunicode(PyObject *, PyObject *);
|
|||
#endif
|
||||
static PyObject *filtertuple (PyObject *, PyObject *);
|
||||
|
||||
static PyObject *
|
||||
builtin___build_class__(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *func, *name, *bases, *mkw, *meta, *prep, *ns, *res;
|
||||
Py_ssize_t nargs, nbases;
|
||||
|
||||
assert(args != NULL);
|
||||
if (!PyTuple_Check(args)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__build_class__: args is not a tuple");
|
||||
return NULL;
|
||||
}
|
||||
nargs = PyTuple_GET_SIZE(args);
|
||||
if (nargs < 2) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__build_class__: not enough arguments");
|
||||
return NULL;
|
||||
}
|
||||
func = PyTuple_GET_ITEM(args, 0); /* Better be callable */
|
||||
name = PyTuple_GET_ITEM(args, 1);
|
||||
if (!PyString_Check(name)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"__build_class__: name is not a string");
|
||||
return NULL;
|
||||
}
|
||||
bases = PyTuple_GetSlice(args, 2, nargs);
|
||||
if (bases == NULL)
|
||||
return NULL;
|
||||
nbases = nargs - 2;
|
||||
|
||||
if (kwds == NULL) {
|
||||
meta = NULL;
|
||||
mkw = NULL;
|
||||
}
|
||||
else {
|
||||
mkw = PyDict_Copy(kwds); /* Don't modify kwds passed in! */
|
||||
if (mkw == NULL) {
|
||||
Py_DECREF(bases);
|
||||
return NULL;
|
||||
}
|
||||
meta = PyDict_GetItemString(mkw, "metaclass");
|
||||
if (meta != NULL) {
|
||||
Py_INCREF(meta);
|
||||
if (PyDict_DelItemString(mkw, "metaclass") < 0) {
|
||||
Py_DECREF(meta);
|
||||
Py_DECREF(mkw);
|
||||
Py_DECREF(bases);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (meta == NULL) {
|
||||
if (PyTuple_GET_SIZE(bases) == 0)
|
||||
meta = (PyObject *) (&PyType_Type);
|
||||
else {
|
||||
PyObject *base0 = PyTuple_GET_ITEM(bases, 0);
|
||||
meta = (PyObject *) (base0->ob_type);
|
||||
}
|
||||
Py_INCREF(meta);
|
||||
}
|
||||
prep = PyObject_GetAttrString(meta, "__prepare__");
|
||||
if (prep == NULL) {
|
||||
PyErr_Clear();
|
||||
ns = PyDict_New();
|
||||
}
|
||||
else {
|
||||
PyObject *pargs = Py_BuildValue("OO", name, bases);
|
||||
if (pargs == NULL) {
|
||||
Py_DECREF(prep);
|
||||
Py_DECREF(meta);
|
||||
Py_XDECREF(mkw);
|
||||
Py_DECREF(bases);
|
||||
return NULL;
|
||||
}
|
||||
ns = PyEval_CallObjectWithKeywords(prep, pargs, mkw);
|
||||
Py_DECREF(pargs);
|
||||
Py_DECREF(prep);
|
||||
if (ns == NULL) {
|
||||
Py_DECREF(meta);
|
||||
Py_XDECREF(mkw);
|
||||
Py_DECREF(bases);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
res = PyObject_CallFunctionObjArgs(func, ns, NULL);
|
||||
if (res != NULL) {
|
||||
PyObject *margs;
|
||||
Py_DECREF(res);
|
||||
res = NULL;
|
||||
margs = Py_BuildValue("OOO", name, bases, ns);
|
||||
if (margs != NULL) {
|
||||
res = PyEval_CallObjectWithKeywords(meta, margs, mkw);
|
||||
Py_DECREF(margs);
|
||||
}
|
||||
}
|
||||
Py_DECREF(ns);
|
||||
Py_DECREF(meta);
|
||||
Py_XDECREF(mkw);
|
||||
Py_DECREF(bases);
|
||||
return res;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(build_class_doc,
|
||||
"__build_class__(func, name, *bases, metaclass=None, **kwds) -> class\n\
|
||||
\n\
|
||||
Internal helper function used by the class statement.");
|
||||
|
||||
static PyObject *
|
||||
builtin___import__(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
|
@ -2103,6 +2210,8 @@ NOTE: This is implemented using itertools.izip().");
|
|||
|
||||
|
||||
static PyMethodDef builtin_methods[] = {
|
||||
{"__build_class__", (PyCFunction)builtin___build_class__,
|
||||
METH_VARARGS | METH_KEYWORDS, build_class_doc},
|
||||
{"__import__", (PyCFunction)builtin___import__, METH_VARARGS | METH_KEYWORDS, import_doc},
|
||||
{"abs", builtin_abs, METH_O, abs_doc},
|
||||
{"all", builtin_all, METH_O, all_doc},
|
||||
|
|
|
@ -117,7 +117,6 @@ static int assign_slice(PyObject *, PyObject *,
|
|||
static PyObject * cmp_outcome(int, PyObject *, PyObject *);
|
||||
static PyObject * import_from(PyObject *, PyObject *);
|
||||
static int import_all_from(PyObject *, PyObject *);
|
||||
static PyObject * build_class(PyObject *, PyObject *, PyObject *);
|
||||
static void set_exc_info(PyThreadState *, PyObject *, PyObject *, PyObject *);
|
||||
static void reset_exc_info(PyThreadState *);
|
||||
static void format_exc_check_arg(PyObject *, char *, PyObject *);
|
||||
|
@ -1532,14 +1531,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
}
|
||||
break;
|
||||
|
||||
case LOAD_LOCALS:
|
||||
if ((x = f->f_locals) != NULL) {
|
||||
Py_INCREF(x);
|
||||
PUSH(x);
|
||||
case STORE_LOCALS:
|
||||
x = POP();
|
||||
v = f->f_locals;
|
||||
Py_XDECREF(v);
|
||||
f->f_locals = x;
|
||||
continue;
|
||||
}
|
||||
PyErr_SetString(PyExc_SystemError, "no locals");
|
||||
break;
|
||||
|
||||
case RETURN_VALUE:
|
||||
retval = POP();
|
||||
|
@ -1586,16 +1583,16 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
|
|||
Py_DECREF(v);
|
||||
break;
|
||||
|
||||
case BUILD_CLASS:
|
||||
u = TOP();
|
||||
v = SECOND();
|
||||
w = THIRD();
|
||||
STACKADJ(-2);
|
||||
x = build_class(u, v, w);
|
||||
SET_TOP(x);
|
||||
Py_DECREF(u);
|
||||
Py_DECREF(v);
|
||||
Py_DECREF(w);
|
||||
case LOAD_BUILD_CLASS:
|
||||
x = PyDict_GetItemString(f->f_builtins,
|
||||
"__build_class__");
|
||||
if (x == NULL) {
|
||||
PyErr_SetString(PyExc_ImportError,
|
||||
"__build_class__ not found");
|
||||
break;
|
||||
}
|
||||
Py_INCREF(x);
|
||||
PUSH(x);
|
||||
break;
|
||||
|
||||
case STORE_NAME:
|
||||
|
@ -4023,60 +4020,6 @@ import_all_from(PyObject *locals, PyObject *v)
|
|||
return err;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
build_class(PyObject *methods, PyObject *bases, PyObject *name)
|
||||
{
|
||||
PyObject *metaclass = NULL, *result, *base;
|
||||
|
||||
if (PyDict_Check(methods))
|
||||
metaclass = PyDict_GetItemString(methods, "__metaclass__");
|
||||
if (metaclass != NULL)
|
||||
Py_INCREF(metaclass);
|
||||
else if (PyTuple_Check(bases) && PyTuple_GET_SIZE(bases) > 0) {
|
||||
base = PyTuple_GET_ITEM(bases, 0);
|
||||
metaclass = PyObject_GetAttrString(base, "__class__");
|
||||
if (metaclass == NULL) {
|
||||
PyErr_Clear();
|
||||
metaclass = (PyObject *)base->ob_type;
|
||||
Py_INCREF(metaclass);
|
||||
}
|
||||
}
|
||||
else {
|
||||
PyObject *g = PyEval_GetGlobals();
|
||||
if (g != NULL && PyDict_Check(g))
|
||||
metaclass = PyDict_GetItemString(g, "__metaclass__");
|
||||
if (metaclass == NULL)
|
||||
metaclass = (PyObject *) &PyType_Type;
|
||||
Py_INCREF(metaclass);
|
||||
}
|
||||
result = PyObject_CallFunctionObjArgs(metaclass, name, bases, methods,
|
||||
NULL);
|
||||
Py_DECREF(metaclass);
|
||||
if (result == NULL && PyErr_ExceptionMatches(PyExc_TypeError)) {
|
||||
/* A type error here likely means that the user passed
|
||||
in a base that was not a class (such the random module
|
||||
instead of the random.random type). Help them out with
|
||||
by augmenting the error message with more information.*/
|
||||
|
||||
PyObject *ptype, *pvalue, *ptraceback;
|
||||
|
||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||
if (PyString_Check(pvalue)) {
|
||||
PyObject *newmsg;
|
||||
newmsg = PyString_FromFormat(
|
||||
"Error when calling the metaclass bases\n"
|
||||
" %s",
|
||||
PyString_AS_STRING(pvalue));
|
||||
if (newmsg != NULL) {
|
||||
Py_DECREF(pvalue);
|
||||
pvalue = newmsg;
|
||||
}
|
||||
}
|
||||
PyErr_Restore(ptype, pvalue, ptraceback);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
format_exc_check_arg(PyObject *exc, char *format_str, PyObject *obj)
|
||||
{
|
||||
|
|
138
Python/compile.c
138
Python/compile.c
|
@ -176,6 +176,11 @@ static int inplace_binop(struct compiler *, operator_ty);
|
|||
static int expr_constant(expr_ty e);
|
||||
|
||||
static int compiler_with(struct compiler *, stmt_ty);
|
||||
static int compiler_call_helper(struct compiler *c, int n,
|
||||
asdl_seq *args,
|
||||
asdl_seq *keywords,
|
||||
expr_ty starargs,
|
||||
expr_ty kwargs);
|
||||
|
||||
static PyCodeObject *assemble(struct compiler *, int addNone);
|
||||
static PyObject *__doc__;
|
||||
|
@ -734,6 +739,8 @@ opcode_stack_effect(int opcode, int oparg)
|
|||
|
||||
case PRINT_EXPR:
|
||||
return -1;
|
||||
case LOAD_BUILD_CLASS:
|
||||
return 1;
|
||||
case INPLACE_LSHIFT:
|
||||
case INPLACE_RSHIFT:
|
||||
case INPLACE_AND:
|
||||
|
@ -744,8 +751,8 @@ opcode_stack_effect(int opcode, int oparg)
|
|||
return 0;
|
||||
case WITH_CLEANUP:
|
||||
return -1; /* XXX Sometimes more */
|
||||
case LOAD_LOCALS:
|
||||
return 1;
|
||||
case STORE_LOCALS:
|
||||
return -1;
|
||||
case RETURN_VALUE:
|
||||
return -1;
|
||||
case IMPORT_STAR:
|
||||
|
@ -757,8 +764,6 @@ opcode_stack_effect(int opcode, int oparg)
|
|||
return 0;
|
||||
case END_FINALLY:
|
||||
return -1; /* or -2 or -3 if exception occurred */
|
||||
case BUILD_CLASS:
|
||||
return -2;
|
||||
|
||||
case STORE_NAME:
|
||||
return -1;
|
||||
|
@ -1509,29 +1514,65 @@ compiler_function(struct compiler *c, stmt_ty s)
|
|||
static int
|
||||
compiler_class(struct compiler *c, stmt_ty s)
|
||||
{
|
||||
int n;
|
||||
static PyObject *build_class = NULL;
|
||||
static PyObject *locals = NULL;
|
||||
PyCodeObject *co;
|
||||
PyObject *str;
|
||||
/* push class name on stack, needed by BUILD_CLASS */
|
||||
ADDOP_O(c, LOAD_CONST, s->v.ClassDef.name, consts);
|
||||
/* push the tuple of base classes on the stack */
|
||||
n = asdl_seq_LEN(s->v.ClassDef.bases);
|
||||
if (n > 0)
|
||||
VISIT_SEQ(c, expr, s->v.ClassDef.bases);
|
||||
ADDOP_I(c, BUILD_TUPLE, n);
|
||||
if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s,
|
||||
s->lineno))
|
||||
PySTEntryObject *ste;
|
||||
|
||||
/* initialize statics */
|
||||
if (build_class == NULL) {
|
||||
build_class = PyString_FromString("__build_class__");
|
||||
if (build_class == NULL)
|
||||
return 0;
|
||||
}
|
||||
if (locals == NULL) {
|
||||
locals = PyString_FromString("__locals__");
|
||||
if (locals == NULL)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ultimately generate code for:
|
||||
<name> = __build_class__(<func>, <name>, *<bases>, **<keywords>)
|
||||
where:
|
||||
<func> is a function/closure created from the class body
|
||||
<name> is the class name
|
||||
<bases> is the positional arguments and *varargs argument
|
||||
<keywords> is the keyword arguments and **kwds argument
|
||||
This borrows from compiler_call.
|
||||
*/
|
||||
|
||||
/* 0. Create a fake variable named __locals__ */
|
||||
ste = PySymtable_Lookup(c->c_st, s);
|
||||
if (ste == NULL)
|
||||
return 0;
|
||||
assert(PyList_Check(ste->ste_varnames));
|
||||
if (PyList_Append(ste->ste_varnames, locals) < 0)
|
||||
return 0;
|
||||
|
||||
/* 1. compile the class body into a code object */
|
||||
if (!compiler_enter_scope(c, s->v.ClassDef.name, (void *)s, s->lineno))
|
||||
return 0;
|
||||
/* this block represents what we do in the new scope */
|
||||
{
|
||||
/* use the class name for name mangling */
|
||||
Py_INCREF(s->v.ClassDef.name);
|
||||
c->u->u_private = s->v.ClassDef.name;
|
||||
Py_INCREF(c->u->u_private);
|
||||
/* force it to have one mandatory argument */
|
||||
c->u->u_argcount = 1;
|
||||
/* load the first argument ... */
|
||||
ADDOP_I(c, LOAD_FAST, 0);
|
||||
/* ... and store it into f_locals */
|
||||
ADDOP_IN_SCOPE(c, STORE_LOCALS);
|
||||
/* load __name__ ... */
|
||||
str = PyString_InternFromString("__name__");
|
||||
if (!str || !compiler_nameop(c, str, Load)) {
|
||||
Py_XDECREF(str);
|
||||
compiler_exit_scope(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
Py_DECREF(str);
|
||||
/* ... and store it as __module__ */
|
||||
str = PyString_InternFromString("__module__");
|
||||
if (!str || !compiler_nameop(c, str, Store)) {
|
||||
Py_XDECREF(str);
|
||||
|
@ -1539,24 +1580,41 @@ compiler_class(struct compiler *c, stmt_ty s)
|
|||
return 0;
|
||||
}
|
||||
Py_DECREF(str);
|
||||
|
||||
/* compile the body proper */
|
||||
if (!compiler_body(c, s->v.ClassDef.body)) {
|
||||
compiler_exit_scope(c);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ADDOP_IN_SCOPE(c, LOAD_LOCALS);
|
||||
/* return None */
|
||||
ADDOP_O(c, LOAD_CONST, Py_None, consts);
|
||||
ADDOP_IN_SCOPE(c, RETURN_VALUE);
|
||||
/* create the code object */
|
||||
co = assemble(c, 1);
|
||||
}
|
||||
/* leave the new scope */
|
||||
compiler_exit_scope(c);
|
||||
if (co == NULL)
|
||||
return 0;
|
||||
|
||||
/* 2. load the 'build_class' function */
|
||||
ADDOP(c, LOAD_BUILD_CLASS);
|
||||
|
||||
/* 3. load a function (or closure) made from the code object */
|
||||
compiler_make_closure(c, co, 0);
|
||||
Py_DECREF(co);
|
||||
|
||||
ADDOP_I(c, CALL_FUNCTION, 0);
|
||||
ADDOP(c, BUILD_CLASS);
|
||||
/* 4. load class name */
|
||||
ADDOP_O(c, LOAD_CONST, s->v.ClassDef.name, consts);
|
||||
|
||||
/* 5. generate the rest of the code for the call */
|
||||
if (!compiler_call_helper(c, 2,
|
||||
s->v.ClassDef.bases,
|
||||
s->v.ClassDef.keywords,
|
||||
s->v.ClassDef.starargs,
|
||||
s->v.ClassDef.kwargs))
|
||||
return 0;
|
||||
|
||||
/* 6. store into <name> */
|
||||
if (!compiler_nameop(c, s->v.ClassDef.name, Store))
|
||||
return 0;
|
||||
return 1;
|
||||
|
@ -2613,21 +2671,37 @@ compiler_compare(struct compiler *c, expr_ty e)
|
|||
static int
|
||||
compiler_call(struct compiler *c, expr_ty e)
|
||||
{
|
||||
int n, code = 0;
|
||||
|
||||
VISIT(c, expr, e->v.Call.func);
|
||||
n = asdl_seq_LEN(e->v.Call.args);
|
||||
VISIT_SEQ(c, expr, e->v.Call.args);
|
||||
if (e->v.Call.keywords) {
|
||||
VISIT_SEQ(c, keyword, e->v.Call.keywords);
|
||||
n |= asdl_seq_LEN(e->v.Call.keywords) << 8;
|
||||
return compiler_call_helper(c, 0,
|
||||
e->v.Call.args,
|
||||
e->v.Call.keywords,
|
||||
e->v.Call.starargs,
|
||||
e->v.Call.kwargs);
|
||||
}
|
||||
|
||||
/* shared code between compiler_call and compiler_class */
|
||||
static int
|
||||
compiler_call_helper(struct compiler *c,
|
||||
int n, /* Args already pushed */
|
||||
asdl_seq *args,
|
||||
asdl_seq *keywords,
|
||||
expr_ty starargs,
|
||||
expr_ty kwargs)
|
||||
{
|
||||
int code = 0;
|
||||
|
||||
n += asdl_seq_LEN(args);
|
||||
VISIT_SEQ(c, expr, args);
|
||||
if (keywords) {
|
||||
VISIT_SEQ(c, keyword, keywords);
|
||||
n |= asdl_seq_LEN(keywords) << 8;
|
||||
}
|
||||
if (e->v.Call.starargs) {
|
||||
VISIT(c, expr, e->v.Call.starargs);
|
||||
if (starargs) {
|
||||
VISIT(c, expr, starargs);
|
||||
code |= 1;
|
||||
}
|
||||
if (e->v.Call.kwargs) {
|
||||
VISIT(c, expr, e->v.Call.kwargs);
|
||||
if (kwargs) {
|
||||
VISIT(c, expr, kwargs);
|
||||
code |= 2;
|
||||
}
|
||||
switch (code) {
|
||||
|
|
|
@ -1635,7 +1635,7 @@ static arc arcs_76_2[2] = {
|
|||
{23, 4},
|
||||
};
|
||||
static arc arcs_76_3[2] = {
|
||||
{9, 5},
|
||||
{14, 5},
|
||||
{15, 6},
|
||||
};
|
||||
static arc arcs_76_4[1] = {
|
||||
|
|
|
@ -72,9 +72,10 @@ extern time_t PyOS_GetLastModificationTime(char *, FILE *);
|
|||
3030 (added keyword-only parameters)
|
||||
3040 (added signature annotations)
|
||||
3050 (print becomes a function)
|
||||
3060 (PEP 3115 metaclass syntax)
|
||||
.
|
||||
*/
|
||||
#define MAGIC (3050 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
||||
#define MAGIC (3060 | ((long)'\r'<<16) | ((long)'\n'<<24))
|
||||
|
||||
/* Magic word as global; note that _PyImport_Init() can change the
|
||||
value of this global to accommodate for alterations of how the
|
||||
|
|
|
@ -983,6 +983,11 @@ symtable_visit_stmt(struct symtable *st, stmt_ty s)
|
|||
if (!symtable_add_def(st, s->v.ClassDef.name, DEF_LOCAL))
|
||||
return 0;
|
||||
VISIT_SEQ(st, expr, s->v.ClassDef.bases);
|
||||
VISIT_SEQ(st, keyword, s->v.ClassDef.keywords);
|
||||
if (s->v.ClassDef.starargs)
|
||||
VISIT(st, expr, s->v.ClassDef.starargs);
|
||||
if (s->v.ClassDef.kwargs)
|
||||
VISIT(st, expr, s->v.ClassDef.kwargs);
|
||||
if (!symtable_enter_block(st, s->v.ClassDef.name, ClassBlock,
|
||||
(void *)s, s->lineno))
|
||||
return 0;
|
||||
|
|
Loading…
Reference in New Issue