]> granicus.if.org Git - python/commitdiff
Issue #20228: Argument Clinic now has special support for class special
authorLarry Hastings <larry@hastings.org>
Sun, 12 Jan 2014 22:12:59 +0000 (14:12 -0800)
committerLarry Hastings <larry@hastings.org>
Sun, 12 Jan 2014 22:12:59 +0000 (14:12 -0800)
methods.

Misc/NEWS
Modules/_pickle.c
Tools/clinic/clinic.py

index 2b6d885fb65f035c0e5fe9de82084c77b4016d58..f47129dc93d892493e6ed3c2f5f2b7221f02e647 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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).
 
index 917dd45f364dcf59a44045c85a507da6e90efd1c..e687a1eb0e28b94e1e867146771fb8620d3d83ba 100644 (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);
 
index 0da669015215da151f1d896415c94cfcd32a48c5..86df329a726b1ab089be43a20f7895153bc385ff 100755 (executable)
@@ -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("""
+
+__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__
 
-DATA, CALLABLE, METHOD, STATIC_METHOD, CLASS_METHOD = range(5)
+""".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,