I propose defining identifiers for use with Py_GetConstant for exceptions
(e.g. Py_CONSTANT_Exc_ValueError to get ValueError) and other built-in types
(e.g. Py_CONSTANT_Bool_Type to get bool).
When compiling for Stable ABI 3.16+, the current API will be defined via calls to Py_GetConstant(). For example:
#define PyExc_ArithmeticError (Py_GetConstantBorrowed(Py_CONSTANT_Exc_ArithmeticError))
#define PyBool_Type (*(PyTypeObject*)Py_GetConstantBorrowed(Py_CONSTANT_Bool_Type))
This means they can’t be used as lvalues (good) or outside C functions (like in static initializers, which didn’t work on Windows across DLL boundaries).
This means 103 new constants – one for every PyObject* data export in the Stable ABI except exceptions below, plus Py_CONSTANT_Method_Type (to expose method type as requested in capi-workgroup/decisions#108).
Expand the full list
Py_CONSTANT_Exc_ArithmeticErrorPy_CONSTANT_Exc_AssertionErrorPy_CONSTANT_Exc_AttributeErrorPy_CONSTANT_Exc_BaseExceptionPy_CONSTANT_Exc_BaseExceptionGroupPy_CONSTANT_Exc_BlockingIOErrorPy_CONSTANT_Exc_BrokenPipeErrorPy_CONSTANT_Exc_BufferErrorPy_CONSTANT_Exc_BytesWarningPy_CONSTANT_Exc_ChildProcessErrorPy_CONSTANT_Exc_ConnectionAbortedErrorPy_CONSTANT_Exc_ConnectionErrorPy_CONSTANT_Exc_ConnectionRefusedErrorPy_CONSTANT_Exc_ConnectionResetErrorPy_CONSTANT_Exc_DeprecationWarningPy_CONSTANT_Exc_EOFErrorPy_CONSTANT_Exc_EncodingWarningPy_CONSTANT_Exc_ExceptionPy_CONSTANT_Exc_FileExistsErrorPy_CONSTANT_Exc_FileNotFoundErrorPy_CONSTANT_Exc_FloatingPointErrorPy_CONSTANT_Exc_FutureWarningPy_CONSTANT_Exc_GeneratorExitPy_CONSTANT_Exc_ImportErrorPy_CONSTANT_Exc_ImportWarningPy_CONSTANT_Exc_IndentationErrorPy_CONSTANT_Exc_IndexErrorPy_CONSTANT_Exc_InterruptedErrorPy_CONSTANT_Exc_IsADirectoryErrorPy_CONSTANT_Exc_KeyErrorPy_CONSTANT_Exc_KeyboardInterruptPy_CONSTANT_Exc_LookupErrorPy_CONSTANT_Exc_MemoryErrorPy_CONSTANT_Exc_ModuleNotFoundErrorPy_CONSTANT_Exc_NameErrorPy_CONSTANT_Exc_NotADirectoryErrorPy_CONSTANT_Exc_NotImplementedErrorPy_CONSTANT_Exc_OSErrorPy_CONSTANT_Exc_OverflowErrorPy_CONSTANT_Exc_PendingDeprecationWarningPy_CONSTANT_Exc_PermissionErrorPy_CONSTANT_Exc_ProcessLookupErrorPy_CONSTANT_Exc_RecursionErrorPy_CONSTANT_Exc_ReferenceErrorPy_CONSTANT_Exc_ResourceWarningPy_CONSTANT_Exc_RuntimeErrorPy_CONSTANT_Exc_RuntimeWarningPy_CONSTANT_Exc_StopAsyncIterationPy_CONSTANT_Exc_StopIterationPy_CONSTANT_Exc_SyntaxErrorPy_CONSTANT_Exc_SyntaxWarningPy_CONSTANT_Exc_SystemErrorPy_CONSTANT_Exc_SystemExitPy_CONSTANT_Exc_TabErrorPy_CONSTANT_Exc_TimeoutErrorPy_CONSTANT_Exc_TypeErrorPy_CONSTANT_Exc_UnboundLocalErrorPy_CONSTANT_Exc_UnicodeDecodeErrorPy_CONSTANT_Exc_UnicodeEncodeErrorPy_CONSTANT_Exc_UnicodeErrorPy_CONSTANT_Exc_UnicodeTranslateErrorPy_CONSTANT_Exc_UnicodeWarningPy_CONSTANT_Exc_UserWarningPy_CONSTANT_Exc_ValueErrorPy_CONSTANT_Exc_WarningPy_CONSTANT_Exc_ZeroDivisionErrorPy_CONSTANT_BaseObject_TypePy_CONSTANT_Bool_TypePy_CONSTANT_ByteArray_TypePy_CONSTANT_Bytes_TypePy_CONSTANT_CFunction_TypePy_CONSTANT_Capsule_TypePy_CONSTANT_ClassMethodDescr_TypePy_CONSTANT_Complex_TypePy_CONSTANT_DictProxy_TypePy_CONSTANT_Dict_TypePy_CONSTANT_Ellipsis_TypePy_CONSTANT_Enum_TypePy_CONSTANT_Filter_TypePy_CONSTANT_Float_TypePy_CONSTANT_FrozenSet_TypePy_CONSTANT_GetSetDescr_TypePy_CONSTANT_List_TypePy_CONSTANT_Long_TypePy_CONSTANT_Map_TypePy_CONSTANT_MemberDescr_TypePy_CONSTANT_MemoryView_TypePy_CONSTANT_MethodDescr_TypePy_CONSTANT_Module_TypePy_CONSTANT_Property_TypePy_CONSTANT_Range_TypePy_CONSTANT_Reversed_TypePy_CONSTANT_Set_TypePy_CONSTANT_Slice_TypePy_CONSTANT_Super_TypePy_CONSTANT_TraceBack_TypePy_CONSTANT_Tuple_TypePy_CONSTANT_Type_TypePy_CONSTANT_Unicode_TypePy_CONSTANT_WrapperDescr_TypePy_CONSTANT_Zip_TypePy_CONSTANT_GenericAliasTypePy_CONSTANT_Method_Type
Why
Data exports are process-wide globals, which means that any implementation that exposes the current ABI (with PyExc_ValueError, PyBool_Type etc.) must implement these as static types, or share them across interpreters and manage their lifetime.
This constrains CPython (where we can’t really do much about it). In RustPython, it’s reportedly a blocker for supporting the Stable ABI.
Data exports are problematic in general; see capi-workgroup/problems#80.
Exceptions, soft-deprecations and removals
The OSError aliases won’t get an identifier for Py_GetConstant, but will be defined as aliases to OSError, and formally soft-deprecated:
- PyExc_EnvironmentError
- PyExc_IOError
- PyExc_WindowsError
Iteration types (which have major caveats w.r.t. instantiation & type checking, see docs) will be formally soft-deprecated, and no longer defined when compiling for Stable ABI 3.16:
- PyCallIter_Type
- PySeqIter_Type
- PyUnicodeIter_Type
- PyByteArrayIter_Type
- PyBytesIter_Type
- PyListIter_Type
- PyListRevIter_Type
- PySetIter_Type
- PyTupleIter_Type
- PyRangeIter_Type
- PyLongRangeIter_Type
- PyDictIterKey_Type
- PyDictRevIterKey_Type
- PyDictIterValue_Type
- PyDictRevIterValue_Type
- PyDictIterItem_Type
- PyDictRevIterItem_Type
The module def type will be soft-deprecated. It will no longer be defined for abi3t 3.16+, but it’ll remain when compiling for abi3:
PyModuleDef_Type
When compiling for Stable ABI 3.16+, the following will be functions rather than macros; the private _PyWeakref_*Type objects won’t be defined:
PyWeakref_CheckRefPyWeakref_CheckRefExactPyWeakref_CheckProxyPyWeakref_Check
The following private APIs will no longer be defined when compiling for Stable ABI 3.16, as they’re not needed for the relevant macros:
_Py_EllipsisObject_Py_FalseStruct_Py_NoneStruct_Py_NotImplementedStruct_Py_TrueStruct_Py_RefTotal(this is aPy_ssize_t, notPyObject*)_Py_SwappedOp(this is an int array, notPyObject*)
What remains
With this, all PyObject data exports in Stable ABI will be “ABI only” (available for backwards compatibility, but not defined in the headers when compiling for 3.16+). The exception is PyModuleDef_Type in abi3.
There are also some non-PyObject ones, out of scope here (except _Py_SwappedOp/_Py_RefTotal above):
Py_Version(ulong)PyStructSequence_UnnamedField(C string)PyOS_InputHook(function pointer; niche)Py_FileSystemDefaultEncodeErrors(C string; deprecated)Py_FileSystemDefaultEncoding(C string; deprecated)Py_HasFileSystemDefaultEncoding(C int; deprecated)Py_UTF8Mode(C int; deprecated)