Issue #20228: Argument Clinic now has special support for class special

methods.
This commit is contained in:
Larry Hastings 2014-01-12 14:12:59 -08:00
parent 0191be3899
commit 8666e65206
3 changed files with 119 additions and 17 deletions

View File

@ -72,6 +72,9 @@ Tests
Tools/Demos
-----------
- Issue #20228: Argument Clinic now has special support for class special
methods.
- Issue #20214: Fixed a number of small issues and documentation errors in
Argument Clinic (see issue for details).

View File

@ -4068,9 +4068,6 @@ PyDoc_STRVAR(_pickle_Pickler___init____doc__,
"to map the new Python 3 names to the old module names used in Python\n"
"2, so that the pickle data stream is readable with Python 2.");
#define _PICKLE_PICKLER___INIT___METHODDEF \
{"__init__", (PyCFunction)_pickle_Pickler___init__, METH_VARARGS|METH_KEYWORDS, _pickle_Pickler___init____doc__},
static PyObject *
_pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports);
@ -4095,7 +4092,7 @@ exit:
static PyObject *
_pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports)
/*[clinic end generated code: checksum=2b5ce6452544600478cf9f4b701ab9d9b5efbab9]*/
/*[clinic end generated code: checksum=defa3d9e9f8b51fb257d4fdfca99db503db0e6df]*/
{
_Py_IDENTIFIER(persistent_id);
_Py_IDENTIFIER(dispatch_table);
@ -6637,9 +6634,6 @@ PyDoc_STRVAR(_pickle_Unpickler___init____doc__,
"respectively. The *encoding* can be \'bytes\' to read these 8-bit\n"
"string instances as bytes objects.");
#define _PICKLE_UNPICKLER___INIT___METHODDEF \
{"__init__", (PyCFunction)_pickle_Unpickler___init__, METH_VARARGS|METH_KEYWORDS, _pickle_Unpickler___init____doc__},
static PyObject *
_pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors);
@ -6665,7 +6659,7 @@ exit:
static PyObject *
_pickle_Unpickler___init___impl(UnpicklerObject *self, PyObject *file, int fix_imports, const char *encoding, const char *errors)
/*[clinic end generated code: checksum=9ce6783224e220573d42a94fe1bb7199d6f1c5a6]*/
/*[clinic end generated code: checksum=26c1d4a06841a8e51d29a0c244ba7f4607ff358a]*/
{
_Py_IDENTIFIER(persistent_load);

View File

@ -111,13 +111,16 @@ is_legal_c_identifier = re.compile('^[A-Za-z_][A-Za-z0-9_]*$').match
def is_legal_py_identifier(s):
return all(is_legal_c_identifier(field) for field in s.split('.'))
# added "module", "self", "cls", and "null" just to be safe
# (clinic will generate variables with these names)
# though it's called c_keywords, really it's a list of parameter names
# that are okay in Python but aren't a good idea in C. so if they're used
# Argument Clinic will add "_value" to the end of the name in C.
# (We added "args", "type", "module", "self", "cls", and "null"
# just to be safe, even though they're not C keywords.)
c_keywords = set("""
asm auto break case char cls const continue default do double
args asm auto break case char cls const continue default do double
else enum extern float for goto if inline int long module null
register return self short signed sizeof static struct switch
typedef typeof union unsigned void volatile while
type typedef typeof union unsigned void volatile while
""".strip().split())
def ensure_legal_c_identifier(s):
@ -392,11 +395,17 @@ class CLanguage(Language):
@staticmethod
def template_base(*args):
flags = '|'.join(f for f in args if f)
return """
# HACK suppress methoddef define for METHOD_NEW and METHOD_INIT
base = """
PyDoc_STRVAR({c_basename}__doc__,
{docstring});
"""
if args[-1] == None:
return base
flags = '|'.join(f for f in args if f)
return base + """
#define {methoddef_name} \\
{{"{name}", (PyCFunction){c_basename}, {methoddef_flags}, {c_basename}__doc__}},
""".replace('{methoddef_flags}', flags)
@ -650,7 +659,13 @@ static {impl_return_type}
name = full_name.rpartition('.')[2]
template_dict['name'] = name
c_basename = f.c_basename or full_name.replace(".", "_")
if f.c_basename:
c_basename = f.c_basename
else:
fields = full_name.split(".")
if fields[-1] == '__new__':
fields.pop()
c_basename = "_".join(fields)
template_dict['c_basename'] = c_basename
methoddef_name = "{}_METHODDEF".format(c_basename.upper())
@ -1171,8 +1186,81 @@ class Class:
def __repr__(self):
return "<clinic.Class " + repr(self.name) + " at " + str(id(self)) + ">"
unsupported_special_methods = set("""
DATA, CALLABLE, METHOD, STATIC_METHOD, CLASS_METHOD = range(5)
__abs__
__add__
__and__
__bytes__
__call__
__complex__
__delitem__
__divmod__
__eq__
__float__
__floordiv__
__ge__
__getattr__
__getattribute__
__getitem__
__gt__
__hash__
__iadd__
__iand__
__idivmod__
__ifloordiv__
__ilshift__
__imod__
__imul__
__index__
__int__
__invert__
__ior__
__ipow__
__irshift__
__isub__
__iter__
__itruediv__
__ixor__
__le__
__len__
__lshift__
__lt__
__mod__
__mul__
__neg__
__new__
__next__
__or__
__pos__
__pow__
__radd__
__rand__
__rdivmod__
__repr__
__rfloordiv__
__rlshift__
__rmod__
__rmul__
__ror__
__round__
__rpow__
__rrshift__
__rshift__
__rsub__
__rtruediv__
__rxor__
__setattr__
__setitem__
__str__
__sub__
__truediv__
__xor__
""".strip().split())
INVALID, CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW = range(6)
class Function:
"""
@ -1207,6 +1295,8 @@ class Function:
@property
def methoddef_flags(self):
if self.kind in (METHOD_INIT, METHOD_NEW):
return None
flags = []
if self.kind == CLASS_METHOD:
flags.append('METH_CLASS')
@ -1846,7 +1936,7 @@ class self_converter(CConverter):
type = "PyObject *"
def converter_init(self, *, type=None):
f = self.function
if f.kind == CALLABLE:
if f.kind in (CALLABLE, METHOD_INIT):
if f.cls:
self.name = "self"
else:
@ -1858,6 +1948,9 @@ class self_converter(CConverter):
elif f.kind == CLASS_METHOD:
self.name = "cls"
self.type = "PyTypeObject *"
elif f.kind == METHOD_NEW:
self.name = "type"
self.type = "PyTypeObject *"
if type:
self.type = type
@ -2258,6 +2351,18 @@ class DSLParser:
function_name = fields.pop()
module, cls = self.clinic._module_and_class(fields)
fields = full_name.split('.')
if fields[-1] == '__new__':
if (self.kind != CLASS_METHOD) or (not cls):
fail("__new__ must be a class method!")
self.kind = METHOD_NEW
elif fields[-1] == '__init__':
if (self.kind != CALLABLE) or (not cls):
fail("__init__ must be a normal method, not a class or static method!")
self.kind = METHOD_INIT
elif fields[-1] in unsupported_special_methods:
fail(fields[-1] + " should not be converted to Argument Clinic! (Yet.)")
if not module:
fail("Undefined module used in declaration of " + repr(full_name.strip()) + ".")
self.function = Function(name=function_name, full_name=full_name, module=module, cls=cls, c_basename=c_basename,