floats passed to DC methods expecting longs

I know we already discussed this, but its late and I can't remember why this
got changed from 2.4.x. It used to be that wxPython accepted floats where it
expected an int or long and it just truncated the float, but now it throws a
SystemError. I just want to get clarification for the change again before I
go and add int() around all my float vars or int(round()). That operation
would certainly be faster in C. Note that even if people use "from wxPython
import wx" they will still get bitten by this change if they use floats.

At least once I've seen "DeprecationWarning: integer argument expected, got
float" in 2.5.x but now I can't seem to find which method output the
message. In 2.4.2.4 I do get warnings.

C:\Python23\lib\site-packages\wxPython\gdi.py:625: DeprecationWarning:
integer argument expected, got float
  val = gdic.wxDC_DrawPoint(self, *_args, **_kwargs)
C:\Python23\lib\site-packages\wxPython\gdi.py:643: DeprecationWarning:
integer argument expected, got float
  val = gdic.wxDC_DrawText(self, *_args, **_kwargs)

Passing a float where an int or long is expected in 2.5.x generally results
in something like...

  File "C:\Python23\Lib\site-packages\wx\gdi.py", line 2381, in DrawLineXY
    return _gdi.DC_DrawLineXY(*args, **kwargs)
SystemError: C:\sf\python\dist\src-maint23\Objects\longobject.c:197: bad
argument to internal function

  File "C:\Python23\Lib\site-packages\wx\gdi.py", line 2421, in DrawPointXY
    return _gdi.DC_DrawPointXY(*args, **kwargs)
SystemError: C:\sf\python\dist\src-maint23\Objects\longobject.c:197: bad
argument to internal function

  File "C:\Python23\Lib\site-packages\wx\gdi.py", line 2489, in DrawTextXY
    return _gdi.DC_DrawTextXY(*args, **kwargs)
SystemError: C:\sf\python\dist\src-maint23\Objects\longobject.c:197: bad
argument to internal function

ka

Kevin Altis wrote:

I know we already discussed this, but its late and I can't remember why this
got changed from 2.4.x. It used to be that wxPython accepted floats where it
expected an int or long and it just truncated the float, but now it throws a
SystemError. I just want to get clarification for the change again before I
go and add int() around all my float vars or int(round()). That operation
would certainly be faster in C. Note that even if people use "from wxPython
import wx" they will still get bitten by this change if they use floats.

At least once I've seen "DeprecationWarning: integer argument expected, got
float" in 2.5.x but now I can't seem to find which method output the
message. In 2.4.2.4 I do get warnings.

There is a combination of factors here. In 2.4.x and also in early previews of 2.5.x, SWIG parsed the args using something like this:

PyArg_ParseTupleAndKeywords(args,kwargs,"Oiiii:DC_DrawLineXY",...

and the conversion to int would happen there. In Python 2.3 they added the deprecation warning when a float was passed for one of the 'i' args.

Since then SWIG has changed such that it only extracts PyObjects (uses "OOOOO" in the ParseTuple call) and then does a separate converstion pass. For int's it generates code like this:

     arg3 = (int) SWIG_AsInt(obj2);
     if (PyErr_Occurred()) SWIG_fail;

where SWIG_AsInt looks like this:

     long SWIG_AsLong(PyObject * obj)
     {
       return PyInt_Check(obj) ? PyInt_AsLong(obj) : PyLong_AsLong(obj);
     }

     #define SWIG_AsInt SWIG_AsLong

When you pass a float the PyInt_Check fails and so the PyLong_AsLong is called which is the one generating the SystemError exception.

I just realized that I can override the code specified for SWIG_AsLong to make it more robust, including allowing floats again. Should I do it? If so, should I hold up the general release for it?

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

Robin Dunn wrote:

I just realized that I can override the code specified for SWIG_AsLong to make it more robust, including allowing floats again. Should I do it? If so, should I hold up the general release for it?

FYI, Here is what it looks like:

SWIG_AsLong(PyObject * obj)
{
     if (PyInt_Check(obj))
         return PyInt_AsLong(obj);
     else if (PyLong_Check(obj))
         return PyLong_AsLong(obj);
     else {
         PyObject* pyint = PyNumber_Int(obj);
         if (pyint == NULL) return 0;
         long rval = PyInt_AsLong(pyint);
         Py_DECREF(pyint);
         return rval;
     }
}

So it will be just as fast for the case that an int is passed, but it cal also try to convert the obj to an int if possible. Here are some examples:

>>> wx.Sleep(1)
>>> wx.Sleep(1.9)
>>> wx.Sleep("2")
>>> wx.Sleep("sfsf")
Traceback (most recent call last):
   File "<input>", line 1, in ?
   File "/projects/wx2.5/wxPython/wx/misc.py", line 296, in Sleep
     return _misc.Sleep(*args, **kwargs)
ValueError: invalid literal for int(): sfsf
>>> wx.Sleep([1,2,3])
Traceback (most recent call last):
   File "<input>", line 1, in ?
   File "/projects/wx2.5/wxPython/wx/misc.py", line 296, in Sleep
     return _misc.Sleep(*args, **kwargs)
TypeError: int() argument must be a string or a number

I'm not sure that I like the fact that it allows strings, and that it mentions "int()" in the exception messages. That could be confusing. I suppose I can be more specific about the types allowed and then raise my own TypeError.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

If you're going to make this change, then yes we should have a, sigh, 2.5.1.4 build, before general release. It looks like there are no speed penalties, the truncation and support of floats seems right. It would be interesting to hear a BDFL pronouncement on this kind of thing since it is an implicit conversion. People can always pass in round(arg) if they need rounding instead of truncation. Hmm, I wonder if round would be a better default than truncation, aka int()?

ka

···

On Mar 30, 2004, at 8:45 AM, Robin Dunn wrote:

I just realized that I can override the code specified for SWIG_AsLong to make it more robust, including allowing floats again. Should I do it? If so, should I hold up the general release for it?

Kevin Altis wrote:

It would be interesting to hear a BDFL pronouncement on this kind of thing since it is an implicit conversion.

The Python BDFL (Guido) or the wxPython BDFL (Robin)?

If Guido, he has pronounced. Auto conversion to an interger type in extensions has been deprecated, that's why we got all those warnings with 2.3.

If Robin, it sounds like he like the idea.

A note: I havn't found it to be a problem to convert to integer types ahead of time, and kind of appreciate being forced to be aware of what I'm doing. For instance, it's much more likely that I'll think aobut whether to round or truncate, which brings me to:

Hmm, I wonder if round would be a better default than truncation, aka int()?

Nope. if there is going to be explicit type casting, it should be done in the mmost common way, truncation.

To speak for someone else, Gordon Williams ended up creating a wrapper class to the DCs in wxPyPlot, so he could pass in floats, and the wrapper converted them. I'm sure he'd prefer wxPython do it for him.

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@noaa.gov

Kevin Altis wrote:

I just realized that I can override the code specified for SWIG_AsLong to make it more robust, including allowing floats again. Should I do it? If so, should I hold up the general release for it?

If you're going to make this change, then yes we should have a, sigh, 2.5.1.4 build, before general release. It looks like there are no speed penalties, the truncation and support of floats seems right.

Here is what I am going with (and also a similar function for Floats):

long SWIG_AsLong(PyObject * obj)
{
     if (PyNumber_Check(obj))
         return PyInt_AsLong(obj);
     else {
         PyObject* errmsg = PyString_FromFormat(
                    "Expected number, got %s",
                    obj->ob_type->tp_name);
         PyErr_SetObject(PyExc_TypeError, errmsg);
         Py_DECREF(errmsg);
         return 0;
     }
}

PyNumber_Check is fast, and this should allow any type (built-in, python classes, or extension types) that has the "number protocol" slots filled to be used as a parameter and converted by the wrappers.

It would be interesting to hear a BDFL pronouncement on this kind of thing since it is an implicit conversion. People can always pass in round(arg) if they need rounding instead of truncation. Hmm, I wonder if round would be a better default than truncation, aka int()?

If it isn't already an Integer then PyInt_AsLong will ask the object to convert itself to an Interger, so whatever the type does for that is what will happen. For regular Python Floats it will truncate, of course that can be changed with a little magic:

>>> class MyFloat(float):
... def __int__(self):
... return int(round(self))
...
>>>
>>> f = MyFloat(23.56)
>>> f
23.559999999999999
>>> int(f)
24
>>> p = wx.Point(5.6, f)
>>> p
wx.Point(5, 24)

···

On Mar 30, 2004, at 8:45 AM, Robin Dunn wrote:

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

>>> wx.Sleep("2")

[...]

I'm not sure that I like the fact that it allows strings,

FWIW, I don't like this one either. But it would be nice to allow floats.

Alberto

Alberto Griggio wrote:

>>> wx.Sleep("2")

[...]

I'm not sure that I like the fact that it allows strings,

FWIW, I don't like this one either. But it would be nice to allow floats.

Here's my new one:

>>> wx.Sleep(1)
>>> wx.Sleep(1.1)
>>> wx.Sleep(1L)
>>> wx.Sleep("2")
Traceback (most recent call last):
   File "<input>", line 1, in ?
   File "/projects/wx2.5/wxPython/wx/misc.py", line 296, in Sleep
     return _misc.Sleep(*args, **kwargs)
TypeError: Expected number, got str
>>> wx.Sleep(None)
Traceback (most recent call last):
   File "<input>", line 1, in ?
   File "/projects/wx2.5/wxPython/wx/misc.py", line 296, in Sleep
     return _misc.Sleep(*args, **kwargs)
TypeError: Expected number, got NoneType
>>> wx.Sleep([1,2,3])
Traceback (most recent call last):
   File "<input>", line 1, in ?
   File "/projects/wx2.5/wxPython/wx/misc.py", line 296, in Sleep
     return _misc.Sleep(*args, **kwargs)
TypeError: Expected number, got list

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!

[Chris, the date on your computer is 2 days off!]

Chris Barker wrote:

Kevin Altis wrote:

It would be interesting to hear a BDFL pronouncement on this kind of thing since it is an implicit conversion.

The Python BDFL (Guido) or the wxPython BDFL (Robin)?

If Guido, he has pronounced. Auto conversion to an interger type in extensions has been deprecated,

Just when using the PyArg_ParseTuple* functions, I believe. But then the vast majority of extensions will use the PyArg_ParseTuple* functions so it amounts to the same thing.

that's why we got all those warnings with 2.3.

If Robin, it sounds like he like the idea.

I like the idea of beter conversions and more correct and informative exceptions. The fact that we can get float --> int conversions too is just icing on the cake. (In C/C++ you can pass a float to a function expecting int and it will convert for you. It just makes sense to me to allow the same in Python code that tightly wraps that C/C++ function.)

A note: I havn't found it to be a problem to convert to integer types ahead of time, and kind of appreciate being forced to be aware of what I'm doing. For instance, it's much more likely that I'll think aobut whether to round or truncate, which brings me to:

Hmm, I wonder if round would be a better default than truncation, aka int()?

Nope. if there is going to be explicit type casting, it should be done in the mmost common way, truncation.

Even better is what I ended up with, allowing the type to convert itself.

To speak for someone else, Gordon Williams ended up creating a wrapper class to the DCs in wxPyPlot, so he could pass in floats, and the wrapper converted them. I'm sure he'd prefer wxPython do it for him.

Yep.

···

--
Robin Dunn
Software Craftsman
http://wxPython.org Java give you jitters? Relax with wxPython!