Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c15bdef

Browse files
committed
Issue python#6012: Add cleanup support to O& argument parsing.
1 parent 2703fd9 commit c15bdef

9 files changed

Lines changed: 120 additions & 11 deletions

File tree

Doc/c-api/arg.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,14 @@ variable(s) whose address should be passed.
250250
the conversion has failed. When the conversion fails, the *converter* function
251251
should raise an exception and leave the content of *address* unmodified.
252252

253+
If the *converter* returns Py_CLEANUP_SUPPORTED, it may get called a second time
254+
if the argument parsing eventually fails, giving the converter a chance to release
255+
any memory that it had already allocated. In this second call, the *object* parameter
256+
will be NULL; *address* will have the same value as in the original call.
257+
258+
.. versionchanged:: 3.1
259+
Py_CLEANUP_SUPPORTED was added.
260+
253261
``S`` (string) [PyStringObject \*]
254262
Like ``O`` but requires that the Python object is a string object. Raises
255263
:exc:`TypeError` if the object is not a string object. The C variable may also

Doc/c-api/unicode.rst

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -379,11 +379,13 @@ Many of the following APIs take two arguments encoding and errors. These
379379
parameters encoding and errors have the same semantics as the ones of the
380380
builtin unicode() Unicode object constructor.
381381

382-
Setting encoding to *NULL* causes the default encoding to be used which is
383-
ASCII. The file system calls should use :cdata:`Py_FileSystemDefaultEncoding`
384-
as the encoding for file names. This variable should be treated as read-only: On
385-
some systems, it will be a pointer to a static string, on others, it will change
386-
at run-time (such as when the application invokes setlocale).
382+
Setting encoding to *NULL* causes the default encoding to be used
383+
which is ASCII. The file system calls should use
384+
:cfunc:`PyUnicode_FSConverter` for encoding file names. This uses the
385+
variable :cdata:`Py_FileSystemDefaultEncoding` internally. This
386+
variable should be treated as read-only: On some systems, it will be a
387+
pointer to a static string, on others, it will change at run-time
388+
(such as when the application invokes setlocale).
387389

388390
Error handling is set by errors which may also be set to *NULL* meaning to use
389391
the default handling defined for the codec. Default error handling for all
@@ -782,6 +784,19 @@ the user settings on the machine running the codec.
782784
object. Error handling is "strict". Return *NULL* if an exception was
783785
raised by the codec.
784786

787+
For decoding file names and other environment strings, :cdata:`Py_FileSystemEncoding`
788+
should be used as the encoding, and ``"surrogateescape"`` should be used as the error
789+
handler. For encoding file names during argument parsing, the ``O&`` converter should
790+
be used, passsing PyUnicode_FSConverter as the conversion function:
791+
792+
.. cfunction:: int PyUnicode_FSConverter(PyObject* obj, void* result)
793+
794+
Convert *obj* into *result*, using the file system encoding, and the ``surrogateescape``
795+
error handler. *result* must be a ``PyObject*``, yielding a bytes or bytearray object
796+
which must be released if it is no longer used.
797+
798+
.. versionadded:: 3.1
799+
785800
.. % --- Methods & Slots ----------------------------------------------------
786801
787802

