Skip to content

set_dynamic_attr: missing return -1 after read-only error + return -1 without exception + DICT_SUBCLASS flag #601

Description

@devdanzin

Three DynamicScope/set_dynamic_attr issues:

1. Missing return -1 after read-only descriptor error (dynamicscope.cpp:296-306)

When a read-only descriptor is found (has __get__ but no __set__), PyErr_Format(PyExc_AttributeError, ...) is called, but the code falls through to the "step up to parent" logic instead of returning -1. The error is set but never delivered — setting a read-only attribute silently succeeds.

Reproducer:

import gc; gc.disable()
import os
from enaml.core._dynamicscope import _DynamicScope

class ReadOnlyDescr:
    def __get__(self, obj, objtype=None):
        return 42

class Owner:
    readonly = ReadOnlyDescr()

ds = _DynamicScope(Owner(), {}, {}, {})
ds["readonly"] = 99  # Should raise AttributeError, but silently succeeds!
os._exit(0)

Fix: add return -1; after the PyErr_Format call.

2. return -1 without exception at bottom of set_dynamic_attr (dynamicscope.cpp:308)

When the while loop exits (reached Py_None at top of scope chain), the function returns -1 without setting an exception. The callers (Nonlocals_setattro, Nonlocals_setitem) check if(res < 0 && !PyErr_Occurred()) and set their own exception, so this doesn't crash — but it violates the Python/C API contract.

3. Py_TPFLAGS_DICT_SUBCLASS on DynamicScope (dynamicscope.cpp:869)

DynamicScope sets Py_TPFLAGS_DICT_SUBCLASS but is NOT a dict subclass — it just implements the mapping protocol. Testing shows PyType_FromSpec currently strips this flag (so isinstance(ds, dict) returns False), but the flag should still be removed to avoid confusion and future breakage if PyType_FromSpec behavior changes.

Found by cext-review-toolkit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions