Skip to content

Commit f808520

Browse files
committed
PEP 820: Changes based on discussion & implementation
1 parent c4d6770 commit f808520

File tree

1 file changed

+138
-30
lines changed

1 file changed

+138
-30
lines changed

peps/pep-0820.rst

Lines changed: 138 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,12 @@ Abstract
1717
Replace type and module slots with a new, more type-safe structure that allows
1818
adding new slots in a more forward-compatible way.
1919

20+
API added in 3.15 (:external+py3.15:c:func:`PyModule_FromSlotsAndSpec` and the
21+
new :external+py3.15:ref:`extension export hook <extension-export-hook>`)
22+
will be changed to use the new slots.
23+
2024
The existing slot structures and related API is soft-deprecated.
21-
(That is: it will continue to work without warnings, and it’ll be fully
25+
(That is: they will continue to work without warnings, and it’ll be fully
2226
documented and supported, but we plan to not add any new features to it.)
2327

2428

@@ -43,10 +47,10 @@ structure of the object to stay opaque (in both the API and the ABI),
4347
allowing future CPython versions (or even alternative implementations) to
4448
change the details.
4549

46-
Both structures contain a *slots* field essentially an array of
47-
`tagged unions <https://en.wikipedia.org/wiki/Tagged_union>`__,
48-
which allows for future expansion.
49-
(In practice, slots are ``void`` pointers taged with an ``int`` ID.)
50+
Both structures contain a *slots* field, essentially an array of
51+
`tagged unions <https://en.wikipedia.org/wiki/Tagged_union>`__
52+
(``void`` pointers taged with an ``int`` ID).
53+
This allows for future expansion.
5054

5155
In :pep:`793`, new module creation API was added.
5256
Instead of the ``PyModuleDef`` structure, it uses only an array of *slots*.
@@ -75,7 +79,7 @@ Type safety
7579
but is technically undefined or implementation-defined behaviour in C.
7680

7781
For example: :c:macro:`Py_tp_doc` marks a string; :c:macro:`Py_mod_gil`
78-
an integer, and :c:macro:`Py_tp_repr` a function; all must
82+
a small integer, and :c:macro:`Py_tp_repr` a function; all must
7983
be cast to ``void*``.
8084

8185
Limited forward compatibility
@@ -140,6 +144,8 @@ near future, users could add it with an "``OPTIONAL``" flag, making their class
140144
support the ``@`` operator only on CPython versions with that operator.
141145

142146

147+
.. _pep820-rationale:
148+
143149
Rationale
144150
=========
145151

@@ -205,9 +211,9 @@ This complicates slot handling inside the interpreter, but allows:
205211

206212
- Mixing dynamically allocated (or stack-allocated) slots with ``static`` ones.
207213
This solves the issue that lead to the ``PyType_From*`` family of
208-
functions expanding with values that typically can't be ``static``
209-
(i.e. it's often a symbol from another DLL, which can't be ``static``
210-
data on Windows).
214+
functions expanding with values that typically can't be ``static``.
215+
For example, the *module* argument to :c:func:`PyType_FromModuleAndSpec`
216+
should be a heap-allocated module object.
211217
- Sharing a subset of the slots to implement functionality
212218
common to several classes/modules.
213219
- Easily including some slots conditionally, e.g. based on the Python version.
@@ -228,23 +234,21 @@ and only use the “new” slots if they need any new features.
228234
Fixed-width integers
229235
---------------------
230236

231-
This proposal uses fixed-width integers (``uint16_t``), for slot IDs and
237+
This proposal uses fixed-width integers (``uint16_t``) for slot IDs and
232238
flags.
233-
With the C ``int`` type, using more that 16 bits would not be portable,
239+
With the C ``int`` type, using more than 16 bits would not be portable,
234240
but it would silently work on common platforms.
235241
Using ``int`` but avoiding values over ``UINT16_MAX`` wastes 16 bits
236242
on common platforms.
237243

238-
With these defined as ``uint16_t``, it seems natural to use fixed-width
239-
integers for everything except pointers and sizes.
240244

241245
Memory layout
242246
-------------
243247

244248
On common 64-bit platforms, we can keep the size of the new struct the same
245249
as the existing ``PyType_Slot`` and ``PyModuleDef_Slot``. (The existing
246250
struct waste 6 out of 16 bytes due to ``int`` portability and padding;
247-
this proposal puts those bits to use for new features.)
251+
this proposal puts some of those bits to use for new features.)
248252
On 32-bit platforms, this proposal calls for the same layout as on 64-bit,
249253
doubling the size compared to the existing structs (from 8 bytes to 16).
250254
For “configuration” data that's usually ``static``, it should be OK.
@@ -283,6 +287,27 @@ The main disadvantage is that any internal lookup tables will be either bigger
283287
or harder to manage (if they're merged).
284288

285289

290+
Deprecation warnings
291+
--------------------
292+
293+
Multiple slots are documented to not allow NULL values, but CPython allows
294+
NULL for backwards compatibility.
295+
Similarly, multiple slot IDs should not appear more than once in a single
296+
array, but CPython allows such duplicates.
297+
298+
This is a maintenance issue, as CPython should preserve its undocumented
299+
(and often untested) behaviour in these cases as the implementation is changed.
300+
301+
It also prevents API extensions.
302+
For example, instead of adding the :c:macro:`Py_TPFLAGS_DISALLOW_INSTANTIATION`
303+
flag in 3.10, we could have allowed settning the ``Py_tp_new`` slot to NULL for
304+
the same effect.
305+
306+
To allow changing the edge case behaviour in the (far) future,
307+
and to allow freedom for possible alternative implementations of the C API,
308+
we'll start issuing runtime deprecation warnings in these cases.
309+
310+
286311
Specification
287312
=============
288313

@@ -311,20 +336,33 @@ A new ``PySlot`` structure will be defined as follows::
311336
- An union with the data, whose type depends on the slot.
312337

313338

314-
Functions that use slots
315-
------------------------
339+
New API
340+
-------
316341

317342
The following function will be added.
318343
It will create the corresponding Python type object from the given
319344
array of slots::
320345

321346
PyObject *PyType_FromSlots(const PySlot *slots);
322347

348+
With this function, the ``Py_tp_token`` slot may not be set to
349+
``Py_TP_USE_SPEC`` (i.e. ``NULL``).
350+
351+
352+
Changed API
353+
-----------
354+
323355
The ``PyModule_FromSlotsAndSpec`` function (added in CPython 3.15 in
324356
:pep:`793`) will be *changed* to take the new slot structure::
325357

326358
PyObject *PyModule_FromSlotsAndSpec(const PySlot *slots, PyObject *spec)
327359

360+
The :external+py3.15:ref:`extension module export hook <extension-export-hook>`
361+
added in :pep:`793` (:samp:`PyModExport_{<name>}`) will be *changed* to
362+
return the new slot structure.
363+
The :external+py3.15:c:macro:`PyMODEXPORT_FUNC` macro will
364+
be updated accordingly.
365+
328366

329367
General slot semantics
330368
----------------------
@@ -354,7 +392,7 @@ Flags
354392
This flag is implied for function pointers.
355393

356394
The flag applies even to data the slot points to "indirectly", except for
357-
nested slots -- see ``Py_slot_subslots`` below -- which can have their
395+
nested slots -- see :ref:`pep820-nested-tables` below -- which can have their
358396
own ``PySlot_STATIC`` flag.
359397
For example, if applied to a ``Py_tp_members`` slot that points to an
360398
*array* of ``PyMemberDef`` structures, then the entire array, as well as the
@@ -371,7 +409,7 @@ Flags
371409
If the entire block is to be optional, it should end with a
372410
slot with the OPTIONAL flag.
373411

374-
- ``PySlot_IS_PTR``: The data is stored in ``sl_ptr``, and must be cast to
412+
- ``PySlot_INTPTR``: The data is stored in ``sl_ptr``, and must be cast to
375413
the appropriate type.
376414

377415
This flag simplifies porting from the existing ``PyType_Slot`` and
@@ -404,14 +442,18 @@ The following macros will be added to the API to simplify slot definition::
404442
#define PySlot_END {0}
405443

406444
We'll also add two more macros that avoid named initializers,
407-
for use in C++11-compatibile code::
445+
for use in C++11-compatibile code.
446+
Note that these cast the value to ``void*``, so they do not improve type safety
447+
over existing slots::
408448

409449
#define PySlot_PTR(NAME, VALUE) \
410-
{NAME, PySlot_IS_PTR, {0}, {(void*)(VALUE)}}
450+
{NAME, PySlot_INTPTR, {0}, {(void*)(VALUE)}}
411451

412452
#define PySlot_PTR_STATIC(NAME, VALUE) \
413-
{NAME, PySlot_IS_PTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}}
453+
{NAME, PySlot_INTPTR|Py_SLOT_STATIC, {0}, {(void*)(VALUE)}}
454+
414455