Include/modsupport.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ PyAPI_FUNC(int) PyModule_AddStringConstant(PyObject *, const char *, const char
4343
#define PyModule_AddIntMacro(m, c) PyModule_AddIntConstant(m, #c, c)
4444
#define PyModule_AddStringMacro(m, c) PyModule_AddStringConstant(m, #c, c)
4545

46+
#define Py_CLEANUP_SUPPORTED 0x20000
47+
4648
#define PYTHON_API_VERSION 1013
4749
#define PYTHON_API_STRING "1013"
4850
/* The API version is maintained (independently from the Python version)

Lib/test/test_capi.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ def test_pendingcalls_non_threaded(self):
115115
self.pendingcalls_submit(l, n)
116116
self.pendingcalls_wait(l, n)
117117

118+
# Bug #6012
119+
class Test6012(unittest.TestCase):
120+
def test(self):
121+
self.assertEqual(_testcapi.argparsing("Hello", "World"), 1)
118122

119123
def test_main():
120124
support.run_unittest(CAPITest)
@@ -159,7 +163,7 @@ def callback():
159163
t.start()
160164
t.join()
161165

162-
support.run_unittest(TestPendingCalls)
166+
support.run_unittest(TestPendingCalls, Test6012)
163167

164168

165169
if __name__ == "__main__":

Misc/NEWS

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ What's New in Python 3.1 release candidate 1?
1212
Core and Builtins
1313
-----------------
1414

15+
- Issue #6012: Add cleanup support to O& argument parsing.
16+
1517
- Issue #6089: Fixed str.format with certain invalid field specifiers
1618
that would raise SystemError.
1719

Modules/_testcapimodule.c

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,36 @@ raise_memoryerror(PyObject *self)
14151415
return NULL;
14161416
}
14171417

1418+
/* Issue 6012 */
1419+
static PyObject *str1, *str2;
1420+
static int
1421+
failing_converter(PyObject *obj, void *arg)
1422+
{
1423+
/* Clone str1, then let the conversion fail. */
1424+
assert(str1);
1425+
str2 = str1;
1426+
Py_INCREF(str2);
1427+
return 0;
1428+
}
1429+
static PyObject*
1430+
argparsing(PyObject *o, PyObject *args)
1431+
{
1432+
PyObject *res;
1433+
str1 = str2 = NULL;
1434+
if (!PyArg_ParseTuple(args, "O&O&",
1435+
PyUnicode_FSConverter, &str1,
1436+
failing_converter, &str2)) {
1437+
if (!str2)
1438+
/* argument converter not called? */
1439+
return NULL;
1440+
/* Should be 1 */
1441+
res = PyLong_FromLong(Py_REFCNT(str2));
1442+
Py_DECREF(str2);
1443+
PyErr_Clear();
1444+
return res;
1445+
}
1446+
Py_RETURN_NONE;
1447+
}
14181448

14191449
static PyMethodDef TestMethods[] = {
14201450
{"raise_exception", raise_exception, METH_VARARGS},
@@ -1433,7 +1463,6 @@ static PyMethodDef TestMethods[] = {
14331463
PyDoc_STR("This is a pretty normal docstring.")},
14341464
{"test_string_to_double", (PyCFunction)test_string_to_double, METH_NOARGS},
14351465
{"test_capsule", (PyCFunction)test_capsule, METH_NOARGS},
1436-
14371466
{"getargs_tuple", getargs_tuple, METH_VARARGS},
14381467
{"getargs_keywords", (PyCFunction)getargs_keywords,
14391468
METH_VARARGS|METH_KEYWORDS},
@@ -1468,6 +1497,7 @@ static PyMethodDef TestMethods[] = {
14681497
#endif
14691498
{"traceback_print", traceback_print, METH_VARARGS},
14701499
{"exception_print", exception_print, METH_VARARGS},
1500+
{"argparsing", argparsing, METH_VARARGS},
14711501
{NULL, NULL} /* sentinel */
14721502
};
14731503

Modules/posixmodule.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -804,8 +804,6 @@ posix_2str(PyObject *args,
804804
if (!PyArg_ParseTuple(args, format,
805805
PyUnicode_FSConverter, &opath1,
806806
PyUnicode_FSConverter, &opath2)) {
807-
Py_XDECREF(opath1);
808-
Py_XDECREF(opath2);
809807
return NULL;
810808
}
811809
path1 = bytes2str(opath1, 1);

Objects/unicodeobject.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1539,6 +1539,10 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
15391539
PyObject *output = NULL;
15401540
Py_ssize_t size;
15411541
void *data;
1542+
if (arg == NULL) {
1543+
Py_DECREF(*(PyObject**)addr);
1544+
return 1;
1545+
}
15421546
if (PyBytes_Check(arg) || PyByteArray_Check(arg)) {
15431547
output = arg;
15441548
Py_INCREF(output);
@@ -1573,7 +1577,7 @@ PyUnicode_FSConverter(PyObject* arg, void* addr)
15731577
return 0;
15741578
}
15751579
*(PyObject**)addr = output;
1576-
return 1;
1580+
return Py_CLEANUP_SUPPORTED;
15771581
}
15781582

15791583

Python/getargs.c

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ _PyArg_VaParse_SizeT(PyObject *args, char *format, va_list va)
141141

142142
#define GETARGS_CAPSULE_NAME_CLEANUP_PTR "getargs.cleanup_ptr"
143143
#define GETARGS_CAPSULE_NAME_CLEANUP_BUFFER "getargs.cleanup_buffer"
144+
#define GETARGS_CAPSULE_NAME_CLEANUP_CONVERT "getargs.cleanup_convert"
144145

145146
static void
146147
cleanup_ptr(PyObject *self)
@@ -194,6 +195,46 @@ addcleanup(void *ptr, PyObject **freelist, PyCapsule_Destructor destr)
194195
return 0;
195196
}
196197

198+
static void
199+
cleanup_convert(PyObject *self)
200+
{
201+
typedef int (*destr_t)(PyObject *, void *);
202+
destr_t destr = (destr_t)PyCapsule_GetContext(self);
203+
void *ptr = PyCapsule_GetPointer(self,
204+
GETARGS_CAPSULE_NAME_CLEANUP_CONVERT);
205+
if (ptr && destr)
206+
destr(NULL, ptr);
207+
}
208+
209+
static int
210+
addcleanup_convert(void *ptr, PyObject **freelist, int (*destr)(PyObject*,void*))
211+
{
212+
PyObject *cobj;
213+
if (!*freelist) {
214+
*freelist = PyList_New(0);
215+
if (!*freelist) {
216+
destr(NULL, ptr);
217+
return -1;
218+
}
219+
}
220+
cobj = PyCapsule_New(ptr, GETARGS_CAPSULE_NAME_CLEANUP_CONVERT,
221+
cleanup_convert);
222+
if (!cobj) {
223+
destr(NULL, ptr);
224+
return -1;
225+
}
226+
if (PyCapsule_SetContext(cobj, destr) == -1) {
227+
/* This really should not happen. */
228+
Py_FatalError("capsule refused setting of context.");
229+
}
230+
if (PyList_Append(*freelist, cobj)) {
231+
Py_DECREF(cobj); /* This will also call destr. */
232+
return -1;
233+
}
234+
Py_DECREF(cobj);
235+
return 0;
236+
}
237+
197238
static int
198239
cleanreturn(int retval, PyObject *freelist)
199240
{
@@ -1253,10 +1294,15 @@ convertsimple(PyObject *arg, const char **p_format, va_list *p_va, int flags,
12531294
typedef int (*converter)(PyObject *, void *);
12541295
converter convert = va_arg(*p_va, converter);
12551296
void *addr = va_arg(*p_va, void *);
1297+
int res;
12561298
format++;
1257-
if (! (*convert)(arg, addr))
1299+
if (! (res = (*convert)(arg, addr)))
12581300
return converterr("(unspecified)",
12591301
arg, msgbuf, bufsize);
1302+
if (res == Py_CLEANUP_SUPPORTED &&
1303+
addcleanup_convert(addr, freelist, convert) == -1)
1304+
return converterr("(cleanup problem)",
1305+
arg, msgbuf, bufsize);
12601306
}
12611307
else {
12621308
p = va_arg(*p_va, PyObject **);

0 commit comments

Comments
 (0)