Skip to content

Property/field mix-up leads to crash after recursion, safe mode restart #6524

@nicolasff

Description

@nicolasff

CircuitPython version

Adafruit CircuitPython 8.0.0-alpha.1 on 2022-06-09; Adafruit Feather ESP32S3 4MB Flash 2MB PSRAM with ESP32S3.

Code/REPL

class RecursiveProperty:
    def __init__(self):
        self.value = 0  # first: "value" is our backing *field*
    
    def set_value(self, new_value):
        """
        A setter for the "value" field
        """
        print('In set_value, new_value =', new_value)
        self.value = new_value
    
    # note that here we use the same "value" name for the property,
    # even though we were already using it for the field backing that property(!)
    value = property(None, set_value)

rec = RecursiveProperty()
rec.value = 42

Behavior

The lines starting with [tio] are messages from the serial client tio.

[tio] Waiting for tty device..
[tio] Connected
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0
In set_value, new_value = 0

[tio] Disconnected
[tio] Connected
Auto-reload is off.
Running in safe mode! Not running saved code.

You are in safe mode because:
CircuitPython core code crashed hard. Whoops!
Crash into the HardFault_Handler.
Please file an issue with the contents of your CIRCUITPY drive at
https://github.com/adafruit/circuitpython/issues

Press any key to enter the REPL. Use CTRL-D to reload.

Description

This crash happens when a class sets a field with a given name in its constructor, and then below the constructor declares a property with the exact same name, providing a setter to assign values.

So:

self.some_name = some_value

followed by:

some_name = property(None, setter_for_the_field)

I have not tried the case where there's also a getter, or just a getter and no setter, just what's in this report.

Note: yes I do know how to write this correctly with different names for the backing field and the property. This was accidental :-)

Additional information

This is what happens when the same code is executed with Python 3.9.13:

In set_value, new_value = 0
In set_value, new_value = 0
[ … many, many more of the same line … ]
In set_value, new_value = 0
In set_value, new_value = 0
Traceback (most recent call last):
  File "/private/tmp/test.py", line 16, in <module>
    rec = RecursiveProperty()
  File "/private/tmp/test.py", line 3, in __init__
    self.value = 0  # first: "value" is our backing *field*
  File "/private/tmp/test.py", line 10, in set_value
    self.value = new_value
  File "/private/tmp/test.py", line 10, in set_value
    self.value = new_value
  File "/private/tmp/test.py", line 10, in set_value
    self.value = new_value
  [Previous line repeated 990 more times]
  File "/private/tmp/test.py", line 9, in set_value
    print('In set_value, new_value =', new_value)
RecursionError: maximum recursion depth exceeded while calling a Python object

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions