473,387 Members | 1,757 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 software developers and data experts.

Type checking inside a C extension

I have a C extension function into which I pass a list of lists
of tuples:

[
[ ("A",1), ("B",2) ("C",3) ],
[ ("A",1), ("B",2) ("C",3) ],
[ ("A",1), ("B",2) ("C",3) ],
]

I then unpack the values (down to the tuple elements) into their C
values using:

PyLong_AsLong(PyTuple_GetItem(tupl,0))

to extract the int from the first element of the tuple and

PyString_AsString(PyTuple_GetItem(tupl,1))[0]

to get the char from the second element.
The problem is that neither PyLong_AsLong() nor PyString_AsString()
does any type checking so the interpreter crashes when I try to use
the values returned by PyLong_AsLong() and PyString_AsString() if
they happen to be fed objects - the tuple elements in this case - of
the wrong type.

I could certainly do a type check using PyLong_Check() and
PyString_Check() on the tuple items and raise a TypeError exception
to avoid leading to a crash, but I am concerned about the speed hit
as this is the innermost loop of a routine which gets called
really often (I use it for text block transfers).

Is there a less expensive way to check the type, or somehow
avoid a crashing situation (i.e. an exception gets properly raised)
without calling PyLong_Check() and PyString_Check() the elements
of each and every tuple?
Jul 18 '05 #1
9 3631
Jon Perez wrote:

The problem is that neither PyLong_AsLong() nor
PyString_AsString() does any type checking so the
interpreter crashes when I try to use the values
returned by PyLong_AsLong() and PyString_AsString() if
they happen to be fed objects - the tuple elements in
this case - of the wrong type.
It isn't true that PyLong_AsLong does no type checking. It does. It
reports problems through the standard Python exception system.
I could certainly do a type check using PyLong_Check() and
PyString_Check() on the tuple items and raise a TypeError exception
to avoid leading to a crash, but I am concerned about the speed hit
as this is the innermost loop of a routine which gets called really
often (I use it for text block transfers).

PyLong_Check is pretty cheap:

#define PyObject_TypeCheck(ob, tp) \
((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp)))
#define PyLong_Check(op) PyObject_TypeCheck(op, &PyLong_Type)

Note that it is basically just a dereference and comparison check. I
wouldn't worry about that even in an inner loop.
Is there a less expensive way to check the type, or somehow
avoid a crashing situation (i.e. an exception gets properly raised)
without calling PyLong_Check() and PyString_Check() the elements
of each and every tuple?


I would usually use Pyrex for a job like this. But barring that, I often
use its output to remember how to do little Python/C things. Given this
input program:

def foo(b):
cdef int a
a = b

it generates code like this:

/* "/private/tmp/foo.pyx":3 */
__pyx_1 = PyInt_AsLong(__pyx_v_b);
if (PyErr_Occurred()) {error handling and return}

By PyErr_Occurred() is a real function call and is probably slower than
PyLong_Check.

Paul Prescod

Jul 18 '05 #2
On Mon, 5 Apr 2004, Jon Perez wrote:
Is there a less expensive way to check the type, or somehow
avoid a crashing situation (i.e. an exception gets properly raised)
without calling PyLong_Check() and PyString_Check() the elements
of each and every tuple?


It might be expensive, but perhaps PyArg_ParseTuple() might be able to do
the decoding of your tuples more neatly than the various PyXX_Check()
calls.

--
Andrew I MacIntyre "These thoughts are mine alone..."
E-mail: an*****@bullseye.apana.org.au (pref) | Snail: PO Box 370
an*****@pcug.org.au (alt) | Belconnen ACT 2616
Web: http://www.andymac.org/ | Australia

Jul 18 '05 #3
Paul Prescod wrote:
It isn't true that PyLong_AsLong does no type checking. It does. It
reports problems through the standard Python exception system.
This illustrates a case where a crash occurs instead of an
exception being thrown. Feed spam.getstring() something besides a
string:

static PyObject *spam_getstring(PyObject *self, PyObject *args) {
PyObject *tup, *a, *b;

char strstr[100];

if (!PyArg_ParseTuple(args, "O", &strobj))
return NULL;

// PyString_AsString(strobj); // raises exception
strcpy(strstr,PyString_AsString(strobj)); /* crashes!! */

Py_INCREF(Py_None);
return Py_None;
}

