[GRASS-user] Question with Python-SWIG example

Hi,

I was trying to run the example given on the "Grass and Python wiki" for
interface between Python-SWIG-GRASS. I ran the raster example, and I got the
following error:

TypeError: 'int' object is unsubscriptable

Which kinds of make sense, because presumably the G_get_raster_row function
returns an integer, to show if the reading was successful or not. So we can
not iterate on that to get the row.
I think that the function returns the result in the "intrast" variable here,
but I can not access it, because it says that it is a "Swig Object of type
'void *'". Any idea on how I can read the rows of the map?

Thanks in advance,
Ahmad
--
View this message in context: http://n2.nabble.com/Question-with-Python-SWIG-example-tp4904954p4904954.html
Sent from the Grass - Users mailing list archive at Nabble.com.

AhmadKhaled wrote:

I was trying to run the example given on the "Grass and Python wiki" for
interface between Python-SWIG-GRASS. I ran the raster example, and I got the
following error:

TypeError: 'int' object is unsubscriptable

Which kinds of make sense, because presumably the G_get_raster_row function
returns an integer, to show if the reading was successful or not. So we can
not iterate on that to get the row.
I think that the function returns the result in the "intrast" variable here,
but I can not access it, because it says that it is a "Swig Object of type
'void *'". Any idea on how I can read the rows of the map?

You have to pass in an array of the correct type and size.

But I wouldn't recommend trying to use the SWIG interface. It's not
particularly robust and not widely understood. If you really need to
use GRASS library functions from Python, use the ctypes module
instead.

--
Glynn Clements <glynn@gclements.plus.com>

On Thu, Apr 15, 2010 at 1:22 PM, Glynn Clements
<glynn@gclements.plus.com> wrote:
...

But I wouldn't recommend trying to use the SWIG interface. It's not
particularly robust and not widely understood. If you really need to
use GRASS library functions from Python, use the ctypes module
instead.

How would
http://grass.osgeo.org/wiki/GRASS_and_Python#Python-SWIG_examples
look like with ctypes? Perhaps the SWIG interface should not be advertised
to much in the Wiki...

Markus

Markus Neteler wrote:

On Thu, Apr 15, 2010 at 1:22 PM, Glynn Clements
<glynn@gclements.plus.com> wrote:
...
> But I wouldn't recommend trying to use the SWIG interface. It's not
> particularly robust and not widely understood. If you really need to
> use GRASS library functions from Python, use the ctypes module
> instead.

How would
http://grass.osgeo.org/wiki/GRASS_and_Python#Python-SWIG_examples
look like with ctypes? Perhaps the SWIG interface should not be advertised
to much in the Wiki...

I've attached versions using ctypes for 6.x and 7.x; only the latter
has been tested.

--
Glynn Clements <glynn@gclements.plus.com>

(attachments)

example6.py (1.04 KB)
example7.py (1.05 KB)

On Sat, Apr 17, 2010 at 1:57 PM, Glynn Clements
<glynn@gclements.plus.com> wrote:

Markus Neteler wrote:

On Thu, Apr 15, 2010 at 1:22 PM, Glynn Clements
<glynn@gclements.plus.com> wrote:
...
> But I wouldn't recommend trying to use the SWIG interface. It's not
> particularly robust and not widely understood. If you really need to
> use GRASS library functions from Python, use the ctypes module
> instead.

How would
http://grass.osgeo.org/wiki/GRASS_and_Python#Python-SWIG_examples
look like with ctypes? Perhaps the SWIG interface should not be advertised
to much in the Wiki...

I've attached versions using ctypes for 6.x and 7.x; only the latter
has been tested.

Thanks for this. For the easy of management, I have put them into SVN
into doc/python/ (all 6.4, 6.5 and 7) along with a simple argument parsing.
Perhaps it should be doxygenized and put elsewhere.

Test results:

