Python-checkins
Threads by month
- ----- 2025 -----
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
December 2022
- 1 participants
- 400 discussions

Dec. 27, 2022
https://github.com/python/cpython/commit/04285502ba7d90e41138b0282434372be1…
commit: 04285502ba7d90e41138b0282434372be18ac042
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: miss-islington <31488909+miss-islington(a)users.noreply.github.com>
date: 2022-12-27T17:16:53-08:00
summary:
gh-100553: Improve accuracy of sqlite3.Row iter test (GH-100555)
(cherry picked from commit 3dc48dabd48864039951715816e07986a4828d80)
Co-authored-by: Nikita Sobolev <mail(a)sobolevn.me>
files:
M Lib/sqlite3/test/factory.py
diff --git a/Lib/sqlite3/test/factory.py b/Lib/sqlite3/test/factory.py
index 876428497542..40a290f0c981 100644
--- a/Lib/sqlite3/test/factory.py
+++ b/Lib/sqlite3/test/factory.py
@@ -155,8 +155,14 @@ def test_sqlite_row_iter(self):
"""Checks if the row object is iterable"""
self.con.row_factory = sqlite.Row
row = self.con.execute("select 1 as a, 2 as b").fetchone()
- for col in row:
- pass
+
+ # Is iterable in correct order and produces valid results:
+ items = [col for col in row]
+ self.assertEqual(items, [1, 2])
+
+ # Is iterable the second time:
+ items = [col for col in row]
+ self.assertEqual(items, [1, 2])
def test_sqlite_row_as_tuple(self):
"""Checks if the row object can be converted to a tuple"""
1
0

