-
-
Notifications
You must be signed in to change notification settings - Fork 32k
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
Conversation
This flag can be used outside CPython code base. For example, the pyrsistent project sets tp_new to NULL:
|
Nice! Do you want to update |
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. |
I can adapt #25653 to use this flag. |
@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. |
Sounds good to me! I have created PR draft #25722. |
About the flag name, another name could be Py_TPFLAGS_DISALLOW_NEW. Or a different name. |
About the complexity of tp_new inheritance, here is an extract of tp_new_wrapper():
|
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? |
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. |
I removed the comment. |
@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? |
This design doesn't prevent calling directly tp_new in C. I clarified the flag documentation:
|
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.
There is already a standard way to prevent creating a new instance: setting tp_new to NULL. No new type flag is needed. BTW, |
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
|
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 |
@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:
|
I mark this PR as a draft. I'm trying to modify PyType_FromSpec() to support |
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:
|
My preference is B, even if we can manually set My preference order is B-> C -> A |
Mine too, FWIW. |
In Python, there is a similar "hack" to mark a type as non hashable:
|
In Python, setting
|
I wouldn't call that surprising TBH :) |
Another slice of type inheritance (tp_new) complexity. Extract of update_one_slot() which is called by type_new() after PyType_Ready():
|
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.
PR rebased to fix a conflict. |
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