cpython: 69c0aa8a8185 (original) (raw)

Mercurial > cpython

changeset 101854:69c0aa8a8185

Issue #26282: PyArg_ParseTupleAndKeywords() and Argument Clinic now support positional-only and keyword parameters in the same function. [#26282]

Serhiy Storchaka storchaka@gmail.com
date Thu, 09 Jun 2016 16:30:29 +0300
parents a3060775b8ad
children a5a013ca5687
files Doc/c-api/arg.rst Doc/glossary.rst Doc/whatsnew/3.6.rst Lib/test/test_capi.py Lib/test/test_getargs2.py Misc/NEWS Modules/_testcapimodule.c Python/getargs.c Tools/clinic/clinic.py
diffstat 9 files changed, 210 insertions(+), 72 deletions(-)[+] [-] Doc/c-api/arg.rst 11 Doc/glossary.rst 2 Doc/whatsnew/3.6.rst 5 Lib/test/test_capi.py 25 Lib/test/test_getargs2.py 33 Misc/NEWS 12 Modules/_testcapimodule.c 18 Python/getargs.c 125 Tools/clinic/clinic.py 51

line wrap: on

line diff

--- a/Doc/c-api/arg.rst +++ b/Doc/c-api/arg.rst @@ -406,8 +406,15 @@ API Functions .. c:function:: int PyArg_ParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], ...) Parse the parameters of a function that takes both positional and keyword

.. c:function:: int PyArg_VaParseTupleAndKeywords(PyObject *args, PyObject *kw, const char *format, char *keywords[], va_list vargs)

--- a/Doc/glossary.rst +++ b/Doc/glossary.rst @@ -718,6 +718,8 @@ Glossary def func(foo, bar=None): ...

+ * :dfn:positional-only: specifies an argument that can be supplied only by position. Python has no syntax for defining positional-only parameters. However, some built-in functions have positional-only

--- a/Doc/whatsnew/3.6.rst +++ b/Doc/whatsnew/3.6.rst @@ -503,6 +503,11 @@ Build and C API Changes

--- a/Lib/test/test_capi.py +++ b/Lib/test/test_capi.py @@ -527,6 +527,31 @@ class SkipitemTest(unittest.TestCase): self.assertRaises(ValueError, _testcapi.parse_tuple_and_keywords, (), {}, b'', [42])

+

+ @unittest.skipUnless(threading, 'Threading required for this test.') class TestThreadState(unittest.TestCase):

--- a/Lib/test/test_getargs2.py +++ b/Lib/test/test_getargs2.py @@ -658,6 +658,39 @@ class KeywordOnly_TestCase(unittest.Test getargs_keyword_only(1, 2, **{'\uDC80': 10}) +class PositionalOnlyAndKeywords_TestCase(unittest.TestCase):

+

+

+

+

+

+

+ + class Bytes_TestCase(unittest.TestCase): def test_c(self): from _testcapi import getargs_c

--- a/Misc/NEWS +++ b/Misc/NEWS @@ -201,6 +201,18 @@ Misc

--- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -1028,6 +1028,21 @@ getargs_keyword_only(PyObject self, PyO return Py_BuildValue("iii", required, optional, keyword_only); } +/ test PyArg_ParseTupleAndKeywords positional-only arguments */ +static PyObject * +getargs_positional_only_and_keywords(PyObject *self, PyObject *args, PyObject *kwargs) +{

+

+} + /* Functions to call PyArg_ParseTuple with integer format codes, and return the result. */ @@ -3963,6 +3978,9 @@ static PyMethodDef TestMethods[] = { METH_VARARGS|METH_KEYWORDS}, {"getargs_keyword_only", (PyCFunction)getargs_keyword_only, METH_VARARGS|METH_KEYWORDS},

--- a/Python/getargs.c +++ b/Python/getargs.c @@ -1443,7 +1443,8 @@ vgetargskeywords(PyObject *args, PyObjec const char *fname, *msg, *custom_msg, *keyword; int min = INT_MAX; int max = INT_MAX;

if (len > STATIC_FREELIST_ENTRIES) { freelist.entries = PyMem_NEW(freelistentry_t, len); @@ -1526,6 +1535,14 @@ vgetargskeywords(PyObject *args, PyObjec max = i; format++;

@@ -1541,48 +1558,59 @@ vgetargskeywords(PyObject *args, PyObjec "format specifiers (%d)", len, i); return cleanreturn(0, &freelist); }

+

+

-

-

/* We are into optional args, skip thru to any remaining * keyword args */ @@ -1594,6 +1622,15 @@ vgetargskeywords(PyObject *args, PyObjec } }

+ if (!IS_END_OF_FORMAT(*format) && (*format != '|') && (*format != '$')) { PyErr_Format(PyExc_SystemError, "more argument specifiers than keyword list entries " @@ -1613,7 +1650,7 @@ vgetargskeywords(PyObject *args, PyObjec return cleanreturn(0, &freelist); } for (i = 0; i < len; i++) {

--- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -644,7 +644,7 @@ class CLanguage(Language): default_return_converter = (not f.return_converter or f.return_converter.type == 'PyObject *')

@@ -661,7 +661,7 @@ class CLanguage(Language): new_or_init = f.kind in (METHOD_NEW, METHOD_INIT) meth_o = (len(parameters) == 1 and

@@ -1075,7 +1075,7 @@ class CLanguage(Language): last_group = 0 first_optional = len(selfless)

@@ -2367,7 +2367,10 @@ class CConverter(metaclass=CConverterAut data.modifications.append('/* modifications for ' + name + ' */\n' + modifications.rstrip()) # keywords

# format_units if self.is_optional() and '|' not in data.format_units: @@ -3192,6 +3195,7 @@ class DSLParser: self.state = self.state_dsl_start self.parameter_indent = None self.keyword_only = False

@@ -3570,8 +3574,8 @@ class DSLParser: # "parameter_state". (Previously the code was a miasma of ifs and # separate boolean state variables.) The states are: #

@@ -3582,9 +3586,8 @@ class DSLParser: # now must have default values. # 5: ps_group_after. in a group, after required parameters. # 6: ps_right_square_after. right square brackets after required parameters.

def state_parameters_start(self, line): if self.ignore_line(line): @@ -3863,9 +3866,6 @@ class DSLParser: return name, False, kwargs def parse_special_symbol(self, symbol):

- if symbol == '': if self.keyword_only: fail("Function " + self.function.name + " uses '' more than once.") @@ -3892,13 +3892,15 @@ class DSLParser: else: fail("Function " + self.function.name + " has an unsupported group configuration. (Unexpected state " + str(self.parameter_state) + ".c)") elif symbol == '/':

@@ -3986,23 +3988,20 @@ class DSLParser: # populate "right_bracket_count" field for every parameter assert parameters, "We should always have a self parameter. " + repr(f) assert isinstance(parameters[0].converter, self_converter)

-

right_bracket_count = 0