Dec. 27, 2022
https://github.com/python/cpython/commit/08e5594cf3d42391a48e0311f6b9393ec2…
commit: 08e5594cf3d42391a48e0311f6b9393ec2e00e1e
branch: main
author: Guido van Rossum <guido(a)python.org>
committer: gvanrossum <gvanrossum(a)gmail.com>
date: 2022-12-27T17:11:03-08:00
summary:
GH-98831: Modernize a ton of simpler instructions (#100545)
* load_const and load_fast aren't families for now
* Don't decref unmoved names
* Modernize GET_ANEXT
* Modernize GET_AWAITABLE
* Modernize ASYNC_GEN_WRAP
* Modernize YIELD_VALUE
* Modernize POP_EXCEPT (in more than one way)
* Modernize PREP_RERAISE_STAR
* Modernize LOAD_ASSERTION_ERROR
* Modernize LOAD_BUILD_CLASS
* Modernize STORE_NAME
* Modernize LOAD_NAME
* Modernize LOAD_CLASSDEREF
* Modernize LOAD_DEREF
* Modernize STORE_DEREF
* Modernize COPY_FREE_VARS (mark it as done)
* Modernize LIST_TO_TUPLE
* Modernize LIST_EXTEND
* Modernize SET_UPDATE
* Modernize SETUP_ANNOTATIONS
* Modernize DICT_UPDATE
* Modernize DICT_MERGE
* Modernize MAP_ADD
* Modernize IS_OP
* Modernize CONTAINS_OP
* Modernize CHECK_EXC_MATCH
* Modernize IMPORT_NAME
* Modernize IMPORT_STAR
* Modernize IMPORT_FROM
* Modernize JUMP_FORWARD (mark it as done)
* Modernize JUMP_BACKWARD (mark it as done)
files:
M Python/bytecodes.c
M Python/generated_cases.c.h
M Tools/cases_generator/generate_cases.py
diff --git a/Python/bytecodes.c b/Python/bytecodes.c
index c0b625bd662c..e1c73ab6b32f 100644
--- a/Python/bytecodes.c
+++ b/Python/bytecodes.c
@@ -83,9 +83,11 @@ static PyObject *value, *value1, *value2, *left, *right, *res, *sum, *prod, *sub
static PyObject *container, *start, *stop, *v, *lhs, *rhs;
static PyObject *list, *tuple, *dict, *owner;
static PyObject *exit_func, *lasti, *val, *retval, *obj, *iter;
+static PyObject *aiter, *awaitable, *iterable, *w, *exc_value, *bc;
+static PyObject *orig, *excs, *update, *b, *fromlist, *level, *from;
static size_t jump;
// Dummy variables for cache effects
-static _Py_CODEUNIT when_to_jump_mask, invert, counter, index, hint;
+static uint16_t when_to_jump_mask, invert, counter, index, hint;
static uint32_t type_version;
// Dummy opcode names for 'op' opcodes
#define _COMPARE_OP_FLOAT 1003
@@ -638,12 +640,9 @@ dummy_func(
}
}
- // stack effect: ( -- __0)
- inst(GET_ANEXT) {
+ inst(GET_ANEXT, (aiter -- aiter, awaitable)) {
unaryfunc getter = NULL;
PyObject *next_iter = NULL;
- PyObject *awaitable = NULL;
- PyObject *aiter = TOP();
PyTypeObject *type = Py_TYPE(aiter);
if (PyAsyncGen_CheckExact(aiter)) {
@@ -685,20 +684,17 @@ dummy_func(
}
}
- PUSH(awaitable);
PREDICT(LOAD_CONST);
}
- // stack effect: ( -- )
- inst(GET_AWAITABLE) {
- PyObject *iterable = TOP();
- PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
+ inst(GET_AWAITABLE, (iterable -- iter)) {
+ iter = _PyCoro_GetAwaitableIter(iterable);
if (iter == NULL) {
format_awaitable_error(tstate, Py_TYPE(iterable), oparg);
}
- Py_DECREF(iterable);
+ DECREF_INPUTS();
if (iter != NULL && PyCoro_CheckExact(iter)) {
PyObject *yf = _PyGen_yf((PyGenObject*)iter);
@@ -714,11 +710,7 @@ dummy_func(
}
}
- SET_TOP(iter); /* Even if it's NULL */
-
- if (iter == NULL) {
- goto error;
- }
+ ERROR_IF(iter == NULL, error);
PREDICT(LOAD_CONST);
}
@@ -773,29 +765,22 @@ dummy_func(
}
}
- // stack effect: ( -- )
- inst(ASYNC_GEN_WRAP) {
- PyObject *v = TOP();
+ inst(ASYNC_GEN_WRAP, (v -- w)) {
assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR);
- PyObject *w = _PyAsyncGenValueWrapperNew(v);
- if (w == NULL) {
- goto error;
- }
- SET_TOP(w);
- Py_DECREF(v);
+ w = _PyAsyncGenValueWrapperNew(v);
+ DECREF_INPUTS();
+ ERROR_IF(w == NULL, error);
}
- // stack effect: ( -- )
- inst(YIELD_VALUE) {
+ inst(YIELD_VALUE, (retval --)) {
// NOTE: It's important that YIELD_VALUE never raises an exception!
// The compiler treats any exception raised here as a failed close()
// or throw() call.
assert(oparg == STACK_LEVEL());
assert(frame != &entry_frame);
- PyObject *retval = POP();
PyGenObject *gen = _PyFrame_GetGenerator(frame);
gen->gi_frame_state = FRAME_SUSPENDED;
- _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyFrame_SetStackPointer(frame, stack_pointer - 1);
TRACE_FUNCTION_EXIT();
DTRACE_FUNCTION_EXIT();
tstate->exc_info = gen->gi_exc_state.previous_item;
@@ -809,12 +794,9 @@ dummy_func(
goto resume_frame;
}
- // stack effect: (__0 -- )
- inst(POP_EXCEPT) {
+ inst(POP_EXCEPT, (exc_value -- )) {
_PyErr_StackItem *exc_info = tstate->exc_info;
- PyObject *value = exc_info->exc_value;
- exc_info->exc_value = POP();
- Py_XDECREF(value);
+ Py_XSETREF(exc_info->exc_value, exc_value);
}
// stack effect: (__0 -- )
@@ -839,21 +821,13 @@ dummy_func(
goto exception_unwind;
}
- // stack effect: (__0 -- )
- inst(PREP_RERAISE_STAR) {
- PyObject *excs = POP();
+ inst(PREP_RERAISE_STAR, (orig, excs -- val)) {
assert(PyList_Check(excs));
- PyObject *orig = POP();
-
- PyObject *val = _PyExc_PrepReraiseStar(orig, excs);
- Py_DECREF(excs);
- Py_DECREF(orig);
- if (val == NULL) {
- goto error;
- }
+ val = _PyExc_PrepReraiseStar(orig, excs);
+ DECREF_INPUTS();
- PUSH(val);
+ ERROR_IF(val == NULL, error);
}
// stack effect: (__0, __1 -- )
@@ -934,16 +908,11 @@ dummy_func(
}
}
-
- // stack effect: ( -- __0)
- inst(LOAD_ASSERTION_ERROR) {
- PyObject *value = PyExc_AssertionError;
- PUSH(Py_NewRef(value));
+ inst(LOAD_ASSERTION_ERROR, ( -- value)) {
+ value = Py_NewRef(PyExc_AssertionError);
}
- // stack effect: ( -- __0)
- inst(LOAD_BUILD_CLASS) {
- PyObject *bc;
+ inst(LOAD_BUILD_CLASS, ( -- bc)) {
if (PyDict_CheckExact(BUILTINS())) {
bc = _PyDict_GetItemWithError(BUILTINS(),
&_Py_ID(__build_class__));
@@ -952,7 +921,7 @@ dummy_func(
_PyErr_SetString(tstate, PyExc_NameError,
"__build_class__ not found");
}
- goto error;
+ ERROR_IF(true, error);
}
Py_INCREF(bc);
}
@@ -962,31 +931,27 @@ dummy_func(
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
_PyErr_SetString(tstate, PyExc_NameError,
"__build_class__ not found");
- goto error;
+ ERROR_IF(true, error);
}
}
- PUSH(bc);
}
- // stack effect: (__0 -- )
- inst(STORE_NAME) {
+ inst(STORE_NAME, (v -- )) {
PyObject *name = GETITEM(names, oparg);
- PyObject *v = POP();
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when storing %R", name);
- Py_DECREF(v);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
- Py_DECREF(v);
- if (err != 0)
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(err, error);
}
inst(DELETE_NAME, (--)) {
@@ -1139,11 +1104,9 @@ dummy_func(
}
}
- // stack effect: ( -- __0)
- inst(LOAD_NAME) {
+ inst(LOAD_NAME, ( -- v)) {
PyObject *name = GETITEM(names, oparg);
PyObject *locals = LOCALS();
- PyObject *v;
if (locals == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals when loading %R", name);
@@ -1200,7 +1163,6 @@ dummy_func(
}
}
}
- PUSH(v);
}
// error: LOAD_GLOBAL has irregular stack effect
@@ -1339,9 +1301,8 @@ dummy_func(
Py_DECREF(oldobj);
}
- // stack effect: ( -- __0)
- inst(LOAD_CLASSDEREF) {
- PyObject *name, *value, *locals = LOCALS();
+ inst(LOAD_CLASSDEREF, ( -- value)) {
+ PyObject *name, *locals = LOCALS();
assert(locals);
assert(oparg >= 0 && oparg < frame->f_code->co_nlocalsplus);
name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, oparg);
@@ -1372,31 +1333,26 @@ dummy_func(
}
Py_INCREF(value);
}
- PUSH(value);
}
- // stack effect: ( -- __0)
- inst(LOAD_DEREF) {
+ inst(LOAD_DEREF, ( -- value)) {
PyObject *cell = GETLOCAL(oparg);
- PyObject *value = PyCell_GET(cell);
+ value = PyCell_GET(cell);
if (value == NULL) {
format_exc_unbound(tstate, frame->f_code, oparg);
- goto error;
+ ERROR_IF(true, error);
}
- PUSH(Py_NewRef(value));
+ Py_INCREF(value);
}
- // stack effect: (__0 -- )
- inst(STORE_DEREF) {
- PyObject *v = POP();
+ inst(STORE_DEREF, (v --)) {
PyObject *cell = GETLOCAL(oparg);
PyObject *oldobj = PyCell_GET(cell);
PyCell_SET(cell, v);
Py_XDECREF(oldobj);
}
- // stack effect: ( -- )
- inst(COPY_FREE_VARS) {
+ inst(COPY_FREE_VARS, (--)) {
/* Copy closure variables to free variables */
PyCodeObject *co = frame->f_code;
assert(PyFunction_Check(frame->f_funcobj));
@@ -1444,21 +1400,14 @@ dummy_func(
PUSH(list);
}
- // stack effect: ( -- )
- inst(LIST_TO_TUPLE) {
- PyObject *list = POP();
- PyObject *tuple = PyList_AsTuple(list);
- Py_DECREF(list);
- if (tuple == NULL) {
- goto error;
- }
- PUSH(tuple);
+ inst(LIST_TO_TUPLE, (list -- tuple)) {
+ tuple = PyList_AsTuple(list);
+ DECREF_INPUTS();
+ ERROR_IF(tuple == NULL, error);
}
- // stack effect: (__0 -- )
- inst(LIST_EXTEND) {
- PyObject *iterable = POP();
- PyObject *list = PEEK(oparg);
+ inst(LIST_EXTEND, (iterable -- )) {
+ PyObject *list = PEEK(oparg + 1); // iterable is still on the stack
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
if (none_val == NULL) {
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
@@ -1469,22 +1418,18 @@ dummy_func(
"Value after * must be an iterable, not %.200s",
Py_TYPE(iterable)->tp_name);
}
- Py_DECREF(iterable);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
Py_DECREF(none_val);
- Py_DECREF(iterable);
+ DECREF_INPUTS();
}
- // stack effect: (__0 -- )
- inst(SET_UPDATE) {
- PyObject *iterable = POP();
- PyObject *set = PEEK(oparg);
+ inst(SET_UPDATE, (iterable --)) {
+ PyObject *set = PEEK(oparg + 1); // iterable is still on the stack
int err = _PySet_Update(set, iterable);
- Py_DECREF(iterable);
- if (err < 0) {
- goto error;
- }
+ DECREF_INPUTS();
+ ERROR_IF(err < 0, error);
}
// stack effect: (__array[oparg] -- __0)
@@ -1524,54 +1469,41 @@ dummy_func(
PUSH(map);
}
- // stack effect: ( -- )
- inst(SETUP_ANNOTATIONS) {
+ inst(SETUP_ANNOTATIONS, (--)) {
int err;
PyObject *ann_dict;
if (LOCALS() == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when setting up annotations");
- goto error;
+ ERROR_IF(true, error);
}
/* check if __annotations__ in locals()... */
if (PyDict_CheckExact(LOCALS())) {
ann_dict = _PyDict_GetItemWithError(LOCALS(),
&_Py_ID(__annotations__));
if (ann_dict == NULL) {
- if (_PyErr_Occurred(tstate)) {
- goto error;
- }
+ ERROR_IF(_PyErr_Occurred(tstate), error);
/* ...if not, create a new one */
ann_dict = PyDict_New();
- if (ann_dict == NULL) {
- goto error;
- }
+ ERROR_IF(ann_dict == NULL, error);
err = PyDict_SetItem(LOCALS(), &_Py_ID(__annotations__),
ann_dict);
Py_DECREF(ann_dict);
- if (err != 0) {
- goto error;
- }
+ ERROR_IF(err, error);
}
}
else {
/* do the same if locals() is not a dict */
ann_dict = PyObject_GetItem(LOCALS(), &_Py_ID(__annotations__));
if (ann_dict == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- goto error;
- }
+ ERROR_IF(!_PyErr_ExceptionMatches(tstate, PyExc_KeyError), error);
_PyErr_Clear(tstate);
ann_dict = PyDict_New();
- if (ann_dict == NULL) {
- goto error;
- }
+ ERROR_IF(ann_dict == NULL, error);
err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__),
ann_dict);
Py_DECREF(ann_dict);
- if (err != 0) {
- goto error;
- }
+ ERROR_IF(err, error);
}
else {
Py_DECREF(ann_dict);
@@ -1603,48 +1535,38 @@ dummy_func(
PUSH(map);
}
- // stack effect: (__0 -- )
- inst(DICT_UPDATE) {
- PyObject *update = POP();
- PyObject *dict = PEEK(oparg);
+ inst(DICT_UPDATE, (update --)) {
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
if (PyDict_Update(dict, update) < 0) {
if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
_PyErr_Format(tstate, PyExc_TypeError,
"'%.200s' object is not a mapping",
Py_TYPE(update)->tp_name);
}
- Py_DECREF(update);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
- Py_DECREF(update);
+ DECREF_INPUTS();
}
- // stack effect: (__0 -- )
- inst(DICT_MERGE) {
- PyObject *update = POP();
- PyObject *dict = PEEK(oparg);
+ inst(DICT_MERGE, (update --)) {
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
if (_PyDict_MergeEx(dict, update, 2) < 0) {
- format_kwargs_error(tstate, PEEK(2 + oparg), update);
- Py_DECREF(update);
- goto error;
+ format_kwargs_error(tstate, PEEK(3 + oparg), update);
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
- Py_DECREF(update);
+ DECREF_INPUTS();
PREDICT(CALL_FUNCTION_EX);
}
- // stack effect: (__0, __1 -- )
- inst(MAP_ADD) {
- PyObject *value = TOP();
- PyObject *key = SECOND();
- PyObject *map;
- STACK_SHRINK(2);
- map = PEEK(oparg); /* dict */
- assert(PyDict_CheckExact(map));
- /* map[key] = value */
- if (_PyDict_SetItem_Take2((PyDictObject *)map, key, value) != 0) {
- goto error;
- }
+ inst(MAP_ADD, (key, value --)) {
+ PyObject *dict = PEEK(oparg + 2); // key, value are still on the stack
+ assert(PyDict_CheckExact(dict));
+ /* dict[key] = value */
+ // Do not DECREF INPUTS because the function steals the references
+ ERROR_IF(_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0, error);
PREDICT(JUMP_BACKWARD);
}
@@ -2073,29 +1995,17 @@ dummy_func(
}
super(COMPARE_OP_STR_JUMP) = _COMPARE_OP_STR + _JUMP_IF;
- // stack effect: (__0 -- )
- inst(IS_OP) {
- PyObject *right = POP();
- PyObject *left = TOP();
+ inst(IS_OP, (left, right -- b)) {
int res = Py_Is(left, right) ^ oparg;
- PyObject *b = res ? Py_True : Py_False;
- SET_TOP(Py_NewRef(b));
- Py_DECREF(left);
- Py_DECREF(right);
+ DECREF_INPUTS();
+ b = Py_NewRef(res ? Py_True : Py_False);
}
- // stack effect: (__0 -- )
- inst(CONTAINS_OP) {
- PyObject *right = POP();
- PyObject *left = POP();
+ inst(CONTAINS_OP, (left, right -- b)) {
int res = PySequence_Contains(right, left);
- Py_DECREF(left);
- Py_DECREF(right);
- if (res < 0) {
- goto error;
- }
- PyObject *b = (res^oparg) ? Py_True : Py_False;
- PUSH(Py_NewRef(b));
+ DECREF_INPUTS();
+ ERROR_IF(res < 0, error);
+ b = Py_NewRef((res^oparg) ? Py_True : Py_False);
}
// stack effect: ( -- )
@@ -2139,76 +2049,57 @@ dummy_func(
}
}
- // stack effect: ( -- )
- inst(CHECK_EXC_MATCH) {
- PyObject *right = POP();
- PyObject *left = TOP();
+ inst(CHECK_EXC_MATCH, (left, right -- left, b)) {
assert(PyExceptionInstance_Check(left));
if (check_except_type_valid(tstate, right) < 0) {
- Py_DECREF(right);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
int res = PyErr_GivenExceptionMatches(left, right);
- Py_DECREF(right);
- PUSH(Py_NewRef(res ? Py_True : Py_False));
+ DECREF_INPUTS();
+ b = Py_NewRef(res ? Py_True : Py_False);
}
- // stack effect: (__0 -- )
- inst(IMPORT_NAME) {
+ inst(IMPORT_NAME, (level, fromlist -- res)) {
PyObject *name = GETITEM(names, oparg);
- PyObject *fromlist = POP();
- PyObject *level = TOP();
- PyObject *res;
res = import_name(tstate, frame, name, fromlist, level);
- Py_DECREF(level);
- Py_DECREF(fromlist);
- SET_TOP(res);
- if (res == NULL)
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(res == NULL, error);
}
- // stack effect: (__0 -- )
- inst(IMPORT_STAR) {
- PyObject *from = POP(), *locals;
+ inst(IMPORT_STAR, (from --)) {
+ PyObject *locals;
int err;
if (_PyFrame_FastToLocalsWithError(frame) < 0) {
- Py_DECREF(from);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
locals = LOCALS();
if (locals == NULL) {
_PyErr_SetString(tstate, PyExc_SystemError,
"no locals found during 'import *'");
- Py_DECREF(from);
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(true, error);
}
err = import_all_from(tstate, locals, from);
_PyFrame_LocalsToFast(frame, 0);
- Py_DECREF(from);
- if (err != 0)
- goto error;
+ DECREF_INPUTS();
+ ERROR_IF(err, error);
}
- // stack effect: ( -- __0)
- inst(IMPORT_FROM) {
+ inst(IMPORT_FROM, (from -- from, res)) {
PyObject *name = GETITEM(names, oparg);
- PyObject *from = TOP();
- PyObject *res;
res = import_from(tstate, from, name);
- PUSH(res);
- if (res == NULL)
- goto error;
+ ERROR_IF(res == NULL, error);
}
- // stack effect: ( -- )
- inst(JUMP_FORWARD) {
+ inst(JUMP_FORWARD, (--)) {
JUMPBY(oparg);
}
- // stack effect: ( -- )
- inst(JUMP_BACKWARD) {
+ inst(JUMP_BACKWARD, (--)) {
assert(oparg < INSTR_OFFSET());
JUMPBY(-oparg);
CHECK_EVAL_BREAKER();
@@ -3631,8 +3522,6 @@ family(load_attr) = {
LOAD_ATTR_PROPERTY, LOAD_ATTR_SLOT, LOAD_ATTR_WITH_HINT,
LOAD_ATTR_METHOD_LAZY_DICT, LOAD_ATTR_METHOD_NO_DICT, LOAD_ATTR_METHOD_WITH_DICT,
LOAD_ATTR_METHOD_WITH_VALUES };
-family(load_const) = { LOAD_CONST, LOAD_CONST__LOAD_FAST };
-family(load_fast) = { LOAD_FAST, LOAD_FAST__LOAD_CONST, LOAD_FAST__LOAD_FAST };
family(load_global) = {
LOAD_GLOBAL, LOAD_GLOBAL_BUILTIN,
LOAD_GLOBAL_MODULE };
diff --git a/Python/generated_cases.c.h b/Python/generated_cases.c.h
index 42b7ca086705..1179bdfc696c 100644
--- a/Python/generated_cases.c.h
+++ b/Python/generated_cases.c.h
@@ -795,10 +795,10 @@
}
TARGET(GET_ANEXT) {
+ PyObject *aiter = PEEK(1);
+ PyObject *awaitable;
unaryfunc getter = NULL;
PyObject *next_iter = NULL;
- PyObject *awaitable = NULL;
- PyObject *aiter = TOP();
PyTypeObject *type = Py_TYPE(aiter);
if (PyAsyncGen_CheckExact(aiter)) {
@@ -840,15 +840,17 @@
}
}
- PUSH(awaitable);
+ STACK_GROW(1);
+ POKE(1, awaitable);
PREDICT(LOAD_CONST);
DISPATCH();
}
TARGET(GET_AWAITABLE) {
PREDICTED(GET_AWAITABLE);
- PyObject *iterable = TOP();
- PyObject *iter = _PyCoro_GetAwaitableIter(iterable);
+ PyObject *iterable = PEEK(1);
+ PyObject *iter;
+ iter = _PyCoro_GetAwaitableIter(iterable);
if (iter == NULL) {
format_awaitable_error(tstate, Py_TYPE(iterable), oparg);
@@ -870,12 +872,9 @@
}
}
- SET_TOP(iter); /* Even if it's NULL */
-
- if (iter == NULL) {
- goto error;
- }
+ if (iter == NULL) goto pop_1_error;
+ POKE(1, iter);
PREDICT(LOAD_CONST);
DISPATCH();
}
@@ -931,27 +930,26 @@
}
TARGET(ASYNC_GEN_WRAP) {
- PyObject *v = TOP();
+ PyObject *v = PEEK(1);
+ PyObject *w;
assert(frame->f_code->co_flags & CO_ASYNC_GENERATOR);
- PyObject *w = _PyAsyncGenValueWrapperNew(v);
- if (w == NULL) {
- goto error;
- }
- SET_TOP(w);
+ w = _PyAsyncGenValueWrapperNew(v);
Py_DECREF(v);
+ if (w == NULL) goto pop_1_error;
+ POKE(1, w);
DISPATCH();
}
TARGET(YIELD_VALUE) {
+ PyObject *retval = PEEK(1);
// NOTE: It's important that YIELD_VALUE never raises an exception!
// The compiler treats any exception raised here as a failed close()
// or throw() call.
assert(oparg == STACK_LEVEL());
assert(frame != &entry_frame);
- PyObject *retval = POP();
PyGenObject *gen = _PyFrame_GetGenerator(frame);
gen->gi_frame_state = FRAME_SUSPENDED;
- _PyFrame_SetStackPointer(frame, stack_pointer);
+ _PyFrame_SetStackPointer(frame, stack_pointer - 1);
TRACE_FUNCTION_EXIT();
DTRACE_FUNCTION_EXIT();
tstate->exc_info = gen->gi_exc_state.previous_item;
@@ -966,10 +964,10 @@
}
TARGET(POP_EXCEPT) {
+ PyObject *exc_value = PEEK(1);
_PyErr_StackItem *exc_info = tstate->exc_info;
- PyObject *value = exc_info->exc_value;
- exc_info->exc_value = POP();
- Py_XDECREF(value);
+ Py_XSETREF(exc_info->exc_value, exc_value);
+ STACK_SHRINK(1);
DISPATCH();
}
@@ -995,19 +993,18 @@
}
TARGET(PREP_RERAISE_STAR) {
- PyObject *excs = POP();
+ PyObject *excs = PEEK(1);
+ PyObject *orig = PEEK(2);
+ PyObject *val;
assert(PyList_Check(excs));
- PyObject *orig = POP();
- PyObject *val = _PyExc_PrepReraiseStar(orig, excs);
- Py_DECREF(excs);
+ val = _PyExc_PrepReraiseStar(orig, excs);
Py_DECREF(orig);
+ Py_DECREF(excs);
- if (val == NULL) {
- goto error;
- }
-
- PUSH(val);
+ if (val == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, val);
DISPATCH();
}
@@ -1091,8 +1088,10 @@
}
TARGET(LOAD_ASSERTION_ERROR) {
- PyObject *value = PyExc_AssertionError;
- PUSH(Py_NewRef(value));
+ PyObject *value;
+ value = Py_NewRef(PyExc_AssertionError);
+ STACK_GROW(1);
+ POKE(1, value);
DISPATCH();
}
@@ -1106,7 +1105,7 @@
_PyErr_SetString(tstate, PyExc_NameError,
"__build_class__ not found");
}
- goto error;
+ if (true) goto error;
}
Py_INCREF(bc);
}
@@ -1116,31 +1115,32 @@
if (_PyErr_ExceptionMatches(tstate, PyExc_KeyError))
_PyErr_SetString(tstate, PyExc_NameError,
"__build_class__ not found");
- goto error;
+ if (true) goto error;
}
}
- PUSH(bc);
+ STACK_GROW(1);
+ POKE(1, bc);
DISPATCH();
}
TARGET(STORE_NAME) {
+ PyObject *v = PEEK(1);
PyObject *name = GETITEM(names, oparg);
- PyObject *v = POP();
PyObject *ns = LOCALS();
int err;
if (ns == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when storing %R", name);
Py_DECREF(v);
- goto error;
+ if (true) goto pop_1_error;
}
if (PyDict_CheckExact(ns))
err = PyDict_SetItem(ns, name, v);
else
err = PyObject_SetItem(ns, name, v);
Py_DECREF(v);
- if (err != 0)
- goto error;
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
DISPATCH();
}
@@ -1304,9 +1304,9 @@
}
TARGET(LOAD_NAME) {
+ PyObject *v;
PyObject *name = GETITEM(names, oparg);
PyObject *locals = LOCALS();
- PyObject *v;
if (locals == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals when loading %R", name);
@@ -1363,7 +1363,8 @@
}
}
}
- PUSH(v);
+ STACK_GROW(1);
+ POKE(1, v);
DISPATCH();
}
@@ -1508,7 +1509,8 @@
}
TARGET(LOAD_CLASSDEREF) {
- PyObject *name, *value, *locals = LOCALS();
+ PyObject *value;
+ PyObject *name, *locals = LOCALS();
assert(locals);
assert(oparg >= 0 && oparg < frame->f_code->co_nlocalsplus);
name = PyTuple_GET_ITEM(frame->f_code->co_localsplusnames, oparg);
@@ -1539,27 +1541,32 @@
}
Py_INCREF(value);
}
- PUSH(value);
+ STACK_GROW(1);
+ POKE(1, value);
DISPATCH();
}
TARGET(LOAD_DEREF) {
+ PyObject *value;
PyObject *cell = GETLOCAL(oparg);
- PyObject *value = PyCell_GET(cell);
+ value = PyCell_GET(cell);
if (value == NULL) {
format_exc_unbound(tstate, frame->f_code, oparg);
- goto error;
+ if (true) goto error;
}
- PUSH(Py_NewRef(value));
+ Py_INCREF(value);
+ STACK_GROW(1);
+ POKE(1, value);
DISPATCH();
}
TARGET(STORE_DEREF) {
- PyObject *v = POP();
+ PyObject *v = PEEK(1);
PyObject *cell = GETLOCAL(oparg);
PyObject *oldobj = PyCell_GET(cell);
PyCell_SET(cell, v);
Py_XDECREF(oldobj);
+ STACK_SHRINK(1);
DISPATCH();
}
@@ -1613,19 +1620,18 @@
}
TARGET(LIST_TO_TUPLE) {
- PyObject *list = POP();
- PyObject *tuple = PyList_AsTuple(list);
+ PyObject *list = PEEK(1);
+ PyObject *tuple;
+ tuple = PyList_AsTuple(list);
Py_DECREF(list);
- if (tuple == NULL) {
- goto error;
- }
- PUSH(tuple);
+ if (tuple == NULL) goto pop_1_error;
+ POKE(1, tuple);
DISPATCH();
}
TARGET(LIST_EXTEND) {
- PyObject *iterable = POP();
- PyObject *list = PEEK(oparg);
+ PyObject *iterable = PEEK(1);
+ PyObject *list = PEEK(oparg + 1); // iterable is still on the stack
PyObject *none_val = _PyList_Extend((PyListObject *)list, iterable);
if (none_val == NULL) {
if (_PyErr_ExceptionMatches(tstate, PyExc_TypeError) &&
@@ -1637,21 +1643,21 @@
Py_TYPE(iterable)->tp_name);
}
Py_DECREF(iterable);
- goto error;
+ if (true) goto pop_1_error;
}
Py_DECREF(none_val);
Py_DECREF(iterable);
+ STACK_SHRINK(1);
DISPATCH();
}
TARGET(SET_UPDATE) {
- PyObject *iterable = POP();
- PyObject *set = PEEK(oparg);
+ PyObject *iterable = PEEK(1);
+ PyObject *set = PEEK(oparg + 1); // iterable is still on the stack
int err = _PySet_Update(set, iterable);
Py_DECREF(iterable);
- if (err < 0) {
- goto error;
- }
+ if (err < 0) goto pop_1_error;
+ STACK_SHRINK(1);
DISPATCH();
}
@@ -1698,47 +1704,35 @@
if (LOCALS() == NULL) {
_PyErr_Format(tstate, PyExc_SystemError,
"no locals found when setting up annotations");
- goto error;
+ if (true) goto error;
}
/* check if __annotations__ in locals()... */
if (PyDict_CheckExact(LOCALS())) {
ann_dict = _PyDict_GetItemWithError(LOCALS(),
&_Py_ID(__annotations__));
if (ann_dict == NULL) {
- if (_PyErr_Occurred(tstate)) {
- goto error;
- }
+ if (_PyErr_Occurred(tstate)) goto error;
/* ...if not, create a new one */
ann_dict = PyDict_New();
- if (ann_dict == NULL) {
- goto error;
- }
+ if (ann_dict == NULL) goto error;
err = PyDict_SetItem(LOCALS(), &_Py_ID(__annotations__),
ann_dict);
Py_DECREF(ann_dict);
- if (err != 0) {
- goto error;
- }
+ if (err) goto error;
}
}
else {
/* do the same if locals() is not a dict */
ann_dict = PyObject_GetItem(LOCALS(), &_Py_ID(__annotations__));
if (ann_dict == NULL) {
- if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) {
- goto error;
- }
+ if (!_PyErr_ExceptionMatches(tstate, PyExc_KeyError)) goto error;
_PyErr_Clear(tstate);
ann_dict = PyDict_New();
- if (ann_dict == NULL) {
- goto error;
- }
+ if (ann_dict == NULL) goto error;
err = PyObject_SetItem(LOCALS(), &_Py_ID(__annotations__),
ann_dict);
Py_DECREF(ann_dict);
- if (err != 0) {
- goto error;
- }
+ if (err) goto error;
}
else {
Py_DECREF(ann_dict);
@@ -1772,8 +1766,8 @@
}
TARGET(DICT_UPDATE) {
- PyObject *update = POP();
- PyObject *dict = PEEK(oparg);
+ PyObject *update = PEEK(1);
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
if (PyDict_Update(dict, update) < 0) {
if (_PyErr_ExceptionMatches(tstate, PyExc_AttributeError)) {
_PyErr_Format(tstate, PyExc_TypeError,
@@ -1781,37 +1775,37 @@
Py_TYPE(update)->tp_name);
}
Py_DECREF(update);
- goto error;
+ if (true) goto pop_1_error;
}
Py_DECREF(update);
+ STACK_SHRINK(1);
DISPATCH();
}
TARGET(DICT_MERGE) {
- PyObject *update = POP();
- PyObject *dict = PEEK(oparg);
+ PyObject *update = PEEK(1);
+ PyObject *dict = PEEK(oparg + 1); // update is still on the stack
if (_PyDict_MergeEx(dict, update, 2) < 0) {
- format_kwargs_error(tstate, PEEK(2 + oparg), update);
+ format_kwargs_error(tstate, PEEK(3 + oparg), update);
Py_DECREF(update);
- goto error;
+ if (true) goto pop_1_error;
}
Py_DECREF(update);
+ STACK_SHRINK(1);
PREDICT(CALL_FUNCTION_EX);
DISPATCH();
}
TARGET(MAP_ADD) {
- PyObject *value = TOP();
- PyObject *key = SECOND();
- PyObject *map;
+ PyObject *value = PEEK(1);
+ PyObject *key = PEEK(2);
+ PyObject *dict = PEEK(oparg + 2); // key, value are still on the stack
+ assert(PyDict_CheckExact(dict));
+ /* dict[key] = value */
+ // Do not DECREF INPUTS because the function steals the references
+ if (_PyDict_SetItem_Take2((PyDictObject *)dict, key, value) != 0) goto pop_2_error;
STACK_SHRINK(2);
- map = PEEK(oparg); /* dict */
- assert(PyDict_CheckExact(map));
- /* map[key] = value */
- if (_PyDict_SetItem_Take2((PyDictObject *)map, key, value) != 0) {
- goto error;
- }
PREDICT(JUMP_BACKWARD);
DISPATCH();
}
@@ -2312,27 +2306,29 @@
}
TARGET(IS_OP) {
- PyObject *right = POP();
- PyObject *left = TOP();
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
int res = Py_Is(left, right) ^ oparg;
- PyObject *b = res ? Py_True : Py_False;
- SET_TOP(Py_NewRef(b));
Py_DECREF(left);
Py_DECREF(right);
+ b = Py_NewRef(res ? Py_True : Py_False);
+ STACK_SHRINK(1);
+ POKE(1, b);
DISPATCH();
}
TARGET(CONTAINS_OP) {
- PyObject *right = POP();
- PyObject *left = POP();
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
int res = PySequence_Contains(right, left);
Py_DECREF(left);
Py_DECREF(right);
- if (res < 0) {
- goto error;
- }
- PyObject *b = (res^oparg) ? Py_True : Py_False;
- PUSH(Py_NewRef(b));
+ if (res < 0) goto pop_2_error;
+ b = Py_NewRef((res^oparg) ? Py_True : Py_False);
+ STACK_SHRINK(1);
+ POKE(1, b);
DISPATCH();
}
@@ -2378,40 +2374,43 @@
}
TARGET(CHECK_EXC_MATCH) {
- PyObject *right = POP();
- PyObject *left = TOP();
+ PyObject *right = PEEK(1);
+ PyObject *left = PEEK(2);
+ PyObject *b;
assert(PyExceptionInstance_Check(left));
if (check_except_type_valid(tstate, right) < 0) {
Py_DECREF(right);
- goto error;
+ if (true) goto pop_1_error;
}
int res = PyErr_GivenExceptionMatches(left, right);
Py_DECREF(right);
- PUSH(Py_NewRef(res ? Py_True : Py_False));
+ b = Py_NewRef(res ? Py_True : Py_False);
+ POKE(1, b);
DISPATCH();
}
TARGET(IMPORT_NAME) {
- PyObject *name = GETITEM(names, oparg);
- PyObject *fromlist = POP();
- PyObject *level = TOP();
+ PyObject *fromlist = PEEK(1);
+ PyObject *level = PEEK(2);
PyObject *res;
+ PyObject *name = GETITEM(names, oparg);
res = import_name(tstate, frame, name, fromlist, level);
Py_DECREF(level);
Py_DECREF(fromlist);
- SET_TOP(res);
- if (res == NULL)
- goto error;
+ if (res == NULL) goto pop_2_error;
+ STACK_SHRINK(1);
+ POKE(1, res);
DISPATCH();
}
TARGET(IMPORT_STAR) {
- PyObject *from = POP(), *locals;
+ PyObject *from = PEEK(1);
+ PyObject *locals;
int err;
if (_PyFrame_FastToLocalsWithError(frame) < 0) {
Py_DECREF(from);
- goto error;
+ if (true) goto pop_1_error;
}
locals = LOCALS();
@@ -2419,24 +2418,24 @@
_PyErr_SetString(tstate, PyExc_SystemError,
"no locals found during 'import *'");
Py_DECREF(from);
- goto error;
+ if (true) goto pop_1_error;
}
err = import_all_from(tstate, locals, from);
_PyFrame_LocalsToFast(frame, 0);
Py_DECREF(from);
- if (err != 0)
- goto error;
+ if (err) goto pop_1_error;
+ STACK_SHRINK(1);
DISPATCH();
}
TARGET(IMPORT_FROM) {
- PyObject *name = GETITEM(names, oparg);
- PyObject *from = TOP();
+ PyObject *from = PEEK(1);
PyObject *res;
+ PyObject *name = GETITEM(names, oparg);
res = import_from(tstate, from, name);
- PUSH(res);
- if (res == NULL)
- goto error;
+ if (res == NULL) goto error;
+ STACK_GROW(1);
+ POKE(1, res);
DISPATCH();
}
diff --git a/Tools/cases_generator/generate_cases.py b/Tools/cases_generator/generate_cases.py
index 85a7c6098e0b..5eed74c5e147 100644
--- a/Tools/cases_generator/generate_cases.py
+++ b/Tools/cases_generator/generate_cases.py
@@ -125,6 +125,7 @@ class Instruction:
# Set later
family: parser.Family | None = None
predicted: bool = False
+ unmoved_names: frozenset[str] = frozenset()
def __init__(self, inst: parser.InstDef):
self.inst = inst
@@ -141,6 +142,13 @@ def __init__(self, inst: parser.InstDef):
effect for effect in inst.inputs if isinstance(effect, StackEffect)
]
self.output_effects = inst.outputs # For consistency/completeness
+ unmoved_names: set[str] = set()
+ for ieffect, oeffect in zip(self.input_effects, self.output_effects):
+ if ieffect.name == oeffect.name:
+ unmoved_names.add(ieffect.name)
+ else:
+ break
+ self.unmoved_names = frozenset(unmoved_names)
def write(self, out: Formatter) -> None:
"""Write one instruction, sans prologue and epilogue."""
@@ -175,12 +183,8 @@ def write(self, out: Formatter) -> None:
out.stack_adjust(diff)
# Write output stack effect assignments
- unmoved_names: set[str] = set()
- for ieffect, oeffect in zip(self.input_effects, self.output_effects):
- if ieffect.name == oeffect.name:
- unmoved_names.add(ieffect.name)
for i, oeffect in enumerate(reversed(self.output_effects), 1):
- if oeffect.name not in unmoved_names:
+ if oeffect.name not in self.unmoved_names:
dst = StackEffect(f"PEEK({i})", "")
out.assign(dst, oeffect)
@@ -235,7 +239,8 @@ def write_body(self, out: Formatter, dedent: int, cache_adjust: int = 0) -> None
elif m := re.match(r"(\s*)DECREF_INPUTS\(\);\s*$", line):
space = m.group(1)
for ieff in self.input_effects:
- out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n")
+ if ieff.name not in self.unmoved_names:
+ out.write_raw(f"{extra}{space}Py_DECREF({ieff.name});\n")
else:
out.write_raw(extra + line)
@@ -533,7 +538,7 @@ def stack_analysis(
) -> tuple[list[StackEffect], int]:
"""Analyze a super-instruction or macro.
- Print an error if there's a cache effect (which we don't support yet).
+ Ignore cache effects.
Return the list of variable names and the initial stack pointer.
"""
1
0

[3.11] gh-64490: Fix bugs in argument clinic varargs processing (GH-32092) (#100368)
by erlend-aasland Dec. 27, 2022
by erlend-aasland Dec. 27, 2022
Dec. 27, 2022
https://github.com/python/cpython/commit/a3dbd4c70e93260432fc024265fdf92229…
commit: a3dbd4c70e93260432fc024265fdf9222926fdad
branch: 3.11
author: colorfulappl <colorfulappl(a)qq.com>
committer: erlend-aasland <erlend.aasland(a)protonmail.com>
date: 2022-12-28T02:10:06+01:00
summary:
[3.11] gh-64490: Fix bugs in argument clinic varargs processing (GH-32092) (#100368)
(cherry picked from commit 0da728387c99fe6c127b070f2d250dc5bdd62ee5)
files:
A Misc/NEWS.d/next/Tools-Demos/2022-08-11-09-58-15.gh-issue-64490.PjwhM4.rst
M Lib/test/clinic.test
M Lib/test/test_clinic.py
M Modules/_testclinic.c
M Modules/clinic/_testclinic.c.h
M Python/getargs.c
M Tools/clinic/clinic.py
diff --git a/Lib/test/clinic.test b/Lib/test/clinic.test
index 991e82d4f966..217aa4c7e97a 100644
--- a/Lib/test/clinic.test
+++ b/Lib/test/clinic.test
@@ -3368,7 +3368,6 @@ test_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
static const char * const _keywords[] = {"a", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg", 0};
PyObject *argsbuf[2];
- Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;
@@ -3387,7 +3386,7 @@ exit:
static PyObject *
test_vararg_impl(PyObject *module, PyObject *a, PyObject *args)
-/*[clinic end generated code: output=a2baf8c1fade41d2 input=81d33815ad1bae6e]*/
+/*[clinic end generated code: output=ce9334333757f6ea input=81d33815ad1bae6e]*/
/*[clinic input]
test_vararg_with_default
@@ -3418,7 +3417,7 @@ test_vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nar
static const char * const _keywords[] = {"a", "b", NULL};
static _PyArg_Parser _parser = {NULL, _keywords, "test_vararg_with_default", 0};
PyObject *argsbuf[3];
- Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
+ Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *a;
PyObject *__clinic_args = NULL;
int b = 0;
@@ -3447,7 +3446,7 @@ exit:
static PyObject *
test_vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
int b)
-/*[clinic end generated code: output=3821d282c29f8616 input=6e110b54acd9b22d]*/
+/*[clinic end generated code: output=32fb19dd6bcf9185 input=6e110b54acd9b22d]*/
/*[clinic input]
test_vararg_with_only_defaults
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index 3f6cc6069669..a84d24a79844 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -730,6 +730,15 @@ def test_parameters_not_permitted_after_slash_for_now(self):
x: int
""")
+ def test_parameters_no_more_than_one_vararg(self):
+ s = self.parse_function_should_fail("""
+module foo
+foo.bar
+ *vararg1: object
+ *vararg2: object
+""")
+ self.assertEqual(s, "Error on line 0:\nToo many var args\n")
+
def test_function_not_at_column_0(self):
function = self.parse_function("""
module foo
@@ -1222,6 +1231,13 @@ def test_keyword_only_parameter(self):
ac_tester.keyword_only_parameter(1)
self.assertEqual(ac_tester.keyword_only_parameter(a=1), (1,))
+ def test_posonly_vararg(self):
+ with self.assertRaises(TypeError):
+ ac_tester.posonly_vararg()
+ self.assertEqual(ac_tester.posonly_vararg(1, 2), (1, 2, ()))
+ self.assertEqual(ac_tester.posonly_vararg(1, b=2), (1, 2, ()))
+ self.assertEqual(ac_tester.posonly_vararg(1, 2, 3, 4), (1, 2, (3, 4)))
+
def test_vararg_and_posonly(self):
with self.assertRaises(TypeError):
ac_tester.vararg_and_posonly()
@@ -1229,6 +1245,33 @@ def test_vararg_and_posonly(self):
ac_tester.vararg_and_posonly(1, b=2)
self.assertEqual(ac_tester.vararg_and_posonly(1, 2, 3, 4), (1, (2, 3, 4)))
+ def test_vararg(self):
+ with self.assertRaises(TypeError):
+ ac_tester.vararg()
+ with self.assertRaises(TypeError):
+ ac_tester.vararg(1, b=2)
+ self.assertEqual(ac_tester.vararg(1, 2, 3, 4), (1, (2, 3, 4)))
+
+ def test_vararg_with_default(self):
+ with self.assertRaises(TypeError):
+ ac_tester.vararg_with_default()
+ self.assertEqual(ac_tester.vararg_with_default(1, b=False), (1, (), False))
+ self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4), (1, (2, 3, 4), False))
+ self.assertEqual(ac_tester.vararg_with_default(1, 2, 3, 4, b=True), (1, (2, 3, 4), True))
+
+ def test_vararg_with_only_defaults(self):
+ self.assertEqual(ac_tester.vararg_with_only_defaults(), ((), None))
+ self.assertEqual(ac_tester.vararg_with_only_defaults(b=2), ((), 2))
+ self.assertEqual(ac_tester.vararg_with_only_defaults(1, b=2), ((1, ), 2))
+ self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4), ((1, 2, 3, 4), None))
+ self.assertEqual(ac_tester.vararg_with_only_defaults(1, 2, 3, 4, b=5), ((1, 2, 3, 4), 5))
+
+ def test_gh_32092_oob(self):
+ ac_tester.gh_32092_oob(1, 2, 3, 4, kw1=5, kw2=6)
+
+ def test_gh_32092_kw_pass(self):
+ ac_tester.gh_32092_kw_pass(1, 2, 3)
+
def test_gh_99233_refcount(self):
arg = '*A unique string is not referenced by anywhere else.*'
arg_refcount_origin = sys.getrefcount(arg)
@@ -1241,5 +1284,6 @@ def test_gh_99240_double_free(self):
with self.assertRaisesRegex(TypeError, expected_error):
ac_tester.gh_99240_double_free('a', '\0b')
+
if __name__ == "__main__":
unittest.main()
diff --git a/Misc/NEWS.d/next/Tools-Demos/2022-08-11-09-58-15.gh-issue-64490.PjwhM4.rst b/Misc/NEWS.d/next/Tools-Demos/2022-08-11-09-58-15.gh-issue-64490.PjwhM4.rst
new file mode 100644
index 000000000000..4a308a930605
--- /dev/null
+++ b/Misc/NEWS.d/next/Tools-Demos/2022-08-11-09-58-15.gh-issue-64490.PjwhM4.rst
@@ -0,0 +1,7 @@
+Argument Clinic varargs bugfixes
+
+* Fix out-of-bounds error in :c:func:`!_PyArg_UnpackKeywordsWithVararg`.
+* Fix incorrect check which allowed more than one varargs in clinic.py.
+* Fix miscalculation of ``noptargs`` in generated code.
+* Do not generate ``noptargs`` when there is a vararg argument and no optional argument.
+
diff --git a/Modules/_testclinic.c b/Modules/_testclinic.c
index 56eddfd6fdbf..91fdee24d328 100644
--- a/Modules/_testclinic.c
+++ b/Modules/_testclinic.c
@@ -950,6 +950,25 @@ keyword_only_parameter_impl(PyObject *module, PyObject *a)
}
+/*[clinic input]
+posonly_vararg
+
+ a: object
+ /
+ b: object
+ *args: object
+
+[clinic start generated code]*/
+
+static PyObject *
+posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b,
+ PyObject *args)
+/*[clinic end generated code: output=ee6713acda6b954e input=783427fe7ec2b67a]*/
+{
+ return pack_arguments_newref(3, a, b, args);
+}
+
+
/*[clinic input]
vararg_and_posonly
@@ -967,6 +986,100 @@ vararg_and_posonly_impl(PyObject *module, PyObject *a, PyObject *args)
}
+/*[clinic input]
+vararg
+
+ a: object
+ *args: object
+
+[clinic start generated code]*/
+
+static PyObject *
+vararg_impl(PyObject *module, PyObject *a, PyObject *args)
+/*[clinic end generated code: output=91ab7a0efc52dd5e input=02c0f772d05f591e]*/
+{
+ return pack_arguments_newref(2, a, args);
+}
+
+
+/*[clinic input]
+vararg_with_default
+
+ a: object
+ *args: object
+ b: bool = False
+
+[clinic start generated code]*/
+
+static PyObject *
+vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
+ int b)
+/*[clinic end generated code: output=182c01035958ce92 input=68cafa6a79f89e36]*/
+{
+ PyObject *obj_b = b ? Py_True : Py_False;
+ return pack_arguments_newref(3, a, args, obj_b);
+}
+
+
+/*[clinic input]
+vararg_with_only_defaults
+
+ *args: object
+ b: object = None
+
+[clinic start generated code]*/
+
+static PyObject *
+vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b)
+/*[clinic end generated code: output=c06b1826d91f2f7b input=678c069bc67550e1]*/
+{
+ return pack_arguments_newref(2, args, b);
+}
+
+
+
+/*[clinic input]
+gh_32092_oob
+
+ pos1: object
+ pos2: object
+ *varargs: object
+ kw1: object = None
+ kw2: object = None
+
+Proof-of-concept of GH-32092 OOB bug.
+
+[clinic start generated code]*/
+
+static PyObject *
+gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2,
+ PyObject *varargs, PyObject *kw1, PyObject *kw2)
+/*[clinic end generated code: output=ee259c130054653f input=46d15c881608f8ff]*/
+{
+ Py_RETURN_NONE;
+}
+
+
+/*[clinic input]
+gh_32092_kw_pass
+
+ pos: object
+ *args: object
+ kw: object = None
+
+Proof-of-concept of GH-32092 keyword args passing bug.
+
+[clinic start generated code]*/
+
+static PyObject *
+gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args,
+ PyObject *kw)
+/*[clinic end generated code: output=4a2bbe4f7c8604e9 input=5c0bd5b9079a0cce]*/
+{
+ Py_RETURN_NONE;
+}
+
+
/*[clinic input]
gh_99233_refcount
@@ -1046,7 +1159,13 @@ static PyMethodDef tester_methods[] = {
POSONLY_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
POSONLY_OPT_KEYWORDS_OPT_KWONLY_OPT_METHODDEF
KEYWORD_ONLY_PARAMETER_METHODDEF
+ POSONLY_VARARG_METHODDEF
VARARG_AND_POSONLY_METHODDEF
+ VARARG_METHODDEF
+ VARARG_WITH_DEFAULT_METHODDEF
+ VARARG_WITH_ONLY_DEFAULTS_METHODDEF
+ GH_32092_OOB_METHODDEF
+ GH_32092_KW_PASS_METHODDEF
GH_99233_REFCOUNT_METHODDEF
GH_99240_DOUBLE_FREE_METHODDEF
{NULL, NULL}
diff --git a/Modules/clinic/_testclinic.c.h b/Modules/clinic/_testclinic.c.h
index 222ea783486e..41555d801059 100644
--- a/Modules/clinic/_testclinic.c.h
+++ b/Modules/clinic/_testclinic.c.h
@@ -1952,6 +1952,43 @@ keyword_only_parameter(PyObject *module, PyObject *const *args, Py_ssize_t nargs
return return_value;
}
+PyDoc_STRVAR(posonly_vararg__doc__,
+"posonly_vararg($module, a, /, b, *args)\n"
+"--\n"
+"\n");
+
+#define POSONLY_VARARG_METHODDEF \
+ {"posonly_vararg", _PyCFunction_CAST(posonly_vararg), METH_FASTCALL|METH_KEYWORDS, posonly_vararg__doc__},
+
+static PyObject *
+posonly_vararg_impl(PyObject *module, PyObject *a, PyObject *b,
+ PyObject *args);
+
+static PyObject *
+posonly_vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"", "b", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "posonly_vararg", 0};
+ PyObject *argsbuf[3];
+ PyObject *a;
+ PyObject *b;
+ PyObject *__clinic_args = NULL;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ a = args[0];
+ b = args[1];
+ __clinic_args = args[2];
+ return_value = posonly_vararg_impl(module, a, b, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
PyDoc_STRVAR(vararg_and_posonly__doc__,
"vararg_and_posonly($module, a, /, *args)\n"
"--\n"
@@ -1985,6 +2022,219 @@ vararg_and_posonly(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
return return_value;
}
+PyDoc_STRVAR(vararg__doc__,
+"vararg($module, /, a, *args)\n"
+"--\n"
+"\n");
+
+#define VARARG_METHODDEF \
+ {"vararg", _PyCFunction_CAST(vararg), METH_FASTCALL|METH_KEYWORDS, vararg__doc__},
+
+static PyObject *
+vararg_impl(PyObject *module, PyObject *a, PyObject *args);
+
+static PyObject *
+vararg(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"a", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "vararg", 0};
+ PyObject *argsbuf[2];
+ PyObject *a;
+ PyObject *__clinic_args = NULL;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ a = args[0];
+ __clinic_args = args[1];
+ return_value = vararg_impl(module, a, __clinic_args);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(vararg_with_default__doc__,
+"vararg_with_default($module, /, a, *args, b=False)\n"
+"--\n"
+"\n");
+
+#define VARARG_WITH_DEFAULT_METHODDEF \
+ {"vararg_with_default", _PyCFunction_CAST(vararg_with_default), METH_FASTCALL|METH_KEYWORDS, vararg_with_default__doc__},
+
+static PyObject *
+vararg_with_default_impl(PyObject *module, PyObject *a, PyObject *args,
+ int b);
+
+static PyObject *
+vararg_with_default(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"a", "b", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "vararg_with_default", 0};
+ PyObject *argsbuf[3];
+ Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
+ PyObject *a;
+ PyObject *__clinic_args = NULL;
+ int b = 0;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ a = args[0];
+ __clinic_args = args[1];
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ b = PyObject_IsTrue(args[2]);
+ if (b < 0) {
+ goto exit;
+ }
+skip_optional_kwonly:
+ return_value = vararg_with_default_impl(module, a, __clinic_args, b);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(vararg_with_only_defaults__doc__,
+"vararg_with_only_defaults($module, /, *args, b=None)\n"
+"--\n"
+"\n");
+
+#define VARARG_WITH_ONLY_DEFAULTS_METHODDEF \
+ {"vararg_with_only_defaults", _PyCFunction_CAST(vararg_with_only_defaults), METH_FASTCALL|METH_KEYWORDS, vararg_with_only_defaults__doc__},
+
+static PyObject *
+vararg_with_only_defaults_impl(PyObject *module, PyObject *args, PyObject *b);
+
+static PyObject *
+vararg_with_only_defaults(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"b", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "vararg_with_only_defaults", 0};
+ PyObject *argsbuf[2];
+ Py_ssize_t noptargs = 0 + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
+ PyObject *__clinic_args = NULL;
+ PyObject *b = Py_None;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 0, 0, 0, 0, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ __clinic_args = args[0];
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ b = args[1];
+skip_optional_kwonly:
+ return_value = vararg_with_only_defaults_impl(module, __clinic_args, b);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
+PyDoc_STRVAR(gh_32092_oob__doc__,
+"gh_32092_oob($module, /, pos1, pos2, *varargs, kw1=None, kw2=None)\n"
+"--\n"
+"\n"
+"Proof-of-concept of GH-32092 OOB bug.");
+
+#define GH_32092_OOB_METHODDEF \
+ {"gh_32092_oob", _PyCFunction_CAST(gh_32092_oob), METH_FASTCALL|METH_KEYWORDS, gh_32092_oob__doc__},
+
+static PyObject *
+gh_32092_oob_impl(PyObject *module, PyObject *pos1, PyObject *pos2,
+ PyObject *varargs, PyObject *kw1, PyObject *kw2);
+
+static PyObject *
+gh_32092_oob(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"pos1", "pos2", "kw1", "kw2", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "gh_32092_oob", 0};
+ PyObject *argsbuf[5];
+ Py_ssize_t noptargs = Py_MIN(nargs, 2) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 2;
+ PyObject *pos1;
+ PyObject *pos2;
+ PyObject *varargs = NULL;
+ PyObject *kw1 = Py_None;
+ PyObject *kw2 = Py_None;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 2, 2, 0, 2, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ pos1 = args[0];
+ pos2 = args[1];
+ varargs = args[2];
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ if (args[3]) {
+ kw1 = args[3];
+ if (!--noptargs) {
+ goto skip_optional_kwonly;
+ }
+ }
+ kw2 = args[4];
+skip_optional_kwonly:
+ return_value = gh_32092_oob_impl(module, pos1, pos2, varargs, kw1, kw2);
+
+exit:
+ Py_XDECREF(varargs);
+ return return_value;
+}
+
+PyDoc_STRVAR(gh_32092_kw_pass__doc__,
+"gh_32092_kw_pass($module, /, pos, *args, kw=None)\n"
+"--\n"
+"\n"
+"Proof-of-concept of GH-32092 keyword args passing bug.");
+
+#define GH_32092_KW_PASS_METHODDEF \
+ {"gh_32092_kw_pass", _PyCFunction_CAST(gh_32092_kw_pass), METH_FASTCALL|METH_KEYWORDS, gh_32092_kw_pass__doc__},
+
+static PyObject *
+gh_32092_kw_pass_impl(PyObject *module, PyObject *pos, PyObject *args,
+ PyObject *kw);
+
+static PyObject *
+gh_32092_kw_pass(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
+{
+ PyObject *return_value = NULL;
+ static const char * const _keywords[] = {"pos", "kw", NULL};
+ static _PyArg_Parser _parser = {NULL, _keywords, "gh_32092_kw_pass", 0};
+ PyObject *argsbuf[3];
+ Py_ssize_t noptargs = Py_MIN(nargs, 1) + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
+ PyObject *pos;
+ PyObject *__clinic_args = NULL;
+ PyObject *kw = Py_None;
+
+ args = _PyArg_UnpackKeywordsWithVararg(args, nargs, NULL, kwnames, &_parser, 1, 1, 0, 1, argsbuf);
+ if (!args) {
+ goto exit;
+ }
+ pos = args[0];
+ __clinic_args = args[1];
+ if (!noptargs) {
+ goto skip_optional_kwonly;
+ }
+ kw = args[2];
+skip_optional_kwonly:
+ return_value = gh_32092_kw_pass_impl(module, pos, __clinic_args, kw);
+
+exit:
+ Py_XDECREF(__clinic_args);
+ return return_value;
+}
+
PyDoc_STRVAR(gh_99233_refcount__doc__,
"gh_99233_refcount($module, /, *args)\n"
"--\n"
@@ -2049,4 +2299,4 @@ gh_99240_double_free(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=4147b953eebc3c82 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=fe398ac790310bc4 input=a9049054013a1b77]*/
diff --git a/Python/getargs.c b/Python/getargs.c
index 9d6483f4fe0f..3105bd556c1b 100644
--- a/Python/getargs.c
+++ b/Python/getargs.c
@@ -2570,7 +2570,25 @@ _PyArg_UnpackKeywordsWithVararg(PyObject *const *args, Py_ssize_t nargs,
current_arg = NULL;
}
- buf[i + vararg + 1] = current_arg;
+ /* If an arguments is passed in as a keyword argument,
+ * it should be placed before `buf[vararg]`.
+ *
+ * For example:
+ * def f(a, /, b, *args):
+ * pass
+ * f(1, b=2)
+ *
+ * This `buf` array should be: [1, 2, NULL].
+ * In this case, nargs < vararg.
+ *
+ * Otherwise, we leave a place at `buf[vararg]` for vararg tuple
+ * so the index is `i + 1`. */
+ if (nargs < vararg) {
+ buf[i] = current_arg;
+ }
+ else {
+ buf[i + 1] = current_arg;
+ }
if (current_arg) {
--nkwargs;
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index c49805ce1a7e..a6d8b86a83ce 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -660,7 +660,7 @@ def output_templates(self, f):
vararg = NO_VARARG
pos_only = min_pos = max_pos = min_kw_only = pseudo_args = 0
for i, p in enumerate(parameters, 1):
- if p.is_keyword_only() or vararg != NO_VARARG:
+ if p.is_keyword_only():
assert not p.is_positional_only()
if not p.is_optional():
min_kw_only = i - max_pos
@@ -957,13 +957,14 @@ def parser_body(prototype, *fields, declarations=''):
parser_definition = parser_body(parser_prototype, *parser_code)
else:
- has_optional_kw = (max(pos_only, min_pos) + min_kw_only < len(converters))
+ has_optional_kw = (max(pos_only, min_pos) + min_kw_only < len(converters) - int(vararg != NO_VARARG))
if vararg == NO_VARARG:
args_declaration = "_PyArg_UnpackKeywords", "%s, %s, %s" % (
min_pos,
max_pos,
min_kw_only
)
+ nargs = "nargs"
else:
args_declaration = "_PyArg_UnpackKeywordsWithVararg", "%s, %s, %s, %s" % (
min_pos,
@@ -971,6 +972,7 @@ def parser_body(prototype, *fields, declarations=''):
min_kw_only,
vararg
)
+ nargs = f"Py_MIN(nargs, {max_pos})" if max_pos else "0"
if not new_or_init:
flags = "METH_FASTCALL|METH_KEYWORDS"
parser_prototype = parser_prototype_fastcall_keywords
@@ -981,8 +983,7 @@ def parser_body(prototype, *fields, declarations=''):
PyObject *argsbuf[%s];
""" % len(converters))
if has_optional_kw:
- pre_buffer = "0" if vararg != NO_VARARG else "nargs"
- declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (pre_buffer, min_pos + min_kw_only)
+ declarations += "\nPy_ssize_t noptargs = %s + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - %d;" % (nargs, min_pos + min_kw_only)
parser_code = [normalize_snippet("""
args = %s(args, nargs, NULL, kwnames, &_parser, %s, argsbuf);
if (!args) {{
@@ -1002,7 +1003,7 @@ def parser_body(prototype, *fields, declarations=''):
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
""" % len(converters))
if has_optional_kw:
- declarations += "\nPy_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (min_pos + min_kw_only)
+ declarations += "\nPy_ssize_t noptargs = %s + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - %d;" % (nargs, min_pos + min_kw_only)
parser_code = [normalize_snippet("""
fastargs = %s(_PyTuple_CAST(args)->ob_item, nargs, kwargs, NULL, &_parser, %s, argsbuf);
if (!fastargs) {{
1
0

Dec. 27, 2022
https://github.com/python/cpython/commit/3dc48dabd48864039951715816e07986a4…
commit: 3dc48dabd48864039951715816e07986a4828d80
branch: main
author: Nikita Sobolev <mail(a)sobolevn.me>
committer: erlend-aasland <erlend.aasland(a)protonmail.com>
date: 2022-12-28T01:58:05+01:00
summary:
gh-100553: Improve accuracy of sqlite3.Row iter test (#100555)
files:
M Lib/test/test_sqlite3/test_factory.py
diff --git a/Lib/test/test_sqlite3/test_factory.py b/Lib/test/test_sqlite3/test_factory.py
index 7fdc45ab6924..7c36135ecadc 100644
--- a/Lib/test/test_sqlite3/test_factory.py
+++ b/Lib/test/test_sqlite3/test_factory.py
@@ -179,8 +179,14 @@ def test_sqlite_row_iter(self):
"""Checks if the row object is iterable"""
self.con.row_factory = sqlite.Row
row = self.con.execute("select 1 as a, 2 as b").fetchone()
- for col in row:
- pass
+
+ # Is iterable in correct order and produces valid results:
+ items = [col for col in row]
+ self.assertEqual(items, [1, 2])
+
+ # Is iterable the second time:
+ items = [col for col in row]
+ self.assertEqual(items, [1, 2])
def test_sqlite_row_as_tuple(self):
"""Checks if the row object can be converted to a tuple"""
1
0

Dec. 27, 2022
https://github.com/python/cpython/commit/b0ea28913e3bf684ef847a71afcdfa8224…
commit: b0ea28913e3bf684ef847a71afcdfa8224bab63d
branch: main
author: Nikita Sobolev <mail(a)sobolevn.me>
committer: kumaraditya303 <59607654+kumaraditya303(a)users.noreply.github.com>
date: 2022-12-27T21:41:39+05:30
summary:
Remove `NoneType` redefinition from `clinic.py` (#100551)
files:
M Tools/clinic/clinic.py
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index fdf8041e14bb..2fb1902a5b54 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -27,7 +27,6 @@
import types
from types import *
-NoneType = type(None)
# TODO:
#
@@ -42,7 +41,6 @@
version = '1'
-NoneType = type(None)
NO_VARARG = "PY_SSIZE_T_MAX"
CLINIC_PREFIX = "__clinic_"
CLINIC_PREFIXED_ARGS = {"args"}
1
0
https://github.com/python/cpython/commit/b3da6989524001d707ebb386bfab998452…
commit: b3da6989524001d707ebb386bfab998452aa57bb
branch: main
author: Pieter Eendebak <pieter.eendebak(a)gmail.com>
committer: corona10 <donghee.na92(a)gmail.com>
date: 2022-12-27T19:55:54+09:00
summary:
gh-94603: micro optimize list.pop (gh-94604)
files:
A Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst
M Objects/listobject.c
diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst b/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst
new file mode 100644
index 000000000000..de4fe4d6df8c
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2022-07-06-18-44-00.gh-issue-94603.Q_03xV.rst
@@ -0,0 +1 @@
+Improve performance of ``list.pop`` for small lists.
diff --git a/Objects/listobject.c b/Objects/listobject.c
index 1d32915b17a1..b093f88a35fc 100644
--- a/Objects/listobject.c
+++ b/Objects/listobject.c
@@ -1022,21 +1022,29 @@ list_pop_impl(PyListObject *self, Py_ssize_t index)
PyErr_SetString(PyExc_IndexError, "pop index out of range");
return NULL;
}
- v = self->ob_item[index];
- if (index == Py_SIZE(self) - 1) {
- status = list_resize(self, Py_SIZE(self) - 1);
- if (status >= 0)
- return v; /* and v now owns the reference the list had */
- else
- return NULL;
+
+ PyObject **items = self->ob_item;
+ v = items[index];
+ const Py_ssize_t size_after_pop = Py_SIZE(self) - 1;
+ if (size_after_pop == 0) {
+ Py_INCREF(v);
+ status = _list_clear(self);
}
- Py_INCREF(v);
- status = list_ass_slice(self, index, index+1, (PyObject *)NULL);
- if (status < 0) {
- Py_DECREF(v);
+ else {
+ if ((size_after_pop - index) > 0) {
+ memmove(&items[index], &items[index+1], (size_after_pop - index) * sizeof(PyObject *));
+ }
+ status = list_resize(self, size_after_pop);
+ }
+ if (status >= 0) {
+ return v; // and v now owns the reference the list had
+ }
+ else {
+ // list resize failed, need to restore
+ memmove(&items[index+1], &items[index], (size_after_pop - index)* sizeof(PyObject *));
+ items[index] = v;
return NULL;
}
- return v;
}
/* Reverse a slice of a list in place, from lo up to (exclusive) hi. */
1
0

gh-99509: Add `__class_getitem__` to `multiprocessing.queues.Queue` (#99511)
by JelleZijlstra Dec. 26, 2022
by JelleZijlstra Dec. 26, 2022
Dec. 26, 2022
https://github.com/python/cpython/commit/ce39aaffeef9aa8af54a8554fe7a5609a6…
commit: ce39aaffeef9aa8af54a8554fe7a5609a6bba471
branch: main
author: Nikita Sobolev <mail(a)sobolevn.me>
committer: JelleZijlstra <jelle.zijlstra(a)gmail.com>
date: 2022-12-26T20:50:55-08:00
summary:
gh-99509: Add `__class_getitem__` to `multiprocessing.queues.Queue` (#99511)
files:
A Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst
M Lib/multiprocessing/queues.py
M Lib/test/test_genericalias.py
diff --git a/Lib/multiprocessing/queues.py b/Lib/multiprocessing/queues.py
index f37f114a9688..daf9ee94a194 100644
--- a/Lib/multiprocessing/queues.py
+++ b/Lib/multiprocessing/queues.py
@@ -280,6 +280,8 @@ def _on_queue_feeder_error(e, obj):
import traceback
traceback.print_exc()
+ __class_getitem__ = classmethod(types.GenericAlias)
+
_sentinel = object()
diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py
index 6d0a556b1f7f..9b59d1e3e0aa 100644
--- a/Lib/test/test_genericalias.py
+++ b/Lib/test/test_genericalias.py
@@ -31,11 +31,15 @@
from multiprocessing.managers import ValueProxy
from multiprocessing.pool import ApplyResult
from multiprocessing.queues import SimpleQueue as MPSimpleQueue
+ from multiprocessing.queues import Queue as MPQueue
+ from multiprocessing.queues import JoinableQueue as MPJoinableQueue
except ImportError:
# _multiprocessing module is optional
ValueProxy = None
ApplyResult = None
MPSimpleQueue = None
+ MPQueue = None
+ MPJoinableQueue = None
try:
from multiprocessing.shared_memory import ShareableList
except ImportError:
@@ -130,7 +134,8 @@ class BaseTest(unittest.TestCase):
if ctypes is not None:
generic_types.extend((ctypes.Array, ctypes.LibraryLoader))
if ValueProxy is not None:
- generic_types.extend((ValueProxy, ApplyResult, MPSimpleQueue))
+ generic_types.extend((ValueProxy, ApplyResult,
+ MPSimpleQueue, MPQueue, MPJoinableQueue))
def test_subscriptable(self):
for t in self.generic_types:
diff --git a/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst b/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst
new file mode 100644
index 000000000000..634281061cec
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2022-11-15-18-45-01.gh-issue-99509.FLK0xU.rst
@@ -0,0 +1 @@
+Add :pep:`585` support for :class:`multiprocessing.queues.Queue`.
1
0

[3.10] gh-99535: Add test for inheritance of annotations and update documentation (GH-99990) (#100509)
by JelleZijlstra Dec. 26, 2022
by JelleZijlstra Dec. 26, 2022
Dec. 26, 2022
https://github.com/python/cpython/commit/1ffc67265f1a622751069997e7ca8193f9…
commit: 1ffc67265f1a622751069997e7ca8193f983e1e2
branch: 3.10
author: Jelle Zijlstra <jelle.zijlstra(a)gmail.com>
committer: JelleZijlstra <jelle.zijlstra(a)gmail.com>
date: 2022-12-26T20:44:53-08:00
summary:
[3.10] gh-99535: Add test for inheritance of annotations and update documentation (GH-99990) (#100509)
(cherry picked from commit f5b7b19bf10724d831285fb04e00f763838bd555)
Co-authored-by: MonadChains <monadchains(a)gmail.com>
files:
M Doc/howto/annotations.rst
M Doc/library/typing.rst
M Lib/test/test_grammar.py
diff --git a/Doc/howto/annotations.rst b/Doc/howto/annotations.rst
index 2bc2f2d4c839..472069032d65 100644
--- a/Doc/howto/annotations.rst
+++ b/Doc/howto/annotations.rst
@@ -57,6 +57,12 @@ Accessing The Annotations Dict Of An Object In Python 3.10 And Newer
newer is to call :func:`getattr` with three arguments,
for example ``getattr(o, '__annotations__', None)``.
+ Before Python 3.10, accessing ``__annotations__`` on a class that
+ defines no annotations but that has a parent class with
+ annotations would return the parent's ``__annotations__``.
+ In Python 3.10 and newer, the child class's annotations
+ will be an empty dict instead.
+
Accessing The Annotations Dict Of An Object In Python 3.9 And Older
===================================================================
diff --git a/Doc/library/typing.rst b/Doc/library/typing.rst
index d415f149027f..d3275209e938 100644
--- a/Doc/library/typing.rst
+++ b/Doc/library/typing.rst
@@ -2233,6 +2233,10 @@ Introspection helpers
.. versionchanged:: 3.9
Added ``include_extras`` parameter as part of :pep:`593`.
+ .. versionchanged:: 3.10
+ Calling ``get_type_hints()`` on a class no longer returns the annotations
+ of its base classes.
+
.. function:: get_args(tp)
.. function:: get_origin(tp)
diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py
index 2b9f7df0bd25..27b14ab836b6 100644
--- a/Lib/test/test_grammar.py
+++ b/Lib/test/test_grammar.py
@@ -406,6 +406,28 @@ class Cbad2(C):
x: int
x.y: list = []
+ def test_annotations_inheritance(self):
+ # Check that annotations are not inherited by derived classes
+ class A:
+ attr: int
+ class B(A):
+ pass
+ class C(A):
+ attr: str
+ class D:
+ attr2: int
+ class E(A, D):
+ pass
+ class F(C, A):
+ pass
+ self.assertEqual(A.__annotations__, {"attr": int})
+ self.assertEqual(B.__annotations__, {})
+ self.assertEqual(C.__annotations__, {"attr" : str})
+ self.assertEqual(D.__annotations__, {"attr2" : int})
+ self.assertEqual(E.__annotations__, {})
+ self.assertEqual(F.__annotations__, {})
+
+
def test_var_annot_metaclass_semantics(self):
class CMeta(type):
@classmethod
1
0

[3.11] gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524) (#100533)
by kumaraditya303 Dec. 26, 2022
by kumaraditya303 Dec. 26, 2022
Dec. 26, 2022
https://github.com/python/cpython/commit/18b43cf95f2455b63a6dbefb813f48fa85…
commit: 18b43cf95f2455b63a6dbefb813f48fa85a07c7b
branch: 3.11
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: kumaraditya303 <59607654+kumaraditya303(a)users.noreply.github.com>
date: 2022-12-26T21:13:11+05:30
summary:
[3.11] gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524) (#100533)
gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524)
(cherry picked from commit 199507b81a302ea19f93593965b1e5088195a6c5)
Co-authored-by: Nikita Sobolev <mail(a)sobolevn.me>
Co-authored-by: Nikita Sobolev <mail(a)sobolevn.me>
files:
M Lib/configparser.py
diff --git a/Lib/configparser.py b/Lib/configparser.py
index de9ee537ac18..df2d7e335d9d 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -19,36 +19,37 @@
inline_comment_prefixes=None, strict=True,
empty_lines_in_values=True, default_section='DEFAULT',
interpolation=<unset>, converters=<unset>):
- Create the parser. When `defaults' is given, it is initialized into the
+
+ Create the parser. When `defaults` is given, it is initialized into the
dictionary or intrinsic defaults. The keys must be strings, the values
must be appropriate for %()s string interpolation.
- When `dict_type' is given, it will be used to create the dictionary
+ When `dict_type` is given, it will be used to create the dictionary
objects for the list of sections, for the options within a section, and
for the default values.
- When `delimiters' is given, it will be used as the set of substrings
+ When `delimiters` is given, it will be used as the set of substrings
that divide keys from values.
- When `comment_prefixes' is given, it will be used as the set of
+ When `comment_prefixes` is given, it will be used as the set of
substrings that prefix comments in empty lines. Comments can be
indented.
- When `inline_comment_prefixes' is given, it will be used as the set of
+ When `inline_comment_prefixes` is given, it will be used as the set of
substrings that prefix comments in non-empty lines.
When `strict` is True, the parser won't allow for any section or option
duplicates while reading from a single source (file, string or
dictionary). Default is True.
- When `empty_lines_in_values' is False (default: True), each empty line
+ When `empty_lines_in_values` is False (default: True), each empty line
marks the end of an option. Otherwise, internal empty lines of
a multiline option are kept as part of the value.
- When `allow_no_value' is True (default: False), options without
+ When `allow_no_value` is True (default: False), options without
values are accepted; the value presented for these is None.
- When `default_section' is given, the name of the special section is
+ When `default_section` is given, the name of the special section is
named accordingly. By default it is called ``"DEFAULT"`` but this can
be customized to point to any other valid section name. Its current
value can be retrieved using the ``parser_instance.default_section``
@@ -87,7 +88,7 @@
read_file(f, filename=None)
Read and parse one configuration file, given as a file object.
The filename defaults to f.name; it is only used in error
- messages (if f has no `name' attribute, the string `<???>' is used).
+ messages (if f has no `name` attribute, the string `<???>` is used).
read_string(string)
Read configuration from a given string.
@@ -103,9 +104,9 @@
Return a string value for the named option. All % interpolations are
expanded in the return values, based on the defaults passed into the
constructor and the DEFAULT section. Additional substitutions may be
- provided using the `vars' argument, which must be a dictionary whose
- contents override any pre-existing defaults. If `option' is a key in
- `vars', the value from `vars' is used.
+ provided using the `vars` argument, which must be a dictionary whose
+ contents override any pre-existing defaults. If `option` is a key in
+ `vars`, the value from `vars` is used.
getint(section, options, raw=False, vars=None, fallback=_UNSET)
Like get(), but convert value to an integer.
@@ -134,7 +135,7 @@
write(fp, space_around_delimiters=True)
Write the configuration state in .ini format. If
- `space_around_delimiters' is True (the default), delimiters
+ `space_around_delimiters` is True (the default), delimiters
between keys and values are surrounded by spaces.
"""
@@ -352,7 +353,7 @@ def __init__(self, filename, lineno, line):
# Used in parser getters to indicate the default behaviour when a specific
-# option is not found it to raise an exception. Created to enable `None' as
+# option is not found it to raise an exception. Created to enable `None` as
# a valid fallback value.
_UNSET = object()
@@ -386,7 +387,7 @@ class BasicInterpolation(Interpolation):
would resolve the "%(dir)s" to the value of dir. All reference
expansions are done late, on demand. If a user needs to use a bare % in
a configuration file, she can escape it by writing %%. Other % usage
- is considered a user error and raises `InterpolationSyntaxError'."""
+ is considered a user error and raises `InterpolationSyntaxError`."""
_KEYCRE = re.compile(r"%\(([^)]+)\)s")
@@ -447,7 +448,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
class ExtendedInterpolation(Interpolation):
"""Advanced variant of interpolation, supports the syntax used by
- `zc.buildout'. Enables interpolation between sections."""
+ `zc.buildout`. Enables interpolation between sections."""
_KEYCRE = re.compile(r"\$\{([^}]+)\}")
@@ -720,10 +721,10 @@ def read(self, filenames, encoding=None):
def read_file(self, f, source=None):
"""Like read() but the argument must be a file-like object.
- The `f' argument must be iterable, returning one line at a time.
- Optional second argument is the `source' specifying the name of the
- file being read. If not given, it is taken from f.name. If `f' has no
- `name' attribute, `<???>' is used.
+ The `f` argument must be iterable, returning one line at a time.
+ Optional second argument is the `source` specifying the name of the
+ file being read. If not given, it is taken from f.name. If `f` has no
+ `name` attribute, `<???>` is used.
"""
if source is None:
try:
@@ -747,7 +748,7 @@ def read_dict(self, dictionary, source='<dict>'):
All types held in the dictionary are converted to strings during
reading, including section names, option names and keys.
- Optional second argument is the `source' specifying the name of the
+ Optional second argument is the `source` specifying the name of the
dictionary being read.
"""
elements_added = set()
@@ -780,15 +781,15 @@ def readfp(self, fp, filename=None):
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
"""Get an option value for a given section.
- If `vars' is provided, it must be a dictionary. The option is looked up
- in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
- If the key is not found and `fallback' is provided, it is used as
- a fallback value. `None' can be provided as a `fallback' value.
+ If `vars` is provided, it must be a dictionary. The option is looked up
+ in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order.
+ If the key is not found and `fallback` is provided, it is used as
+ a fallback value. `None` can be provided as a `fallback` value.
- If interpolation is enabled and the optional argument `raw' is False,
+ If interpolation is enabled and the optional argument `raw` is False,
all interpolations are expanded in the return values.
- Arguments `raw', `vars', and `fallback' are keyword only.
+ Arguments `raw`, `vars`, and `fallback` are keyword only.
The section DEFAULT is special.
"""
@@ -848,8 +849,8 @@ def items(self, section=_UNSET, raw=False, vars=None):
All % interpolations are expanded in the return values, based on the
defaults passed into the constructor, unless the optional argument
- `raw' is true. Additional substitutions may be provided using the
- `vars' argument, which must be a dictionary whose contents overrides
+ `raw` is true. Additional substitutions may be provided using the
+ `vars` argument, which must be a dictionary whose contents overrides
any pre-existing defaults.
The section DEFAULT is special.
@@ -891,8 +892,8 @@ def optionxform(self, optionstr):
def has_option(self, section, option):
"""Check for the existence of a given option in a given section.
- If the specified `section' is None or an empty string, DEFAULT is
- assumed. If the specified `section' does not exist, returns False."""
+ If the specified `section` is None or an empty string, DEFAULT is
+ assumed. If the specified `section` does not exist, returns False."""
if not section or section == self.default_section:
option = self.optionxform(option)
return option in self._defaults
@@ -920,7 +921,7 @@ def set(self, section, option, value=None):
def write(self, fp, space_around_delimiters=True):
"""Write an .ini-format representation of the configuration state.
- If `space_around_delimiters' is True (the default), delimiters
+ If `space_around_delimiters` is True (the default), delimiters
between keys and values are surrounded by spaces.
Please note that comments in the original configuration file are not
@@ -938,7 +939,7 @@ def write(self, fp, space_around_delimiters=True):
self._sections[section].items(), d)
def _write_section(self, fp, section_name, section_items, delimiter):
- """Write a single section to the specified `fp'."""
+ """Write a single section to the specified `fp`."""
fp.write("[{}]\n".format(section_name))
for key, value in section_items:
value = self._interpolation.before_write(self, section_name, key,
@@ -1012,8 +1013,8 @@ def _read(self, fp, fpname):
"""Parse a sectioned configuration file.
Each section in a configuration file contains a header, indicated by
- a name in square brackets (`[]'), plus key/value options, indicated by
- `name' and `value' delimited with a specific substring (`=' or `:' by
+ a name in square brackets (`[]`), plus key/value options, indicated by
+ `name` and `value` delimited with a specific substring (`=` or `:` by
default).
Values can span multiple lines, as long as they are indented deeper
@@ -1021,7 +1022,7 @@ def _read(self, fp, fpname):
lines may be treated as parts of multiline values or ignored.
Configuration files may include comments, prefixed by specific
- characters (`#' and `;' by default). Comments may appear on their own
+ characters (`#` and `;` by default). Comments may appear on their own
in an otherwise empty line or may be entered in lines holding values or
section names. Please note that comments get stripped off when reading configuration files.
"""
1
0

[3.10] gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524) (#100534)
by kumaraditya303 Dec. 26, 2022
by kumaraditya303 Dec. 26, 2022
Dec. 26, 2022
https://github.com/python/cpython/commit/bb159b448170b24756db9ddfde0e0a7354…
commit: bb159b448170b24756db9ddfde0e0a735489fce6
branch: 3.10
author: Miss Islington (bot) <31488909+miss-islington(a)users.noreply.github.com>
committer: kumaraditya303 <59607654+kumaraditya303(a)users.noreply.github.com>
date: 2022-12-26T21:12:56+05:30
summary:
[3.10] gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524) (#100534)
gh-100520: Fix `rst` markup in `configparser` docstrings (GH-100524)
(cherry picked from commit 199507b81a302ea19f93593965b1e5088195a6c5)
Co-authored-by: Nikita Sobolev <mail(a)sobolevn.me>
Co-authored-by: Nikita Sobolev <mail(a)sobolevn.me>
files:
M Lib/configparser.py
diff --git a/Lib/configparser.py b/Lib/configparser.py
index 3470624e63f6..785527082d07 100644
--- a/Lib/configparser.py
+++ b/Lib/configparser.py
@@ -19,36 +19,37 @@
inline_comment_prefixes=None, strict=True,
empty_lines_in_values=True, default_section='DEFAULT',
interpolation=<unset>, converters=<unset>):
- Create the parser. When `defaults' is given, it is initialized into the
+
+ Create the parser. When `defaults` is given, it is initialized into the
dictionary or intrinsic defaults. The keys must be strings, the values
must be appropriate for %()s string interpolation.
- When `dict_type' is given, it will be used to create the dictionary
+ When `dict_type` is given, it will be used to create the dictionary
objects for the list of sections, for the options within a section, and
for the default values.
- When `delimiters' is given, it will be used as the set of substrings
+ When `delimiters` is given, it will be used as the set of substrings
that divide keys from values.
- When `comment_prefixes' is given, it will be used as the set of
+ When `comment_prefixes` is given, it will be used as the set of
substrings that prefix comments in empty lines. Comments can be
indented.
- When `inline_comment_prefixes' is given, it will be used as the set of
+ When `inline_comment_prefixes` is given, it will be used as the set of
substrings that prefix comments in non-empty lines.
When `strict` is True, the parser won't allow for any section or option
duplicates while reading from a single source (file, string or
dictionary). Default is True.
- When `empty_lines_in_values' is False (default: True), each empty line
+ When `empty_lines_in_values` is False (default: True), each empty line
marks the end of an option. Otherwise, internal empty lines of
a multiline option are kept as part of the value.
- When `allow_no_value' is True (default: False), options without
+ When `allow_no_value` is True (default: False), options without
values are accepted; the value presented for these is None.
- When `default_section' is given, the name of the special section is
+ When `default_section` is given, the name of the special section is
named accordingly. By default it is called ``"DEFAULT"`` but this can
be customized to point to any other valid section name. Its current
value can be retrieved using the ``parser_instance.default_section``
@@ -87,7 +88,7 @@
read_file(f, filename=None)
Read and parse one configuration file, given as a file object.
The filename defaults to f.name; it is only used in error
- messages (if f has no `name' attribute, the string `<???>' is used).
+ messages (if f has no `name` attribute, the string `<???>` is used).
read_string(string)
Read configuration from a given string.
@@ -103,9 +104,9 @@
Return a string value for the named option. All % interpolations are
expanded in the return values, based on the defaults passed into the
constructor and the DEFAULT section. Additional substitutions may be
- provided using the `vars' argument, which must be a dictionary whose
- contents override any pre-existing defaults. If `option' is a key in
- `vars', the value from `vars' is used.
+ provided using the `vars` argument, which must be a dictionary whose
+ contents override any pre-existing defaults. If `option` is a key in
+ `vars`, the value from `vars` is used.
getint(section, options, raw=False, vars=None, fallback=_UNSET)
Like get(), but convert value to an integer.
@@ -134,7 +135,7 @@
write(fp, space_around_delimiters=True)
Write the configuration state in .ini format. If
- `space_around_delimiters' is True (the default), delimiters
+ `space_around_delimiters` is True (the default), delimiters
between keys and values are surrounded by spaces.
"""
@@ -352,7 +353,7 @@ def __init__(self, filename, lineno, line):
# Used in parser getters to indicate the default behaviour when a specific
-# option is not found it to raise an exception. Created to enable `None' as
+# option is not found it to raise an exception. Created to enable `None` as
# a valid fallback value.
_UNSET = object()
@@ -386,7 +387,7 @@ class BasicInterpolation(Interpolation):
would resolve the "%(dir)s" to the value of dir. All reference
expansions are done late, on demand. If a user needs to use a bare % in
a configuration file, she can escape it by writing %%. Other % usage
- is considered a user error and raises `InterpolationSyntaxError'."""
+ is considered a user error and raises `InterpolationSyntaxError`."""
_KEYCRE = re.compile(r"%\(([^)]+)\)s")
@@ -447,7 +448,7 @@ def _interpolate_some(self, parser, option, accum, rest, section, map,
class ExtendedInterpolation(Interpolation):
"""Advanced variant of interpolation, supports the syntax used by
- `zc.buildout'. Enables interpolation between sections."""
+ `zc.buildout`. Enables interpolation between sections."""
_KEYCRE = re.compile(r"\$\{([^}]+)\}")
@@ -706,10 +707,10 @@ def read(self, filenames, encoding=None):
def read_file(self, f, source=None):
"""Like read() but the argument must be a file-like object.
- The `f' argument must be iterable, returning one line at a time.
- Optional second argument is the `source' specifying the name of the
- file being read. If not given, it is taken from f.name. If `f' has no
- `name' attribute, `<???>' is used.
+ The `f` argument must be iterable, returning one line at a time.
+ Optional second argument is the `source` specifying the name of the
+ file being read. If not given, it is taken from f.name. If `f` has no
+ `name` attribute, `<???>` is used.
"""
if source is None:
try:
@@ -733,7 +734,7 @@ def read_dict(self, dictionary, source='<dict>'):
All types held in the dictionary are converted to strings during
reading, including section names, option names and keys.
- Optional second argument is the `source' specifying the name of the
+ Optional second argument is the `source` specifying the name of the
dictionary being read.
"""
elements_added = set()
@@ -766,15 +767,15 @@ def readfp(self, fp, filename=None):
def get(self, section, option, *, raw=False, vars=None, fallback=_UNSET):
"""Get an option value for a given section.
- If `vars' is provided, it must be a dictionary. The option is looked up
- in `vars' (if provided), `section', and in `DEFAULTSECT' in that order.
- If the key is not found and `fallback' is provided, it is used as
- a fallback value. `None' can be provided as a `fallback' value.
+ If `vars` is provided, it must be a dictionary. The option is looked up
+ in `vars` (if provided), `section`, and in `DEFAULTSECT` in that order.
+ If the key is not found and `fallback` is provided, it is used as
+ a fallback value. `None` can be provided as a `fallback` value.
- If interpolation is enabled and the optional argument `raw' is False,
+ If interpolation is enabled and the optional argument `raw` is False,
all interpolations are expanded in the return values.
- Arguments `raw', `vars', and `fallback' are keyword only.
+ Arguments `raw`, `vars`, and `fallback` are keyword only.
The section DEFAULT is special.
"""
@@ -834,8 +835,8 @@ def items(self, section=_UNSET, raw=False, vars=None):
All % interpolations are expanded in the return values, based on the
defaults passed into the constructor, unless the optional argument
- `raw' is true. Additional substitutions may be provided using the
- `vars' argument, which must be a dictionary whose contents overrides
+ `raw` is true. Additional substitutions may be provided using the
+ `vars` argument, which must be a dictionary whose contents overrides
any pre-existing defaults.
The section DEFAULT is special.
@@ -877,8 +878,8 @@ def optionxform(self, optionstr):
def has_option(self, section, option):
"""Check for the existence of a given option in a given section.
- If the specified `section' is None or an empty string, DEFAULT is
- assumed. If the specified `section' does not exist, returns False."""
+ If the specified `section` is None or an empty string, DEFAULT is
+ assumed. If the specified `section` does not exist, returns False."""
if not section or section == self.default_section:
option = self.optionxform(option)
return option in self._defaults
@@ -906,7 +907,7 @@ def set(self, section, option, value=None):
def write(self, fp, space_around_delimiters=True):
"""Write an .ini-format representation of the configuration state.
- If `space_around_delimiters' is True (the default), delimiters
+ If `space_around_delimiters` is True (the default), delimiters
between keys and values are surrounded by spaces.
Please note that comments in the original configuration file are not
@@ -924,7 +925,7 @@ def write(self, fp, space_around_delimiters=True):
self._sections[section].items(), d)
def _write_section(self, fp, section_name, section_items, delimiter):
- """Write a single section to the specified `fp'."""
+ """Write a single section to the specified `fp`."""
fp.write("[{}]\n".format(section_name))
for key, value in section_items:
value = self._interpolation.before_write(self, section_name, key,
@@ -998,8 +999,8 @@ def _read(self, fp, fpname):
"""Parse a sectioned configuration file.
Each section in a configuration file contains a header, indicated by
- a name in square brackets (`[]'), plus key/value options, indicated by
- `name' and `value' delimited with a specific substring (`=' or `:' by
+ a name in square brackets (`[]`), plus key/value options, indicated by
+ `name` and `value` delimited with a specific substring (`=` or `:` by
default).
Values can span multiple lines, as long as they are indented deeper
@@ -1007,7 +1008,7 @@ def _read(self, fp, fpname):
lines may be treated as parts of multiline values or ignored.
Configuration files may include comments, prefixed by specific
- characters (`#' and `;' by default). Comments may appear on their own
+ characters (`#` and `;` by default). Comments may appear on their own
in an otherwise empty line or may be entered in lines holding values or
section names. Please note that comments get stripped off when reading configuration files.
"""
1
0