Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 34k
bpo-43916: Add Py_TPFLAGS_DISALLOW_INSTANTIATION type flag#25721
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Uh oh!
There was an error while loading. Please reload this page.
Conversation
vstinner commented Apr 29, 2021 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
vstinner commented Apr 29, 2021
This flag can be used outside CPython code base. For example, the pyrsistent project sets tp_new to NULL: |
vstinner commented Apr 29, 2021
tiran commented Apr 29, 2021
Nice! Do you want to update |
vstinner commented Apr 29, 2021
I prefer to keep the PR simple to make the review easier, and write a second PR to use the flag on heap types that should not be instanciated directly: rewrite PR #25653 with this flag (or let @erlend-aasland do it) if my PR is accepted. |
erlend-aasland commented Apr 29, 2021
I can adapt #25653 to use this flag. |
tiran commented Apr 29, 2021
@morzen CPython uses an external issue tracker. You can find more information in the top level README, https://github.com/python/cpython#issue-tracker-and-mailing-list And please don't post pictures. They are neither searchable nor accessible. |
tiran commented Apr 29, 2021
Sounds good to me! I have created PR draft #25722. |
vstinner commented Apr 29, 2021
About the flag name, another name could be Py_TPFLAGS_DISALLOW_NEW. Or a different name. |
vstinner commented Apr 29, 2021
About the complexity of tp_new inheritance, here is an extract of tp_new_wrapper(): |
tiran commented Apr 29, 2021 • edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
Naming is hard. Both Py_TPFLAGS_DISABLE_NEW and Py_TPFLAGS_DISALLOW_NEW reflect how Python prevents creation instances, not what the flag actually accomplishes. How about something like Py_TPFLAGS_DISALLOW_INSTANTIATION? |
erlend-aasland commented Apr 29, 2021
vstinner commented Apr 29, 2021
I renamed the flag to Py_TPFLAGS_DISALLOW_INSTANTIATION. I also updated _xxsubinterpretersmodule.ChannelID to use the new flag. "The creation of an instance is called instantiation." says Wikipedia: https://en.wikipedia.org/wiki/Instance_(computer_science) I compared the usage of "disallow" versus "deny" in Include/ header files and in Doc/ documentation: "disallow" is more popular. |
vstinner commented Apr 29, 2021
I removed the comment. |
vstinner commented Apr 29, 2021
@tiran@erlend-aasland: my PR uses the flag in 9 types (and 1 documentation example). @erlend-aasland's PR disallow instanciation in many types. Do you think that these usages are common enough to justify to add a type flag? |
vstinner commented Apr 29, 2021
This design doesn't prevent calling directly tp_new in C. I clarified the flag documentation: |
pablogsal left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a much more elegant solution, the only thing I am slightly wary of is to expose this publicly.
serhiy-storchaka commented Apr 29, 2021
There is already a standard way to prevent creating a new instance: setting tp_new to NULL. No new type flag is needed. BTW, |
vstinner commented Apr 29, 2021
If tp_new is set NULL after the type is created, creating an instance in Python fails with a weird error message. Example in Python 3.9: The problem is that the the type |
vstinner commented Apr 29, 2021
Extract of inherit_special(): PyType_FromSpec() creates a type and then calls PyType_Ready() on it. PyType_Ready() calls inherit_special() which inherits tp_new (since it's a heap type), and then creates |
vstinner commented Apr 29, 2021
@serhiy-storchaka: I updated the PR. The flag now explicitly sets tp_new to NULL. So code checking if tp_new is NULL (like pickle) is not affected. Advantages of using the new flag compared to setting tp_new to NULL:
|
vstinner commented Apr 29, 2021
I mark this PR as a draft. I'm trying to modify PyType_FromSpec() to support |
vstinner commented Apr 29, 2021
Ok, there are now 3 API options to solve https://bugs.python.org/issue43916:
There are 3 main ways to create types
A drawbacks:
B drawbacks:
C drawbacks:
|
pablogsal commented Apr 29, 2021
My preference is B, even if we can manually set My preference order is B-> C -> A |
erlend-aasland commented Apr 29, 2021
Mine too, FWIW. |
vstinner commented Apr 29, 2021
In Python, there is a similar "hack" to mark a type as non hashable: |
vstinner commented Apr 29, 2021
In Python, setting |
pablogsal commented Apr 29, 2021
I wouldn't call that surprising TBH :) |
vstinner commented Apr 29, 2021
Another slice of type inheritance (tp_new) complexity. Extract of update_one_slot() which is called by type_new() after PyType_Ready(): |
erlend-aasland commented Apr 30, 2021
I've converted #25653 to apply Let me know if you want me to create a PR for it, @pablogsal. |
Add a new Py_TPFLAGS_DISALLOW_INSTANTIATION type flag to disallow creating type instances: set tp_new to NULL and don't create the "__new__" key in the type dictionary. The flag is set automatically on static types if tp_base is NULL or &PyBaseObject_Type and tp_new is NULL. Use the flag on the following types: * _curses.ncurses_version type * _curses_panel.panel * _tkinter.Tcl_Obj * _tkinter.tkapp * _tkinter.tktimertoken * _xxsubinterpretersmodule.ChannelID * sys.flags type * sys.getwindowsversion() type * sys.version_info type Update MyStr example in the C API documentation to use Py_TPFLAGS_DISALLOW_INSTANTIATION. Add _PyStructSequence_InitType() function to create a structseq type with the Py_TPFLAGS_DISALLOW_INSTANTIATION flag set. type_new() calls _PyType_CheckConsistency() at exit.
vstinner commented Apr 30, 2021
PR rebased to fix a conflict. |
vstinner commented Apr 30, 2021
I took @pablogsal and @serhiy-storchaka concerns in account. Pablo would prefer to avoid adding a public type, Serhiy would prefer to modify PyType_FromSpec() to accept tp_new=NULL. I implemented Serhiy's idea to compare the API with this Py_TPFLAGS_DISALLOW_INSTANTIATION PR. Most people prefer the explicit Py_TPFLAGS_DISALLOW_INSTANTIATION flag which is less surprising than tp_new=NULL. I also listed other advantages of the Py_TPFLAGS_DISALLOW_INSTANTIATION flag in my previous comments. Usually, I would prefer to get a clear consensus, but we are out of time: Python 3.10 beta1 (feature freeze) is next Wednesday, and I will be in holiday for 1 week starting tonight. If there is still a disagreement, we can still revisit the API or the implementation before Python 3.10 final release ("3.10.0 final: Monday, 2021-10-04" says PEP 619). I merged my PR to unblock https://bugs.python.org/issue43916 release blocker issue (and there are other open release blocker issues to be solved before Wednesday). Anyway, thanks everyone for this constructive and interesting discussion ;-) The final merged change also takes in account your remarks. For example, the flag now sets tp_new to NULL, to take Serhiy's comment in account. |
Add a new Py_TPFLAGS_DISALLOW_INSTANTIATION type flag to disallow
creating type instances: set tp_new to NULL and don't create the
"new" key in the type dictionary.
The flag is set automatically on static types if tp_base is NULL or
&PyBaseObject_Type and tp_new is NULL.
Use the flag on the following types:
Update MyStr example in the C API documentation to use
Py_TPFLAGS_DISALLOW_INSTANTIATION.
Add _PyStructSequence_InitType() function to create a structseq type
with the Py_TPFLAGS_DISALLOW_INSTANTIATION flag set.
https://bugs.python.org/issue43916