python / python/cpython

C API/GC: datetime.datetime doesn't have `Py_TPFLAGS_HAVE_GC`, potentially causing leaks in `tzinfo` cycles

Offen
#132,705 4 Kommentare 0 Reaktionen 0 zugewiesene Personen Auf GitHub ansehen

Dieses Issue hat noch niemand übernommen.

extension-modules topic-C-API type-bug
Vorherrschende Sprache
Python
Sterne
77.2k
Forks
35.9k
PR-Merge-Kennzahlen
PR-Kennzahlen ausstehend

Beschreibung

Bug report

Bug description:

The GC docs say (emphasis mine)

Python’s support for detecting and collecting garbage which involves circular references requires support from object types which are “containers” for other objects which may also be containers. Types which do not store references to other objects, or which only store references to atomic types (such as numbers or strings), do not need to provide any explicit support for garbage collection.

However in datetime, PyDateTime_DateTimeType is a container (of tzinfo), but doesn’t have the GC flags set.

With help on Python's discuss forums, I was able to get to this minimal example that appears to show a possible leak.

from datetime import datetime, tzinfo
import gc, sys

class MyTzinfo(tzinfo): pass

tz = MyTzinfo()
dt = datetime(2000, 1, 1, tzinfo=tz)
tz.foo = dt  # circular ref introduced here
assert sys.getrefcount(tz) == 3 # 1 from dt, 1 variable, 1extra from passing it in
assert sys.getrefcount(tz) == 3 # 1 from tz, 1 variable, 1extra from passing it in
assert len([obj for obj in gc.get_objects() if isinstance(obj, MyTzinfo)]) == 1
del tz
assert len([obj for obj in gc.get_objects() if isinstance(obj, MyTzinfo)]) == 1
del dt
gc.collect()
# At this point, dt and tz should be gone
assert len([obj for obj in gc.get_objects() if isinstance(obj, MyTzinfo)]) == 0

If datetime is replaced with a dummy class, the above program works:

class datetime:
    def __init__(self, *args, **kwargs):
        self.args = args
        self.kwargs = kwargs

I'm happy to submit a PR if needed.

CPython versions tested on:

3.12

Operating systems tested on:

macOS

Beitragsleitfaden

Beitragsleitfaden öffnen

Erste Schritte

  1. Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
  2. Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
  3. Forke das Repository und arbeite in einem Branch.
  4. Öffne einen Pull Request, der die Issue-Nummer nennt.

Rechercherichtung

Beginnen Sie in Modules/_datetimemodule.c mit der Untersuchung von PyDateTime_DateTimeType und der Frage, wie seine tzinfo-Referenz mit der Unterstützung für Garbage Collection interagiert. Führen Sie den minimalen datetime/tzinfo-Zyklus-Reproduzierer aus und untersuchen Sie vorhandene datetime-Tests. Erledigt ist die Aufgabe, wenn der Zyklus nach gc.collect() eingesammelt wird und ein Regressionstest dieses Verhalten abdeckt.

Vom Indexierungsmodell aus dem Issue-Text verfasst.

Bewertung

Tech-Stack
c, python
Bereich
backend
Issue-Typ
Bug
Schwierigkeit
4/5
Geschätzter Aufwand
3-5 Tage
Aktivitätsstatus
Veraltet
Klarheit
Größtenteils klar
Anfängerfreundlichkeit
45/100

Neue Issues direkt in Ihr Postfach

Eine kurze Übersicht über anfängerfreundliche GitHub-Issues.