PythonScript icon indicating copy to clipboard operation
PythonScript copied to clipboard

Fix `__str__` call for all Python objects

Open tooruu opened this issue 1 year ago • 4 comments

https://github.com/bruderstein/PythonScript/blob/52f7d7b3745ebd629ff4549365b13aac4c813c95/PythonScript/src/ScintillaWrapper.cpp#L61 Naively calling __str__() on an object assumes it is bound, which is not the case for non-instantiated classes. Would it be possible to use Python's str builtin?

tooruu avatar Jul 09 '24 07:07 tooruu

Can you explain when this is a problem when using PythonScript?

Ekopalypse avatar Jul 11 '24 12:07 Ekopalypse

Can you explain when this is a problem when using PythonScript?

Let's take editor.write for example. Its C++ signature expects a boost::python::api::object. As I understand, we can pass any Python object and you convert it to string via ScintillaWrapper::getStringFromObject by calling o.__str__(). This works because you usually pass an instantiated class object, meaning you don't need to manually pass self to its methods (like __str__), so a call without arguments works. But it breaks if you pass an uninstantiated class object (class itself). It doesn't bind self, so it expects you to pass something to the __str__ method.

>>> editor.write('text')  # 'text'.__str__() == 'text'
>>> editor.write(42)  # (42).__str__() == '42'

So far it looks fine with instances of str and int class (return values are omitted for brevity). Let's try a custom class.

>>> class Foo:
...     def __str__(self):
...         return __class__.__name__.lower()
...
>>> editor.write(Foo())  # Foo().__str__() == 'foo'
>>> editor.write(Foo)  # There is no `self` to auto insert into the `__str__` call.
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: Foo.__str__() missing 1 required positional argument: 'self'

One might expect the value of str(Foo) to be written into the editor, but it wants us to pass something to __str__.

P.S. In this example str(Foo) == "<class '__main__.Foo'>". Not the most common use case, but you never know what the user is up to!

>>> class Meta(type):
...     def __str__(cls):
...         return cls.__name__.upper()
... 
>>> class Bar(metaclass=Meta):
...     pass
... 
>>> str(Bar)
'BAR'
>>> editor.write(Bar)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
TypeError: descriptor '__str__' of 'object' object needs an argument

tooruu avatar Jul 11 '24 14:07 tooruu

I understand what you mean, but usually PythonScript is used to manipulate Notepad++, Scintilla and the buffer content, but

but you never know what the user is up to!

I guess you're right

Ekopalypse avatar Jul 12 '24 06:07 Ekopalypse

This isn't high priority, or might not even get addressed. Regardless, it is a bug so I had to bring it to your attention.

tooruu avatar Jul 12 '24 06:07 tooruu