Opened 3 weeks ago

Last modified 3 weeks ago

#20335 assigned defect

Python 3.14 changes refcount semantics

Reported by: Zach Pearson Owned by: Tom Goddard
Priority: blocker Milestone: 1.13
Component: Core Version:
Keywords: Cc: chimera-programmers
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 (2)

comment:1 by Eric Pettersen, 3 weeks ago

Cc: chimera-programmers added
Component: UnassignedCore
Owner: set to Tom Goddard
Status: newassigned

comment:2 by Eric Pettersen, 3 weeks ago

AFAIK the atomic_lib C++ Py_REFCNT use should be fine. It is testing whether there are still references to a Chain object after the "C++ reference" to it has been removed, and doing one thing if there still are references, and something else if there aren't.

Note: See TracTickets for help on using tickets.