456+
.. _pep820-nested-tables:
415457

416458
Nested slot tables
417459
------------------
@@ -427,7 +469,7 @@ Two more slots will allow similar nesting for existing slot structures:
427469
- ``Py_mod_slots`` for an array of ``PyModuleDef_Slot``
428470

429471
Each ``PyType_Slot`` in the array will be converted to
430-
``(PySlot){.sl_id=slot, .sl_flags=PySlot_IS_PTR, .sl_ptr=func}``,
472+
``(PySlot){.sl_id=slot, .sl_flags=PySlot_INTPTR, .sl_ptr=func}``,
431473
and similar with ``PyModuleDef_Slot``.
432474

433475
The initial implementation will have restrictions that may be lifted
@@ -437,8 +479,6 @@ in the future:
437479
``PySlot_HAS_FALLBACK`` (the flag cannot be set on them nor a slot that
438480
precedes them).
439481
- Nesting depth will be limited to 5 levels.
440-
(4 levels for the existing ``PyType_From*``, ``PyModule_From*`` functions,
441-
which will use up one level internally.)
442482

443483

444484
New slot IDs
@@ -454,8 +494,9 @@ definitions, will be added:
454494
allowed with ``Py_slot_end``.
455495

456496
- ``Py_slot_subslots``, ``Py_tp_slots``, ``Py_mod_slots``: see
457-
*Nested slot tables* above
458-
- ``Py_slot_invalid``: treated as an unknown slot ID.
497+
:ref:`pep820-nested-tables` above
498+
- ``Py_slot_invalid`` (defined as ``UINT16_MAX``, i.e. ``-1``): treated as an
499+
unknown slot ID.
459500