GRASS 6.4.0svn (nc_spm_08):~ > g.region rast=elevation
GRASS 6.4.0svn (nc_spm_08):~ > python example_ctypes.py elevation
0 [141.99613952636719, 141.27848815917969, 141.37904357910156
...

GRASS 6.4.0svn (nc_spm_08):~ > r.mapcalc "one = 1"
GRASS 6.4.0svn (nc_spm_08):~ > python example_ctypes.py one
0 [1, 1, ...

-> looks both good.

I get the same results in GRASS 6.5 and 7

Thanks for your examples,
Markus

Markus Neteler wrote:

>> > But I wouldn't recommend trying to use the SWIG interface. It's not
>> > particularly robust and not widely understood. If you really need to
>> > use GRASS library functions from Python, use the ctypes module
>> > instead.
>>
>> How would
>> http://grass.osgeo.org/wiki/GRASS_and_Python#Python-SWIG_examples
>> look like with ctypes? Perhaps the SWIG interface should not be advertised
>> to much in the Wiki...
>
> I've attached versions using ctypes for 6.x and 7.x; only the latter
> has been tested.

Thanks for this. For the easy of management, I have put them into SVN
into doc/python/ (all 6.4, 6.5 and 7) along with a simple argument parsing.
Perhaps it should be doxygenized and put elsewhere.

FWIW, a caveat about using ctypes: because nothing is autogenerated
from the C source code, any Python code has to be manually kept in
sync with the C interface. E.g. the use of "g.version -r" to get the
GIS_H_VERSION string to pass to G__gisinit(), and the hardcoded 0/1/2
values for {CELL,FCELL,DCELL}_TYPE. This could become a significant
issue for definitions which are prone to change, e.g. "struct Option"
and the G_OPT_* constants.

One option would be to use the SWIG wrappers to obtain values for
manifest constants. It might also be possible to use the SWIG wrappers
for the structure types, but I can't figure out how to convert between
a bare pointer and the SWIG wrapper.

Nor can I figure out how to make use of any pointers returned from
functions. Typemaps exist to allow strings, arrays, etc to be
converted to pointers when passing them into functions, but it isn't
possible to do the reverse (converting to a pointer discards the size
and type; converting from a pointer would require some way to obtain
this information). The functions generated by cpointer.i and carrays.i
don't seem to work as expected (I get a segfault).

--
Glynn Clements <glynn@gclements.plus.com>

Glynn wrote:

It might also be possible to use the SWIG wrappers
for the structure types, but I can't figure out how to
convert between a bare pointer and the SWIG wrapper.

Nor can I figure out how to make use of any pointers
returned from functions. Typemaps exist to allow strings,
arrays, etc to be converted to pointers when passing them
into functions, but it isn't possible to do the reverse
(converting to a pointer discards the size and type;
converting from a pointer would require some way
to obtain this information). The functions generated by
cpointer.i and carrays.i don't seem to work as expected
(I get a segfault).

this may be a completely useless suggestion because I only half-
understand the issues, but better to say something than not- if it
helps at all, remember the trunk/swig/python/NumPtr/ library, as
demonstrated by trunk/swig/python/examples/m.distance.py.

regards,
Hamish

Glynn Clements wrote:

{...}

Nor can I figure out how to make use of any pointers returned from
functions. Typemaps exist to allow strings, arrays, etc to be
converted to pointers when passing them into functions, but it isn't
possible to do the reverse (converting to a pointer discards the size
and type; converting from a pointer would require some way to obtain
this information). The functions generated by cpointer.i and carrays.i
don't seem to work as expected (I get a segfault).

Doesn't the ctypes cast() function work for you in making use of
returned pointers?

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

> Glynn Clements wrote:

{...}

> Nor can I figure out how to make use of any pointers returned from
> functions. Typemaps exist to allow strings, arrays, etc to be
> converted to pointers when passing them into functions, but it isn't
> possible to do the reverse (converting to a pointer discards the size
> and type; converting from a pointer would require some way to obtain
> this information). The functions generated by cpointer.i and carrays.i
> don't seem to work as expected (I get a segfault).

Doesn't the ctypes cast() function work for you in making use of
returned pointers?

In case it wasn't clear, I was talking about SWIG-wrapped pointers,
e.g.:

import grass.lib.raster as rast
import grass.lib.grass as gis
import ctypes
gis.G_gisinit("")
p = rast.Rast_allocate_c_buf()
p

<Swig Object of type 'int *' at 0x912e5b8>

q = ctypes.cast(p, ctypes.c_void_p)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/ctypes/__init__.py", line 489, in cast
    return _cast(obj, obj, typ)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong type

--
Glynn Clements <glynn@gclements.plus.com>

[Oops - original inadvertently sent only to Glynn]

From: Glynn Clements [mailto:glynn@gclements.plus.com]

Andrew MacIntyre wrote:

> > Glynn Clements wrote:
>
> {...}
>
> > Nor can I figure out how to make use of any pointers returned from

> > functions. Typemaps exist to allow strings, arrays, etc to be
> > converted to pointers when passing them into functions, but it
> > isn't possible to do the reverse (converting to a pointer discards

> > the size and type; converting from a pointer would require some
> > way to obtain this information). The functions generated by
> > cpointer.i and carrays.i don't seem to work as expected (I get a

segfault).

