Package: pycapi
PyCAPIPyCAPI is a Python package containing over 600 fast bindings to the CPython C API. Its goal is to support as many of the Python 3.7 - 3.11 stable public APIs as possible.To install, just run:$pipinstallpycapiWhere is the documentation?Documentation of the full CPython C API can be foundhere. It's not a goal of this project to maintain a separate API reference.Any type conversions (such as Pythonintwith Clong, or Pythonbyteswith Cchar*) should be obvious, and all other semantics (such as refcounts, etc.) are identical to the documented API behavior. For simplicity, PyCAPI doesn't provide any additional functionality or utilities beyond CPython's documented stable public API.How is PyCAPI better thanctypes.pythonapi?It's easier to use.pycapiworks as expected, right out of the box:>>>importpycapi>>>pycapi.PyNumber_Add(1,2)3ctypes.pythonapiimplicity requires users to specify the argument and return types asctypestypes:>>>importctypes>>>ctypes.pythonapi.PyNumber_Add(1,2)Segmentationfault:11>>>importctypes>>>ctypes.pythonapi.PyNumber_Add.argtypes=(ctypes.py_object,ctypes.py_object)>>>ctypes.pythonapi.PyNumber_Add.restype=ctypes.py_object>>>ctypes.pythonapi.PyNumber_Add(1,2)3It's more complete.pycapiis designed to provide properly typed bindings foranypart of the C API that's reasonable to call from the Python layer:>>>importpycapi>>>pycapi.PyDict_Check({})1In comparison,ctypes.pythonapiis loaded directly from thePython.hDLL. As a consequence, it isn't able to offer any APIs that happen to be implemented as macros:>>>importctypes>>>ctypes.pythonapi.PyDict_Check(ctypes.py_object({}))Traceback(mostrecentcalllast):File"<stdin>",line1,in<module>File"/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ctypes/__init__.py",line369,in__getattr__func=self.__getitem__(name)File"/usr/local/Cellar/python/3.7.2_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/ctypes/__init__.py",line374,in__getitem__func=self._FuncPtr((name_or_ordinal,self))AttributeError:dlsym(RTLD_DEFAULT,PyDict_Check):symbolnotfoundpycapiis also fully loaded on import, so you can use tab-completion and other introspection techniques to discover APIs (it's also fully typed, so linters and other static editing tools "just work").ctypes.pythonapirequires you to access the attributebeforeit is loaded, and there is no way to get a complete listing of what it supports.It's faster.In many cases, it can be evenfaster than the built-in equivalentin the Python layer. The numbers speak for themselves:In[1]:frompycapiimportPyDict_New,PyDict_Clear,PyDict_CopyIn[2]:%timeitPyDict_New()44.7ns±1.38nsperloop(mean±std.dev.of7runs,10000000loopseach)In[3]:%timeitPyDict_Clear({})54ns±0.448nsperloop(mean±std.dev.of7runs,10000000loopseach)In[4]:%timeitPyDict_Copy({})68.9ns±0.362nsperloop(mean±std.dev.of7runs,10000000loopseach)In[1]:PyDict_New=dict...:PyDict_Clear=dict.clear...:PyDict_Copy=dict.copyIn[2]:%timeitPyDict_New()71.7ns±0.569nsperloop(mean±std.dev.of7runs,10000000loopseach)In[3]:%timeitPyDict_Clear({})55.8ns±0.506nsperloop(mean±std.dev.of7runs,10000000loopseach)In[4]:%timeitPyDict_Copy({})73.1ns±1.06nsperloop(mean±std.dev.of7runs,10000000loopseach)In[1]:importctypes...:...:PyDict_New=ctypes.pythonapi.PyDict_New...:PyDict_New.argtypes=()...:PyDict_New.restype=ctypes.py_object...:...:PyDict_Clear=ctypes.pythonapi.PyDict_Clear...:PyDict_Clear.argtypes=(ctypes.py_object,)...:PyDict_Clear.restype=None...:...:PyDict_Copy=ctypes.pythonapi.PyDict_Copy...:PyDict_Copy.argtypes=(ctypes.py_object,)...:PyDict_Copy.restype=NoneIn[2]:%timeitPyDict_New()113ns±0.424nsperloop(mean±std.dev.of7runs,10000000loopseach)In[3]:%timeitPyDict_Clear({})273ns±3.34nsperloop(mean±std.dev.of7runs,1000000loopseach)In[4]:%timeitPyDict_Copy({})378ns±9.77nsperloop(mean±std.dev.of7runs,1000000loopseach)