<korvo>
uis: AFAICT this is a mismatch in expectations; Python's GC isn't specified to that degree, so those tests should really only be expected to pass on a reference-counting interpreter like CPython.
<uis>
Even with explicit delete and call to collect?
<korvo>
cfbolz: Thanks! I've started a new build.
<korvo>
uis: Yep! Also `gc.disable()` doesn't actually disable PyPy's GC. Users don't have control over when finalizers run, only the promise that the finalizer will run after the object is dead. Users also don't control when the object dies, only when the object is unreachable.
<korvo>
The tradeoff is that the object might never exist on the heap at all, if the JIT can prove that the object can be broken up into pieces somehow. I don't know if that happens for (micro)numpy arrays, and probably not for larger arrays.
<cfbolz>
korvo: gc.disable *does* stop the calling of finalizers, I think
<cfbolz>
it doesn't disable minor collections, though
<korvo>
cfbolz: Oh, TIL!
<cfbolz>
but yes, in theory after an explicit call to collect (maybe a few of them) all unreachable objects should be finalized
<korvo>
uis: So, I don't want to just barge onto wjakob's closed issue, but I might advise them to conditionally skip these tests on non-CPython. If they want to support something like MicroPython then maybe they'd want to flip that condition around, but that's the right way to go.
<korvo>
Hm. Maybe after a finite number of calls to .collect()? But I'm not sure about a bounded number of calls; that sounds like a flaky test that'll fail with a lambda distribution.
<korvo>
(Consider: The test is *already* flaky with a lambda distribution, using a bound of 1.)
<uis>
Pypy docs say collect() calls collect_step() until major collection finishes. Whatever that means.
<uis>
JIT may not allocate space on heap, but constructor may be called on stack memory. Unless my guess about "Maybe interpreter sees that object is never interacted with and does not call constructor in first place?" is correct.
<korvo>
Oh, I don't think that the JIT's to blame. I'm justifying why PyPy's promises about GC behavior are weaker than CPython's; the weaker promises lead to opportunities for speed.
<korvo>
The GC has multiple heaps. When objects are created, they always start in the *nursery*, a heap for young objects. A minor collection only clears out the nursery. A major collection clears out all heaps.
<korvo>
The reason for .collect_step() internally is that PyPy's major collections are incremental and broken up into tiny steps. It makes programs more responsive to user input.
<korvo>
uis: So, let me explain cfbolz' idea. After one major collection, maybe the object under test is unreachable, but its finalizer hasn't run yet, or maybe it isn't even collected yet. But the GC will get it next time! So just run one more major collection.
<korvo>
...But maybe the finalizer doesn't run after another major collection. It's not guaranteed. What is guaranteed (I think) is that the GC eventually finishes all of its work. So after some finite number of major collections, the finalizer will run, because the GC can't not do it.
<korvo>
cfbolz: The bootstrap builds! Now building PyPy 3.11 and doing that import-test again.
<korvo>
Oh, hm. "cp: cannot stat 'pypy3.11-c': No such file or directory" I suppose that I didn't configure the tarball correctly. But everything else works!