bpo-42545: Check that all symbols in the limited ABI are exported (GH-23616)

This commit is contained in:
Pablo Galindo 2020-12-04 22:05:58 +00:00 committed by GitHub
parent 2e0760bb2e
commit 85f1dedb8d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 1027 additions and 0 deletions

View file

@ -75,6 +75,8 @@ jobs:
fi
- name: Check exported libpython symbols
run: make smelly
- name: Check limited ABI symbols
run: make check-limited-abi
build_win32:
name: 'Windows (x86)'

View file

@ -192,6 +192,8 @@ script:
- if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then ./python Tools/scripts/patchcheck.py --travis $TRAVIS_PULL_REQUEST; fi
# Check that all symbols exported by libpython start with "Py" or "_Py"
- make smelly
# Check that all symbols in the limited abi are present
- make check-limited-abi
# `-r -w` implicitly provided through `make buildbottest`.
- |
if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then

779
Doc/data/stable_abi.dat Normal file
View file

@ -0,0 +1,779 @@
# File generated by 'make regen-limited-abi'
# This is NOT an authoritative list of stable ABI symbols
PyArg_Parse
PyArg_ParseTuple
PyArg_ParseTupleAndKeywords
PyArg_UnpackTuple
PyArg_VaParse
PyArg_VaParseTupleAndKeywords
PyArg_ValidateKeywordArguments
PyBaseObject_Type
PyBool_FromLong
PyBool_Type
PyByteArrayIter_Type
PyByteArray_AsString
PyByteArray_Concat
PyByteArray_FromObject
PyByteArray_FromStringAndSize
PyByteArray_Resize
PyByteArray_Size
PyByteArray_Type
PyBytesIter_Type
PyBytes_AsString
PyBytes_AsStringAndSize
PyBytes_Concat
PyBytes_ConcatAndDel
PyBytes_DecodeEscape
PyBytes_FromFormat
PyBytes_FromFormatV
PyBytes_FromObject
PyBytes_FromString
PyBytes_FromStringAndSize
PyBytes_Repr
PyBytes_Size
PyBytes_Type
PyCFunction_Call
PyCFunction_GetFlags
PyCFunction_GetFunction
PyCFunction_GetSelf
PyCFunction_NewEx
PyCFunction_Type
PyCMethod_New
PyCallIter_New
PyCallIter_Type
PyCallable_Check
PyCapsule_GetContext
PyCapsule_GetDestructor
PyCapsule_GetName
PyCapsule_GetPointer
PyCapsule_Import
PyCapsule_IsValid
PyCapsule_New
PyCapsule_SetContext
PyCapsule_SetDestructor
PyCapsule_SetName
PyCapsule_SetPointer
PyCapsule_Type
PyClassMethodDescr_Type
PyCodec_BackslashReplaceErrors
PyCodec_Decode
PyCodec_Decoder
PyCodec_Encode
PyCodec_Encoder
PyCodec_IgnoreErrors
PyCodec_IncrementalDecoder
PyCodec_IncrementalEncoder
PyCodec_KnownEncoding
PyCodec_LookupError
PyCodec_NameReplaceErrors
PyCodec_Register
PyCodec_RegisterError
PyCodec_ReplaceErrors
PyCodec_StreamReader
PyCodec_StreamWriter
PyCodec_StrictErrors
PyCodec_Unregister
PyCodec_XMLCharRefReplaceErrors
PyComplex_FromDoubles
PyComplex_ImagAsDouble
PyComplex_RealAsDouble
PyComplex_Type
PyDescr_NewClassMethod
PyDescr_NewGetSet
PyDescr_NewMember
PyDescr_NewMethod
PyDictItems_Type
PyDictIterItem_Type
PyDictIterKey_Type
PyDictIterValue_Type
PyDictKeys_Type
PyDictProxy_New
PyDictProxy_Type
PyDictRevIterItem_Type
PyDictRevIterKey_Type
PyDictRevIterValue_Type
PyDictValues_Type
PyDict_Clear
PyDict_Contains
PyDict_Copy
PyDict_DelItem
PyDict_DelItemString
PyDict_GetItem
PyDict_GetItemString
PyDict_GetItemWithError
PyDict_Items
PyDict_Keys
PyDict_Merge
PyDict_MergeFromSeq2
PyDict_New
PyDict_Next
PyDict_SetItem
PyDict_SetItemString
PyDict_Size
PyDict_Type
PyDict_Update
PyDict_Values
PyEllipsis_Type
PyEnum_Type
PyErr_BadArgument
PyErr_BadInternalCall
PyErr_CheckSignals
PyErr_Clear
PyErr_Display
PyErr_ExceptionMatches
PyErr_Fetch
PyErr_Format
PyErr_FormatV
PyErr_GetExcInfo
PyErr_GivenExceptionMatches
PyErr_NewException
PyErr_NewExceptionWithDoc
PyErr_NoMemory
PyErr_NormalizeException
PyErr_Occurred
PyErr_Print
PyErr_PrintEx
PyErr_ProgramText
PyErr_ResourceWarning
PyErr_Restore
PyErr_SetExcInfo
PyErr_SetFromErrno
PyErr_SetFromErrnoWithFilename
PyErr_SetFromErrnoWithFilenameObject
PyErr_SetFromErrnoWithFilenameObjects
PyErr_SetImportError
PyErr_SetImportErrorSubclass
PyErr_SetInterrupt
PyErr_SetNone
PyErr_SetObject
PyErr_SetString
PyErr_SyntaxLocation
PyErr_SyntaxLocationEx
PyErr_WarnEx
PyErr_WarnExplicit
PyErr_WarnFormat
PyErr_WriteUnraisable
PyEval_AcquireLock
PyEval_AcquireThread
PyEval_CallFunction
PyEval_CallMethod
PyEval_CallObjectWithKeywords
PyEval_EvalCode
PyEval_EvalCodeEx
PyEval_EvalFrame
PyEval_EvalFrameEx
PyEval_GetBuiltins
PyEval_GetFrame
PyEval_GetFuncDesc
PyEval_GetFuncName
PyEval_GetGlobals
PyEval_GetLocals
PyEval_InitThreads
PyEval_ReleaseLock
PyEval_ReleaseThread
PyEval_RestoreThread
PyEval_SaveThread
PyEval_ThreadsInitialized
PyExc_ArithmeticError
PyExc_AssertionError
PyExc_AttributeError
PyExc_BaseException
PyExc_BlockingIOError
PyExc_BrokenPipeError
PyExc_BufferError
PyExc_BytesWarning
PyExc_ChildProcessError
PyExc_ConnectionAbortedError
PyExc_ConnectionError
PyExc_ConnectionRefusedError
PyExc_ConnectionResetError
PyExc_DeprecationWarning
PyExc_EOFError
PyExc_EnvironmentError
PyExc_Exception
PyExc_FileExistsError
PyExc_FileNotFoundError
PyExc_FloatingPointError
PyExc_FutureWarning
PyExc_GeneratorExit
PyExc_IOError
PyExc_ImportError
PyExc_ImportWarning
PyExc_IndentationError
PyExc_IndexError
PyExc_InterruptedError
PyExc_IsADirectoryError
PyExc_KeyError
PyExc_KeyboardInterrupt
PyExc_LookupError
PyExc_MemoryError
PyExc_ModuleNotFoundError
PyExc_NameError
PyExc_NotADirectoryError
PyExc_NotImplementedError
PyExc_OSError
PyExc_OverflowError
PyExc_PendingDeprecationWarning
PyExc_PermissionError
PyExc_ProcessLookupError
PyExc_RecursionError
PyExc_ReferenceError
PyExc_ResourceWarning
PyExc_RuntimeError
PyExc_RuntimeWarning
PyExc_StopAsyncIteration
PyExc_StopIteration
PyExc_SyntaxError
PyExc_SyntaxWarning
PyExc_SystemError
PyExc_SystemExit
PyExc_TabError
PyExc_TimeoutError
PyExc_TypeError
PyExc_UnboundLocalError
PyExc_UnicodeDecodeError
PyExc_UnicodeEncodeError
PyExc_UnicodeError
PyExc_UnicodeTranslateError
PyExc_UnicodeWarning
PyExc_UserWarning
PyExc_ValueError
PyExc_Warning
PyExc_ZeroDivisionError
PyExceptionClass_Name
PyException_GetCause
PyException_GetContext
PyException_GetTraceback
PyException_SetCause
PyException_SetContext
PyException_SetTraceback
PyFile_FromFd
PyFile_GetLine
PyFile_WriteObject
PyFile_WriteString
PyFilter_Type
PyFloat_AsDouble
PyFloat_FromDouble
PyFloat_FromString
PyFloat_GetInfo
PyFloat_GetMax
PyFloat_GetMin
PyFloat_Type
PyFrame_GetCode
PyFrame_GetLineNumber
PyFrozenSet_New
PyFrozenSet_Type
PyGC_Collect
PyGILState_Ensure
PyGILState_GetThisThreadState
PyGILState_Release
PyGetSetDescr_Type
PyImport_AddModule
PyImport_AddModuleObject
PyImport_AppendInittab
PyImport_ExecCodeModule
PyImport_ExecCodeModuleEx
PyImport_ExecCodeModuleObject
PyImport_ExecCodeModuleWithPathnames
PyImport_GetImporter
PyImport_GetMagicNumber
PyImport_GetMagicTag
PyImport_GetModule
PyImport_GetModuleDict
PyImport_Import
PyImport_ImportFrozenModule
PyImport_ImportFrozenModuleObject
PyImport_ImportModule
PyImport_ImportModuleLevel
PyImport_ImportModuleLevelObject
PyImport_ImportModuleNoBlock
PyImport_ReloadModule
PyIndex_Check
PyInterpreterState_Clear
PyInterpreterState_Delete
PyInterpreterState_Get
PyInterpreterState_GetDict
PyInterpreterState_GetID
PyInterpreterState_New
PyIter_Check
PyIter_Next
PyIter_Send
PyListIter_Type
PyListRevIter_Type
PyList_Append
PyList_AsTuple
PyList_GetItem
PyList_GetSlice
PyList_Insert
PyList_New
PyList_Reverse
PyList_SetItem
PyList_SetSlice
PyList_Size
PyList_Sort
PyList_Type
PyLongRangeIter_Type
PyLong_AsDouble
PyLong_AsLong
PyLong_AsLongAndOverflow
PyLong_AsLongLong
PyLong_AsLongLongAndOverflow
PyLong_AsSize_t
PyLong_AsSsize_t
PyLong_AsUnsignedLong
PyLong_AsUnsignedLongLong
PyLong_AsUnsignedLongLongMask
PyLong_AsUnsignedLongMask
PyLong_AsVoidPtr
PyLong_FromDouble
PyLong_FromLong
PyLong_FromLongLong
PyLong_FromSize_t
PyLong_FromSsize_t
PyLong_FromString
PyLong_FromUnsignedLong
PyLong_FromUnsignedLongLong
PyLong_FromVoidPtr
PyLong_GetInfo
PyLong_Type
PyMap_Type
PyMapping_Check
PyMapping_GetItemString
PyMapping_HasKey
PyMapping_HasKeyString
PyMapping_Items
PyMapping_Keys
PyMapping_Length
PyMapping_SetItemString
PyMapping_Size
PyMapping_Values
PyMarshal_ReadObjectFromString
PyMarshal_WriteLongToFile
PyMarshal_WriteObjectToFile
PyMarshal_WriteObjectToString
PyMem_Free
PyMem_Malloc
PyMem_Realloc
PyMemberDescr_Type
PyMember_GetOne
PyMember_SetOne
PyMemoryView_FromMemory
PyMemoryView_FromObject
PyMemoryView_GetContiguous
PyMemoryView_Type
PyMethodDescr_Type
PyModuleDef_Init
PyModuleDef_Type
PyModule_AddFunctions
PyModule_AddIntConstant
PyModule_AddObject
PyModule_AddObjectRef
PyModule_AddStringConstant
PyModule_AddType
PyModule_Create2
PyModule_ExecDef
PyModule_FromDefAndSpec2
PyModule_GetDef
PyModule_GetDict
PyModule_GetFilename
PyModule_GetFilenameObject
PyModule_GetName
PyModule_GetNameObject
PyModule_GetState
PyModule_New
PyModule_NewObject
PyModule_SetDocString
PyModule_Type
PyNumber_Absolute
PyNumber_Add
PyNumber_And
PyNumber_AsSsize_t
PyNumber_Check
PyNumber_Divmod
PyNumber_Float
PyNumber_FloorDivide
PyNumber_InPlaceAdd
PyNumber_InPlaceAnd
PyNumber_InPlaceFloorDivide
PyNumber_InPlaceLshift
PyNumber_InPlaceMatrixMultiply
PyNumber_InPlaceMultiply
PyNumber_InPlaceOr
PyNumber_InPlacePower
PyNumber_InPlaceRemainder
PyNumber_InPlaceRshift
PyNumber_InPlaceSubtract
PyNumber_InPlaceTrueDivide
PyNumber_InPlaceXor
PyNumber_Index
PyNumber_Invert
PyNumber_Long
PyNumber_Lshift
PyNumber_MatrixMultiply
PyNumber_Multiply
PyNumber_Negative
PyNumber_Or
PyNumber_Positive
PyNumber_Power
PyNumber_Remainder
PyNumber_Rshift
PyNumber_Subtract
PyNumber_ToBase
PyNumber_TrueDivide
PyNumber_Xor
PyOS_AfterFork
PyOS_AfterFork_Child
PyOS_AfterFork_Parent
PyOS_BeforeFork
PyOS_FSPath
PyOS_InterruptOccurred
PyOS_double_to_string
PyOS_getsig
PyOS_mystricmp
PyOS_mystrnicmp
PyOS_setsig
PyOS_snprintf
PyOS_string_to_double
PyOS_strtol
PyOS_strtoul
PyOS_vsnprintf
PyObject_ASCII
PyObject_AsFileDescriptor
PyObject_Bytes
PyObject_Call
PyObject_CallFunction
PyObject_CallFunctionObjArgs
PyObject_CallMethod
PyObject_CallMethodObjArgs
PyObject_CallNoArgs
PyObject_CallObject
PyObject_Calloc
PyObject_ClearWeakRefs
PyObject_DelItem
PyObject_DelItemString
PyObject_Dir
PyObject_Format
PyObject_Free
PyObject_GC_Del
PyObject_GC_IsFinalized
PyObject_GC_IsTracked
PyObject_GC_Track
PyObject_GC_UnTrack
PyObject_GenericGetAttr
PyObject_GenericGetDict
PyObject_GenericSetAttr
PyObject_GenericSetDict
PyObject_GetAttr
PyObject_GetAttrString
PyObject_GetItem
PyObject_GetIter
PyObject_HasAttr
PyObject_HasAttrString
PyObject_Hash
PyObject_HashNotImplemented
PyObject_Init
PyObject_InitVar
PyObject_IsInstance
PyObject_IsSubclass
PyObject_IsTrue
PyObject_Length
PyObject_Malloc
PyObject_Not
PyObject_Realloc
PyObject_Repr
PyObject_RichCompare
PyObject_RichCompareBool
PyObject_SelfIter
PyObject_SetAttr
PyObject_SetAttrString
PyObject_SetItem
PyObject_Size
PyObject_Str
PyObject_Type
PyProperty_Type
PyRangeIter_Type
PyRange_Type
PyReversed_Type
PySeqIter_New
PySeqIter_Type
PySequence_Check
PySequence_Concat
PySequence_Contains
PySequence_Count
PySequence_DelItem
PySequence_DelSlice
PySequence_Fast
PySequence_GetItem
PySequence_GetSlice
PySequence_In
PySequence_InPlaceConcat
PySequence_InPlaceRepeat
PySequence_Index
PySequence_Length
PySequence_List
PySequence_Repeat
PySequence_SetItem
PySequence_SetSlice
PySequence_Size
PySequence_Tuple
PySetIter_Type
PySet_Add
PySet_Clear
PySet_Contains
PySet_Discard
PySet_New
PySet_Pop
PySet_Size
PySet_Type
PySlice_AdjustIndices
PySlice_GetIndices
PySlice_GetIndicesEx
PySlice_New
PySlice_Type
PySlice_Unpack
PyState_AddModule
PyState_FindModule
PyState_RemoveModule
PyStructSequence_GetItem
PyStructSequence_New
PyStructSequence_NewType
PyStructSequence_SetItem
PySuper_Type
PySys_AddWarnOption
PySys_AddWarnOptionUnicode
PySys_AddXOption
PySys_FormatStderr
PySys_FormatStdout
PySys_GetObject
PySys_GetXOptions
PySys_HasWarnOptions
PySys_ResetWarnOptions
PySys_SetArgv
PySys_SetArgvEx
PySys_SetObject
PySys_SetPath
PySys_WriteStderr
PySys_WriteStdout
PyThreadState_Clear
PyThreadState_Delete
PyThreadState_Get
PyThreadState_GetDict
PyThreadState_GetFrame
PyThreadState_GetID
PyThreadState_GetInterpreter
PyThreadState_New
PyThreadState_SetAsyncExc
PyThreadState_Swap
PyThread_GetInfo
PyThread_ReInitTLS
PyThread_acquire_lock
PyThread_acquire_lock_timed
PyThread_allocate_lock
PyThread_create_key
PyThread_delete_key
PyThread_delete_key_value
PyThread_exit_thread
PyThread_free_lock
PyThread_get_key_value
PyThread_get_stacksize
PyThread_get_thread_ident
PyThread_get_thread_native_id
PyThread_init_thread
PyThread_release_lock
PyThread_set_key_value
PyThread_set_stacksize
PyThread_start_new_thread
PyThread_tss_alloc
PyThread_tss_create
PyThread_tss_delete
PyThread_tss_free
PyThread_tss_get
PyThread_tss_is_created
PyThread_tss_set
PyTraceBack_Here
PyTraceBack_Print
PyTraceBack_Type
PyTupleIter_Type
PyTuple_GetItem
PyTuple_GetSlice
PyTuple_New
PyTuple_Pack
PyTuple_SetItem
PyTuple_Size
PyTuple_Type
PyType_ClearCache
PyType_FromModuleAndSpec
PyType_FromSpec
PyType_FromSpecWithBases
PyType_GenericAlloc
PyType_GenericNew
PyType_GetFlags
PyType_GetModule
PyType_GetModuleState
PyType_GetSlot
PyType_IsSubtype
PyType_Modified
PyType_Ready
PyType_Type
PyUnicodeDecodeError_Create
PyUnicodeDecodeError_GetEncoding
PyUnicodeDecodeError_GetEnd
PyUnicodeDecodeError_GetObject
PyUnicodeDecodeError_GetReason
PyUnicodeDecodeError_GetStart
PyUnicodeDecodeError_SetEnd
PyUnicodeDecodeError_SetReason
PyUnicodeDecodeError_SetStart
PyUnicodeEncodeError_GetEncoding
PyUnicodeEncodeError_GetEnd
PyUnicodeEncodeError_GetObject
PyUnicodeEncodeError_GetReason
PyUnicodeEncodeError_GetStart
PyUnicodeEncodeError_SetEnd
PyUnicodeEncodeError_SetReason
PyUnicodeEncodeError_SetStart
PyUnicodeIter_Type
PyUnicodeTranslateError_GetEnd
PyUnicodeTranslateError_GetObject
PyUnicodeTranslateError_GetReason
PyUnicodeTranslateError_GetStart
PyUnicodeTranslateError_SetEnd
PyUnicodeTranslateError_SetReason
PyUnicodeTranslateError_SetStart
PyUnicode_Append
PyUnicode_AppendAndDel
PyUnicode_AsASCIIString
PyUnicode_AsCharmapString
PyUnicode_AsDecodedObject
PyUnicode_AsDecodedUnicode
PyUnicode_AsEncodedObject
PyUnicode_AsEncodedString
PyUnicode_AsEncodedUnicode
PyUnicode_AsLatin1String
PyUnicode_AsRawUnicodeEscapeString
PyUnicode_AsUCS4
PyUnicode_AsUCS4Copy
PyUnicode_AsUTF16String
PyUnicode_AsUTF32String
PyUnicode_AsUTF8AndSize
PyUnicode_AsUTF8String
PyUnicode_AsUnicodeEscapeString
PyUnicode_AsWideChar
PyUnicode_AsWideCharString
PyUnicode_BuildEncodingMap
PyUnicode_Compare
PyUnicode_CompareWithASCIIString
PyUnicode_Concat
PyUnicode_Contains
PyUnicode_Count
PyUnicode_Decode
PyUnicode_DecodeASCII
PyUnicode_DecodeCharmap
PyUnicode_DecodeFSDefault
PyUnicode_DecodeFSDefaultAndSize
PyUnicode_DecodeLatin1
PyUnicode_DecodeLocale
PyUnicode_DecodeLocaleAndSize
PyUnicode_DecodeRawUnicodeEscape
PyUnicode_DecodeUTF16
PyUnicode_DecodeUTF16Stateful
PyUnicode_DecodeUTF32
PyUnicode_DecodeUTF32Stateful
PyUnicode_DecodeUTF7
PyUnicode_DecodeUTF7Stateful
PyUnicode_DecodeUTF8
PyUnicode_DecodeUTF8Stateful
PyUnicode_DecodeUnicodeEscape
PyUnicode_EncodeFSDefault
PyUnicode_EncodeLocale
PyUnicode_FSConverter
PyUnicode_FSDecoder
PyUnicode_Find
PyUnicode_FindChar
PyUnicode_Format
PyUnicode_FromEncodedObject
PyUnicode_FromFormat
PyUnicode_FromFormatV
PyUnicode_FromObject
PyUnicode_FromOrdinal
PyUnicode_FromString
PyUnicode_FromStringAndSize
PyUnicode_FromWideChar
PyUnicode_GetDefaultEncoding
PyUnicode_GetLength
PyUnicode_GetSize
PyUnicode_InternFromString
PyUnicode_InternImmortal
PyUnicode_InternInPlace
PyUnicode_IsIdentifier
PyUnicode_Join
PyUnicode_Partition
PyUnicode_RPartition
PyUnicode_RSplit
PyUnicode_ReadChar
PyUnicode_Replace
PyUnicode_Resize
PyUnicode_RichCompare
PyUnicode_Split
PyUnicode_Splitlines
PyUnicode_Substring
PyUnicode_Tailmatch
PyUnicode_Translate
PyUnicode_Type
PyUnicode_WriteChar
PyWeakref_GetObject
PyWeakref_NewProxy
PyWeakref_NewRef
PyWrapperDescr_Type
PyWrapper_New
PyZip_Type
Py_AddPendingCall
Py_AtExit
Py_BuildValue
Py_BytesMain
Py_CompileString
Py_DecRef
Py_DecodeLocale
Py_EncodeLocale
Py_EndInterpreter
Py_EnterRecursiveCall
Py_Exit
Py_FatalError
Py_FileSystemDefaultEncodeErrors
Py_FileSystemDefaultEncoding
Py_Finalize
Py_FinalizeEx
Py_GenericAlias
Py_GenericAliasType
Py_GetBuildInfo
Py_GetCompiler
Py_GetCopyright
Py_GetExecPrefix
Py_GetPath
Py_GetPlatform
Py_GetPrefix
Py_GetProgramFullPath
Py_GetProgramName
Py_GetPythonHome
Py_GetRecursionLimit
Py_GetVersion
Py_HasFileSystemDefaultEncoding
Py_IncRef
Py_Initialize
Py_InitializeEx
Py_IsInitialized
Py_LeaveRecursiveCall
Py_Main
Py_MakePendingCalls
Py_NewInterpreter
Py_NewRef
Py_ReprEnter
Py_ReprLeave
Py_SetPath
Py_SetProgramName
Py_SetPythonHome
Py_SetRecursionLimit
Py_SymtableString
Py_UTF8Mode
Py_VaBuildValue
Py_XNewRef

View file

@ -744,6 +744,13 @@ regen-importlib: Programs/_freeze_importlib
$(UPDATE_FILE) $(srcdir)/Python/importlib_zipimport.h $(srcdir)/Python/importlib_zipimport.h.new
regen-limited-abi: all
@$(MKDIR_P) $(srcdir)/Doc/data/
$(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/scripts/stable_abi.py generate $(srcdir)/Doc/data/stable_abi.dat.new
$(UPDATE_FILE) $(srcdir)/Doc/data/stable_abi.dat \
$(srcdir)/Doc/data/stable_abi.dat.new
############################################################################
# Regenerate all generated files
@ -1900,6 +1907,9 @@ funny:
patchcheck: @DEF_MAKE_RULE@
$(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/scripts/patchcheck.py
check-limited-abi: all
$(RUNSHARED) ./$(BUILDPYTHON) $(srcdir)/Tools/scripts/stable_abi.py check $(srcdir)/Doc/data/stable_abi.dat
# Dependencies
Python/thread.o: @THREADHEADERS@ $(srcdir)/Python/condvar.h

234
Tools/scripts/stable_abi.py Executable file
View file

@ -0,0 +1,234 @@
#!/usr/bin/env python
import argparse
import glob
import re
import pathlib
import subprocess
import sys
import sysconfig
EXCLUDED_HEADERS = {
"bytes_methods.h",
"cellobject.h",
"classobject.h",
"code.h",
"compile.h",
"datetime.h",
"dtoa.h",
"frameobject.h",
"funcobject.h",
"genobject.h",
"longintrepr.h",
"parsetok.h",
"pyarena.h",
"pyatomic.h",
"pyctype.h",
"pydebug.h",
"pytime.h",
"symtable.h",
"token.h",
"ucnhash.h",
}
def get_exported_symbols(library, dynamic=False):
# Only look at dynamic symbols
args = ["nm", "--no-sort"]
if dynamic:
args.append("--dynamic")
args.append(library)
proc = subprocess.run(args, stdout=subprocess.PIPE, universal_newlines=True)
if proc.returncode:
sys.stdout.write(proc.stdout)
sys.exit(proc.returncode)
stdout = proc.stdout.rstrip()
if not stdout:
raise Exception("command output is empty")
for line in stdout.splitlines():
# Split line '0000000000001b80 D PyTextIOWrapper_Type'
if not line:
continue
parts = line.split(maxsplit=2)
if len(parts) < 3:
continue
symbol = parts[-1]
yield symbol
def check_library(library, abi_funcs, dynamic=False):
available_symbols = set(get_exported_symbols(library, dynamic))
missing_symbols = abi_funcs - available_symbols
if missing_symbols:
print(
f"Some symbols from the stable ABI are missing: {', '.join(missing_symbols)}"
)
return 1
return 0
def generate_limited_api_symbols(args):
if hasattr(sys, "gettotalrefcount"):
print(
"Stable ABI symbols cannot be generated from a debug build", file=sys.stderr
)
sys.exit(1)
library = sysconfig.get_config_var("LIBRARY")
ldlibrary = sysconfig.get_config_var("LDLIBRARY")
if ldlibrary != library:
raise Exception("Limited ABI symbols can only be generated from a static build")
available_symbols = {
symbol for symbol in get_exported_symbols(library) if symbol.startswith("Py")
}
headers = [
file
for file in pathlib.Path("Include").glob("*.h")
if file.name not in EXCLUDED_HEADERS
]
stable_data, stable_exported_data, stable_functions = get_limited_api_definitions(
headers
)
macros = get_limited_api_macros(headers)
stable_symbols = {
symbol
for symbol in (stable_functions | stable_exported_data | stable_data | macros)
if symbol.startswith("Py") and symbol in available_symbols
}
with open(args.output_file, "w") as output_file:
output_file.write(f"# File generated by 'make regen-limited-abi'\n")
output_file.write(
f"# This is NOT an authoritative list of stable ABI symbols\n"
)
for symbol in sorted(stable_symbols):
output_file.write(f"{symbol}\n")
sys.exit(0)
def get_limited_api_macros(headers):
"""Run the preprocesor over all the header files in "Include" setting
"-DPy_LIMITED_API" to the correct value for the running version of the interpreter
and extracting all macro definitions (via adding -dM to the compiler arguments).
"""
preprocesor_output_with_macros = subprocess.check_output(
sysconfig.get_config_var("CC").split()
+ [
# Prevent the expansion of the exported macros so we can capture them later
"-DSIZEOF_WCHAR_T=4", # The actual value is not important
f"-DPy_LIMITED_API={sys.version_info.major << 24 | sys.version_info.minor << 16}",
"-I.",
"-I./Include",
"-dM",
"-E",
]
+ [str(file) for file in headers],
text=True,
stderr=subprocess.DEVNULL,
)
return {
target
for _, target in re.findall(
r"#define (\w+)\s*(?:\(.*?\))?\s+(\w+)", preprocesor_output_with_macros
)
}
def get_limited_api_definitions(headers):
"""Run the preprocesor over all the header files in "Include" setting
"-DPy_LIMITED_API" to the correct value for the running version of the interpreter.
The limited API symbols will be extracted from the output of this command as it includes
the prototypes and definitions of all the exported symbols that are in the limited api.
This function does *NOT* extract the macros defined on the limited API
"""
preprocesor_output = subprocess.check_output(
sysconfig.get_config_var("CC").split()
+ [
# Prevent the expansion of the exported macros so we can capture them later
"-DPyAPI_FUNC=__PyAPI_FUNC",
"-DPyAPI_DATA=__PyAPI_DATA",
"-DEXPORT_DATA=__EXPORT_DATA",
"-D_Py_NO_RETURN=",
"-DSIZEOF_WCHAR_T=4", # The actual value is not important
f"-DPy_LIMITED_API={sys.version_info.major << 24 | sys.version_info.minor << 16}",
"-I.",
"-I./Include",
"-E",
]
+ [str(file) for file in headers],
text=True,
stderr=subprocess.DEVNULL,
)
stable_functions = set(
re.findall(r"__PyAPI_FUNC\(.*?\)\s*(.*?)\s*\(", preprocesor_output)
)
stable_exported_data = set(
re.findall(r"__EXPORT_DATA\((.*?)\)", preprocesor_output)
)
stable_data = set(
re.findall(r"__PyAPI_DATA\(.*?\)\s*\(?(.*?)\)?\s*;", preprocesor_output)
)
return stable_data, stable_exported_data, stable_functions
def check_symbols(parser_args):
with open(parser_args.stable_abi_file, "r") as filename:
abi_funcs = {
symbol
for symbol in filename.read().splitlines()
if symbol and not symbol.startswith("#")
}
ret = 0
# static library
LIBRARY = sysconfig.get_config_var("LIBRARY")
if not LIBRARY:
raise Exception("failed to get LIBRARY variable from sysconfig")
ret = check_library(LIBRARY, abi_funcs)
# dynamic library
LDLIBRARY = sysconfig.get_config_var("LDLIBRARY")
if not LDLIBRARY:
raise Exception("failed to get LDLIBRARY variable from sysconfig")
if LDLIBRARY != LIBRARY:
ret |= check_library(LDLIBRARY, abi_funcs, dynamic=True)
sys.exit(ret)
def main():
parser = argparse.ArgumentParser(description="Process some integers.")
subparsers = parser.add_subparsers()
check_parser = subparsers.add_parser(
"check", help="Check the exported symbols against a given ABI file"
)
check_parser.add_argument(
"stable_abi_file", type=str, help="File with the stable abi functions"
)
check_parser.set_defaults(func=check_symbols)
generate_parser = subparsers.add_parser(
"generate",
help="Generate symbols from the header files and the exported symbols",
)
generate_parser.add_argument(
"output_file", type=str, help="File to dump the symbols to"
)
generate_parser.set_defaults(func=generate_limited_api_symbols)
args = parser.parse_args()
if "func" not in args:
parser.error("Either 'check' or 'generate' must be used")
sys.exit(1)
args.func(args)
if __name__ == "__main__":
main()