Skip to content

gh-157176: Make time.struct_time type immutable - #157179

Merged
vstinner merged 6 commits into
python:mainfrom
ashm-dev:gh-157176
Oct 7, 2026
Merged

vstinner merged 6 commits into
python:mainfrom
ashm-dev:gh-157176

Conversation

@ashm-dev

@ashm-dev ashm-dev commented Sep 8, 2026 •

Copy link
Copy Markdown
Contributor

@ashm-dev

ashm-dev commented Sep 8, 2026

Copy link
Copy Markdown
Contributor Author

@sergey-miryanov, could you please review this PR?

Comment thread Objects/structseq.c Outdated
@vstinner

Copy link
Copy Markdown
Member

The reported issue is a leak when modifying structseq types. But this PR changes how structseq instances are tracked by the GC. Can you explain me the relationship between the two?

@ashm-dev

Copy link
Copy Markdown
Contributor Author

The reported issue is a leak when modifying structseq types. But this PR changes how structseq instances are tracked by the GC. Can you explain me the relationship between the two?

They are the same bug. Modifying the type creates the cycle type -> tp_dict -> t -> type. It leaks because t is untracked, so the GC cannot see the t -> type edge. Tracking the instance makes that edge visible and lets the GC collect the cycle.

@vstinner

Copy link
Copy Markdown
Member

See also #157447 change which is limited to sys.unraisablehook.

@serhiy-storchaka

Copy link
Copy Markdown
Member

Claude initially wrote exactly this change. But it is potentially breaking, the following PyObject_GC_Track() in the user code will crash.

@ashm-dev

Copy link
Copy Markdown
Contributor Author

Claude initially wrote exactly this change. But it is potentially breaking, the following PyObject_GC_Track() in the user code will crash.

But the fix in your PR doesn't resolve the leak from my issue.

@sergey-miryanov

Copy link
Copy Markdown
Contributor

But the fix in your PR doesn't resolve the leak from my issue.

Your both issues have a common root but fix from #157447 shouldn't fix your issue.

@ashm-dev

ashm-dev commented Oct 3, 2026

Copy link
Copy Markdown
Contributor Author

Friendly ping @serhiy-storchaka @vstinner — what's the plan here?

@vstinner

vstinner commented Oct 5, 2026

Copy link
Copy Markdown
Member

Sadly, changing the public PyStructSequence_New() C API sounds dangerous. As @serhiy-storchaka wrote, it can break existing C extensions which call PyObject_GC_Track().

You can change internal C APIs or modify stdlib extensions to call PyObject_GC_Track() after PyStructSequence_New(). I'm not sure of what's the best approach.

Also, if it's recommended to track objects created by PyStructSequence_New() in the GC, you should mention it in PyStructSequence_New() documentation.

@ashm-dev

ashm-dev commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

Should I revert the PyStructSequence_New() change, add PyObject_GC_Track() to stdlib callers (time, os, etc.) similar to #157447, and document the tracking requirement in PyStructSequence_New() docs?

@vstinner

vstinner commented Oct 5, 2026

Copy link
Copy Markdown
Member

Should I revert the PyStructSequence_New() change, add PyObject_GC_Track() to stdlib callers (time, os, etc.) similar to #157447, and document the tracking requirement in PyStructSequence_New() docs?

Yes, but only add PyObject_GC_Track() if an object can contains other objects which can be involved in a reference cycle.

@ashm-dev

ashm-dev commented Oct 5, 2026

Copy link
Copy Markdown
Contributor Author

@vstinner Done. Reverted changes to PyStructSequence_New(), added PyObject_GC_Track() to time.struct_time and sys.get_asyncgen_hooks(), documented the requirement in PyStructSequence_New() docs, and updated tests.

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

        # Instances created via Python or stdlib callers that may participate
        # in cycles are GC-tracked; simple instances like os.stat_result are not.
        self.assertTrue(gc.is_tracked(time.gmtime()))

time.gmtime() members are all integers. How can it be involved in a reference cycle?

Code from the issue:

./python -c "import time; t = time.gmtime(); type(t).refcyle = t;"

In your example, you modify the type, not a structseq instance.

I'm not sure that this change is correct.

cc @serhiy-storchaka

@vstinner

vstinner commented Oct 5, 2026

Copy link
Copy Markdown
Member

Instead, the type should be made immutable: #157176 (comment).

@serhiy-storchaka

Copy link
Copy Markdown
Member

AFAIK only the argument of sys.unraisablehook contains other objects which can be involved in a reference cycle (this is #157447).

@vstinner vstinner left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I just have a request on the test.

cc @methane

Comment thread Lib/test/test_structseq.py Outdated
@vstinner

vstinner commented Oct 7, 2026

Copy link
Copy Markdown
Member

The Docs CI failed with:

      File "/home/runner/work/cpython/cpython/Doc/venv/lib/python3.14/site-packages/sphinx/builders/changes.py", line 61, in write_documents
        ttext = self.typemap[changeset.type]
                ~~~~~~~~~~~~^^^^^^^^^^^^^^^^
    KeyError: 'soft-deprecated'

Let me update the branch to see if it does fix the issue.

@vstinner
vstinner enabled auto-merge (squash) October 7, 2026 14:30
@vstinner

vstinner commented Oct 7, 2026

Copy link
Copy Markdown
Member

I enabled auto-merged, please don't touch the PR (your branch) anymore.

Thanks for support.check_immutable_type(self, type(time.gmtime())). The intent of the test is now more obvious (at least,to me).

@vstinner vstinner changed the title gh-157176: Fix GC tracking in PyStructSequence_New gh-157176: Make time.struct_time type immutable Oct 7, 2026
@vstinner
vstinner merged commit acfa776 into python:main Oct 7, 2026
55 checks passed
@ashm-dev
ashm-dev deleted the gh-157176 branch October 7, 2026 15:03

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be in Library.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh! I didn't pay attention to the section of the Changelog entry. Sadly, I saw your comment after the PR was merged.

@vstinner

vstinner commented Oct 7, 2026

Copy link
Copy Markdown
Member

Merged. Thanks for your fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants