Assigning to an objects __dict__: TypeError

General discussions and questions abound development of code with MicroPython that is not hardware specific.
Target audience: MicroPython Users.
Post Reply
User avatar
scy
Posts: 7
Joined: Sat Sep 05, 2020 12:50 am
Contact:

Assigning to an objects __dict__: TypeError

Post by scy » Fri Oct 09, 2020 6:26 pm

Hi there!

I’m currently trying out several things to work around MicroPython not supporting __setattr__ on ESP32. One of them requires writing to an instance’s __dict__. However, neither updating an existing value nor creating a new one works:

Code: Select all

class Foo:
    def __init__(self):
        self.a = 123

f = Foo()

print(f.__dict__)  # {'a': 123}
print(type(f.__dict__))  # <class 'dict'>
f.__dict__['a'] = 456  # "TypeError:"
f.__dict__['b'] = 456  # "TypeError:"
The resulting TypeError has no associated message. Searching the forums brought me to a thread where someone tried to subclass dict and got the same message, but that’s not quite what I’m trying to do here.

Why am I not using setattr() instead? Because the attribute I’m trying to set is using a custom descriptor (that’s my workaround for the missing __setattr__), and using setattr() would lead to infinite recursion.

If you’re interested, my descriptor attempt is designed to try to manually invoke the __setattr__ method. It basically works like this:

Code: Select all

class ViaSetAttr:

    def __init__(self, name):
        self.name = name

    def __get__(self, obj, type=None):
        return obj.__dict__[self.name]

    def __set__(self, obj, value):
        obj.__setattr__(self.name, value)

class Foo:

    def __setattr__(self, name, value):
        # This shall basically behave like cPython's __setattr__.
        # However, since it's not called by MicroPython, the special descriptor calls it manually.

# This programmatically creates new attributes on the Foo class and saves me
# some repetitive typing. It's not the issue.
for name in ['some', 'properties', 'that', 'should', 'go', 'through', 'setattr']:
    setattr(Foo, name, ViaSetAttr(name))
And yes, I might instead use @property and define getters and setters, but since I need to only catch the write operations, not the reads, this will lead to quite some overhead that I had hoped to avoid.

Is there a way I can write to __dict__? If not, is there a workaround that will not lead to infinite recursion with my custom descriptor?

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by pythoncoder » Sun Oct 11, 2020 4:12 pm

I assume you've read the differences doc. That discusses descriptors. Adafruit make extensive use of descriptors in their CircuitPython libraries so you may get some ideas from there.

The differences doc also has this statement:
Instance __dict__ support is optional (disabled in many ports) and read-only, so foo.__dict__['bar'] = 23 or foo.__dict__.update({'bar': 23}) does not work. #1757 #2139
Peter Hinch
Index to my micropython libraries.

User avatar
scy
Posts: 7
Joined: Sat Sep 05, 2020 12:50 am
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by scy » Sun Oct 11, 2020 5:46 pm

pythoncoder wrote:
Sun Oct 11, 2020 4:12 pm
I assume you've read the differences doc.
Oh! No, in fact I did not, thanks for pointing me to it. I’ve just read the MicroPython differences from CPython page in the docs on the website, didn’t even know that there was additional content in the GitHub wiki.

That definitely explains it, and it has links to the relevant issues, thank you very much :)

Since __setattr__ support looks like it’s coming soon and I’ve rewritten my code to use properties anyway (which bloats up the number of lines, but works), I’ll just wait for that to arrive.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by pythoncoder » Mon Oct 12, 2020 10:54 am

I don't know if your code is performance critical, but properties are deprecated for high performance applications because they are near the bottom of the search tree. Of course in many cases this is not an issue.
Peter Hinch
Index to my micropython libraries.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by jimmo » Tue Oct 20, 2020 6:25 am

scy wrote:
Sun Oct 11, 2020 5:46 pm
Since __setattr__ support looks like it’s coming soon and I’ve rewritten my code to use properties anyway (which bloats up the number of lines, but works), I’ll just wait for that to arrive.
I don't think there's any __setattr__ changes currently in the pipeline? However there was a big improvement in v1.13 (released last month, but the code was added in December last year -- see https://github.com/micropython/micropython/pull/5404)

User avatar
scy
Posts: 7
Joined: Sat Sep 05, 2020 12:50 am
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by scy » Tue Oct 20, 2020 8:37 am

jimmo wrote:
Tue Oct 20, 2020 6:25 am
I don't think there's any __setattr__ changes currently in the pipeline?
I don’t know, but #6427 unix/mpconfigport: Enable MICROPY_PY_DELATTR_SETATTR looks to me as if the flag to enable the current __setattr__ implementation, that is currently only active for the STM32 port, might be switched on for other ports soon.

And yes, of course building your own version with that flag enabled is an option already, but it’s more inconvenient for most of us.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Assigning to an objects __dict__: TypeError

Post by jimmo » Tue Oct 20, 2020 10:18 pm

scy wrote:
Tue Oct 20, 2020 8:37 am
I don’t know, but #6427 unix/mpconfigport: Enable MICROPY_PY_DELATTR_SETATTR looks to me as if the flag to enable the current __setattr__ implementation, that is currently only active for the STM32 port, might be switched on for other ports soon.
Ah cool, sorry I didn't realise this wasn't enabled everywhere. That PR is now merged. ESP32 should be enabled shortly too (#6560).

Post Reply