Skip to content

SharedNPArray.__del__ AttributeError on partially-constructed instance (mem.py:76) #97

Description

@tavateva

Split out from #61 as a pre-existing bug that predates PR #96 and is out-of-scope for the coverage work.

Symptom

Observed during the test_coverage CI run (e.g. https://github.com/ChengLabResearch/ouroboros/actions/runs/28622389980) as a PytestUnraisableExceptionWarning:

test/helpers/test_mem.py::test_direct_create
  .../unraisableexception.py: PytestUnraisableExceptionWarning: Exception ignored in: <function SharedNPArray.__del__ at 0x...>
  Traceback (most recent call last):
    File ".../python/ouroboros/helpers/mem.py", line 76, in __del__
      if not self.__shutdown:
             ^^^^^^^^^^^^^^^
  AttributeError: 'SharedNPArray' object has no attribute '_SharedNPArray__shutdown'

Cause

SharedNPArray.__init__ (mem.py:24) sets self.__shutdown = False at line 36 — after several other attribute assignments that can raise (SharedMemory(name), np.dtype(dtype), etc.). When any of those raises, the partially-constructed instance still gets __del__'d during GC, and __del__ at line 76 references self.__shutdown before it was assigned. Python swallows the AttributeError into an unraisable warning.

Reproduction

The warning also fires without an exception if a SharedNPArray is created via object.__new__(SharedNPArray) (e.g. some pickle round-trips or object reconstruction paths) — but the more common trigger is an exception mid-__init__.

Suggested fix

Two options, either works:

  1. Class-level default (preferred — minimal diff):

    class SharedNPArray:
        _SharedNPArray__shutdown = True  # or set at class body scope as: __shutdown = True
    
        def __init__(self, ...):
            ...
            self.__shutdown = False  # flip to False after all mandatory attrs are set

    Set the class-level default to True so __del__ treats an unconstructed instance as already-shut-down.

  2. Guard __del__:

    def __del__(self):
        if not getattr(self, "_SharedNPArray__shutdown", True):
            self.shutdown()

Option 1 is cleaner; option 2 is safer if there might be other name-mangling edge cases.

Acceptance

  • SharedNPArray.__del__ no longer raises AttributeError on partially-constructed instances.
  • A test — e.g. constructing SharedNPArray with a missing/invalid SharedMemory name and letting it be GC'd — confirms no PytestUnraisableExceptionWarning in the pytest run.
  • The test_coverage workflow output shows the two-warning count drop to 1 (the Pydantic deprecation, tracked separately) or 0 if that's also fixed in the same window.

Out of scope

  • Pydantic v2.11 model_fields-on-instance deprecation in models.py:117 — tracked separately.

Metadata

Metadata

Assignees

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