GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — gh-121654: Add Py_tp_create_callback slot by vstinner · Pull Request #124789 · python/cpython

Conversation

@vstinner

@vstinner vstinner commented Sep 30, 2024 •
edited by github-actions Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member
  • Issue: Limited API way of assigning to immutable types and/or making a class immutable #121654

📚 Documentation preview 📚: https://cpython-previews--124789.org.readthedocs.build/

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vstinner
pythongh-121654: Add Py_tp_create_callback slot
444a17c
@bedevere-app bedevere-app Bot mentioned this pull request Sep 30, 2024
Limited API way of assigning to immutable types and/or making a class immutable #121654
Closed
@vstinner
Add NEWS entry
e294f84
This was referenced Sep 30, 2024
Add PyType_Freeze() function capi-workgroup/decisions#40
Closed
gh-121654: Add PyType_Freeze() function #122457
Merged
@vstinner

vstinner commented Sep 30, 2024

Copy link
Copy Markdown
Member Author

cc @zooba

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Sep 30, 2024

Copy link
Copy Markdown
Member

Yep, this is the idea I had in mind. It looks weird until you remember that we intend for the spec to be static, which means each type will have its own callback that knows how to add additional members. You could use the type object to look up dynamic state, but that's not the intent of the entire API to begin with.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@AraHaan

AraHaan commented Sep 30, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

I honestly like this PR, and the PyType_Freeze function PR as well.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Oct 2, 2024

Copy link
Copy Markdown
Member

It's not clear what kind of restrictions are in place for the callback. For example, are you allowed to instantiate the type in the callback? (I imagine a singleton class could want to instantiate itself and set tp_new to NULL; what's keeping users from doing that?)
Whatever restrictions would be added can also be added to the code between PyType_From* and PyType_Freeze. The only advantage here is that PyType_From* knows the future value of Py_TPFLAGS_IMMUTABLETYPE; with PyType_Freeze we'll need another flag (or a different mechanism) if/when we need that.

Arbitrary code between PyType_From* and PyType_Freeze has the advantage of access to any local variables the user might have around as state. In general, a callback without a void* arg raises a red flag.

we intend for the spec to be static

That's most common, but they could also be filled dynamically and live on the stack. (And so you can't get a spec back from a type.)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Oct 2, 2024

Copy link
Copy Markdown
Member

It's not clear what kind of restrictions are in place for the callback. For example, are you allowed to instantiate the type in the callback?

Yeah, it's a good point, and it probably would be good to allow that if it's doable (at great risk of getting attribute errors in any code that may run). It's why my alternate proposals focused around working with the type dict, rather than the type object itself.

Arbitrary code between PyType_From* and PyType_Freeze has the advantage of access to any local variables the user might have around as state. In general, a callback without a void* arg raises a red flag.

Yeah, agreed, which is why I preferred providing an array of struct {const char *name; PyObject *value} in the slot that would be inserted into the type's dict.

The callback pointer could become a struct with a pointer and a userdata void*, but now we're likely getting to three layers of indirection for anyone to use it. The chances of messing that up are probably about equal with remembering to Py_DECREF everything in a list of PyObject, but I still think either makes it easier to avoid obscure semantic issues compared to being able to PyType_Freeze an arbitrary object at any arbitrary time.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Oct 2, 2024

Copy link
Copy Markdown
Member Author

Arbitrary code between PyType_From* and PyType_Freeze has the advantage of access to any local variables the user might have around as state. In general, a callback without a void* arg raises a red flag.

Honestly, I prefer PyType_Freeze() API. It's more natural and convenient, and I'm not convinced that Py_tp_create_callback brings much forward compatibility or is more "future proof". Well, that's my opinion :-)

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@zooba

zooba commented Oct 2, 2024

Copy link
Copy Markdown
Member

I'm not convinced that Py_tp_create_callback brings much forward compatibility or is more "future proof".

It means we can change the way the type is constructed or allocated based on the fact that it's going to be immutable.

It also avoids adding a new API, which proves the forwards-compatibility of _FromSpec. Having to add a totally new API just means that one we thought was forwards-compatible actually wasn't, and so we need to make backwards-incompatible changes (modules that use PyType_Freeze can't even compile or load on older versions, whereas with only a type slot it can do a runtime version check to avoid actually passing it).

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@encukou

encukou commented Oct 7, 2024

Copy link
Copy Markdown
Member

with only a type slot it can do a runtime version check to avoid actually passing it

That's a good point, thank you!

I don't think this point is worth replacing imperative code with a callback, though. We don't have a good track records with those: as years roll by, callbacks always seem to be needing a bit more context than they're getting.

my alternate proposals focused around working with the type dict, rather than the type object itself.

Modifying tp_dict using dict API is one of the more dangerous things you can do. Let's do less of that.
Alternately, the callback could return entries to add to the dict -- but that makes the callback a lot less powerful. OTOH,

providing an array of struct {const char *name; PyObject *value} in the slot that would be inserted into the type's dict.

I think this is a good idea regardless. Especially if we add a PyType_ApplySlots function.
If this array is enough for some type, it will be able to set Py_TPFLAGS_IMMUTABLETYPE in the initial FromSpec call, and get the future optimization you're thinking of. With runtime version checks!


The callback pointer could become a struct with a pointer and a userdata void*

... and now the spec isn't static any more :(

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Oct 9, 2024

Copy link
Copy Markdown
Member Author

The C API Working Group approved PyType_Freeze() API: capi-workgroup/decisions#40 So I suggest to close this PR.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@AraHaan

AraHaan commented Oct 9, 2024 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Contributor

The C API Working Group approved PyType_Freeze() API: capi-workgroup/decisions#40 So I suggest to close this PR.

I think having both is good though as there can be use cases to having both options.

Especially if the Freeze call is not in itself part of the limited API but the spec slot is and having it internally call Freeze for the user in a forwards compatible way.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner

vstinner commented Oct 17, 2024

Copy link
Copy Markdown
Member Author

I close this PR since the C API Working Group approved PyType_Freeze() API: capi-workgroup/decisions#40

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vstinner vstinner closed this Oct 17, 2024
@vstinner
vstinner deleted the slot_create_callback branch October 17, 2024 14:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@markshannon markshannon Awaiting requested review from markshannon markshannon will be requested when the pull request is marked ready for review markshannon is a code owner
@encukou encukou Awaiting requested review from encukou encukou will be requested when the pull request is marked ready for review encukou is a code owner
@AA-Turner AA-Turner Awaiting requested review from AA-Turner AA-Turner will be requested when the pull request is marked ready for review AA-Turner is a code owner
@ZeroIntensity ZeroIntensity Awaiting requested review from ZeroIntensity ZeroIntensity will be requested when the pull request is marked ready for review ZeroIntensity is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

4 participants

@vstinner @zooba @AraHaan @encukou
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.