cpython: 2e32462e4832 (original) (raw)

Mercurial > cpython

changeset 88570:2e32462e4832

Issue #20294: Argument Clinic now supports argument parsing for __new__ and __init__ functions. [#20294]

Larry Hastings larry@hastings.org
date Sat, 18 Jan 2014 23:50:21 -0800
parents 8f11493cf727
children cc53c49d38c8
files Doc/howto/clinic.rst Include/modsupport.h Misc/NEWS Modules/_pickle.c Python/getargs.c Tools/clinic/clinic.py
diffstat 6 files changed, 379 insertions(+), 253 deletions(-)[+] [-] Doc/howto/clinic.rst 23 Include/modsupport.h 1 Misc/NEWS 3 Modules/_pickle.c 78 Python/getargs.c 21 Tools/clinic/clinic.py 506

line wrap: on

line diff

--- a/Doc/howto/clinic.rst +++ b/Doc/howto/clinic.rst @@ -784,8 +784,8 @@ Argument Clinic converters. On the left on the right is the text you'd replace it with. ========= ================================================================================= -'B' byte(bitwise=True) -'b' byte +'B' unsigned_char(bitwise=True) +'b' unsigned_char 'c' char 'C' int(types='str') 'd' double @@ -1275,6 +1275,25 @@ any arguments. You can still use a self converter, a return converter, and specify a type argument to the object converter for METH_O. +tp_new and tp_init functions +---------------------------------------------- + +You can convert tp_new and tp_init +functions. Just name them __new__ or +__init__ as appropriate. Notes: + +* The function name generated for __new__ doesn't end in __new__

--- a/Include/modsupport.h +++ b/Include/modsupport.h @@ -36,6 +36,7 @@ PyAPI_FUNC(PyObject *) _Py_BuildValue_Si #endif #ifndef Py_LIMITED_API PyAPI_FUNC(int) _PyArg_NoKeywords(const char *funcname, PyObject *kw); +PyAPI_FUNC(int) _PyArg_NoPositional(const char *funcname, PyObject *args); PyAPI_FUNC(int) PyArg_VaParse(PyObject *, const char *, va_list); PyAPI_FUNC(int) PyArg_VaParseTupleAndKeywords(PyObject *, PyObject *,

--- a/Misc/NEWS +++ b/Misc/NEWS @@ -101,6 +101,9 @@ Tests Tools/Demos ----------- +- Issue #20294: Argument Clinic now supports argument parsing for new and

--- a/Modules/_pickle.c +++ b/Modules/_pickle.c @@ -4064,13 +4064,13 @@ PyDoc_STRVAR(_pickle_Pickler___init____d "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."); -static PyObject * +static int pickle_Pickler___init___impl(PicklerObject *self, PyObject *file, PyObject *protocol, int fix_imports); -static PyObject * +static int pickle_Pickler___init(PyObject *self, PyObject *args, PyObject *kwargs) {

-static PyObject +static int _pickle_Pickler___init___impl(PicklerObject self, PyObject file, PyObject protocol, int fix_imports) -/[clinic end generated code: checksum=defa3d9e9f8b51fb257d4fdfca99db503db0e6df]/ +/[clinic end generated code: checksum=10c8ea05194d08108471163d8202cf5e12975544]/ { _Py_IDENTIFIER(persistent_id); _Py_IDENTIFIER(dispatch_table); @@ -4098,16 +4098,16 @@ static PyObject * (void)Pickler_clear(self); if (_Pickler_SetProtocol(self, protocol, fix_imports) < 0)

if (_Pickler_SetOutputStream(self, file) < 0)

/* memo and output_buffer may have already been created in _Pickler_New */ if (self->memo == NULL) { self->memo = PyMemoTable_New(); if (self->memo == NULL)

-

-} - -/* Wrap the Clinic generated signature to slot it in tp_init. */ -static int -Pickler_init(PyObject *self, PyObject *args, PyObject *kwargs) -{

+ return 0; } + /* Define a proxy object for the Pickler's internal memo object. This is to

-static PyObject * +static int _pickle_Unpickler___init___impl(UnpicklerObject self, PyObject file, int fix_imports, const char encoding, const char errors) -/[clinic end generated code: checksum=26c1d4a06841a8e51d29a0c244ba7f4607ff358a]/ +/[clinic end generated code: checksum=6936e9188104e45b1b15e1c11fe77b3965409471]/ { _Py_IDENTIFIER(persistent_load); @@ -6648,20 +6637,20 @@ static PyObject * (void)Unpickler_clear(self); if (_Unpickler_SetInputStream(self, file) < 0)

if (_Unpickler_SetInputEncoding(self, encoding, errors) < 0)

self->fix_imports = fix_imports; if (self->fix_imports == -1)

if (_PyObject_HasAttrId((PyObject *)self, &PyId_persistent_load)) { self->pers_func = _PyObject_GetAttrId((PyObject *)self, &PyId_persistent_load); if (self->pers_func == NULL)

@@ -6669,30 +6658,19 @@ static PyObject * self->stack = (Pdata *)Pdata_New(); if (self->stack == NULL)

self->memo_size = 32; self->memo = _Unpickler_NewMemo(self->memo_size); if (self->memo == NULL)

self->proto = 0;

-} - -/* Wrap the Clinic generated signature to slot it in tp_init. */ -static int -Unpickler_init(PyObject *self, PyObject *args, PyObject *kwargs) -{

+ /* Define a proxy object for the Unpickler's internal memo object. This is to

--- a/Python/getargs.c +++ b/Python/getargs.c @@ -1805,7 +1805,7 @@ PyArg_UnpackTuple(PyObject args, const / For type constructors that don't take keyword args *

+

+} + #ifdef __cplusplus }; #endif

--- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -169,6 +169,8 @@ def linear_format(s, **kwargs): themselves. (This line is the "source line".) * If the substitution text is empty, the source line is removed in the output.

@@ -454,6 +456,182 @@ class CLanguage(Language): add('"') return ''.join(text)

+ + +docstring_prototype + +PyDoc_VAR({c_basename}doc); + + +docstring_definition + +PyDoc_STRVAR({c_basename}doc, +{docstring}); +_ + +impl_definition + +static {impl_return_type} +{c_basename}impl({impl_parameters}) +_ + +parser_prototype_noargs + +static PyObject * +{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) +_ + +parser_prototype_meth_o + +# SLIGHT HACK +# METH_O uses {impl_parameters} for the parser + +static PyObject * +{c_basename}({impl_parameters}) + + +parser_prototype_varargs + +static PyObject * +{c_basename}({self_type}{self_name}, PyObject *args) + + +parser_prototype_keyword + +static PyObject * +{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) + + +parser_prototype_init + +static int +{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) +________________________________________________ + +parser_definition_simple_no_parsing + +{{

+}} +__________________________________________________ + +parser_definition_start + +{{

+{empty line} +__________________________________________________ + +parser_definition_end +

+ +{exit_label}

+}} +__________________________________________________ + +parser_definition_impl_call +

+__________________________________________________ + +parser_definition_unpack_tuple +

+__________________________________________________ + +parser_definition_parse_tuple +

+__________________________________________________ + +parser_definition_option_groups

+ +__________________________________________________ + +parser_definition_parse_tuple_and_keywords +

+__________________________________________________ + +parser_definition_no_positional +

+ +__________________________________________________ + +parser_definition_no_keywords +

+ +__________________________________________________ + +methoddef_define + +#define {methoddef_name} \

+__________________________________________________ +""".rstrip() +

+

+

+

def output_templates(self, f): parameters = list(f.parameters.values()) @@ -477,12 +655,14 @@ class CLanguage(Language): else: all_boring_objects = True

+ meth_o = (len(parameters) == 1 and parameters[0].kind == inspect.Parameter.POSITIONAL_ONLY and not converters[0].is_optional() and isinstance(converters[0], object_converter) and

-

# we have to set seven things before we're done: # @@ -493,246 +673,144 @@ class CLanguage(Language): # parser_prototype # parser_definition # impl_definition

-

-

-PyDoc_STRVAR({c_basename}doc, -{docstring}); -""".strip() -

-static {impl_return_type} -{c_basename}_impl({impl_parameters})""".strip() - +

+

+

-

-

-static PyObject * -{c_basename}({self_type}{self_name}, PyObject *args) -""".strip()

+

if not parameters: # no parameters, METH_NOARGS flags = "METH_NOARGS"

-static PyObject * -{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored)) -""".strip()

if default_return_converter:

-{{

-}} -""".rstrip()

-{{

-

- -{exit_label}

-}} -""".rstrip()

elif meth_o:

+ if default_return_converter:

-

-

-

-static PyObject * -{c_basename}({impl_parameters}) -""".strip() -

-

-

-

-static PyObject * -{c_basename}({impl_parameters}) -""".strip() -

-{{

-

- -{exit_label}

-}} -""".rstrip()

elif has_option_groups: # positional parameters with option groups # (we have to generate lots of PyArg_ParseTuple calls # in a big switch statement)

-

-{{

-

- -{exit_label}

-}} -""".rstrip()

+

elif positional and all_boring_objects: # positional-only, but no option groups, # and nothing but normal objects: # PyArg_UnpackTuple

-

-

-{{

-

- -exit:

-}} -""".rstrip().replace('{min}', min_o).replace('{max}', max_o)

+

elif positional: # positional-only, but no option groups # we only need one call to PyArg_ParseTuple

-

-{{

-

- -exit:

-}} -""".rstrip()

+

else: # positional-or-keyword arguments flags = "METH_VARARGS|METH_KEYWORDS"

-static PyObject * -{c_basename}({self_type}{self_name}, PyObject *args, PyObject *kwargs) -""".strip() -

-{{

-

- -exit:

-}} -""".rstrip()

+

+ +

+

+

+

+

+ if f.methoddef_flags:

-#define {methoddef_name} \

-""".strip().replace('{methoddef_flags}', flags) -

+

- if parser_prototype:

-

+

d = { "docstring_prototype" : docstring_prototype, @@ -744,8 +822,11 @@ exit: "impl_definition" : impl_definition, }

@@ -881,12 +962,17 @@ exit: positional = has_option_groups = False

+ if parameters: last_group = 0

+ # insert group variable group = p.group if last_group != group: @@ -950,6 +1036,10 @@ exit: template_dict['cleanup'] = "".join(data.cleanup) template_dict['return_value'] = data.return_value

+ if has_option_groups: self.render_option_group_parsing(f, template_dict) @@ -2063,7 +2153,7 @@ class char_converter(CConverter): @add_legacy_c_converter('B', bitwise=True) -class byte_converter(CConverter): +class unsigned_char_converter(CConverter): type = 'unsigned char' default_type = int format_unit = 'b' @@ -2073,6 +2163,8 @@ class byte_converter(CConverter): if bitwise: self.format_unit = 'B' +class byte_converter(unsigned_char_converter): pass + class short_converter(CConverter): type = 'short' default_type = int @@ -2455,6 +2547,16 @@ class int_return_converter(long_return_c type = 'int' cast = '(long)' +class init_return_converter(long_return_converter):

+

+ class unsigned_long_return_converter(long_return_converter): type = 'unsigned long' conversion_fn = 'PyLong_FromUnsignedLong' @@ -2858,9 +2960,8 @@ class DSLParser: if c_basename and not is_legal_c_identifier(c_basename): fail("Illegal C basename: {}".format(c_basename))

@@ -2893,9 +2994,14 @@ class DSLParser: if (self.kind != CALLABLE) or (not cls): fail("init must be a normal method, not a class or static method!") self.kind = METHOD_INIT

+ 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,