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
January 2025
- 1 participants
- 705 discussions
Jan. 31, 2025
https://github.com/python/cpython/commit/881984b41a103b4c7fac262a00cc240ae8…
commit: 881984b41a103b4c7fac262a00cc240ae8fbdba9
branch: main
author: Bénédikt Tran <10796600+picnixz(a)users.noreply.github.com>
committer: encukou <encukou(a)gmail.com>
date: 2025-01-31T14:33:30+01:00
summary:
gh-111178: fix UBSan failures in `Modules/_sqlite` (GH-129087)
* fix UBSan failures for `pysqlite_Blob`
* fix UBSan failures for `pysqlite_Connection`
* fix UBSan failures for `pysqlite_Cursor`
* fix UBSan failures for `pysqlite_PrepareProtocol`
* fix UBSan failures for `pysqlite_Row`
* fix UBSan failures for `pysqlite_Statement`
* suppress unused return values
files:
M Modules/_sqlite/blob.c
M Modules/_sqlite/connection.c
M Modules/_sqlite/cursor.c
M Modules/_sqlite/module.c
M Modules/_sqlite/prepare_protocol.c
M Modules/_sqlite/row.c
M Modules/_sqlite/statement.c
diff --git a/Modules/_sqlite/blob.c b/Modules/_sqlite/blob.c
index d1a549a971c24a..390375628bfb4f 100644
--- a/Modules/_sqlite/blob.c
+++ b/Modules/_sqlite/blob.c
@@ -9,6 +9,8 @@
#include "clinic/blob.c.h"
#undef clinic_state
+#define _pysqlite_Blob_CAST(op) ((pysqlite_Blob *)(op))
+
/*[clinic input]
module _sqlite3
class _sqlite3.Blob "pysqlite_Blob *" "clinic_state()->BlobType"
@@ -29,32 +31,35 @@ close_blob(pysqlite_Blob *self)
}
static int
-blob_traverse(pysqlite_Blob *self, visitproc visit, void *arg)
+blob_traverse(PyObject *op, visitproc visit, void *arg)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->connection);
return 0;
}
static int
-blob_clear(pysqlite_Blob *self)
+blob_clear(PyObject *op)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
Py_CLEAR(self->connection);
return 0;
}
static void
-blob_dealloc(pysqlite_Blob *self)
+blob_dealloc(PyObject *op)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
close_blob(self);
if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject*)self);
+ PyObject_ClearWeakRefs(op);
}
- tp->tp_clear((PyObject *)self);
+ (void)tp->tp_clear(op);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -373,8 +378,9 @@ blob_exit_impl(pysqlite_Blob *self, PyObject *type, PyObject *val,
}
static Py_ssize_t
-blob_length(pysqlite_Blob *self)
+blob_length(PyObject *op)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
if (!check_blob(self)) {
return -1;
}
@@ -449,8 +455,9 @@ subscript_slice(pysqlite_Blob *self, PyObject *item)
}
static PyObject *
-blob_subscript(pysqlite_Blob *self, PyObject *item)
+blob_subscript(PyObject *op, PyObject *item)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
if (!check_blob(self)) {
return NULL;
}
@@ -546,8 +553,9 @@ ass_subscript_slice(pysqlite_Blob *self, PyObject *item, PyObject *value)
}
static int
-blob_ass_subscript(pysqlite_Blob *self, PyObject *item, PyObject *value)
+blob_ass_subscript(PyObject *op, PyObject *item, PyObject *value)
{
+ pysqlite_Blob *self = _pysqlite_Blob_CAST(op);
if (!check_blob(self)) {
return -1;
}
diff --git a/Modules/_sqlite/connection.c b/Modules/_sqlite/connection.c
index 0c98f5065ee303..80021ccad4629e 100644
--- a/Modules/_sqlite/connection.c
+++ b/Modules/_sqlite/connection.c
@@ -135,6 +135,8 @@ sqlite3_int64_converter(PyObject *obj, sqlite3_int64 *result)
#include "clinic/connection.c.h"
#undef clinic_state
+#define _pysqlite_Connection_CAST(op) ((pysqlite_Connection *)(op))
+
/*[clinic input]
module _sqlite3
class _sqlite3.Connection "pysqlite_Connection *" "clinic_state()->ConnectionType"
@@ -384,8 +386,9 @@ do { \
} while (0)
static int
-connection_traverse(pysqlite_Connection *self, visitproc visit, void *arg)
+connection_traverse(PyObject *op, visitproc visit, void *arg)
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->statement_cache);
Py_VISIT(self->cursors);
@@ -409,8 +412,9 @@ clear_callback_context(callback_context *ctx)
}
static int
-connection_clear(pysqlite_Connection *self)
+connection_clear(PyObject *op)
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
Py_CLEAR(self->statement_cache);
Py_CLEAR(self->cursors);
Py_CLEAR(self->blobs);
@@ -517,7 +521,7 @@ connection_dealloc(PyObject *self)
}
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
- tp->tp_clear(self);
+ (void)tp->tp_clear(self);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -1715,8 +1719,10 @@ int pysqlite_check_thread(pysqlite_Connection* self)
return 1;
}
-static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* self, void* unused)
+static PyObject *
+pysqlite_connection_get_isolation_level(PyObject *op, void *Py_UNUSED(closure))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_connection(self)) {
return NULL;
}
@@ -1726,16 +1732,20 @@ static PyObject* pysqlite_connection_get_isolation_level(pysqlite_Connection* se
Py_RETURN_NONE;
}
-static PyObject* pysqlite_connection_get_total_changes(pysqlite_Connection* self, void* unused)
+static PyObject *
+pysqlite_connection_get_total_changes(PyObject *op, void *Py_UNUSED(closure))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_connection(self)) {
return NULL;
}
return PyLong_FromLong(sqlite3_total_changes(self->db));
}
-static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* self, void* unused)
+static PyObject *
+pysqlite_connection_get_in_transaction(PyObject *op, void *Py_UNUSED(closure))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_connection(self)) {
return NULL;
}
@@ -1746,8 +1756,11 @@ static PyObject* pysqlite_connection_get_in_transaction(pysqlite_Connection* sel
}
static int
-pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* isolation_level, void *Py_UNUSED(ignored))
+pysqlite_connection_set_isolation_level(PyObject *op,
+ PyObject *isolation_level,
+ void *Py_UNUSED(ignored))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (isolation_level == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
@@ -1770,11 +1783,11 @@ pysqlite_connection_set_isolation_level(pysqlite_Connection* self, PyObject* iso
}
static PyObject *
-pysqlite_connection_call(pysqlite_Connection *self, PyObject *args,
- PyObject *kwargs)
+pysqlite_connection_call(PyObject *op, PyObject *args, PyObject *kwargs)
{
PyObject* sql;
pysqlite_Statement* statement;
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return NULL;
@@ -2525,8 +2538,9 @@ getconfig_impl(pysqlite_Connection *self, int op)
}
static PyObject *
-get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
+get_autocommit(PyObject *op, void *Py_UNUSED(closure))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return NULL;
}
@@ -2540,8 +2554,9 @@ get_autocommit(pysqlite_Connection *self, void *Py_UNUSED(ctx))
}
static int
-set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx))
+set_autocommit(PyObject *op, PyObject *val, void *Py_UNUSED(closure))
{
+ pysqlite_Connection *self = _pysqlite_Connection_CAST(op);
if (!pysqlite_check_thread(self) || !pysqlite_check_connection(self)) {
return -1;
}
@@ -2566,7 +2581,7 @@ set_autocommit(pysqlite_Connection *self, PyObject *val, void *Py_UNUSED(ctx))
}
static PyObject *
-get_sig(PyObject *self, void *Py_UNUSED(ctx))
+get_sig(PyObject *Py_UNUSED(self), void *Py_UNUSED(closure))
{
return PyUnicode_FromString("(sql, /)");
}
@@ -2576,11 +2591,12 @@ static const char connection_doc[] =
PyDoc_STR("SQLite database connection object.");
static PyGetSetDef connection_getset[] = {
- {"isolation_level", (getter)pysqlite_connection_get_isolation_level, (setter)pysqlite_connection_set_isolation_level},
- {"total_changes", (getter)pysqlite_connection_get_total_changes, (setter)0},
- {"in_transaction", (getter)pysqlite_connection_get_in_transaction, (setter)0},
- {"autocommit", (getter)get_autocommit, (setter)set_autocommit},
- {"__text_signature__", get_sig, (setter)0},
+ {"isolation_level", pysqlite_connection_get_isolation_level,
+ pysqlite_connection_set_isolation_level},
+ {"total_changes", pysqlite_connection_get_total_changes, NULL},
+ {"in_transaction", pysqlite_connection_get_in_transaction, NULL},
+ {"autocommit", get_autocommit, set_autocommit},
+ {"__text_signature__", get_sig, NULL},
{NULL}
};
diff --git a/Modules/_sqlite/cursor.c b/Modules/_sqlite/cursor.c
index 24e97fcf1897e9..02d598040775b0 100644
--- a/Modules/_sqlite/cursor.c
+++ b/Modules/_sqlite/cursor.c
@@ -44,6 +44,8 @@ typedef enum {
#include "clinic/cursor.c.h"
#undef clinic_state
+#define _pysqlite_Cursor_CAST(op) ((pysqlite_Cursor *)(op))
+
static inline int
check_cursor_locked(pysqlite_Cursor *cur)
{
@@ -146,8 +148,9 @@ stmt_reset(pysqlite_Statement *self)
}
static int
-cursor_traverse(pysqlite_Cursor *self, visitproc visit, void *arg)
+cursor_traverse(PyObject *op, visitproc visit, void *arg)
{
+ pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->connection);
Py_VISIT(self->description);
@@ -159,8 +162,9 @@ cursor_traverse(pysqlite_Cursor *self, visitproc visit, void *arg)
}
static int
-cursor_clear(pysqlite_Cursor *self)
+cursor_clear(PyObject *op)
{
+ pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
Py_CLEAR(self->connection);
Py_CLEAR(self->description);
Py_CLEAR(self->row_cast_map);
@@ -176,14 +180,15 @@ cursor_clear(pysqlite_Cursor *self)
}
static void
-cursor_dealloc(pysqlite_Cursor *self)
+cursor_dealloc(PyObject *op)
{
+ pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
if (self->in_weakreflist != NULL) {
- PyObject_ClearWeakRefs((PyObject*)self);
+ PyObject_ClearWeakRefs(op);
}
- tp->tp_clear((PyObject *)self);
+ (void)tp->tp_clear(op);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -1087,8 +1092,9 @@ pysqlite_cursor_executescript_impl(pysqlite_Cursor *self,
}
static PyObject *
-pysqlite_cursor_iternext(pysqlite_Cursor *self)
+pysqlite_cursor_iternext(PyObject *op)
{
+ pysqlite_Cursor *self = _pysqlite_Cursor_CAST(op);
if (!check_cursor(self)) {
return NULL;
}
@@ -1125,7 +1131,7 @@ pysqlite_cursor_iternext(pysqlite_Cursor *self)
}
if (!Py_IsNone(self->row_factory)) {
PyObject *factory = self->row_factory;
- PyObject *args[] = { (PyObject *)self, row, };
+ PyObject *args[] = { op, row, };
PyObject *new_row = PyObject_Vectorcall(factory, args, 2, NULL);
Py_SETREF(row, new_row);
}
@@ -1144,7 +1150,7 @@ pysqlite_cursor_fetchone_impl(pysqlite_Cursor *self)
{
PyObject* row;
- row = pysqlite_cursor_iternext(self);
+ row = pysqlite_cursor_iternext((PyObject *)self);
if (!row && !PyErr_Occurred()) {
Py_RETURN_NONE;
}
@@ -1174,7 +1180,7 @@ pysqlite_cursor_fetchmany_impl(pysqlite_Cursor *self, int maxrows)
return NULL;
}
- while ((row = pysqlite_cursor_iternext(self))) {
+ while ((row = pysqlite_cursor_iternext((PyObject *)self))) {
if (PyList_Append(list, row) < 0) {
Py_DECREF(row);
break;
@@ -1212,7 +1218,7 @@ pysqlite_cursor_fetchall_impl(pysqlite_Cursor *self)
return NULL;
}
- while ((row = pysqlite_cursor_iternext(self))) {
+ while ((row = pysqlite_cursor_iternext((PyObject *)self))) {
if (PyList_Append(list, row) < 0) {
Py_DECREF(row);
break;
diff --git a/Modules/_sqlite/module.c b/Modules/_sqlite/module.c
index 73d55fb44e2e15..27e8dab92e0e67 100644
--- a/Modules/_sqlite/module.c
+++ b/Modules/_sqlite/module.c
@@ -617,7 +617,7 @@ module_clear(PyObject *module)
static void
module_free(void *module)
{
- module_clear((PyObject *)module);
+ (void)module_clear((PyObject *)module);
}
#define ADD_TYPE(module, type) \
diff --git a/Modules/_sqlite/prepare_protocol.c b/Modules/_sqlite/prepare_protocol.c
index 44533225665dab..31092417cb480d 100644
--- a/Modules/_sqlite/prepare_protocol.c
+++ b/Modules/_sqlite/prepare_protocol.c
@@ -24,8 +24,7 @@
#include "prepare_protocol.h"
static int
-pysqlite_prepare_protocol_init(pysqlite_PrepareProtocol *self, PyObject *args,
- PyObject *kwargs)
+pysqlite_prepare_protocol_init(PyObject *self, PyObject *args, PyObject *kwargs)
{
return 0;
}
@@ -38,7 +37,7 @@ pysqlite_prepare_protocol_traverse(PyObject *self, visitproc visit, void *arg)
}
static void
-pysqlite_prepare_protocol_dealloc(pysqlite_PrepareProtocol *self)
+pysqlite_prepare_protocol_dealloc(PyObject *self)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
diff --git a/Modules/_sqlite/row.c b/Modules/_sqlite/row.c
index 14555076a7e79a..79660008b180dc 100644
--- a/Modules/_sqlite/row.c
+++ b/Modules/_sqlite/row.c
@@ -32,6 +32,8 @@
#include "clinic/row.c.h"
#undef clinic_state
+#define _pysqlite_Row_CAST(op) ((pysqlite_Row *)(op))
+
/*[clinic input]
module _sqlite3
class _sqlite3.Row "pysqlite_Row *" "clinic_state()->RowType"
@@ -39,16 +41,18 @@ class _sqlite3.Row "pysqlite_Row *" "clinic_state()->RowType"
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=966c53403d7f3a40]*/
static int
-row_clear(pysqlite_Row *self)
+row_clear(PyObject *op)
{
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
Py_CLEAR(self->data);
Py_CLEAR(self->description);
return 0;
}
static int
-row_traverse(pysqlite_Row *self, visitproc visit, void *arg)
+row_traverse(PyObject *op, visitproc visit, void *arg)
{
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
Py_VISIT(Py_TYPE(self));
Py_VISIT(self->data);
Py_VISIT(self->description);
@@ -60,7 +64,7 @@ pysqlite_row_dealloc(PyObject *self)
{
PyTypeObject *tp = Py_TYPE(self);
PyObject_GC_UnTrack(self);
- tp->tp_clear(self);
+ (void)tp->tp_clear(self);
tp->tp_free(self);
Py_DECREF(tp);
}
@@ -94,10 +98,12 @@ pysqlite_row_new_impl(PyTypeObject *type, pysqlite_Cursor *cursor,
return (PyObject *) self;
}
-PyObject* pysqlite_row_item(pysqlite_Row* self, Py_ssize_t idx)
+static PyObject *
+pysqlite_row_item(PyObject *op, Py_ssize_t idx)
{
- PyObject *item = PyTuple_GetItem(self->data, idx);
- return Py_XNewRef(item);
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
+ PyObject *item = PyTuple_GetItem(self->data, idx);
+ return Py_XNewRef(item);
}
static int
@@ -129,10 +135,11 @@ equal_ignore_case(PyObject *left, PyObject *right)
}
static PyObject *
-pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx)
+pysqlite_row_subscript(PyObject *op, PyObject *idx)
{
Py_ssize_t _idx;
Py_ssize_t nitems, i;
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
if (PyLong_Check(idx)) {
_idx = PyNumber_AsSsize_t(idx, PyExc_IndexError);
@@ -174,8 +181,9 @@ pysqlite_row_subscript(pysqlite_Row *self, PyObject *idx)
}
static Py_ssize_t
-pysqlite_row_length(pysqlite_Row* self)
+pysqlite_row_length(PyObject *op)
{
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
return PyTuple_GET_SIZE(self->data);
}
@@ -208,24 +216,30 @@ pysqlite_row_keys_impl(pysqlite_Row *self)
return list;
}
-static PyObject* pysqlite_iter(pysqlite_Row* self)
+static PyObject *
+pysqlite_iter(PyObject *op)
{
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
return PyObject_GetIter(self->data);
}
-static Py_hash_t pysqlite_row_hash(pysqlite_Row *self)
+static Py_hash_t
+pysqlite_row_hash(PyObject *op)
{
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
return PyObject_Hash(self->description) ^ PyObject_Hash(self->data);
}
-static PyObject* pysqlite_row_richcompare(pysqlite_Row *self, PyObject *_other, int opid)
+static PyObject *
+pysqlite_row_richcompare(PyObject *op, PyObject *opother, int opid)
{
if (opid != Py_EQ && opid != Py_NE)
Py_RETURN_NOTIMPLEMENTED;
+ pysqlite_Row *self = _pysqlite_Row_CAST(op);
pysqlite_state *state = pysqlite_get_state_by_type(Py_TYPE(self));
- if (PyObject_TypeCheck(_other, state->RowType)) {
- pysqlite_Row *other = (pysqlite_Row *)_other;
+ if (PyObject_TypeCheck(opother, state->RowType)) {
+ pysqlite_Row *other = (pysqlite_Row *)opother;
int eq = PyObject_RichCompareBool(self->description, other->description, Py_EQ);
if (eq < 0) {
return NULL;
diff --git a/Modules/_sqlite/statement.c b/Modules/_sqlite/statement.c
index 229bfc3b504165..facced0dfbfafd 100644
--- a/Modules/_sqlite/statement.c
+++ b/Modules/_sqlite/statement.c
@@ -25,6 +25,8 @@
#include "statement.h"
#include "util.h"
+#define _pysqlite_Statement_CAST(op) ((pysqlite_Statement *)(op))
+
/* prototypes */
static const char *lstrip_sql(const char *sql);
@@ -99,10 +101,11 @@ pysqlite_statement_create(pysqlite_Connection *connection, PyObject *sql)
}
static void
-stmt_dealloc(pysqlite_Statement *self)
+stmt_dealloc(PyObject *op)
{
+ pysqlite_Statement *self = _pysqlite_Statement_CAST(op);
PyTypeObject *tp = Py_TYPE(self);
- PyObject_GC_UnTrack(self);
+ PyObject_GC_UnTrack(op);
if (self->st) {
Py_BEGIN_ALLOW_THREADS
sqlite3_finalize(self->st);
@@ -114,7 +117,7 @@ stmt_dealloc(pysqlite_Statement *self)
}
static int
-stmt_traverse(pysqlite_Statement *self, visitproc visit, void *arg)
+stmt_traverse(PyObject *self, visitproc visit, void *arg)
{
Py_VISIT(Py_TYPE(self));
return 0;
1
0
Jan. 31, 2025
https://github.com/python/cpython/commit/9d63ae5fe52d95059ab1bcd4cbb1f9e170…
commit: 9d63ae5fe52d95059ab1bcd4cbb1f9e17033c897
branch: main
author: Serhiy Storchaka <storchaka(a)gmail.com>
committer: serhiy-storchaka <storchaka(a)gmail.com>
date: 2025-01-31T15:30:19+02:00
summary:
gh-129502: Fix handling errors in ctypes callbacks (GH-129504)
Unlikely errors in preparing arguments for ctypes callback are now
handled in the same way as errors raised in the callback of in converting
the result of the callback -- using sys.unraisablehook() instead of
sys.excepthook() and not setting sys.last_exc and other variables.
files:
A Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst
M Modules/_ctypes/callbacks.c
diff --git a/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst
new file mode 100644
index 00000000000000..e9e9d12c11d0ac
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2025-01-31-11-14-05.gh-issue-129502.j_ArNo.rst
@@ -0,0 +1,5 @@
+Unlikely errors in preparing arguments for :mod:`ctypes` callback are now
+handled in the same way as errors raised in the callback of in converting
+the result of the callback -- using :func:`sys.unraisablehook` instead of
+:func:`sys.excepthook` and not setting :data:`sys.last_exc` and other
+variables.
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index 11f49963a54c54..b84bd25af8ec2c 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -81,22 +81,6 @@ PyType_Spec cthunk_spec = {
/**************************************************************/
-static void
-PrintError(const char *msg, ...)
-{
- char buf[512];
- PyObject *f = PySys_GetObject("stderr");
- va_list marker;
-
- va_start(marker, msg);
- PyOS_vsnprintf(buf, sizeof(buf), msg, marker);
- va_end(marker);
- if (f != NULL && f != Py_None)
- PyFile_WriteString(buf, f);
- PyErr_Print();
-}
-
-
#ifdef MS_WIN32
/*
* We must call AddRef() on non-NULL COM pointers we receive as arguments
@@ -108,26 +92,23 @@ PrintError(const char *msg, ...)
* after checking for PyObject_IsTrue(), but this would probably be somewhat
* slower.
*/
-static void
+static int
TryAddRef(PyObject *cnv, CDataObject *obj)
{
IUnknown *punk;
PyObject *attrdict = _PyType_GetDict((PyTypeObject *)cnv);
if (!attrdict) {
- return;
+ return 0;
}
int r = PyDict_Contains(attrdict, &_Py_ID(_needs_com_addref_));
if (r <= 0) {
- if (r < 0) {
- PrintError("getting _needs_com_addref_");
- }
- return;
+ return r;
}
punk = *(IUnknown **)obj->b_ptr;
if (punk)
punk->lpVtbl->AddRef(punk);
- return;
+ return 0;
}
#endif
@@ -162,14 +143,13 @@ static void _CallPythonObject(ctypes_state *st,
StgInfo *info;
if (PyStgInfo_FromType(st, cnv, &info) < 0) {
- goto Done;
+ goto Error;
}
if (info && info->getfunc && !_ctypes_simple_instance(st, cnv)) {
PyObject *v = info->getfunc(*pArgs, info->size);
if (!v) {
- PrintError("create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
args[i] = v;
/* XXX XXX XX
@@ -182,24 +162,25 @@ static void _CallPythonObject(ctypes_state *st,
/* Hm, shouldn't we use PyCData_AtAddress() or something like that instead? */
CDataObject *obj = (CDataObject *)_PyObject_CallNoArgs(cnv);
if (!obj) {
- PrintError("create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
if (!CDataObject_Check(st, obj)) {
+ PyErr_Format(PyExc_TypeError,
+ "%R returned unexpected result of type %T", cnv, obj);
Py_DECREF(obj);
- PrintError("unexpected result of create argument %zd:\n", i);
- goto Done;
+ goto Error;
}
memcpy(obj->b_ptr, *pArgs, info->size);
args[i] = (PyObject *)obj;
#ifdef MS_WIN32
- TryAddRef(cnv, obj);
+ if (TryAddRef(cnv, obj) < 0) {
+ goto Error;
+ }
#endif
} else {
- PyErr_SetString(PyExc_TypeError,
- "cannot build parameter");
- PrintError("Parsing argument %zd\n", i);
- goto Done;
+ PyErr_Format(PyExc_TypeError,
+ "cannot build parameter of type %R", cnv);
+ goto Error;
}
/* XXX error handling! */
pArgs++;
@@ -207,8 +188,13 @@ static void _CallPythonObject(ctypes_state *st,
if (flags & (FUNCFLAG_USE_ERRNO | FUNCFLAG_USE_LASTERROR)) {
error_object = _ctypes_get_errobj(st, &space);
- if (error_object == NULL)
+ if (error_object == NULL) {
+ PyErr_FormatUnraisable(
+ "Exception ignored while setting error for "
+ "ctypes callback function %R",
+ callable);
goto Done;
+ }
if (flags & FUNCFLAG_USE_ERRNO) {
int temp = space[0];
space[0] = errno;
@@ -295,6 +281,14 @@ static void _CallPythonObject(ctypes_state *st,
for (j = 0; j < i; j++) {
Py_DECREF(args[j]);
}
+ return;
+
+ Error:
+ PyErr_FormatUnraisable(
+ "Exception ignored while creating argument %zd for "
+ "ctypes callback function %R",
+ i, callable);
+ goto Done;
}
static void closure_fcn(ffi_cif *cif,
1
0
https://github.com/python/cpython/commit/3447f4a56a71a4017e55d8f46160a63f11…
commit: 3447f4a56a71a4017e55d8f46160a63f111ec373
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T14:20:35+01:00
summary:
gh-129354: Use PyErr_FormatUnraisable() function (#129514)
Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().
files:
M Modules/_asynciomodule.c
M Modules/_testcapi/gc.c
M Modules/posixmodule.c
M Modules/pyexpat.c
M Python/gc.c
M Python/gc_free_threading.c
diff --git a/Modules/_asynciomodule.c b/Modules/_asynciomodule.c
index d5d49658555f1a..b488fd92aa6817 100644
--- a/Modules/_asynciomodule.c
+++ b/Modules/_asynciomodule.c
@@ -1715,7 +1715,8 @@ FutureObj_finalize(FutureObj *fut)
if (func != NULL) {
PyObject *res = PyObject_CallOneArg(func, context);
if (res == NULL) {
- PyErr_WriteUnraisable(func);
+ PyErr_FormatUnraisable("Exception ignored while calling asyncio "
+ "function %R", func);
}
else {
Py_DECREF(res);
@@ -2978,7 +2979,8 @@ TaskObj_finalize(TaskObj *task)
if (func != NULL) {
PyObject *res = PyObject_CallOneArg(func, context);
if (res == NULL) {
- PyErr_WriteUnraisable(func);
+ PyErr_FormatUnraisable("Exception ignored while calling asyncio "
+ "function %R", func);
}
else {
Py_DECREF(res);
diff --git a/Modules/_testcapi/gc.c b/Modules/_testcapi/gc.c
index 7e33e0d4861e84..3691796302e500 100644
--- a/Modules/_testcapi/gc.c
+++ b/Modules/_testcapi/gc.c
@@ -94,7 +94,7 @@ slot_tp_del(PyObject *self)
PyObject *tp_del = PyUnicode_InternFromString("__tp_del__");
if (tp_del == NULL) {
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored while deallocating");
PyErr_SetRaisedException(exc);
return;
}
@@ -104,10 +104,13 @@ slot_tp_del(PyObject *self)
if (del != NULL) {
res = PyObject_CallOneArg(del, self);
Py_DECREF(del);
- if (res == NULL)
- PyErr_WriteUnraisable(del);
- else
+ if (res == NULL) {
+ PyErr_FormatUnraisable("Exception ignored while calling "
+ "deallocator %R", del);
+ }
+ else {
Py_DECREF(res);
+ }
}
/* Restore the saved exception. */
diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 6d9b365ea9ceb2..b3b5572e1cfa30 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -606,8 +606,10 @@ run_at_forkers(PyObject *lst, int reverse)
* one of the callbacks.
*/
cpy = PyList_GetSlice(lst, 0, PyList_GET_SIZE(lst));
- if (cpy == NULL)
- PyErr_WriteUnraisable(lst);
+ if (cpy == NULL) {
+ PyErr_FormatUnraisable("Exception ignored in atfork callback "
+ "while copying list %R", lst);
+ }
else {
if (reverse)
PyList_Reverse(cpy);
@@ -615,10 +617,13 @@ run_at_forkers(PyObject *lst, int reverse)
PyObject *func, *res;
func = PyList_GET_ITEM(cpy, i);
res = _PyObject_CallNoArgs(func);
- if (res == NULL)
- PyErr_WriteUnraisable(func);
- else
+ if (res == NULL) {
+ PyErr_FormatUnraisable("Exception ignored "
+ "in atfork callback %R", func);
+ }
+ else {
Py_DECREF(res);
+ }
}
Py_DECREF(cpy);
}
@@ -16330,7 +16335,8 @@ ScandirIterator_finalize(ScandirIterator *iterator)
"unclosed scandir iterator %R", iterator)) {
/* Spurious errors can appear at shutdown */
if (PyErr_ExceptionMatches(PyExc_Warning)) {
- PyErr_WriteUnraisable((PyObject *) iterator);
+ PyErr_FormatUnraisable("Exception ignored while finalizing "
+ "scandir iterator %R", iterator);
}
}
}
diff --git a/Modules/pyexpat.c b/Modules/pyexpat.c
index 9931ca2a8d4749..3290706f143b9a 100644
--- a/Modules/pyexpat.c
+++ b/Modules/pyexpat.c
@@ -1948,7 +1948,8 @@ pyexpat_capsule_destructor(PyObject *capsule)
{
void *p = PyCapsule_GetPointer(capsule, PyExpat_CAPSULE_NAME);
if (p == NULL) {
- PyErr_WriteUnraisable(capsule);
+ PyErr_FormatUnraisable("Exception ignored while destroying "
+ "pyexact capsule");
return;
}
PyMem_Free(p);
diff --git a/Python/gc.c b/Python/gc.c
index 420240fc3020be..0fb2f03b0406ad 100644
--- a/Python/gc.c
+++ b/Python/gc.c
@@ -994,7 +994,8 @@ handle_weakrefs(PyGC_Head *unreachable, PyGC_Head *old)
/* copy-paste of weakrefobject.c's handle_callback() */
temp = PyObject_CallOneArg(callback, (PyObject *)wr);
if (temp == NULL) {
- PyErr_WriteUnraisable(callback);
+ PyErr_FormatUnraisable("Exception ignored on "
+ "calling weakref callback %R", callback);
}
else {
Py_DECREF(temp);
@@ -1797,7 +1798,8 @@ do_gc_callback(GCState *gcstate, const char *phase,
Py_INCREF(cb); /* make sure cb doesn't go away */
r = PyObject_Vectorcall(cb, stack, 2, NULL);
if (r == NULL) {
- PyErr_WriteUnraisable(cb);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling GC callback %R", cb);
}
else {
Py_DECREF(r);
@@ -2086,13 +2088,14 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp)
"gc", NULL, message,
PyList_GET_SIZE(gcstate->garbage)))
{
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored in GC shutdown");
}
if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) {
PyObject *repr = NULL, *bytes = NULL;
repr = PyObject_Repr(gcstate->garbage);
if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) {
- PyErr_WriteUnraisable(gcstate->garbage);
+ PyErr_FormatUnraisable("Exception ignored in GC shutdown "
+ "while formatting garbage");
}
else {
PySys_WriteStderr(
@@ -2344,9 +2347,12 @@ PyObject_GC_Del(void *op)
#ifdef Py_DEBUG
PyObject *exc = PyErr_GetRaisedException();
if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0,
- "gc", NULL, "Object of type %s is not untracked before destruction",
- Py_TYPE(op)->tp_name)) {
- PyErr_WriteUnraisable(NULL);
+ "gc", NULL,
+ "Object of type %s is not untracked "
+ "before destruction",
+ Py_TYPE(op)->tp_name))
+ {
+ PyErr_FormatUnraisable("Exception ignored on object deallocation");
}
PyErr_SetRaisedException(exc);
#endif
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 905a14f660ec6c..5d264e407e1cc8 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -1128,7 +1128,8 @@ call_weakref_callbacks(struct collection_state *state)
/* copy-paste of weakrefobject.c's handle_callback() */
PyObject *temp = PyObject_CallOneArg(callback, (PyObject *)wr);
if (temp == NULL) {
- PyErr_WriteUnraisable(callback);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling weakref callback %R", callback);
}
else {
Py_DECREF(temp);
@@ -1447,7 +1448,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
Py_INCREF(cb); /* make sure cb doesn't go away */
r = PyObject_Vectorcall(cb, stack, 2, NULL);
if (r == NULL) {
- PyErr_WriteUnraisable(cb);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling GC callback %R", cb);
}
else {
Py_DECREF(r);
@@ -2029,13 +2031,14 @@ _PyGC_DumpShutdownStats(PyInterpreterState *interp)
"gc", NULL, message,
PyList_GET_SIZE(gcstate->garbage)))
{
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored in GC shutdown");
}
if (gcstate->debug & _PyGC_DEBUG_UNCOLLECTABLE) {
PyObject *repr = NULL, *bytes = NULL;
repr = PyObject_Repr(gcstate->garbage);
if (!repr || !(bytes = PyUnicode_EncodeFSDefault(repr))) {
- PyErr_WriteUnraisable(gcstate->garbage);
+ PyErr_FormatUnraisable("Exception ignored in GC shutdown "
+ "while formatting garbage");
}
else {
PySys_WriteStderr(
@@ -2243,9 +2246,12 @@ PyObject_GC_Del(void *op)
#ifdef Py_DEBUG
PyObject *exc = PyErr_GetRaisedException();
if (PyErr_WarnExplicitFormat(PyExc_ResourceWarning, "gc", 0,
- "gc", NULL, "Object of type %s is not untracked before destruction",
- ((PyObject*)op)->ob_type->tp_name)) {
- PyErr_WriteUnraisable(NULL);
+ "gc", NULL,
+ "Object of type %s is not untracked "
+ "before destruction",
+ Py_TYPE(op)->tp_name))
+ {
+ PyErr_FormatUnraisable("Exception ignored on object deallocation");
}
PyErr_SetRaisedException(exc);
#endif
1
0
https://github.com/python/cpython/commit/8b70ff587220ebaefae9b6283b4993c78a…
commit: 8b70ff587220ebaefae9b6283b4993c78a726324
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T13:51:58+01:00
summary:
gh-93649: Move PyFrame C API tests to test_capi (#129512)
* Add Lib/test/test_capi/test_frame.py file.
* Move C API tests from test_frame to test_capi.test_frame.
* Add Modules/_testcapi/frame.c file.
* Move C API tests from _testcapimodule.c to frame.c
files:
A Lib/test/test_capi/test_frame.py
A Modules/_testcapi/frame.c
M Lib/test/test_frame.py
M Modules/Setup.stdlib.in
M Modules/_testcapi/parts.h
M Modules/_testcapimodule.c
M PCbuild/_testcapi.vcxproj
M PCbuild/_testcapi.vcxproj.filters
diff --git a/Lib/test/test_capi/test_frame.py b/Lib/test/test_capi/test_frame.py
new file mode 100644
index 00000000000000..23cb8e3dada9d4
--- /dev/null
+++ b/Lib/test/test_capi/test_frame.py
@@ -0,0 +1,56 @@
+import sys
+import unittest
+from test.support import import_helper
+
+
+_testcapi = import_helper.import_module('_testcapi')
+
+
+class FrameTest(unittest.TestCase):
+ def getframe(self):
+ return sys._getframe()
+
+ def test_frame_getters(self):
+ frame = self.getframe()
+ self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
+ self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
+ self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
+ self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
+
+ def test_getvar(self):
+ current_frame = sys._getframe()
+ x = 1
+ self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
+ self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
+ with self.assertRaises(NameError):
+ _testcapi.frame_getvar(current_frame, "y")
+ with self.assertRaises(NameError):
+ _testcapi.frame_getvarstring(current_frame, b"y")
+
+ # wrong name type
+ with self.assertRaises(TypeError):
+ _testcapi.frame_getvar(current_frame, b'x')
+ with self.assertRaises(TypeError):
+ _testcapi.frame_getvar(current_frame, 123)
+
+ def getgenframe(self):
+ yield sys._getframe()
+
+ def test_frame_get_generator(self):
+ gen = self.getgenframe()
+ frame = next(gen)
+ self.assertIs(gen, _testcapi.frame_getgenerator(frame))
+
+ def test_frame_fback_api(self):
+ """Test that accessing `f_back` does not cause a segmentation fault on
+ a frame created with `PyFrame_New` (GH-99110)."""
+ def dummy():
+ pass
+
+ frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
+ # The following line should not cause a segmentation fault.
+ self.assertIsNone(frame.f_back)
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_frame.py b/Lib/test/test_frame.py
index 7bd13eada8fedf..4d086064023488 100644
--- a/Lib/test/test_frame.py
+++ b/Lib/test/test_frame.py
@@ -773,51 +773,6 @@ def f():
self.assertIs(catcher.unraisable.exc_type, TypeError)
self.assertIsNone(weak())
-(a)unittest.skipIf(_testcapi is None, 'need _testcapi')
-class TestCAPI(unittest.TestCase):
- def getframe(self):
- return sys._getframe()
-
- def test_frame_getters(self):
- frame = self.getframe()
- self.assertEqual(frame.f_locals, _testcapi.frame_getlocals(frame))
- self.assertIs(frame.f_globals, _testcapi.frame_getglobals(frame))
- self.assertIs(frame.f_builtins, _testcapi.frame_getbuiltins(frame))
- self.assertEqual(frame.f_lasti, _testcapi.frame_getlasti(frame))
-
- def test_getvar(self):
- current_frame = sys._getframe()
- x = 1
- self.assertEqual(_testcapi.frame_getvar(current_frame, "x"), 1)
- self.assertEqual(_testcapi.frame_getvarstring(current_frame, b"x"), 1)
- with self.assertRaises(NameError):
- _testcapi.frame_getvar(current_frame, "y")
- with self.assertRaises(NameError):
- _testcapi.frame_getvarstring(current_frame, b"y")
-
- # wrong name type
- with self.assertRaises(TypeError):
- _testcapi.frame_getvar(current_frame, b'x')
- with self.assertRaises(TypeError):
- _testcapi.frame_getvar(current_frame, 123)
-
- def getgenframe(self):
- yield sys._getframe()
-
- def test_frame_get_generator(self):
- gen = self.getgenframe()
- frame = next(gen)
- self.assertIs(gen, _testcapi.frame_getgenerator(frame))
-
- def test_frame_fback_api(self):
- """Test that accessing `f_back` does not cause a segmentation fault on
- a frame created with `PyFrame_New` (GH-99110)."""
- def dummy():
- pass
-
- frame = _testcapi.frame_new(dummy.__code__, globals(), locals())
- # The following line should not cause a segmentation fault.
- self.assertIsNone(frame.f_back)
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index b31bf2734e4fd1..1fd7c8bd2e2d73 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -162,7 +162,7 @@
@MODULE__XXTESTFUZZ_TRUE@_xxtestfuzz _xxtestfuzz/_xxtestfuzz.c _xxtestfuzz/fuzzer.c
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
-@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c
+@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c _testcapi/frame.c
@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
diff --git a/Modules/_testcapi/frame.c b/Modules/_testcapi/frame.c
new file mode 100644
index 00000000000000..5748dca948ea94
--- /dev/null
+++ b/Modules/_testcapi/frame.c
@@ -0,0 +1,134 @@
+#include "parts.h"
+#include "util.h"
+
+#include "frameobject.h" // PyFrame_New()
+
+
+static PyObject *
+frame_getlocals(PyObject *self, PyObject *frame)
+{
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+ return PyFrame_GetLocals((PyFrameObject *)frame);
+}
+
+
+static PyObject *
+frame_getglobals(PyObject *self, PyObject *frame)
+{
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+ return PyFrame_GetGlobals((PyFrameObject *)frame);
+}
+
+
+static PyObject *
+frame_getgenerator(PyObject *self, PyObject *frame)
+{
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+ return PyFrame_GetGenerator((PyFrameObject *)frame);
+}
+
+
+static PyObject *
+frame_getbuiltins(PyObject *self, PyObject *frame)
+{
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+ return PyFrame_GetBuiltins((PyFrameObject *)frame);
+}
+
+
+static PyObject *
+frame_getlasti(PyObject *self, PyObject *frame)
+{
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+ int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
+ if (lasti < 0) {
+ assert(lasti == -1);
+ Py_RETURN_NONE;
+ }
+ return PyLong_FromLong(lasti);
+}
+
+
+static PyObject *
+frame_new(PyObject *self, PyObject *args)
+{
+ PyObject *code, *globals, *locals;
+ if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
+ return NULL;
+ }
+ if (!PyCode_Check(code)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a code object");
+ return NULL;
+ }
+ PyThreadState *tstate = PyThreadState_Get();
+
+ return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
+}
+
+
+static PyObject *
+frame_getvar(PyObject *self, PyObject *args)
+{
+ PyObject *frame, *name;
+ if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
+ return NULL;
+ }
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+
+ return PyFrame_GetVar((PyFrameObject *)frame, name);
+}
+
+
+static PyObject *
+frame_getvarstring(PyObject *self, PyObject *args)
+{
+ PyObject *frame;
+ const char *name;
+ if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
+ return NULL;
+ }
+ if (!PyFrame_Check(frame)) {
+ PyErr_SetString(PyExc_TypeError, "argument must be a frame");
+ return NULL;
+ }
+
+ return PyFrame_GetVarString((PyFrameObject *)frame, name);
+}
+
+
+static PyMethodDef test_methods[] = {
+ {"frame_getlocals", frame_getlocals, METH_O, NULL},
+ {"frame_getglobals", frame_getglobals, METH_O, NULL},
+ {"frame_getgenerator", frame_getgenerator, METH_O, NULL},
+ {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
+ {"frame_getlasti", frame_getlasti, METH_O, NULL},
+ {"frame_new", frame_new, METH_VARARGS, NULL},
+ {"frame_getvar", frame_getvar, METH_VARARGS, NULL},
+ {"frame_getvarstring", frame_getvarstring, METH_VARARGS, NULL},
+ {NULL},
+};
+
+int
+_PyTestCapi_Init_Frame(PyObject *m)
+{
+ return PyModule_AddFunctions(m, test_methods);
+}
+
diff --git a/Modules/_testcapi/parts.h b/Modules/_testcapi/parts.h
index 792552d8097312..e5d2502edf5362 100644
--- a/Modules/_testcapi/parts.h
+++ b/Modules/_testcapi/parts.h
@@ -62,5 +62,6 @@ int _PyTestCapi_Init_Monitoring(PyObject *module);
int _PyTestCapi_Init_Object(PyObject *module);
int _PyTestCapi_Init_Config(PyObject *mod);
int _PyTestCapi_Init_Import(PyObject *mod);
+int _PyTestCapi_Init_Frame(PyObject *mod);
#endif // Py_TESTCAPI_PARTS_H
diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c
index 4da23ba82d5756..d25e61dbc3d588 100644
--- a/Modules/_testcapimodule.c
+++ b/Modules/_testcapimodule.c
@@ -2533,109 +2533,6 @@ test_tstate_capi(PyObject *self, PyObject *Py_UNUSED(args))
Py_RETURN_NONE;
}
-static PyObject *
-frame_getlocals(PyObject *self, PyObject *frame)
-{
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
- return PyFrame_GetLocals((PyFrameObject *)frame);
-}
-
-static PyObject *
-frame_getglobals(PyObject *self, PyObject *frame)
-{
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
- return PyFrame_GetGlobals((PyFrameObject *)frame);
-}
-
-static PyObject *
-frame_getgenerator(PyObject *self, PyObject *frame)
-{
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
- return PyFrame_GetGenerator((PyFrameObject *)frame);
-}
-
-static PyObject *
-frame_getbuiltins(PyObject *self, PyObject *frame)
-{
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
- return PyFrame_GetBuiltins((PyFrameObject *)frame);
-}
-
-static PyObject *
-frame_getlasti(PyObject *self, PyObject *frame)
-{
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
- int lasti = PyFrame_GetLasti((PyFrameObject *)frame);
- if (lasti < 0) {
- assert(lasti == -1);
- Py_RETURN_NONE;
- }
- return PyLong_FromLong(lasti);
-}
-
-static PyObject *
-frame_new(PyObject *self, PyObject *args)
-{
- PyObject *code, *globals, *locals;
- if (!PyArg_ParseTuple(args, "OOO", &code, &globals, &locals)) {
- return NULL;
- }
- if (!PyCode_Check(code)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a code object");
- return NULL;
- }
- PyThreadState *tstate = PyThreadState_Get();
-
- return (PyObject *)PyFrame_New(tstate, (PyCodeObject *)code, globals, locals);
-}
-
-static PyObject *
-test_frame_getvar(PyObject *self, PyObject *args)
-{
- PyObject *frame, *name;
- if (!PyArg_ParseTuple(args, "OO", &frame, &name)) {
- return NULL;
- }
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
-
- return PyFrame_GetVar((PyFrameObject *)frame, name);
-}
-
-static PyObject *
-test_frame_getvarstring(PyObject *self, PyObject *args)
-{
- PyObject *frame;
- const char *name;
- if (!PyArg_ParseTuple(args, "Oy", &frame, &name)) {
- return NULL;
- }
- if (!PyFrame_Check(frame)) {
- PyErr_SetString(PyExc_TypeError, "argument must be a frame");
- return NULL;
- }
-
- return PyFrame_GetVarString((PyFrameObject *)frame, name);
-}
-
-
static PyObject *
gen_get_code(PyObject *self, PyObject *gen)
{
@@ -3599,14 +3496,6 @@ static PyMethodDef TestMethods[] = {
{"type_get_tp_mro", type_get_tp_mro, METH_O},
{"get_basic_static_type", get_basic_static_type, METH_VARARGS, NULL},
{"test_tstate_capi", test_tstate_capi, METH_NOARGS, NULL},
- {"frame_getlocals", frame_getlocals, METH_O, NULL},
- {"frame_getglobals", frame_getglobals, METH_O, NULL},
- {"frame_getgenerator", frame_getgenerator, METH_O, NULL},
- {"frame_getbuiltins", frame_getbuiltins, METH_O, NULL},
- {"frame_getlasti", frame_getlasti, METH_O, NULL},
- {"frame_new", frame_new, METH_VARARGS, NULL},
- {"frame_getvar", test_frame_getvar, METH_VARARGS, NULL},
- {"frame_getvarstring", test_frame_getvarstring, METH_VARARGS, NULL},
{"gen_get_code", gen_get_code, METH_O, NULL},
{"get_feature_macros", get_feature_macros, METH_NOARGS, NULL},
{"test_code_api", test_code_api, METH_NOARGS, NULL},
@@ -4404,6 +4293,9 @@ PyInit__testcapi(void)
if (_PyTestCapi_Init_Import(m) < 0) {
return NULL;
}
+ if (_PyTestCapi_Init_Frame(m) < 0) {
+ return NULL;
+ }
PyState_AddModule(m, &_testcapimodule);
return m;
diff --git a/PCbuild/_testcapi.vcxproj b/PCbuild/_testcapi.vcxproj
index 733bb69acb16e2..e94b4c46e6dbb8 100644
--- a/PCbuild/_testcapi.vcxproj
+++ b/PCbuild/_testcapi.vcxproj
@@ -128,6 +128,7 @@
<ClCompile Include="..\Modules\_testcapi\monitoring.c" />
<ClCompile Include="..\Modules\_testcapi\config.c" />
<ClCompile Include="..\Modules\_testcapi\import.c" />
+ <ClCompile Include="..\Modules\_testcapi\frame.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testcapi.vcxproj.filters b/PCbuild/_testcapi.vcxproj.filters
index e8ddd537674a9c..782f91afb1996f 100644
--- a/PCbuild/_testcapi.vcxproj.filters
+++ b/PCbuild/_testcapi.vcxproj.filters
@@ -117,6 +117,9 @@
<ClCompile Include="..\Modules\_testcapi\import.c">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\Modules\_testcapi\frame.c">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc">
1
0
gh-71494: string.Formatter: support keys/attributes in unnumbered fields (GH-21767)
by encukou Jan. 31, 2025
by encukou Jan. 31, 2025
Jan. 31, 2025
https://github.com/python/cpython/commit/22b2d37f4211f51a3c90680edeb4c10261…
commit: 22b2d37f4211f51a3c90680edeb4c10261f58158
branch: main
author: qm2k <qm2k(a)yandex.ru>
committer: encukou <encukou(a)gmail.com>
date: 2025-01-31T13:16:24+01:00
summary:
gh-71494: string.Formatter: support keys/attributes in unnumbered fields (GH-21767)
files:
A Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
M Lib/string.py
M Lib/test/test_string.py
diff --git a/Lib/string.py b/Lib/string.py
index 2eab6d4f595c4e..c4f05c7223ce8a 100644
--- a/Lib/string.py
+++ b/Lib/string.py
@@ -212,19 +212,20 @@ def _vformat(self, format_string, args, kwargs, used_args, recursion_depth,
# this is some markup, find the object and do
# the formatting
- # handle arg indexing when empty field_names are given.
- if field_name == '':
+ # handle arg indexing when empty field first parts are given.
+ field_first, _ = _string.formatter_field_name_split(field_name)
+ if field_first == '':
if auto_arg_index is False:
raise ValueError('cannot switch from manual field '
'specification to automatic field '
'numbering')
- field_name = str(auto_arg_index)
+ field_name = str(auto_arg_index) + field_name
auto_arg_index += 1
- elif field_name.isdigit():
+ elif isinstance(field_first, int):
if auto_arg_index:
- raise ValueError('cannot switch from manual field '
- 'specification to automatic field '
- 'numbering')
+ raise ValueError('cannot switch from automatic field '
+ 'numbering to manual field '
+ 'specification')
# disable auto arg incrementing, if it gets
# used later on, then an exception will be raised
auto_arg_index = False
diff --git a/Lib/test/test_string.py b/Lib/test/test_string.py
index 824b89ad517c12..f6d112d8a93ec4 100644
--- a/Lib/test/test_string.py
+++ b/Lib/test/test_string.py
@@ -1,6 +1,7 @@
import unittest
import string
from string import Template
+import types
class ModuleTest(unittest.TestCase):
@@ -101,6 +102,24 @@ def test_index_lookup(self):
with self.assertRaises(KeyError):
fmt.format("{0[2]}{0[0]}", {})
+ def test_auto_numbering_lookup(self):
+ fmt = string.Formatter()
+ namespace = types.SimpleNamespace(foo=types.SimpleNamespace(bar='baz'))
+ widths = [None, types.SimpleNamespace(qux=4)]
+ self.assertEqual(
+ fmt.format("{.foo.bar:{[1].qux}}", namespace, widths), 'baz ')
+
+ def test_auto_numbering_reenterability(self):
+ class ReenteringFormatter(string.Formatter):
+ def format_field(self, value, format_spec):
+ if format_spec.isdigit() and int(format_spec) > 0:
+ return self.format('{:{}}!', value, int(format_spec) - 1)
+ else:
+ return super().format_field(value, format_spec)
+ fmt = ReenteringFormatter()
+ x = types.SimpleNamespace(a='X')
+ self.assertEqual(fmt.format('{.a:{}}', x, 3), 'X!!!')
+
def test_override_get_value(self):
class NamespaceFormatter(string.Formatter):
def __init__(self, namespace={}):
diff --git a/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
new file mode 100644
index 00000000000000..6e7a856d994cb6
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2020-08-07-16-55-57.bpo-27307.Xqzzda.rst
@@ -0,0 +1 @@
+Add attribute and item access support to :class:`string.Formatter` in auto-numbering mode, which allows format strings like '{.name}' and '{[1]}'.
1
0
https://github.com/python/cpython/commit/0373926260b2b96b025019b5ddcdd44238…
commit: 0373926260b2b96b025019b5ddcdd4423858e41f
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T13:16:08+01:00
summary:
gh-129354: Use PyErr_FormatUnraisable() function (#129511)
Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().
files:
M Modules/_ctypes/_ctypes.c
M Modules/_ctypes/callbacks.c
M Modules/_lsprof.c
M Objects/unicodeobject.c
M Objects/weakrefobject.c
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index 606b4636380f11..7c0ac1a57f534c 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -464,7 +464,8 @@ CType_Type_traverse(PyObject *self, visitproc visit, void *arg)
{
StgInfo *info = _PyStgInfo_FromType_NoState(self);
if (!info) {
- PyErr_WriteUnraisable(self);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling ctypes traverse function %R", self);
}
if (info) {
Py_VISIT(info->proto);
@@ -495,7 +496,8 @@ CType_Type_clear(PyObject *self)
{
StgInfo *info = _PyStgInfo_FromType_NoState(self);
if (!info) {
- PyErr_WriteUnraisable(self);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing ctypes %R", self);
}
if (info) {
ctype_clear_stginfo(info);
@@ -508,7 +510,8 @@ CType_Type_dealloc(PyObject *self)
{
StgInfo *info = _PyStgInfo_FromType_NoState(self);
if (!info) {
- PyErr_WriteUnraisable(NULL); // NULL avoids segfault here
+ PyErr_FormatUnraisable("Exception ignored while "
+ "deallocating ctypes %R", self);
}
if (info) {
PyMem_Free(info->ffi_type_pointer.elements);
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index f2df7052d84fb1..11f49963a54c54 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -487,39 +487,31 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
{
PyObject *func, *result;
long retval;
- static PyObject *context;
-
- if (context == NULL)
- context = PyUnicode_InternFromString("_ctypes.DllGetClassObject");
func = PyImport_ImportModuleAttrString("ctypes", "DllGetClassObject");
if (!func) {
- PyErr_WriteUnraisable(context ? context : Py_None);
/* There has been a warning before about this already */
- return E_FAIL;
+ goto error;
}
{
PyObject *py_rclsid = PyLong_FromVoidPtr((void *)rclsid);
if (py_rclsid == NULL) {
Py_DECREF(func);
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
PyObject *py_riid = PyLong_FromVoidPtr((void *)riid);
if (py_riid == NULL) {
Py_DECREF(func);
Py_DECREF(py_rclsid);
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
PyObject *py_ppv = PyLong_FromVoidPtr(ppv);
if (py_ppv == NULL) {
Py_DECREF(py_rclsid);
Py_DECREF(py_riid);
Py_DECREF(func);
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
result = PyObject_CallFunctionObjArgs(func,
py_rclsid,
@@ -532,17 +524,21 @@ long Call_GetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv)
}
Py_DECREF(func);
if (!result) {
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
retval = PyLong_AsLong(result);
if (PyErr_Occurred()) {
- PyErr_WriteUnraisable(context ? context : Py_None);
- retval = E_FAIL;
+ Py_DECREF(result);
+ goto error;
}
Py_DECREF(result);
return retval;
+
+error:
+ PyErr_FormatUnraisable("Exception ignored while calling "
+ "ctypes.DllGetClassObject");
+ return E_FAIL;
}
STDAPI DllGetClassObject(REFCLSID rclsid,
@@ -563,10 +559,6 @@ long Call_CanUnloadNow(void)
{
PyObject *mod, *func, *result;
long retval;
- static PyObject *context;
-
- if (context == NULL)
- context = PyUnicode_InternFromString("_ctypes.DllCanUnloadNow");
mod = PyImport_ImportModule("ctypes");
if (!mod) {
@@ -580,24 +572,27 @@ long Call_CanUnloadNow(void)
func = PyObject_GetAttrString(mod, "DllCanUnloadNow");
Py_DECREF(mod);
if (!func) {
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
result = _PyObject_CallNoArgs(func);
Py_DECREF(func);
if (!result) {
- PyErr_WriteUnraisable(context ? context : Py_None);
- return E_FAIL;
+ goto error;
}
retval = PyLong_AsLong(result);
if (PyErr_Occurred()) {
- PyErr_WriteUnraisable(context ? context : Py_None);
- retval = E_FAIL;
+ Py_DECREF(result);
+ goto error;
}
Py_DECREF(result);
return retval;
+
+error:
+ PyErr_FormatUnraisable("Exception ignored while calling "
+ "ctypes.DllCanUnloadNow");
+ return E_FAIL;
}
/*
diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c
index fa1d333ecf3829..eab26b39be14ef 100644
--- a/Modules/_lsprof.c
+++ b/Modules/_lsprof.c
@@ -97,7 +97,8 @@ static PyTime_t CallExternalTimer(ProfilerObject *pObj)
pObj->flags &= ~POF_EXT_TIMER;
if (o == NULL) {
- PyErr_WriteUnraisable(pObj->externalTimer);
+ PyErr_FormatUnraisable("Exception ignored while calling "
+ "_lsprof timer %R", pObj->externalTimer);
return 0;
}
@@ -116,7 +117,8 @@ static PyTime_t CallExternalTimer(ProfilerObject *pObj)
}
Py_DECREF(o);
if (err < 0) {
- PyErr_WriteUnraisable(pObj->externalTimer);
+ PyErr_FormatUnraisable("Exception ignored while calling "
+ "_lsprof timer %R", pObj->externalTimer);
return 0;
}
return result;
diff --git a/Objects/unicodeobject.c b/Objects/unicodeobject.c
index c6f13f60ad741f..75967d69ed374d 100644
--- a/Objects/unicodeobject.c
+++ b/Objects/unicodeobject.c
@@ -1735,7 +1735,9 @@ unicode_dealloc(PyObject *unicode)
PyObject *popped;
int r = PyDict_Pop(interned, unicode, &popped);
if (r == -1) {
- PyErr_WriteUnraisable(unicode);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "removing an interned string %R",
+ unicode);
// We don't know what happened to the string. It's probably
// best to leak it:
// - if it was popped, there are no more references to it
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 05ae43d1df475b..bd4c4ac9b3475a 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -987,10 +987,13 @@ handle_callback(PyWeakReference *ref, PyObject *callback)
{
PyObject *cbresult = PyObject_CallOneArg(callback, (PyObject *)ref);
- if (cbresult == NULL)
- PyErr_WriteUnraisable(callback);
- else
+ if (cbresult == NULL) {
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling weakref callback %R", callback);
+ }
+ else {
Py_DECREF(cbresult);
+ }
}
/* This function is called by the tp_dealloc handler to clear weak references.
1
0
GH-128563: Simplify recursion check in `_PyEval_EvalFrameDefault` (GH-129481)
by markshannon Jan. 31, 2025
by markshannon Jan. 31, 2025
Jan. 31, 2025
https://github.com/python/cpython/commit/c3ae5c9e4ad121f8ba60ffe81ca4e2a9c5…
commit: c3ae5c9e4ad121f8ba60ffe81ca4e2a9c52dc659
branch: main
author: Mark Shannon <mark(a)hotpy.org>
committer: markshannon <mark(a)hotpy.org>
date: 2025-01-31T12:12:24Z
summary:
GH-128563: Simplify recursion check in `_PyEval_EvalFrameDefault` (GH-129481)
Simplify recursion check in _PyEval_EvalFrameDefault
files:
M Python/ceval.c
diff --git a/Python/ceval.c b/Python/ceval.c
index 10c20faf852479..e3b87441f8088d 100644
--- a/Python/ceval.c
+++ b/Python/ceval.c
@@ -786,7 +786,11 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
_PyInterpreterFrame entry_frame;
-
+ if (_Py_EnterRecursiveCallTstate(tstate, "")) {
+ assert(frame->owner != FRAME_OWNED_BY_INTERPRETER);
+ _PyEval_FrameClearAndPop(tstate, frame);
+ return NULL;
+ }
#if defined(Py_DEBUG) && !defined(Py_STACKREF_DEBUG)
/* Set these to invalid but identifiable values for debugging. */
@@ -811,11 +815,6 @@ _PyEval_EvalFrameDefault(PyThreadState *tstate, _PyInterpreterFrame *frame, int
tstate->current_frame = frame;
tstate->c_recursion_remaining -= (PY_EVAL_C_STACK_UNITS - 1);
- if (_Py_EnterRecursiveCallTstate(tstate, "")) {
- tstate->c_recursion_remaining--;
- tstate->py_recursion_remaining--;
- goto exit_unwind;
- }
/* support for generator.throw() */
if (throwflag) {
1
0
GH-128469: Revert "warn when libpython was loaded from outside the build directory (#128645)" (#129506)
by FFY00 Jan. 31, 2025
by FFY00 Jan. 31, 2025
Jan. 31, 2025
https://github.com/python/cpython/commit/31c82c28f927b7e55c7dfdd548322c6c36…
commit: 31c82c28f927b7e55c7dfdd548322c6c36760278
branch: main
author: Petr Viktorin <encukou(a)gmail.com>
committer: FFY00 <filipe.lains(a)gmail.com>
date: 2025-01-31T11:17:37Z
summary:
GH-128469: Revert "warn when libpython was loaded from outside the build directory (#128645)" (#129506)
files:
M Lib/test/test_getpath.py
M Modules/getpath.py
diff --git a/Lib/test/test_getpath.py b/Lib/test/test_getpath.py
index ca7cee0c39872a..f86df9d0d03485 100644
--- a/Lib/test/test_getpath.py
+++ b/Lib/test/test_getpath.py
@@ -1,13 +1,7 @@
import copy
import ntpath
-import os
import pathlib
import posixpath
-import shutil
-import subprocess
-import sys
-import sysconfig
-import tempfile
import unittest
from test.support import verbose
@@ -870,37 +864,6 @@ def test_PYTHONHOME_in_venv(self):
actual = getpath(ns, expected)
self.assertEqual(expected, actual)
-
-class RealGetPathTests(unittest.TestCase):
- @unittest.skipUnless(
- sysconfig.is_python_build(),
- 'Test only available when running from the buildir',
- )
- @unittest.skipUnless(
- any(sys.platform.startswith(p) for p in ('linux', 'freebsd', 'centos')),
- 'Test only support on Linux-like OS-es (support LD_LIBRARY_PATH)',
- )
- @unittest.skipUnless(
- sysconfig.get_config_var('LDLIBRARY') != sysconfig.get_config_var('LIBRARY'),
- 'Test only available when using a dynamic libpython',
- )
- def test_builddir_wrong_library_warning(self):
- library_name = sysconfig.get_config_var('INSTSONAME')
- with tempfile.TemporaryDirectory() as tmpdir:
- shutil.copy2(
- os.path.join(sysconfig.get_config_var('srcdir'), library_name),
- os.path.join(tmpdir, library_name)
- )
- env = os.environ.copy()
- env['LD_LIBRARY_PATH'] = tmpdir
- process = subprocess.run(
- [sys.executable, '-c', ''],
- env=env, check=True, capture_output=True, text=True,
- )
- error_msg = 'The runtime library has been loaded from outside the build directory'
- self.assertTrue(process.stderr.startswith(error_msg), process.stderr)
-
-
# ******************************************************************************
DEFAULT_NAMESPACE = dict(
diff --git a/Modules/getpath.py b/Modules/getpath.py
index 9d531e29becbc8..be2210345afbda 100644
--- a/Modules/getpath.py
+++ b/Modules/getpath.py
@@ -785,19 +785,6 @@ def search_up(prefix, *landmarks, test=isfile):
base_exec_prefix = config.get('base_exec_prefix') or EXEC_PREFIX or base_prefix
-# ******************************************************************************
-# MISC. RUNTIME WARNINGS
-# ******************************************************************************
-
-# When running Python from the build directory, if libpython is dynamically
-# linked, the wrong library might be loaded.
-if build_prefix and library and not dirname(abspath(library)).startswith(build_prefix):
- msg = f'The runtime library has been loaded from outside the build directory ({library})!'
- if os_name == 'posix':
- msg += ' Consider setting LD_LIBRARY_PATH=. to force it to be loaded from the build directory.'
- warn(msg)
-
-
# ******************************************************************************
# SET pythonpath FROM _PTH FILE
# ******************************************************************************
1
0
https://github.com/python/cpython/commit/674befbd7be3bffee66772ff9fdef168fe…
commit: 674befbd7be3bffee66772ff9fdef168feda47ef
branch: main
author: Brandt Bucher <brandtbucher(a)microsoft.com>
committer: encukou <encukou(a)gmail.com>
date: 2025-01-31T11:50:54+01:00
summary:
GH-129386: Add `test.support.reset_code` (GH-129486)
files:
A Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst
M Lib/test/support/__init__.py
M Lib/test/test_capi/test_opt.py
M Lib/test/test_dis.py
M Lib/test/test_embed.py
M Lib/test/test_opcache.py
diff --git a/Lib/test/support/__init__.py b/Lib/test/support/__init__.py
index 6436753f998a16..230bb240c89f77 100644
--- a/Lib/test/support/__init__.py
+++ b/Lib/test/support/__init__.py
@@ -66,6 +66,7 @@
"BrokenIter",
"in_systemd_nspawn_sync_suppressed",
"run_no_yield_async_fn", "run_yielding_async_fn", "async_yield",
+ "reset_code",
]
@@ -1286,6 +1287,12 @@ def requires_specialization_ft(test):
_opcode.ENABLE_SPECIALIZATION_FT, "requires specialization")(test)
+def reset_code(f: types.FunctionType) -> types.FunctionType:
+ """Clear all specializations, local instrumentation, and JIT code for the given function."""
+ f.__code__ = f.__code__.replace()
+ return f
+
+
#=======================================================================
# Check for the presence of docstrings.
diff --git a/Lib/test/test_capi/test_opt.py b/Lib/test/test_capi/test_opt.py
index d3aea37e094e61..02e534caec1162 100644
--- a/Lib/test/test_capi/test_opt.py
+++ b/Lib/test/test_capi/test_opt.py
@@ -9,7 +9,8 @@
import _opcode
from test.support import (script_helper, requires_specialization,
- import_helper, Py_GIL_DISABLED, requires_jit_enabled)
+ import_helper, Py_GIL_DISABLED, requires_jit_enabled,
+ reset_code)
_testinternalcapi = import_helper.import_module("_testinternalcapi")
@@ -19,11 +20,11 @@
@contextlib.contextmanager
def clear_executors(func):
# Clear executors in func before and after running a block
- func.__code__ = func.__code__.replace()
+ reset_code(func)
try:
yield
finally:
- func.__code__ = func.__code__.replace()
+ reset_code(func)
def get_first_executor(func):
diff --git a/Lib/test/test_dis.py b/Lib/test/test_dis.py
index bba2ac80aa6769..e99289cf66af67 100644
--- a/Lib/test/test_dis.py
+++ b/Lib/test/test_dis.py
@@ -15,7 +15,7 @@
import unittest
from test.support import (captured_stdout, requires_debug_ranges,
requires_specialization, cpython_only,
- os_helper, import_helper)
+ os_helper, import_helper, reset_code)
from test.support.bytecode_helper import BytecodeTestCase
@@ -1356,7 +1356,7 @@ def f():
self.code_quicken(f)
else:
# "copy" the code to un-quicken it:
- f.__code__ = f.__code__.replace()
+ reset_code(f)
for instruction in _unroll_caches_as_Instructions(dis.get_instructions(
f, show_caches=True, adaptive=adaptive
), show_caches=True):
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 72221379a00051..cd65496cafb04d 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -391,6 +391,7 @@ def test_specialized_static_code_gets_unspecialized_at_Py_FINALIZE(self):
import importlib._bootstrap
import opcode
import test.test_dis
+ import test.support
def is_specialized(f):
for instruction in dis.get_instructions(f, adaptive=True):
@@ -409,7 +410,7 @@ def is_specialized(f):
func = importlib._bootstrap._handle_fromlist
# "copy" the code to un-specialize it:
- func.__code__ = func.__code__.replace()
+ test.support.reset_code(func)
assert not is_specialized(func), "specialized instructions found"
diff --git a/Lib/test/test_opcache.py b/Lib/test/test_opcache.py
index 2defe74892786d..87de4c94ba26fb 100644
--- a/Lib/test/test_opcache.py
+++ b/Lib/test/test_opcache.py
@@ -6,7 +6,7 @@
import unittest
from test.support import (threading_helper, check_impl_detail,
requires_specialization, requires_specialization_ft,
- cpython_only, requires_jit_disabled)
+ cpython_only, requires_jit_disabled, reset_code)
from test.support.import_helper import import_module
# Skip this module on other interpreters, it is cpython specific:
@@ -579,9 +579,9 @@ def assert_races_do_not_crash(
# Reset:
if check_items:
for item in items:
- item.__code__ = item.__code__.replace()
+ reset_code(item)
else:
- read.__code__ = read.__code__.replace()
+ reset_code(read)
# Specialize:
for _ in range(_testinternalcapi.SPECIALIZATION_THRESHOLD):
read(items)
@@ -1552,6 +1552,7 @@ def test_store_attr_instance_value(self):
class C:
pass
+ @reset_code
def set_value(n):
c = C()
for i in range(n):
@@ -1577,6 +1578,7 @@ class C:
for i in range(_testinternalcapi.SHARED_KEYS_MAX_SIZE - 1):
setattr(c, f"_{i}", None)
+ @reset_code
def set_value(n):
for i in range(n):
c.x = i
diff --git a/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst b/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst
new file mode 100644
index 00000000000000..a03f596bc46c30
--- /dev/null
+++ b/Misc/NEWS.d/next/Tests/2025-01-30-13-09-27.gh-issue-129386.iNtbEi.rst
@@ -0,0 +1,2 @@
+Add ``test.support.reset_code``, which can be used to reset various
+bytecode-level optimizations and local instrumentation for a function.
1
0
Jan. 31, 2025
https://github.com/python/cpython/commit/48f08fe6c8023b852b642c130dda74f35d…
commit: 48f08fe6c8023b852b642c130dda74f35d5997cd
branch: 3.12
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T10:27:35+01:00
summary:
[3.12] gh-111495: Add PyFile tests (#129449) (#129477) (#129501)
[3.13] gh-111495: Add PyFile tests (#129449) (#129477)
gh-111495: Add PyFile tests (#129449)
Add tests for the following functions in test_capi.test_file:
* PyFile_FromFd()
* PyFile_GetLine()
* PyFile_NewStdPrinter()
* PyFile_WriteObject()
* PyFile_WriteString()
* PyObject_AsFileDescriptor()
Remove test_embed.StdPrinterTests which became redundant.
(cherry picked from commit 4ca9fc08f89bf7172d41e523d9e520eb1729ee8c)
(cherry picked from commit 9a59a51733e58b6091ca9157fd43cc9d0f93a96f)
files:
A Lib/test/test_capi/test_file.py
A Modules/_testcapi/clinic/file.c.h
M Lib/test/test_embed.py
M Modules/_testcapi/file.c
diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py
new file mode 100644
index 00000000000000..8042c8e5de0b0a
--- /dev/null
+++ b/Lib/test/test_capi/test_file.py
@@ -0,0 +1,231 @@
+import io
+import os
+import unittest
+import warnings
+from test import support
+from test.support import import_helper, os_helper, warnings_helper
+
+
+_testcapi = import_helper.import_module('_testcapi')
+_io = import_helper.import_module('_io')
+NULL = None
+STDOUT_FD = 1
+
+with open(__file__, 'rb') as fp:
+ FIRST_LINE = next(fp).decode()
+FIRST_LINE_NORM = FIRST_LINE.rstrip() + '\n'
+
+
+class CAPIFileTest(unittest.TestCase):
+ def test_pyfile_fromfd(self):
+ # Test PyFile_FromFd() which is a thin wrapper to _io.open()
+ pyfile_fromfd = _testcapi.pyfile_fromfd
+ filename = __file__
+ with open(filename, "rb") as fp:
+ fd = fp.fileno()
+
+ # FileIO
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 0, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.FileIO)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # BufferedReader
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 1024, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.BufferedReader)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # TextIOWrapper
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "r", 1,
+ "utf-8", "replace", NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.TextIOWrapper)
+ self.assertEqual(obj.encoding, "utf-8")
+ self.assertEqual(obj.errors, "replace")
+ self.assertEqual(obj.readline(), FIRST_LINE_NORM)
+ finally:
+ obj.close()
+
+ def test_pyfile_getline(self):
+ # Test PyFile_GetLine(file, n): call file.readline()
+ # and strip "\n" suffix if n < 0.
+ pyfile_getline = _testcapi.pyfile_getline
+
+ # Test Unicode
+ with open(__file__, "r") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE_NORM.rstrip('\n'))
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE_NORM)
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE_NORM[:6])
+
+ # Test bytes
+ with open(__file__, "rb") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE.rstrip('\n').encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE.encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE.encode()[:6])
+
+ def test_pyfile_writestring(self):
+ # Test PyFile_WriteString(str, file): call file.write(str)
+ writestr = _testcapi.pyfile_writestring
+
+ with io.StringIO() as fp:
+ self.assertEqual(writestr("a\xe9\u20ac\U0010FFFF".encode(), fp), 0)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr(b"\xff", fp)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr("\udc80".encode("utf-8", "surrogatepass"), fp)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "a\xe9\u20ac\U0010FFFF")
+
+ with self.assertRaises(SystemError):
+ writestr(b"abc", NULL)
+
+ def test_pyfile_writeobject(self):
+ # Test PyFile_WriteObject(obj, file, flags):
+ # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW.
+ # - Call file.write(repr(obj)) otherwise.
+ writeobject = _testcapi.pyfile_writeobject
+ Py_PRINT_RAW = 1
+
+ with io.StringIO() as fp:
+ # Test flags=Py_PRINT_RAW
+ self.assertEqual(writeobject("raw", fp, Py_PRINT_RAW), 0)
+ writeobject(NULL, fp, Py_PRINT_RAW)
+
+ # Test flags=0
+ self.assertEqual(writeobject("repr", fp, 0), 0)
+ writeobject(NULL, fp, 0)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "raw<NULL>'repr'<NULL>")
+
+ # invalid file type
+ for invalid_file in (123, "abc", object()):
+ with self.subTest(file=invalid_file):
+ with self.assertRaises(AttributeError):
+ writeobject("abc", invalid_file, Py_PRINT_RAW)
+
+ with self.assertRaises(TypeError):
+ writeobject("abc", NULL, 0)
+
+ def test_pyobject_asfiledescriptor(self):
+ # Test PyObject_AsFileDescriptor(obj):
+ # - Return obj if obj is an integer.
+ # - Return obj.fileno() otherwise.
+ # File descriptor must be >= 0.
+ asfd = _testcapi.pyobject_asfiledescriptor
+
+ self.assertEqual(asfd(123), 123)
+ self.assertEqual(asfd(0), 0)
+
+ with open(__file__, "rb") as fp:
+ self.assertEqual(asfd(fp), fp.fileno())
+
+ self.assertEqual(asfd(False), 0)
+ self.assertEqual(asfd(True), 1)
+
+ class FakeFile:
+ def __init__(self, fd):
+ self.fd = fd
+ def fileno(self):
+ return self.fd
+
+ # file descriptor must be positive
+ with self.assertRaises(ValueError):
+ asfd(-1)
+ with self.assertRaises(ValueError):
+ asfd(FakeFile(-1))
+
+ # fileno() result must be an integer
+ with self.assertRaises(TypeError):
+ asfd(FakeFile("text"))
+
+ # unsupported types
+ for obj in ("string", ["list"], object()):
+ with self.subTest(obj=obj):
+ with self.assertRaises(TypeError):
+ asfd(obj)
+
+ # CRASHES asfd(NULL)
+
+ def test_pyfile_newstdprinter(self):
+ # Test PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.closed, False)
+ self.assertIsNone(file.encoding)
+ self.assertEqual(file.mode, "w")
+
+ self.assertEqual(file.fileno(), STDOUT_FD)
+ self.assertEqual(file.isatty(), os.isatty(STDOUT_FD))
+
+ # flush() is a no-op
+ self.assertIsNone(file.flush())
+
+ # close() is a no-op
+ self.assertIsNone(file.close())
+ self.assertEqual(file.closed, False)
+
+ support.check_disallow_instantiation(self, type(file))
+
+ def test_pyfile_newstdprinter_write(self):
+ # Test the write() method of PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ filename = os_helper.TESTFN
+ self.addCleanup(os_helper.unlink, filename)
+
+ try:
+ old_stdout = os.dup(STDOUT_FD)
+ except OSError as exc:
+ # os.dup(STDOUT_FD) is not supported on WASI
+ self.skipTest(f"os.dup() failed with {exc!r}")
+
+ try:
+ with open(filename, "wb") as fp:
+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
+ # or fileno(stderr) file descriptor.
+ fd = fp.fileno()
+ os.dup2(fd, STDOUT_FD)
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.write("text"), 4)
+ # The surrogate character is encoded with
+ # the "surrogateescape" error handler
+ self.assertEqual(file.write("[\udc80]"), 8)
+ finally:
+ os.dup2(old_stdout, STDOUT_FD)
+ os.close(old_stdout)
+
+ with open(filename, "r") as fp:
+ self.assertEqual(fp.read(), "text[\\udc80]")
+
+ # TODO: Test Py_UniversalNewlineFgets()
+
+ # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
+ # test_embed.test_open_code_hook()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index c931d160350545..6e728107b2f7f0 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -1866,56 +1866,5 @@ def test_no_memleak(self):
self.assertEqual(blocks, 0, out)
-class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase):
- # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr():
- # "Set up a preliminary stderr printer until we have enough
- # infrastructure for the io module in place."
-
- STDOUT_FD = 1
-
- def create_printer(self, fd):
- ctypes = import_helper.import_module('ctypes')
- PyFile_NewStdPrinter = ctypes.pythonapi.PyFile_NewStdPrinter
- PyFile_NewStdPrinter.argtypes = (ctypes.c_int,)
- PyFile_NewStdPrinter.restype = ctypes.py_object
- return PyFile_NewStdPrinter(fd)
-
- def test_write(self):
- message = "unicode:\xe9-\u20ac-\udc80!\n"
-
- stdout_fd = self.STDOUT_FD
- stdout_fd_copy = os.dup(stdout_fd)
- self.addCleanup(os.close, stdout_fd_copy)
-
- rfd, wfd = os.pipe()
- self.addCleanup(os.close, rfd)
- self.addCleanup(os.close, wfd)
- try:
- # PyFile_NewStdPrinter() only accepts fileno(stdout)
- # or fileno(stderr) file descriptor.
- os.dup2(wfd, stdout_fd)
-
- printer = self.create_printer(stdout_fd)
- printer.write(message)
- finally:
- os.dup2(stdout_fd_copy, stdout_fd)
-
- data = os.read(rfd, 100)
- self.assertEqual(data, message.encode('utf8', 'backslashreplace'))
-
- def test_methods(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- self.assertEqual(printer.fileno(), fd)
- self.assertEqual(printer.isatty(), os.isatty(fd))
- printer.flush() # noop
- printer.close() # noop
-
- def test_disallow_instantiation(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- support.check_disallow_instantiation(self, type(printer))
-
-
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h
new file mode 100644
index 00000000000000..52e59f14b91673
--- /dev/null
+++ b/Modules/_testcapi/clinic/file.c.h
@@ -0,0 +1,112 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+# include "pycore_gc.h" // PyGC_Head
+# include "pycore_runtime.h" // _Py_ID()
+#endif
+
+
+PyDoc_STRVAR(_testcapi_pyfile_getline__doc__,
+"pyfile_getline($module, file, n, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_GETLINE_METHODDEF \
+ {"pyfile_getline", _PyCFunction_CAST(_testcapi_pyfile_getline), METH_FASTCALL, _testcapi_pyfile_getline__doc__},
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n);
+
+static PyObject *
+_testcapi_pyfile_getline(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *file;
+ int n;
+
+ if (!_PyArg_CheckPositional("pyfile_getline", nargs, 2, 2)) {
+ goto exit;
+ }
+ file = args[0];
+ n = _PyLong_AsInt(args[1]);
+ if (n == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_getline_impl(module, file, n);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyfile_writeobject__doc__,
+"pyfile_writeobject($module, obj, file, flags, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF \
+ {"pyfile_writeobject", _PyCFunction_CAST(_testcapi_pyfile_writeobject), METH_FASTCALL, _testcapi_pyfile_writeobject__doc__},
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags);
+
+static PyObject *
+_testcapi_pyfile_writeobject(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *obj;
+ PyObject *file;
+ int flags;
+
+ if (!_PyArg_CheckPositional("pyfile_writeobject", nargs, 3, 3)) {
+ goto exit;
+ }
+ obj = args[0];
+ file = args[1];
+ flags = _PyLong_AsInt(args[2]);
+ if (flags == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_writeobject_impl(module, obj, file, flags);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyobject_asfiledescriptor__doc__,
+"pyobject_asfiledescriptor($module, obj, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF \
+ {"pyobject_asfiledescriptor", (PyCFunction)_testcapi_pyobject_asfiledescriptor, METH_O, _testcapi_pyobject_asfiledescriptor__doc__},
+
+PyDoc_STRVAR(_testcapi_pyfile_newstdprinter__doc__,
+"pyfile_newstdprinter($module, fd, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF \
+ {"pyfile_newstdprinter", (PyCFunction)_testcapi_pyfile_newstdprinter, METH_O, _testcapi_pyfile_newstdprinter__doc__},
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd);
+
+static PyObject *
+_testcapi_pyfile_newstdprinter(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int fd;
+
+ fd = _PyLong_AsInt(arg);
+ if (fd == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_newstdprinter_impl(module, fd);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=bb69f2c213a490f6 input=a9049054013a1b77]*/
diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c
index 634563f6ea12cb..8763dbba62c0a8 100644
--- a/Modules/_testcapi/file.c
+++ b/Modules/_testcapi/file.c
@@ -1,17 +1,140 @@
+#define PY_SSIZE_T_CLEAN
#include "parts.h"
#include "util.h"
+#include "clinic/file.c.h"
+
+
+/*[clinic input]
+module _testcapi
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
+
+
+static PyObject *
+pyfile_fromfd(PyObject *module, PyObject *args)
+{
+ int fd;
+ const char *name;
+ Py_ssize_t size;
+ const char *mode;
+ int buffering;
+ const char *encoding;
+ const char *errors;
+ const char *newline;
+ int closefd;
+ if (!PyArg_ParseTuple(args,
+ "iz#z#"
+ "iz#z#"
+ "z#i",
+ &fd, &name, &size, &mode, &size,
+ &buffering, &encoding, &size, &errors, &size,
+ &newline, &size, &closefd)) {
+ return NULL;
+ }
+
+ return PyFile_FromFd(fd, name, mode, buffering,
+ encoding, errors, newline, closefd);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_getline
+
+ file: object
+ n: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n)
+/*[clinic end generated code: output=137fde2774563266 input=df26686148b3657e]*/
+{
+ return PyFile_GetLine(file, n);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_writeobject
+
+ obj: object
+ file: object
+ flags: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags)
+/*[clinic end generated code: output=ebb4d802e3db489c input=64a34a3e75b9935a]*/
+{
+ NULLABLE(obj);
+ NULLABLE(file);
+ RETURN_INT(PyFile_WriteObject(obj, file, flags));
+}
+
+
+static PyObject *
+pyfile_writestring(PyObject *module, PyObject *args)
+{
+ const char *str;
+ Py_ssize_t size;
+ PyObject *file;
+ if (!PyArg_ParseTuple(args, "z#O", &str, &size, &file)) {
+ return NULL;
+ }
+ NULLABLE(file);
+
+ RETURN_INT(PyFile_WriteString(str, file));
+}
+
+
+/*[clinic input]
+_testcapi.pyobject_asfiledescriptor
+
+ obj: object
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyobject_asfiledescriptor(PyObject *module, PyObject *obj)
+/*[clinic end generated code: output=2d640c6a1970c721 input=45fa1171d62b18d7]*/
+{
+ NULLABLE(obj);
+ RETURN_INT(PyObject_AsFileDescriptor(obj));
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_newstdprinter
+
+ fd: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd)
+/*[clinic end generated code: output=8a2d1c57b6892db3 input=442f1824142262ea]*/
+{
+ return PyFile_NewStdPrinter(fd);
+}
static PyMethodDef test_methods[] = {
+ {"pyfile_fromfd", pyfile_fromfd, METH_VARARGS},
+ _TESTCAPI_PYFILE_GETLINE_METHODDEF
+ _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF
+ {"pyfile_writestring", pyfile_writestring, METH_VARARGS},
+ _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF
+ _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF
{NULL},
};
int
_PyTestCapi_Init_File(PyObject *m)
{
- if (PyModule_AddFunctions(m, test_methods) < 0){
- return -1;
- }
-
- return 0;
+ return PyModule_AddFunctions(m, test_methods);
}
1
0