460501
The following new slot IDs will be added to cover existing
461502
members of ``PyModuleDef``:
@@ -483,6 +524,9 @@ Specifying both in a single definition will be deprecated (currently,
483524
None of the new slots will be usable with ``PyType_GetSlot``.
484525
(This limitation may be lifted in the future, with C API WG approval.)
485526

527+
Of the new slots, only ``Py_slot_end``, ``Py_slot_subslots``, ``Py_tp_slots``,
528+
``Py_mod_slots`` will be allowed in ``PyType_Spec`` and/or ``PyModuleDef``.
529+
486530

487531
Slot renumbering
488532
----------------
@@ -496,7 +540,7 @@ Slots numbered 1 through 4 (``Py_bf_getbuffer``...\ ``Py_mp_length`` and
496540
The old numbers will remain as aliases, and will be used when compiling for
497541
Stable ABI versions below 3.15.
498542

499-
Slots for members of ``PyType_Spec``, which were added in
543+
Slots for members of ``PyModuleDef``, which were added in
500544
:ref:`PEP 793 <pep793-api-summary>`, will be renumbered so that they have
501545
unique IDs:
502546

@@ -532,10 +576,48 @@ in this PEP.
532576
This includes nested "new-style" slots (``Py_slot_subslots``).
533577

534578

579+
.. _pep820-hard-deprecations:
580+
581+
Deprecation warnings
582+
--------------------
583+
584+
CPython will emit runtime deprecation warnings for the following cases,
585+
for slots where the case is currently disallowed in documentation but allowed
586+
by the runtime:
587+
588+
- setting a slot value to NULL:
589+
590+
- all type slots except ``Py_tp_doc``
591+
- ``Py_mod_create``
592+
- ``Py_mod_exec``
593+
594+
- repeating a slot ID in a single slots array (including sub-slot arrays
595+
added in this PEP):
596+
597+
- all type slots, except slots where this is already a runtime error
598+
(``Py_tp_doc``, ``Py_tp_members``)
599+
- ``Py_mod_create``
600+
- ``Py_mod_abi``
601+
602+
535603
Backwards Compatibility
536604
=======================
537605

538-
This PEP only adds APIs, so it's backwards compatible.
606+
This PEP proposes to change API that was already released in alpha versions of
607+
Python 3.15.
608+
This will inconvenience early adopters of that API, but -- as long as the
609+
PEP is accepted and implemented before the first bety -- this change is within
610+
the letter and spirit of our backwards compatibility policy.
611+
612+
Renumbering of slots is done in a backwards-compatible way.
613+
Old values continue to be accepted, and are used when compiling for
614+
earlier Stable ABI.
615+
616+
Some cases that are documented as illegal will begin emitting deprecation
617+
warnings (see :ref:`pep820-hard-deprecations`).
618+
619+
Otherwise, this PEP only adds and soft-deprecates APIs, which is backwards
620+
compatible.
539621

540622

541623
Security Implications
@@ -553,13 +635,32 @@ Adjust the "Extending and Embedding" tutorial to use this.
553635
Reference Implementation
554636
========================
555637

556-
None yet.
638+
Draft implementation is available as `pull request #37 in the author's fork
639+
<https://github.com/encukou/cpython/pull/37>`__.
557640

558641

559642
Rejected Ideas
560643
==============
561644

562-
None yet.
645+
See the :ref:`pep820-rationale` section for several alternative ideas.
646+
647+
Third-party slot ID allocation
648+
------------------------------
649+
650+
It was suggested to allow third parties to reserve slot IDs for their own use.
651+
This would be mainly useful for alternate implementations. For example,
652+
something like GraalPy might want custom type slots (e.g. an "inherits
653+
from this Java class" slot).
654+
Similarly, at one point PyPy had an extra ``tp_pypy_flags`` in their
655+
typeobject struct.
656+
657+
This PEP does not specify a namespace mechanism.
658+
One can be added in the future.
659+
We're also free to reserve individual slot IDs for alternate implementations.
660+
661+
Note that slots are not a good way for *extension modules* to add extra data
662+
to types or modules, as there is no API to retrieve the slots used to create
663+
a specific object.
563664

564665

565666
Open Issues
@@ -568,6 +669,13 @@ Open Issues
568669
None yet.
569670

570671

672+
Acknowledgements
673+
================
674+
675+
Thanks to Da Woods and Antoine Pitrou for substantial input on this iteration
676+
of the proposal.
677+
678+
571679
Copyright
572680
=========
573681

0 commit comments

Comments
 (0)