Opened 5 hours ago

#20335 new defect

Python 3.14 changes refcount semantics

Reported by: Zach Pearson Owned by:
Priority: blocker Milestone: 1.13
Component: Unassigned Version:
Keywords: Cc:
Blocked By: Blocking:
Notify when closed: Platform: all
Project: ChimeraX

Description

From https://docs.python.org/dev/whatsnew/3.14.html in the 'Optimizations' section:

"The interpreter now avoids some reference count modifications internally when it’s safe to do so. This can lead to different values being returned from sys.getrefcount() and Py_REFCNT() compared to previous versions of Python. See below for details."

From the same webpage in the 'Changes in the C API' section:

"The interpreter internally avoids some reference count modifications when loading objects onto the operands stack by borrowing references when possible. This can lead to smaller reference count values compared to previous Python versions. C API extensions that checked Py_REFCNT() of 1 to determine if an function argument is not referenced by any other code should instead use PyUnstable_Object_IsUniqueReferencedTemporary() as a safer replacement."

I grepped the code for 'getrefcount' and 'Py_REFCNT' and found three uses of the former and one use of the latter:

develop git:(develop) $ rg getrefcount
src/bundles/map_data/src/memoryuse.py
90: refs = sys.getrefcount(d.value) - 2

src/bundles/map_data/src/datacache.py
113: if sys.getrefcount(d.value) == 2:

src/bundles/geometry/src/place.py
616:from sys import getrefcount
627: if getrefcount(p) == 3:

develop git:(develop) $ rg Py_REFCNT
src/bundles/atomic_lib/atomic_cpp/atomstruct_cpp/Structure.cpp

105: if (inst == Py_None
Py_REFCNT(inst) == 1)

I'm not sure what user-facing effect the two calls to getrefcount in the map_data bundle will have, if any, but the one in src/bundles/geometry/src/place.py is the root cause of the bug that causes any camera movement to put the near plane on the camera and send models to who-knows-where. Putting this code:

from chimerax.geometry import place
place._reuse_place = lambda create=True: place.Place() if create else None

into ChimeraX's shell stops the bug for the remainder of the time ChimeraX is open.

Change History (0)

Note: See TracTickets for help on using tickets.