Python bindings: Account.getName() raises TypeError

John Ralls jralls at ceridwen.us
Tue Mar 18 10:28:56 EDT 2014


On Mar 18, 2014, at 6:30 AM, Derek Atkins <warlord at MIT.EDU> wrote:

> John Ralls <jralls at ceridwen.us> writes:
> 
>> On Mar 17, 2014, at 10:52 AM, Derek Atkins <warlord at mit.edu> wrote:
>> 
>>> John Ralls <jralls at ceridwen.us> writes:
>>> 
>>>>> TypeError: in method 'xaccAccountGetName', argument 1 of type
>>>>> Account const *'
>>> [snip]
>>>> 
>>>> The signature of xaccAccountGetName is const char* xaccAccountGetName
>>>> (const Account *); the const was added in 2005. A "const Account *" is
>>>> not the same as an "Account const *": The former means that the
>>>> contents of the pointer won't change, the latter means that the
>>>> pointer itself won't change (see
>>>> https://www.cs.bu.edu/teaching/cpp/const/).
>>>> 
>>>> Somewhere in the SWIG-generated Python-to-C translation code the
>>>> argument type is wrong, but it's not directly in the stack trace from
>>>> Python. It's more likely due to a change in SWIG than to a change in
>>>> GnuCash.
>>> 
>>> Could it be a const v non-const issue?  I.e., if python has an Account*
>>> object (non-const) does it realize that it can pass it to a function
>>> that is asking for a const object (either const Account* or Account
>>> const * -- doesn't matter)?  C certainly knows this, but it sounds like
>>> Python might not.
>> 
>> At that level, it’s all C; in this case it’s C generated by SWIG
>> instead of written by hand, but it’s still C.
>> 
>> Python doesn’t even have the concept of const, and its handling of
>> types is deliberately generic: If an object has a member with the
>> right name, it’s happy.
> 
> Is the TypeError coming from Python or coming from the SWIG bindings?
> The fact that Python doesn't know about 'const' is exactly my point.  It
> probably sees "Account *" and "Account const *" as two distinct types,
> which would explain why it's complaining about the "Type Error".

My point is that Python barely knows about type at all, except for its own built-in types [1]. It will raise a TypeError if you try to divide a string, like this:
>>> 'foo' / 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: unsupported operand type(s) for /: 'str' and 'int'
or dereference an index of something that isn't a sequence and so on. Otherwise it blithely tries to do what you ask it, raising an AttributeError if you try to dereference a non-existant class member, which includes calling a function that the object's class doesn't have:
>>> 'foo'.framish()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'str' object has no attribute 'framish'

Python doesn't have a way to declare type arguments to a function, so it can't raise a TypeError if an argument is the wrong type: It has no way of knowing. One can write code like this:
  if not isinstance(foo, 'Account'):
     raise TypeError("foo is a %s which is not a subclass of Account" % type(foo))

But it's rare to see that in live code. It's considered more "Pythonic" to try to do what you want and handle the exception if it fails.

Regards,
John Ralls

[1] http://docs.python.org/2/library/stdtypes.html




More information about the gnucash-devel mailing list