>
> Doesn't the ctypes cast() function work for you in making use of
> returned pointers?

In case it wasn't clear, I was talking about SWIG-wrapped pointers,
e.g.:

> import grass.lib.raster as rast
> import grass.lib.grass as gis
> import ctypes
> gis.G_gisinit("")
> p = rast.Rast_allocate_c_buf()
> p
<Swig Object of type 'int *' at 0x912e5b8>
> q = ctypes.cast(p, ctypes.c_void_p)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.6/ctypes/__init__.py", line 489, in cast
    return _cast(obj, obj, typ)
ctypes.ArgumentError: argument 1: <type 'exceptions.TypeError'>: wrong

type

Ahh, sorry for not paying close enough attention.

I've never used SWIG and don't profess to understand exactly what its
returning in the code for _wrap_Rast_allocate_c_buf in raster_wrap.c
(~line 12530).

If the pointer object being made available in Python can be made to
publish the pointer address, then the from_address() method of the
target ctypes type (such as a structure) can be used.

After some googling, it seems that the pointer address should be
obtainable by using int() on the pointer object returned by the SWIG
wrapper (i.e. int(p) in the above example), with the resulting value
usable in ctypes' from_address() method. I might be wrong, but I think
that e.g. ctypes.c_void_p(int(p)) might also work to create a ctypes
void pointer instance.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

After some googling, it seems that the pointer address should be
obtainable by using int() on the pointer object returned by the SWIG
wrapper (i.e. int(p) in the above example), with the resulting value
usable in ctypes' from_address() method. I might be wrong, but I think
that e.g. ctypes.c_void_p(int(p)) might also work to create a ctypes
void pointer instance.

That doesn't seem to work either:

  > import grass.lib.grass as gis
  > import grass.lib.raster as rast
  > import ctypes
  > gis.G_gisinit("")
  > p = rast.Rast_allocate_c_buf()
  > a = ctypes.cast(int(p), ctypes.POINTER(ctypes.c_int))
  > infd = rast.Rast_open_old("elevation.dem", "")
  > rast.Rast_get_c_row(infd, a, 10)
  > a[0]
  
  Segmentation fault (core dumped) 14:52:56

It's possible that the typemap is getting in the way here, but I have
no idea what should be done instead.

--
Glynn Clements <glynn@gclements.plus.com>

[Oops - original inadvertently sent only to Glynn, again :-(]

From: Glynn Clements

Andrew MacIntyre wrote:

> After some googling, it seems that the pointer address should be
> obtainable by using int() on the pointer object returned by the SWIG
> wrapper (i.e. int(p) in the above example), with the resulting value
> usable in ctypes' from_address() method. I might be wrong, but I
> think that e.g. ctypes.c_void_p(int(p)) might also work to create a
> ctypes void pointer instance.

That doesn't seem to work either:

  > import grass.lib.grass as gis
  > import grass.lib.raster as rast
  > import ctypes
  > gis.G_gisinit("")
  > p = rast.Rast_allocate_c_buf()
  > a = ctypes.cast(int(p), ctypes.POINTER(ctypes.c_int))
  > infd = rast.Rast_open_old("elevation.dem", "")
  > rast.Rast_get_c_row(infd, a, 10)

I think the above line should read:

   rast. Rast_get_c_row(infd, p, 10)

  > a[0]

Then this might work.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

> > After some googling, it seems that the pointer address should be
> > obtainable by using int() on the pointer object returned by the SWIG
> > wrapper (i.e. int(p) in the above example), with the resulting value
> > usable in ctypes' from_address() method. I might be wrong, but I
> > think that e.g. ctypes.c_void_p(int(p)) might also work to create a
> > ctypes void pointer instance.
>
> That doesn't seem to work either:
>
> > import grass.lib.grass as gis
> > import grass.lib.raster as rast
> > import ctypes
> > gis.G_gisinit("")
> > p = rast.Rast_allocate_c_buf()
> > a = ctypes.cast(int(p), ctypes.POINTER(ctypes.c_int))
> > infd = rast.Rast_open_old("elevation.dem", "")
> > rast.Rast_get_c_row(infd, a, 10)

I think the above line should read:

   rast. Rast_get_c_row(infd, p, 10)

That was what I tried first, but it results in Rast_get_c_row()
segfaulting.

There is a typemap for pointers, which converts a CObject, string,
buffer or sequence. I have no idea how to test for or convert a
SWIG-wrapped pointer.

The fact that passing "a" doesn't segfault appears to indicate that it
was successfully converted to a pointer. The fact that accessing a[0]
afterwards leads me to suspect that it may be getting converted to a
pointer to the "container" rather than to the array proper.

--
Glynn Clements <glynn@gclements.plus.com>

From: Glynn Clements

Andrew MacIntyre wrote:

> > > After some googling, it seems that the pointer address should be
> > > obtainable by using int() on the pointer object returned by the

SWIG

> > > wrapper (i.e. int(p) in the above example), with the resulting

value

> > > usable in ctypes' from_address() method. I might be wrong, but

I

> > > think that e.g. ctypes.c_void_p(int(p)) might also work to

create a

> > > ctypes void pointer instance.
> >
> > That doesn't seem to work either:
> >
> > > import grass.lib.grass as gis
> > > import grass.lib.raster as rast
> > > import ctypes
> > > gis.G_gisinit("")
> > > p = rast.Rast_allocate_c_buf()
> > > a = ctypes.cast(int(p), ctypes.POINTER(ctypes.c_int))
> > > infd = rast.Rast_open_old("elevation.dem", "")
> > > rast.Rast_get_c_row(infd, a, 10)
>
> I think the above line should read:
>
> rast. Rast_get_c_row(infd, p, 10)

That was what I tried first, but it results in Rast_get_c_row()
segfaulting.

Then it seems to me that there's some problem with the SWIG wrapper for
Rast_get_c_row(), if it won't accept a valid SWIG pointer object
returned from another wrapped function.

There is a typemap for pointers, which converts a CObject, string,
buffer or sequence. I have no idea how to test for or convert a
SWIG-wrapped pointer.

In the SWIG wrapper? I see a note about SWIG_ConvertPtr() in
http://www.swig.org/Doc1.3/Python.html#Python_nn64 ?

The fact that passing "a" doesn't segfault appears to indicate that it
was successfully converted to a pointer. The fact that accessing a[0]
afterwards leads me to suspect that it may be getting converted to a
pointer to the "container" rather than to the array proper.

Well, "a" is a ctypes object, so I wouldn't have expected a useful
coercion.

Sadly, I doubt I can contribute much more to this matter :frowning:

Andrew.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

> That was what I tried first, but it results in Rast_get_c_row()
> segfaulting.

Then it seems to me that there's some problem with the SWIG wrapper for
Rast_get_c_row(), if it won't accept a valid SWIG pointer object
returned from another wrapped function.

That's what I suspect, namely that the typemap for pointers is getting
in the way.

Removing the typemap would mean that you couldn't pass a Python
string, array, buffer, sequence, etc directly to a GRASS function, but
would have to explicitly allocate and populate a block of memory.

> There is a typemap for pointers, which converts a CObject, string,
> buffer or sequence. I have no idea how to test for or convert a
> SWIG-wrapped pointer.

In the SWIG wrapper? I see a note about SWIG_ConvertPtr() in
http://www.swig.org/Doc1.3/Python.html#Python_nn64 ?

That might help, although I still don't know how to test whether a
given "PyObject *" is a SWIG-wrapped pointer.

--
Glynn Clements <glynn@gclements.plus.com>

From: Glynn Clements
Andrew MacIntyre wrote:

> > That was what I tried first, but it results in Rast_get_c_row()
> > segfaulting.
>
> Then it seems to me that there's some problem with the SWIG wrapper

for

> Rast_get_c_row(), if it won't accept a valid SWIG pointer object
> returned from another wrapped function.

That's what I suspect, namely that the typemap for pointers is getting
in the way.

Removing the typemap would mean that you couldn't pass a Python
string, array, buffer, sequence, etc directly to a GRASS function, but
would have to explicitly allocate and populate a block of memory.

I don't think you need to get rid of the typemap - I suspect you might
have to expand it to distinguish between the different types of pointers
though (see below)...

> > There is a typemap for pointers, which converts a CObject, string,
> > buffer or sequence. I have no idea how to test for or convert a
> > SWIG-wrapped pointer.
>
> In the SWIG wrapper? I see a note about SWIG_ConvertPtr() in
> http://www.swig.org/Doc1.3/Python.html#Python_nn64 ?

That might help, although I still don't know how to test whether a
given "PyObject *" is a SWIG-wrapped pointer.

I get the impression from the above linked documentation that, for
example, there should be an explicit typemap reference for CELL pointers
(as returned by Rast_allocate_c_buf()) and that the typemap for CELL
pointers as an input argument should use SWIG_ConvertPtr(), which will
set an exception if the inbound object is not a suitable input (if the
exception flag is used; returning NULL will then propagate the
exception). As far as SWIGged pointers go, this seems to be the closest
you can get to Python's *Check* APIs.

Cheers,
Andrew.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

> That's what I suspect, namely that the typemap for pointers is getting
> in the way.
>
> Removing the typemap would mean that you couldn't pass a Python
> string, array, buffer, sequence, etc directly to a GRASS function, but
> would have to explicitly allocate and populate a block of memory.

I don't think you need to get rid of the typemap - I suspect you might
have to expand it to distinguish between the different types of pointers
though (see below)...

Yes, but I know how to get rid of it; I don't know how to expand it.

> > In the SWIG wrapper? I see a note about SWIG_ConvertPtr() in
> > http://www.swig.org/Doc1.3/Python.html#Python_nn64 ?
>
> That might help, although I still don't know how to test whether a
> given "PyObject *" is a SWIG-wrapped pointer.

I get the impression from the above linked documentation that, for
example, there should be an explicit typemap reference for CELL pointers
(as returned by Rast_allocate_c_buf()) and that the typemap for CELL
pointers as an input argument should use SWIG_ConvertPtr(), which will
set an exception if the inbound object is not a suitable input (if the
exception flag is used; returning NULL will then propagate the
exception). As far as SWIGged pointers go, this seems to be the closest
you can get to Python's *Check* APIs.

But does that convert arrays, buffers, etc?

FWIW, the typemaps are in swig/include/python/my_typemaps.i.

--
Glynn Clements <glynn@gclements.plus.com>

From: Glynn Clements

[mailto:glynn@gclements.plus.com]

Sent: Saturday, 24 April 2010 2:34 AM
To: Andrew MacIntyre
Cc: GRASS user list
Subject: RE: [GRASS-user] Question with Python-SWIG example
[SEC=UNCLASSIFIED]

Andrew MacIntyre wrote:

> > That's what I suspect, namely that the typemap for pointers is

getting

> > in the way.
> >
> > Removing the typemap would mean that you couldn't pass a Python
> > string, array, buffer, sequence, etc directly to a GRASS function,

but

> > would have to explicitly allocate and populate a block of memory.
>
> I don't think you need to get rid of the typemap - I suspect you

might

> have to expand it to distinguish between the different types of

pointers

> though (see below)...

Yes, but I know how to get rid of it; I don't know how to expand it.

> > > In the SWIG wrapper? I see a note about SWIG_ConvertPtr() in
> > > http://www.swig.org/Doc1.3/Python.html#Python_nn64 ?
> >
> > That might help, although I still don't know how to test whether a
> > given "PyObject *" is a SWIG-wrapped pointer.
>
> I get the impression from the above linked documentation that, for
> example, there should be an explicit typemap reference for CELL

pointers

> (as returned by Rast_allocate_c_buf()) and that the typemap for CELL
> pointers as an input argument should use SWIG_ConvertPtr(), which

will

> set an exception if the inbound object is not a suitable input (if

the

> exception flag is used; returning NULL will then propagate the
> exception). As far as SWIGged pointers go, this seems to be the

closest

> you can get to Python's *Check* APIs.

But does that convert arrays, buffers, etc?

FWIW, the typemaps are in swig/include/python/my_typemaps.i.

I finally had the chance to look at this last night.

I think the issue is that pyobj_to_ptr() in my_typemaps.i is expecting
the SWIGged pointer as a Python CObject (though it does seem odd that a
pointer object, previously returned from a SWIGged call, being passed in
is at least accepted without a ValueError - which suggests that these
objects are at least "look" like CObjects to Python).

I would suggest that this isn't correct unless you can determine that
SWIG is definitely generating CObjects to contain returned pointers (I
couldn't find anything to suggest this).

In the case where SWIG is using its own objects to contain returned
pointers, then I expect pyobj_to_ptr() needs to be changed to use
SWIG_ConvertPtr() rather than PyCObject_AsVoidPtr() to retrieve the
actual pointer, or the output typemaps for returned pointers need to be
changed to use Python's CObjects rather than native SWIG wrapper
objects.

Several other approaches occur to me:
- use simple SWIG typemaps for the various pointer types and create
helper functions which translate from Python objects to GRASS data
structures and vice versa. It might be possible to use ctypes objects
too, though that could be quite tricky as I couldn't find any direct API
support.

- go to the trouble of actually creating full Python object wrappers for
the various data structures and use them exclusively instead of using
thinly wrapped pointers (this has the potential advantage that memory
management becomes a lot more automatic for the user).

- ditch SWIG completely for Python and use ctypes exclusively.

The ctypes approach is the one I myself would take, but I acknowledge
there are downsides - the potential for sub-optimal performance in
certain cases and the cost of initially generating the wrapper
definitions being the most obvious. I haven't tried the gccxml based
code generator that ctypes author Thomas Heller created, which might
make the job a lot easier if it works on the GRASS headers.

I suspect that the output typemaps change might be easiest to implement.

Cheers,
Andrew.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.

Andrew MacIntyre wrote:

I think the issue is that pyobj_to_ptr() in my_typemaps.i is expecting
the SWIGged pointer as a Python CObject (though it does seem odd that a
pointer object, previously returned from a SWIGged call, being passed in
is at least accepted without a ValueError - which suggests that these
objects are at least "look" like CObjects to Python).

I would suggest that this isn't correct unless you can determine that
SWIG is definitely generating CObjects to contain returned pointers (I
couldn't find anything to suggest this).

The support for CObjects isn't critical; it was really just a case of
"what Python types can reasonably be converted to a pointer?".

In the case where SWIG is using its own objects to contain returned
pointers, then I expect pyobj_to_ptr() needs to be changed to use
SWIG_ConvertPtr() rather than PyCObject_AsVoidPtr() to retrieve the
actual pointer, or the output typemaps for returned pointers need to be
changed to use Python's CObjects rather than native SWIG wrapper
objects.

So, what do you suggest? Remove the PyCObject_Check() call, explicitly
check for string, read buffer, write buffer, and sequence, then call
SWIG_ConvertPtr() on anything which is left over?

Several other approaches occur to me:
- use simple SWIG typemaps for the various pointer types and create
helper functions which translate from Python objects to GRASS data
structures and vice versa. It might be possible to use ctypes objects
too, though that could be quite tricky as I couldn't find any direct API
support.

IOW, strings, lists, arrays, etc would need to be converted
explicitly? That's inconvenient, but I personally consider the issue
of what's possible to be more important than what's simple. If the
typemaps make certain things impossible, they should go, even if that
makes other things less convenient.

- go to the trouble of actually creating full Python object wrappers for
the various data structures and use them exclusively instead of using
thinly wrapped pointers (this has the potential advantage that memory
management becomes a lot more automatic for the user).

- ditch SWIG completely for Python and use ctypes exclusively.

The ctypes approach is the one I myself would take, but I acknowledge
there are downsides - the potential for sub-optimal performance in
certain cases and the cost of initially generating the wrapper
definitions being the most obvious.

The problem isn't initial generation, but maintenance. If someone
changes a structure, enum, prototype, etc in the headers, it's
unlikely that they'll remember to change any ctypes wrapper to match.

I haven't tried the gccxml based
code generator that ctypes author Thomas Heller created, which might
make the job a lot easier if it works on the GRASS headers.

If ctypes wrappers can be generated automatically from the headers,
that solves the problem. Manually generated ctypes wrappers have to be
kept in sync, while SWIG wrappers tend to be hard to use (mostly due
to inadequate documentation).

--
Glynn Clements <glynn@gclements.plus.com>

From: Glynn Clements
Andrew MacIntyre wrote:

> In the case where SWIG is using its own objects to contain returned
> pointers, then I expect pyobj_to_ptr() needs to be changed to use
> SWIG_ConvertPtr() rather than PyCObject_AsVoidPtr() to retrieve the
> actual pointer, or the output typemaps for returned pointers need to

be

> changed to use Python's CObjects rather than native SWIG wrapper
> objects.

So, what do you suggest? Remove the PyCObject_Check() call, explicitly
check for string, read buffer, write buffer, and sequence, then call
SWIG_ConvertPtr() on anything which is left over?

That's what I would try.

Cheers,
Andrew.

-------------------------> "These thoughts are mine alone!" <---------
Andrew MacIntyre Operations Branch
tel: +61 2 6219 5356 Communications Infrastructure Division
fax: +61 2 6253 3277 Australian Communications & Media Authority
email: andrew.macintyre@acma.gov.au http://www.acma.gov.au/

If you have received this email in error, please notify the sender immediately and erase all copies of the email and any attachments to it. The information contained in this email and any attachments may be private, confidential and legally privileged or the subject of copyright. If you are not the addressee it may be illegal to review, disclose, use, forward, or distribute this email and/or its contents.

Unless otherwise specified, the information in the email and any attachments is intended as a guide only and should not be relied upon as legal or technical advice or regarded as a substitute for legal or technical advice in individual cases. Opinions contained in this email or any of its attachments do not necessarily reflect the opinions of ACMA.