Skip to content

Commit f83f5aa

Browse files
committed
Minor fixes to default argument handling. Adds C++ use of PyArg_ParseTupleAndKeywords. Adds common exception patterns.
1 parent e0623a7 commit f83f5aa

File tree

2 files changed

+90
-14
lines changed

2 files changed

+90
-14
lines changed

doc/sphinx/source/exceptions.rst

+28
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,34 @@ The other thing to note is that if there are multiple calls to ``PyErr_SetString
8787
return NULL;
8888
}
8989
90+
---------------------------------
91+
Common Exception Patterns
92+
---------------------------------
93+
94+
Here are some common use cases for raising exceptions.
95+
96+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
97+
Type Checking
98+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
99+
100+
A common requirement is to check the types of the arguments and raise a ``TypeError`` if they are wrong. Here is an example where we require a ``bytes`` object:
101+
102+
.. code-block:: c
103+
:linenos:
104+
:emphasize-lines: 4-9
105+
106+
static PyObject*
107+
function(PyObject *self, PyObject *arg) {
108+
/* ... */
109+
if (! PyBytes_Check(arg)) {
110+
PyErr_Format(PyExc_TypeError,
111+
"Argument \"value\" to %s must be a bytes object not a \"%s\"",
112+
__FUNCTION__, Py_TYPE(arg)->tp_name);
113+
goto except;
114+
}
115+
/* ... */
116+
}
117+
90118
---------------------------------
91119
Creating Specialised Excpetions
92120
---------------------------------

doc/sphinx/source/parsing_arguments.rst

+62-14
Original file line numberDiff line numberDiff line change
@@ -232,16 +232,34 @@ If you want the function signature to be ``argsKwargs(theString, theOptInt=8)``
232232

233233
.. code-block:: c
234234
235-
...
235+
/* ... */
236236
static char *kwlist[] = {
237237
"",
238238
"theOptInt",
239239
NULL
240240
};
241-
...
241+
/* ... */
242242
243243
.. note::
244244
If you use ``|`` in the parser format string you have to set the default values for those optional arguments yourself in the C code. This is pretty straightforward if they are fundamental C types as ``arg2 = 8`` above. For Python values is a bit more tricky as described next.
245+
246+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
247+
Keyword Arguments and C++11
248+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
249+
250+
C++11 compilers warn when creating non-const ``char*`` from string literals as we have done with the keyword array above. The solution is to declare these ``const char*`` however ``PyArg_ParseTupleAndKeywords`` expects a ``char **``. The solution is to cast away const in the call:
251+
252+
.. code-block:: c
253+
254+
/* ... */
255+
static const char *kwlist[] = { "foo", "bar", "baz", NULL };
256+
if (! PyArg_ParseTupleAndKeywords(args, kwds, "OOO",
257+
const_cast<char**>(kwlist),
258+
&foo, &bar, &baz)) {
259+
return NULL;
260+
}
261+
/* ... */
262+
245263
246264
--------------------------------------------------------------------------
247265
Being Pythonic with Default Arguments
@@ -461,31 +479,61 @@ For simple default values some macros may help. The first one declares and initi
461479
} \
462480
}
463481
464-
The second one assigns the argument to the default if it is not initialised. It just takes the name of the argument:
482+
The second one assigns the argument to the default if it is not initialised and increments the reference count. It just takes the name of the argument:
465483

466484
.. code-block:: c
467485
468-
#define PY_DEFAULT_ARGUMENT_SET(name) if (! name) name = default_##name
486+
#define PY_DEFAULT_ARGUMENT_SET(name) if (! name) name = default_##name; \
487+
Py_INCREF(name)
469488
470-
And they can be used thus:
489+
And they can be used like this when implementing a Python function signature such as::
490+
491+
def do_something(self, encoding='utf-8', the_id=0, must_log=True):
492+
# ...
493+
return None
494+
495+
Here is that function implemented in C:
471496

472497
.. code-block:: c
473498
474-
static int
475-
something_init(something *self, PyObject *args, PyObject *kwds) {
476-
/* Initialise default arguments */
477-
PY_DEFAULT_ARGUMENT_INIT(encoding, PyUnicode_FromString("utf-8"), -1);
478-
PY_DEFAULT_ARGUMENT_INIT(the_id, PyLong_FromLong(0L), -1);
479-
PY_DEFAULT_ARGUMENT_INIT(must_log, PyBool_FromLong(1L), -1);
499+
static PyObject*
500+
do_something(something *self, PyObject *args, PyObject *kwds) {
501+
PyObject *ret = NULL;
502+
/* Initialise default arguments. Note: these might cause an early return. */
503+
PY_DEFAULT_ARGUMENT_INIT(encoding, PyUnicode_FromString("utf-8"), NULL);
504+
PY_DEFAULT_ARGUMENT_INIT(the_id, PyLong_FromLong(0L), NULL);
505+
PY_DEFAULT_ARGUMENT_INIT(must_log, PyBool_FromLong(1L), NULL);
480506
481507
static const char *kwlist[] = { "encoding", "the_id", "must_log", NULL };
482508
if (! PyArg_ParseTupleAndKeywords(args, kwds, "|Oip",
483509
const_cast<char**>(kwlist),
484510
&encoding, &the_id, &must_log)) {
485-
return -1;
511+
return NULL;
486512
}
487-
/* Assign absent arguments to defaults. */
513+
/*
514+
* Assign absent arguments to defaults and increment the reference count.
515+
* Don't forget to decrement the reference count before returning!
516+
*/
488517
PY_DEFAULT_ARGUMENT_SET(encoding);
489518
PY_DEFAULT_ARGUMENT_SET(the_id);
490519
PY_DEFAULT_ARGUMENT_SET(must_log);
491-
/* Use encoding, the_id, must_log from here on... */
520+
521+
/*
522+
* Use encoding, the_id, must_log from here on...
523+
*/
524+
525+
Py_INCREF(Py_None);
526+
ret = Py_None;
527+
assert(! PyErr_Occurred());
528+
assert(ret);
529+
goto finally;
530+
except:
531+
assert(PyErr_Occurred());
532+
Py_XDECREF(ret);
533+
ret = NULL;
534+
finally:
535+
Py_DECREF(encoding);
536+
Py_DECREF(the_id);
537+
Py_DECREF(must_log);
538+
return ret;
539+
}

0 commit comments

Comments
 (0)