What gives? Shouldn't the exception be raised within PyString_AsString()
before it even gets a chance to return a value to strcpy()? (Note:
I have been using IDLE - in the default separate subprocess mode - to observe
this behaviour)
Assuming I am resigned to calling PyString_Check() myself, can I
avoid the redundant PyString_Check() that PyString_AsString() is
probably calling?
PyLong_Check is pretty cheap:

#define PyObject_TypeCheck(ob, tp) \
((ob)->ob_type == (tp) || PyType_IsSubtype((ob)->ob_type, (tp)))
#define PyLong_Check(op) PyObject_TypeCheck(op, &PyLong_Type)
Kewl...
it generates code like this:

/* "/private/tmp/foo.pyx":3 */
__pyx_1 = PyInt_AsLong(__pyx_v_b);
if (PyErr_Occurred()) {error handling and return}

By PyErr_Occurred() is a real function call and is probably slower than
PyLong_Check.


Is PyType_IsSubtype((ob)->ob_Type,(tp))) a real function call as well though?

Jul 18 '05 #4
On Tue, 6 Apr 2004, Jon Perez wrote:
// PyString_AsString(strobj); // raises exception
strcpy(strstr,PyString_AsString(strobj)); /* crashes!! */

Py_INCREF(Py_None);
return Py_None;
}

What gives? Shouldn't the exception be raised within PyString_AsString()
before it even gets a chance to return a value to strcpy()?


First rule of using the Python C API: _always_ check the return value of
API functions.

In the case you exhibit, if the return value of PyString_AsString is NULL,
a TypeError exception will have been set and you should immediately
return NULL.

When the interpreter gets a NULL return from a called function, it checks
for an exception. If you don't return NULL, it can't detect the exception
until some other unrelated event turns it up.

--
Andrew I MacIntyre "These thoughts are mine alone..."
E-mail: an*****@bullseye.apana.org.au (pref) | Snail: PO Box 370
an*****@pcug.org.au (alt) | Belconnen ACT 2616
Web: http://www.andymac.org/ | Australia

Jul 18 '05 #5
On Tue, 06 Apr 2004 11:54:30 +0800, Jon Perez <jb********@yahoo.com>
wrote:
Paul Prescod wrote:
It isn't true that PyLong_AsLong does no type checking. It does. It
reports problems through the standard Python exception system.


This illustrates a case where a crash occurs instead of an
exception being thrown. Feed spam.getstring() something besides a
string:

static PyObject *spam_getstring(PyObject *self, PyObject *args) {
PyObject *tup, *a, *b;

char strstr[100];

if (!PyArg_ParseTuple(args, "O", &strobj))
return NULL;

// PyString_AsString(strobj); // raises exception
strcpy(strstr,PyString_AsString(strobj)); /* crashes!! */

Py_INCREF(Py_None);
return Py_None;
}

What gives? Shouldn't the exception be raised within PyString_AsString()
before it even gets a chance to return a value to strcpy()?


I assume you're talking about when the PyString_AsString *isn't*
commented out ;-)
Python exceptions aren't converted to C exceptions, and the Python C
API doesn't use C exceptions at all. There is a good reason for this.
C doesn't have exceptions - C++ does, but this is a C API.

Given that C lacks exceptions, there is no practical (and portable)
way to forcibly unwind the stack. Even if something could be done with
longjump, for instance, there would be the issue of how your function
gets to do its cleanup.

Therefore, the simplest way for a Python C API function to propogate a
Python exception is by returning an error return value to the caller,
and keeping associated data in global variables. The caller then takes
on the responsibility for propogating the exception up to its caller
and so on. Which is precisely what Python appears to do. The caller
needs to check for the error value, do any neccessary cleanup, and
then propogate the error out of the function.

Python C API functions often return zero for error IIRC, so...

if (PyString_AsString(strobj) == 0) { return 0; }

Should ensure that the Python exception raised by PyString_AsString is
propogated correctly.
--
Steve Horne

steve at ninereeds dot fsnet dot co dot uk
Jul 18 '05 #6
Stephen Horne wrote:
Python exceptions aren't converted to C exceptions, and the Python C
API doesn't use C exceptions at all. There is a good reason for this.
C doesn't have exceptions - C++ does, but this is a C API.
Ah... now I get this seemingly mysterious behaviour.

If PyString_AsString() attempts to convert a non-string Python object to a
C string, the exception is not thrown from within it, it only sets a flag
indicating that such an exception should eventually be thrown.

While the C extension function itself does need to return a NULL for said
exception occur, it does need to to return to Python runtime first to
trigger the exception.

Python C API functions often return zero for error IIRC, so...

if (PyString_AsString(strobj) == 0) { return 0; }

Should ensure that the Python exception raised by PyString_AsString is
propogated correctly.


This also answers my earlier question. So there is no need to do a
PyString_Check() before feeding a value to PyString_AsString(), just
check the return value of PyString_AsString()...

Thanks everyone.
Jul 18 '05 #7
Andrew MacIntyre wrote:
First rule of using the Python C API: _always_ check the return value of
API functions.


This only applies to API functions which return PyObject*, right?
Jul 18 '05 #8
Jon Perez wrote:
Andrew MacIntyre wrote:
First rule of using the Python C API: _always_ check the return value of
API functions.


This only applies to API functions which return PyObject*, right?


....because it is not hard to find instances in the source code for
the modules that come with Python where, for example, PyTuple_SetItem()'s
return value is not checked for.
Jul 18 '05 #9
On Wed, 07 Apr 2004 11:50:05 +0800, Jon Perez <jb********@yahoo.com>
wrote:
Andrew MacIntyre wrote:
First rule of using the Python C API: _always_ check the return value of
API functions.


This only applies to API functions which return PyObject*, right?


It isn't that simple. There are API functions returning an int, IIRC,
where that int can indicate an error condition.

However, if you can ensure that an error cannot exist OR if you can
arrange things such that the error is naturally handled, THEN you
don't need an explicit check.

For instance, sometimes you can just return the result of an API
function. If the API function returns a valid result, so does your
function. If the API function returns NULL, your function does so too
so the exception is propogated. If you don't need any further
processing or cleanup, why make a fuss?

"_always_ check the return value" is a very good principle, however,
even if not quite literally correct.
--
Steve Horne

steve at ninereeds dot fsnet dot co dot uk
Jul 18 '05 #10

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

3
by: Vinodh Kumar P | last post by:
Whenever I read any C++ literature I find the words "C++ is statically type checked".OK.Agreed. Is there any language that supports "Dynamic type checking"? In such a case any type can be assigned...
0
by: Pedro Werneck | last post by:
Hi list I'm trying to implement a new type in a C extension and it must support some binary operators, like &, |, ^, << and >>. With &, | and ^, the method must receive another object of the...
4
by: Mark | last post by:
Hi.. I have a c# class that i'm using to implement some extension functions and one of those functions is a simple push/pop stack. I made the c# code fairly generic, taking and returning objects...
0
by: Brian Henry | last post by:
I am using the following code to get the "Type" of a file Imports System.Runtime.InteropServices Public Class ShellAPI Public Declare Auto Function SHGetFileInfo Lib "shell32.dll" ( _ ...
3
by: Mark Rae | last post by:
Hi, I've been asked to write a WinForms app which will create reports as PDF documents. The client has agreed that all desktops (WinXP Pro, 32-bit Vista Business) *will* have Adobe Acrobat...
14
by: GeezerButler | last post by:
For any given type i want to know its default value. There is a neat keyword called default for doing this like object x = default(DateTime); but I have an instance of Type (called someType) and...
6
by: Mad Hatter | last post by:
Hi folks I'm a bit confused with an upload script that I've written. I want to be able to check the file type of an upload by checking the mime type but I'm not getting the results that I...
2
by: Paul Moore | last post by:
I'm trying to implement an extension type with a power operator. The operator is unusual in that I want to allow my objects to be raised to an integer power: p = Pattern() p3 = p ** 3 I've...
1
by: sasimca007 | last post by:
Hai friends, I have a doubt that if we are having a page which having the functionality to upload a file, then if we want to restrict to some types of files only have to be...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: aa123db | last post by:
Variable and constants Use var or let for variables and const fror constants. Var foo ='bar'; Let foo ='bar';const baz ='bar'; Functions function $name$ ($parameters$) { } ...
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: ryjfgjl | last post by:
In our work, we often receive Excel tables with data in the same format. If we want to analyze these data, it can be difficult to analyze them because the data is spread across multiple Excel files...
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.