Files
micropython/tests/basics/class_setname_hazard.py
Anson Mansfield 82db5c81e0 tests/basics: Add tests for PEP487 __set_name__.
Including the stochastic tests needed to guarantee sensitivity to the
potential iterate-while-modifying hazard a naive implementation might have.

Signed-off-by: Anson Mansfield <amansfield@mantaro.com>
2025-07-29 09:41:10 +10:00

183 lines
3.0 KiB
Python

# Test that __set_name__ can access and mutate its owner argument.
def skip_if_no_descriptors():
class Descriptor:
def __get__(self, obj, cls):
return
class TestClass:
Forward = Descriptor()
a = TestClass()
try:
a.__class__
except AttributeError:
# Target doesn't support __class__.
print("SKIP")
raise SystemExit
b = a.Forward
if "Descriptor" in repr(b.__class__):
# Target doesn't support descriptors.
print("SKIP")
raise SystemExit
skip_if_no_descriptors()
# Test basic accesses and mutations.
class GetSibling:
def __set_name__(self, owner, name):
print(getattr(owner, name + "_sib"))
class GetSiblingTest:
desc = GetSibling()
desc_sib = 111
t110 = GetSiblingTest()
class SetSibling:
def __set_name__(self, owner, name):
setattr(owner, name + "_sib", 121)
class SetSiblingTest:
desc = SetSibling()
t120 = SetSiblingTest()
print(t120.desc_sib)
class DelSibling:
def __set_name__(self, owner, name):
delattr(owner, name + "_sib")
class DelSiblingTest:
desc = DelSibling()
desc_sib = 131
t130 = DelSiblingTest()
try:
print(t130.desc_sib)
except AttributeError:
print("AttributeError")
class GetSelf:
x = 211
def __set_name__(self, owner, name):
print(getattr(owner, name).x)
class GetSelfTest:
desc = GetSelf()
t210 = GetSelfTest()
class SetSelf:
def __set_name__(self, owner, name):
setattr(owner, name, 221)
class SetSelfTest:
desc = SetSelf()
t220 = SetSelfTest()
print(t220.desc)
class DelSelf:
def __set_name__(self, owner, name):
delattr(owner, name)
class DelSelfTest:
desc = DelSelf()
t230 = DelSelfTest()
try:
print(t230.desc)
except AttributeError:
print("AttributeError")
# Test exception behavior.
class Raise:
def __set_name__(self, owner, name):
raise Exception()
try:
class RaiseTest:
desc = Raise()
except Exception as e: # CPython raises RuntimeError, MicroPython propagates the original exception
print("Exception")
# Ensure removed/overwritten class members still get __set_name__ called.
class SetSpecific:
def __init__(self, sib_name, sib_replace):
self.sib_name = sib_name
self.sib_replace = sib_replace
def __set_name__(self, owner, name):
setattr(owner, self.sib_name, self.sib_replace)
class SetReplaceTest:
a = SetSpecific("b", 312) # one of these is changed first
b = SetSpecific("a", 311)
t310 = SetReplaceTest()
print(t310.a)
print(t310.b)
class DelSpecific:
def __init__(self, sib_name):
self.sib_name = sib_name
def __set_name__(self, owner, name):
delattr(owner, self.sib_name)
class DelReplaceTest:
a = DelSpecific("b") # one of these is removed first
b = DelSpecific("a")
t320 = DelReplaceTest()
try:
print(t320.a)
except AttributeError:
print("AttributeError")
try:
print(t320.b)
except AttributeError:
print("AttributeError")