From 1c97e4b350c31ba5cf5ee4061f94f8faaf497d3a Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Mon, 18 Apr 2022 06:39:32 +0300 Subject: [PATCH 1/2] gh-91118: Fix docstrings that do not honor --without-doc-strings (#31769) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Éric Co-authored-by: Jelle Zijlstra (cherry picked from commit a573cb2fec664c645ab744658d7e941d72e1a398) --- Doc/c-api/typeobj.rst | 8 +- Doc/extending/newtypes_tutorial.rst | 4 +- Doc/includes/custom.c | 2 +- Doc/includes/custom2.c | 2 +- Doc/includes/custom3.c | 2 +- Doc/includes/custom4.c | 2 +- Doc/includes/sublist.c | 2 +- .../2022-03-08-21-59-57.bpo-46962.UomDfz.rst | 9 + .../2022-03-08-22-10-38.bpo-46962.FIVe9I.rst | 4 + Modules/_ctypes/_ctypes.c | 54 +- Modules/_ctypes/callbacks.c | 2 +- Modules/_ctypes/callproc.c | 32 +- Modules/_ctypes/cfield.c | 2 +- Modules/_testcapimodule.c | 2 +- Objects/genericaliasobject.c | 10 +- Objects/picklebufobject.c | 2 +- Objects/unionobject.c | 492 ++++++++++++++++++ 17 files changed, 569 insertions(+), 62 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2022-03-08-21-59-57.bpo-46962.UomDfz.rst create mode 100644 Misc/NEWS.d/next/Documentation/2022-03-08-22-10-38.bpo-46962.FIVe9I.rst create mode 100644 Objects/unionobject.c diff --git a/Doc/c-api/typeobj.rst b/Doc/c-api/typeobj.rst index d58a53b1d69fb1..d49f3ffa0a5092 100644 --- a/Doc/c-api/typeobj.rst +++ b/Doc/c-api/typeobj.rst @@ -2482,7 +2482,7 @@ A basic static type:: PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mymod.MyObject", .tp_basicsize = sizeof(MyObject), - .tp_doc = "My objects", + .tp_doc = PyDoc_STR("My objects"), .tp_new = myobj_new, .tp_dealloc = (destructor)myobj_dealloc, .tp_repr = (reprfunc)myobj_repr, @@ -2512,7 +2512,7 @@ with a more verbose initializer:: 0, /* tp_setattro */ 0, /* tp_as_buffer */ 0, /* tp_flags */ - "My objects", /* tp_doc */ + PyDoc_STR("My objects"), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -2545,7 +2545,7 @@ A type that supports weakrefs, instance dicts, and hashing:: PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "mymod.MyObject", .tp_basicsize = sizeof(MyObject), - .tp_doc = "My objects", + .tp_doc = PyDoc_STR("My objects"), .tp_weaklistoffset = offsetof(MyObject, weakreflist), .tp_dictoffset = offsetof(MyObject, inst_dict), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, @@ -2572,7 +2572,7 @@ to create instances (e.g. uses a separate factory func):: .tp_name = "mymod.MyStr", .tp_basicsize = sizeof(MyStr), .tp_base = NULL, // set to &PyUnicode_Type in module init - .tp_doc = "my custom str", + .tp_doc = PyDoc_STR("my custom str"), .tp_flags = Py_TPFLAGS_DEFAULT, .tp_new = NULL, .tp_repr = (reprfunc)myobj_repr, diff --git a/Doc/extending/newtypes_tutorial.rst b/Doc/extending/newtypes_tutorial.rst index 4b5eb69ba7701b..7771e20520b1e3 100644 --- a/Doc/extending/newtypes_tutorial.rst +++ b/Doc/extending/newtypes_tutorial.rst @@ -89,7 +89,7 @@ The second bit is the definition of the type object. :: static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom.Custom", - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT, @@ -160,7 +160,7 @@ you will need to OR the corresponding flags. We provide a doc string for the type in :c:member:`~PyTypeObject.tp_doc`. :: - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), To enable object creation, we have to provide a :c:member:`~PyTypeObject.tp_new` handler. This is the equivalent of the Python method :meth:`__new__`, but diff --git a/Doc/includes/custom.c b/Doc/includes/custom.c index f361baf830dd1b..26ca754964733d 100644 --- a/Doc/includes/custom.c +++ b/Doc/includes/custom.c @@ -9,7 +9,7 @@ typedef struct { static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom.Custom", - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT, diff --git a/Doc/includes/custom2.c b/Doc/includes/custom2.c index 5bacab7a2a9714..2a3c59f8f04c3d 100644 --- a/Doc/includes/custom2.c +++ b/Doc/includes/custom2.c @@ -98,7 +98,7 @@ static PyMethodDef Custom_methods[] = { static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom2.Custom", - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, diff --git a/Doc/includes/custom3.c b/Doc/includes/custom3.c index 2b7a99ecf96c76..5a47530f0a6b0d 100644 --- a/Doc/includes/custom3.c +++ b/Doc/includes/custom3.c @@ -148,7 +148,7 @@ static PyMethodDef Custom_methods[] = { static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom3.Custom", - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, diff --git a/Doc/includes/custom4.c b/Doc/includes/custom4.c index 584992fc5f1a8a..c7ee55578488ed 100644 --- a/Doc/includes/custom4.c +++ b/Doc/includes/custom4.c @@ -160,7 +160,7 @@ static PyMethodDef Custom_methods[] = { static PyTypeObject CustomType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "custom4.Custom", - .tp_doc = "Custom objects", + .tp_doc = PyDoc_STR("Custom objects"), .tp_basicsize = sizeof(CustomObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, diff --git a/Doc/includes/sublist.c b/Doc/includes/sublist.c index b2c26e73ebaf7e..b36dadf07eae87 100644 --- a/Doc/includes/sublist.c +++ b/Doc/includes/sublist.c @@ -31,7 +31,7 @@ SubList_init(SubListObject *self, PyObject *args, PyObject *kwds) static PyTypeObject SubListType = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "sublist.SubList", - .tp_doc = "SubList objects", + .tp_doc = PyDoc_STR("SubList objects"), .tp_basicsize = sizeof(SubListObject), .tp_itemsize = 0, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, diff --git a/Misc/NEWS.d/next/Core and Builtins/2022-03-08-21-59-57.bpo-46962.UomDfz.rst b/Misc/NEWS.d/next/Core and Builtins/2022-03-08-21-59-57.bpo-46962.UomDfz.rst new file mode 100644 index 00000000000000..98f19260a7ed26 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2022-03-08-21-59-57.bpo-46962.UomDfz.rst @@ -0,0 +1,9 @@ +Classes and functions that unconditionally declared their docstrings +ignoring the `--without-doc-strings` compilation flag no longer do so. + +The classes affected are :class:`pickle.PickleBuffer`, +:class:`testcapi.RecursingInfinitelyError`, and :class:`types.GenericAlias`. + +The functions affected are 24 methods in :mod:`ctypes`. + +Patch by Oleg Iarygin. diff --git a/Misc/NEWS.d/next/Documentation/2022-03-08-22-10-38.bpo-46962.FIVe9I.rst b/Misc/NEWS.d/next/Documentation/2022-03-08-22-10-38.bpo-46962.FIVe9I.rst new file mode 100644 index 00000000000000..f5b54013bd6720 --- /dev/null +++ b/Misc/NEWS.d/next/Documentation/2022-03-08-22-10-38.bpo-46962.FIVe9I.rst @@ -0,0 +1,4 @@ +All docstrings in code snippets are now wrapped into :func:`PyDoc_STR` to +follow the guideline of `PEP 7's Documentation Strings paragraph +`_. Patch +by Oleg Iarygin. diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c index ceae67ebb16127..0ffc8858de6412 100644 --- a/Modules/_ctypes/_ctypes.c +++ b/Modules/_ctypes/_ctypes.c @@ -181,7 +181,7 @@ static PyTypeObject DictRemover_Type = { 0, /* tp_as_buffer */ /* XXX should participate in GC? */ Py_TPFLAGS_DEFAULT, /* tp_flags */ - "deletes a key from a dictionary", /* tp_doc */ + PyDoc_STR("deletes a key from a dictionary"), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -563,8 +563,8 @@ UnionType_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return StructUnionType_new(type, args, kwds, 0); } -static const char from_address_doc[] = -"C.from_address(integer) -> C instance\naccess a C instance at the specified address"; +PyDoc_STRVAR(from_address_doc, +"C.from_address(integer) -> C instance\naccess a C instance at the specified address"); static PyObject * CDataType_from_address(PyObject *type, PyObject *value) @@ -581,8 +581,8 @@ CDataType_from_address(PyObject *type, PyObject *value) return PyCData_AtAddress(type, buf); } -static const char from_buffer_doc[] = -"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer"; +PyDoc_STRVAR(from_buffer_doc, +"C.from_buffer(object, offset=0) -> C instance\ncreate a C instance from a writeable buffer"); static int KeepRef(CDataObject *target, Py_ssize_t index, PyObject *keep); @@ -661,8 +661,8 @@ CDataType_from_buffer(PyObject *type, PyObject *args) return result; } -static const char from_buffer_copy_doc[] = -"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer"; +PyDoc_STRVAR(from_buffer_copy_doc, +"C.from_buffer_copy(object, offset=0) -> C instance\ncreate a C instance from a readable buffer"); static PyObject * GenericPyCData_new(PyTypeObject *type, PyObject *args, PyObject *kwds); @@ -712,8 +712,8 @@ CDataType_from_buffer_copy(PyObject *type, PyObject *args) return result; } -static const char in_dll_doc[] = -"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll"; +PyDoc_STRVAR(in_dll_doc, +"C.in_dll(dll, name) -> C instance\naccess a C instance in a dll"); static PyObject * CDataType_in_dll(PyObject *type, PyObject *args) @@ -774,8 +774,8 @@ CDataType_in_dll(PyObject *type, PyObject *args) return PyCData_AtAddress(type, address); } -static const char from_param_doc[] = -"Convert a Python object into a function call parameter."; +PyDoc_STRVAR(from_param_doc, +"Convert a Python object into a function call parameter."); static PyObject * CDataType_from_param(PyObject *type, PyObject *value) @@ -929,7 +929,7 @@ PyTypeObject PyCStructType_Type = { PyCStructType_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "metatype for the CData Objects", /* tp_doc */ + PyDoc_STR("metatype for the CData Objects"), /* tp_doc */ (traverseproc)CDataType_traverse, /* tp_traverse */ (inquiry)CDataType_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -971,7 +971,7 @@ static PyTypeObject UnionType_Type = { UnionType_setattro, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "metatype for the CData Objects", /* tp_doc */ + PyDoc_STR("metatype for the CData Objects"), /* tp_doc */ (traverseproc)CDataType_traverse, /* tp_traverse */ (inquiry)CDataType_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -1229,7 +1229,7 @@ PyTypeObject PyCPointerType_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "metatype for the Pointer Objects", /* tp_doc */ + PyDoc_STR("metatype for the Pointer Objects"), /* tp_doc */ (traverseproc)CDataType_traverse, /* tp_traverse */ (inquiry)CDataType_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -1651,7 +1651,7 @@ PyTypeObject PyCArrayType_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "metatype for the Array Objects", /* tp_doc */ + PyDoc_STR("metatype for the Array Objects"), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -2345,7 +2345,7 @@ PyTypeObject PyCSimpleType_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "metatype for the PyCSimpleType Objects", /* tp_doc */ + PyDoc_STR("metatype for the PyCSimpleType Objects"), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ @@ -2627,7 +2627,7 @@ PyTypeObject PyCFuncPtrType_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "metatype for C function pointers", /* tp_doc */ + PyDoc_STR("metatype for C function pointers"), /* tp_doc */ (traverseproc)CDataType_traverse, /* tp_traverse */ (inquiry)CDataType_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -2932,7 +2932,7 @@ PyTypeObject PyCData_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "XXX to be provided", /* tp_doc */ + PyDoc_STR("XXX to be provided"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -4327,7 +4327,7 @@ PyTypeObject PyCFuncPtr_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Function Pointer", /* tp_doc */ + PyDoc_STR("Function Pointer"), /* tp_doc */ (traverseproc)PyCFuncPtr_traverse, /* tp_traverse */ (inquiry)PyCFuncPtr_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -4481,7 +4481,7 @@ static PyTypeObject Struct_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Structure base class", /* tp_doc */ + PyDoc_STR("Structure base class"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -4523,7 +4523,7 @@ static PyTypeObject Union_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Union base class", /* tp_doc */ + PyDoc_STR("Union base class"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -4845,7 +4845,7 @@ PyTypeObject PyCArray_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "XXX to be provided", /* tp_doc */ + PyDoc_STR("XXX to be provided"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -5064,7 +5064,7 @@ static PyTypeObject Simple_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "XXX to be provided", /* tp_doc */ + PyDoc_STR("XXX to be provided"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -5448,7 +5448,7 @@ PyTypeObject PyCPointer_Type = { 0, /* tp_setattro */ &PyCData_as_buffer, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "XXX to be provided", /* tp_doc */ + PyDoc_STR("XXX to be provided"), /* tp_doc */ (traverseproc)PyCData_traverse, /* tp_traverse */ (inquiry)PyCData_clear, /* tp_clear */ 0, /* tp_richcompare */ @@ -5475,12 +5475,12 @@ PyTypeObject PyCPointer_Type = { * Module initialization. */ -static const char module_docs[] = -"Create and manipulate C compatible data types in Python."; +PyDoc_STRVAR(module_docs, +"Create and manipulate C compatible data types in Python."); #ifdef MS_WIN32 -static const char comerror_doc[] = "Raised when a COM method call failed."; +PyDoc_STRVAR(comerror_doc, "Raised when a COM method call failed."); int comerror_init(PyObject *self, PyObject *args, PyObject *kwds) diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c index e6e101829a6995..dbe6f29fc8171c 100644 --- a/Modules/_ctypes/callbacks.c +++ b/Modules/_ctypes/callbacks.c @@ -65,7 +65,7 @@ PyTypeObject PyCThunk_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "CThunkObject", /* tp_doc */ + PyDoc_STR("CThunkObject"), /* tp_doc */ CThunkObject_traverse, /* tp_traverse */ CThunkObject_clear, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 9aff890afe9def..b83f7364de0a17 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1316,11 +1316,11 @@ _parse_voidp(PyObject *obj, void **address) #ifdef MS_WIN32 -static const char format_error_doc[] = +PyDoc_STRVAR(format_error_doc, "FormatError([integer]) -> string\n\ \n\ Convert a win32 error code into a string. If the error code is not\n\ -given, the return value of a call to GetLastError() is used.\n"; +given, the return value of a call to GetLastError() is used.\n"); static PyObject *format_error(PyObject *self, PyObject *args) { PyObject *result; @@ -1340,13 +1340,13 @@ static PyObject *format_error(PyObject *self, PyObject *args) return result; } -static const char load_library_doc[] = +PyDoc_STRVAR(load_library_doc, "LoadLibrary(name, load_flags) -> handle\n\ \n\ Load an executable (usually a DLL), and return a handle to it.\n\ The handle may be used to locate exported functions in this\n\ module. load_flags are as defined for LoadLibraryEx in the\n\ -Windows API.\n"; +Windows API.\n"); static PyObject *load_library(PyObject *self, PyObject *args) { const WCHAR *name; @@ -1394,10 +1394,10 @@ _Py_COMP_DIAG_POP #endif } -static const char free_library_doc[] = +PyDoc_STRVAR(free_library_doc, "FreeLibrary(handle) -> void\n\ \n\ -Free the handle of an executable previously loaded by LoadLibrary.\n"; +Free the handle of an executable previously loaded by LoadLibrary.\n"); static PyObject *free_library(PyObject *self, PyObject *args) { void *hMod; @@ -1417,8 +1417,8 @@ static PyObject *free_library(PyObject *self, PyObject *args) Py_RETURN_NONE; } -static const char copy_com_pointer_doc[] = -"CopyComPointer(src, dst) -> HRESULT value\n"; +PyDoc_STRVAR(copy_com_pointer_doc, +"CopyComPointer(src, dst) -> HRESULT value\n"); static PyObject * copy_com_pointer(PyObject *self, PyObject *args) @@ -1656,10 +1656,10 @@ call_cdeclfunction(PyObject *self, PyObject *args) /***************************************************************** * functions */ -static const char sizeof_doc[] = +PyDoc_STRVAR(sizeof_doc, "sizeof(C type) -> integer\n" "sizeof(C instance) -> integer\n" -"Return the size in bytes of a C instance"; +"Return the size in bytes of a C instance"); static PyObject * sizeof_func(PyObject *self, PyObject *obj) @@ -1677,10 +1677,10 @@ sizeof_func(PyObject *self, PyObject *obj) return NULL; } -static const char alignment_doc[] = +PyDoc_STRVAR(alignment_doc, "alignment(C type) -> integer\n" "alignment(C instance) -> integer\n" -"Return the alignment requirements of a C instance"; +"Return the alignment requirements of a C instance"); static PyObject * align_func(PyObject *self, PyObject *obj) @@ -1700,10 +1700,10 @@ align_func(PyObject *self, PyObject *obj) return NULL; } -static const char byref_doc[] = +PyDoc_STRVAR(byref_doc, "byref(C instance[, offset=0]) -> byref-object\n" "Return a pointer lookalike to a C instance, only usable\n" -"as function argument"; +"as function argument"); /* * We must return something which can be converted to a parameter, @@ -1744,9 +1744,9 @@ byref(PyObject *self, PyObject *args) return (PyObject *)parg; } -static const char addressof_doc[] = +PyDoc_STRVAR(addressof_doc, "addressof(C instance) -> integer\n" -"Return the address of the C instance internal buffer"; +"Return the address of the C instance internal buffer"); static PyObject * addressof(PyObject *self, PyObject *obj) diff --git a/Modules/_ctypes/cfield.c b/Modules/_ctypes/cfield.c index 06b98a6d66fc36..8582af9c3a209a 100644 --- a/Modules/_ctypes/cfield.c +++ b/Modules/_ctypes/cfield.c @@ -320,7 +320,7 @@ PyTypeObject PyCField_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, /* tp_flags */ - "Structure/Union member", /* tp_doc */ + PyDoc_STR("Structure/Union member"), /* tp_doc */ (traverseproc)PyCField_traverse, /* tp_traverse */ (inquiry)PyCField_clear, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index ad1b07454355ed..90841270e47387 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5990,7 +5990,7 @@ static PyTypeObject PyRecursingInfinitelyError_Type = { 0, /* tp_setattro */ 0, /* tp_as_buffer */ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */ - "Instantiating this exception starts infinite recursion.", /* tp_doc */ + PyDoc_STR("Instantiating this exception starts infinite recursion."), /* tp_doc */ 0, /* tp_traverse */ 0, /* tp_clear */ 0, /* tp_richcompare */ diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index acbb01cfef92c1..36dfbe43cd88c6 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -293,6 +293,11 @@ subs_tvars(PyObject *obj, PyObject *params, PyObject **argitems) return obj; } +PyDoc_STRVAR(genericalias_doc, +"Represent a PEP 585 generic type\n" +"\n" +"E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,)."); + static PyObject * ga_getitem(PyObject *self, PyObject *item) { @@ -612,14 +617,11 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds) // TODO: // - argument clinic? -// - __doc__? // - cache? PyTypeObject Py_GenericAliasType = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "types.GenericAlias", - .tp_doc = "Represent a PEP 585 generic type\n" - "\n" - "E.g. for t = list[int], t.__origin__ is list and t.__args__ is (int,).", + .tp_doc = genericalias_doc, .tp_basicsize = sizeof(gaobject), .tp_dealloc = ga_dealloc, .tp_repr = ga_repr, diff --git a/Objects/picklebufobject.c b/Objects/picklebufobject.c index a135e5575e28c5..aaa852cfbb05b0 100644 --- a/Objects/picklebufobject.c +++ b/Objects/picklebufobject.c @@ -206,7 +206,7 @@ static PyMethodDef picklebuf_methods[] = { PyTypeObject PyPickleBuffer_Type = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "pickle.PickleBuffer", - .tp_doc = "Wrapper for potentially out-of-band buffers", + .tp_doc = PyDoc_STR("Wrapper for potentially out-of-band buffers"), .tp_basicsize = sizeof(PyPickleBufferObject), .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, .tp_new = picklebuf_new, diff --git a/Objects/unionobject.c b/Objects/unionobject.c new file mode 100644 index 00000000000000..5432c6faa3f909 --- /dev/null +++ b/Objects/unionobject.c @@ -0,0 +1,492 @@ +// types.UnionType -- used to represent e.g. Union[int, str], int | str +#include "Python.h" +#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK +#include "pycore_unionobject.h" +#include "structmember.h" + + +static PyObject *make_union(PyObject *); + + +typedef struct { + PyObject_HEAD + PyObject *args; + PyObject *parameters; +} unionobject; + +static void +unionobject_dealloc(PyObject *self) +{ + unionobject *alias = (unionobject *)self; + + _PyObject_GC_UNTRACK(self); + + Py_XDECREF(alias->args); + Py_XDECREF(alias->parameters); + Py_TYPE(self)->tp_free(self); +} + +static int +union_traverse(PyObject *self, visitproc visit, void *arg) +{ + unionobject *alias = (unionobject *)self; + Py_VISIT(alias->args); + Py_VISIT(alias->parameters); + return 0; +} + +static Py_hash_t +union_hash(PyObject *self) +{ + unionobject *alias = (unionobject *)self; + PyObject *args = PyFrozenSet_New(alias->args); + if (args == NULL) { + return (Py_hash_t)-1; + } + Py_hash_t hash = PyObject_Hash(args); + Py_DECREF(args); + return hash; +} + +static int +is_generic_alias_in_args(PyObject *args) +{ + Py_ssize_t nargs = PyTuple_GET_SIZE(args); + for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(args, iarg); + if (_PyGenericAlias_Check(arg)) { + return 0; + } + } + return 1; +} + +static PyObject * +union_instancecheck(PyObject *self, PyObject *instance) +{ + unionobject *alias = (unionobject *) self; + Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args); + if (!is_generic_alias_in_args(alias->args)) { + PyErr_SetString(PyExc_TypeError, + "isinstance() argument 2 cannot contain a parameterized generic"); + return NULL; + } + for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg); + if (PyType_Check(arg)) { + int res = PyObject_IsInstance(instance, arg); + if (res < 0) { + return NULL; + } + if (res) { + Py_RETURN_TRUE; + } + } + } + Py_RETURN_FALSE; +} + +static PyObject * +union_subclasscheck(PyObject *self, PyObject *instance) +{ + if (!PyType_Check(instance)) { + PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class"); + return NULL; + } + unionobject *alias = (unionobject *)self; + if (!is_generic_alias_in_args(alias->args)) { + PyErr_SetString(PyExc_TypeError, + "issubclass() argument 2 cannot contain a parameterized generic"); + return NULL; + } + Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args); + for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg); + if (PyType_Check(arg)) { + int res = PyObject_IsSubclass(instance, arg); + if (res < 0) { + return NULL; + } + if (res) { + Py_RETURN_TRUE; + } + } + } + Py_RETURN_FALSE; +} + +static PyObject * +union_richcompare(PyObject *a, PyObject *b, int op) +{ + if (!_PyUnion_Check(b) || (op != Py_EQ && op != Py_NE)) { + Py_RETURN_NOTIMPLEMENTED; + } + + PyObject *a_set = PySet_New(((unionobject*)a)->args); + if (a_set == NULL) { + return NULL; + } + PyObject *b_set = PySet_New(((unionobject*)b)->args); + if (b_set == NULL) { + Py_DECREF(a_set); + return NULL; + } + PyObject *result = PyObject_RichCompare(a_set, b_set, op); + Py_DECREF(b_set); + Py_DECREF(a_set); + return result; +} + +static PyObject* +flatten_args(PyObject* args) +{ + Py_ssize_t arg_length = PyTuple_GET_SIZE(args); + Py_ssize_t total_args = 0; + // Get number of total args once it's flattened. + for (Py_ssize_t i = 0; i < arg_length; i++) { + PyObject *arg = PyTuple_GET_ITEM(args, i); + if (_PyUnion_Check(arg)) { + total_args += PyTuple_GET_SIZE(((unionobject*) arg)->args); + } else { + total_args++; + } + } + // Create new tuple of flattened args. + PyObject *flattened_args = PyTuple_New(total_args); + if (flattened_args == NULL) { + return NULL; + } + Py_ssize_t pos = 0; + for (Py_ssize_t i = 0; i < arg_length; i++) { + PyObject *arg = PyTuple_GET_ITEM(args, i); + if (_PyUnion_Check(arg)) { + PyObject* nested_args = ((unionobject*)arg)->args; + Py_ssize_t nested_arg_length = PyTuple_GET_SIZE(nested_args); + for (Py_ssize_t j = 0; j < nested_arg_length; j++) { + PyObject* nested_arg = PyTuple_GET_ITEM(nested_args, j); + Py_INCREF(nested_arg); + PyTuple_SET_ITEM(flattened_args, pos, nested_arg); + pos++; + } + } else { + if (arg == Py_None) { + arg = (PyObject *)&_PyNone_Type; + } + Py_INCREF(arg); + PyTuple_SET_ITEM(flattened_args, pos, arg); + pos++; + } + } + assert(pos == total_args); + return flattened_args; +} + +static PyObject* +dedup_and_flatten_args(PyObject* args) +{ + args = flatten_args(args); + if (args == NULL) { + return NULL; + } + Py_ssize_t arg_length = PyTuple_GET_SIZE(args); + PyObject *new_args = PyTuple_New(arg_length); + if (new_args == NULL) { + Py_DECREF(args); + return NULL; + } + // Add unique elements to an array. + Py_ssize_t added_items = 0; + for (Py_ssize_t i = 0; i < arg_length; i++) { + int is_duplicate = 0; + PyObject* i_element = PyTuple_GET_ITEM(args, i); + for (Py_ssize_t j = 0; j < added_items; j++) { + PyObject* j_element = PyTuple_GET_ITEM(new_args, j); + int is_ga = _PyGenericAlias_Check(i_element) && + _PyGenericAlias_Check(j_element); + // RichCompare to also deduplicate GenericAlias types (slower) + is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ) + : i_element == j_element; + // Should only happen if RichCompare fails + if (is_duplicate < 0) { + Py_DECREF(args); + Py_DECREF(new_args); + return NULL; + } + if (is_duplicate) + break; + } + if (!is_duplicate) { + Py_INCREF(i_element); + PyTuple_SET_ITEM(new_args, added_items, i_element); + added_items++; + } + } + Py_DECREF(args); + _PyTuple_Resize(&new_args, added_items); + return new_args; +} + +static int +is_unionable(PyObject *obj) +{ + return (obj == Py_None || + PyType_Check(obj) || + _PyGenericAlias_Check(obj) || + _PyUnion_Check(obj)); +} + +PyObject * +_Py_union_type_or(PyObject* self, PyObject* other) +{ + if (!is_unionable(self) || !is_unionable(other)) { + Py_RETURN_NOTIMPLEMENTED; + } + + PyObject *tuple = PyTuple_Pack(2, self, other); + if (tuple == NULL) { + return NULL; + } + + PyObject *new_union = make_union(tuple); + Py_DECREF(tuple); + return new_union; +} + +static int +union_repr_item(_PyUnicodeWriter *writer, PyObject *p) +{ + PyObject *qualname = NULL; + PyObject *module = NULL; + PyObject *tmp; + PyObject *r = NULL; + int err; + + if (p == (PyObject *)&_PyNone_Type) { + return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4); + } + + if (_PyObject_LookupAttr(p, &_Py_ID(__origin__), &tmp) < 0) { + goto exit; + } + + if (tmp) { + Py_DECREF(tmp); + if (_PyObject_LookupAttr(p, &_Py_ID(__args__), &tmp) < 0) { + goto exit; + } + if (tmp) { + // It looks like a GenericAlias + Py_DECREF(tmp); + goto use_repr; + } + } + + if (_PyObject_LookupAttr(p, &_Py_ID(__qualname__), &qualname) < 0) { + goto exit; + } + if (qualname == NULL) { + goto use_repr; + } + if (_PyObject_LookupAttr(p, &_Py_ID(__module__), &module) < 0) { + goto exit; + } + if (module == NULL || module == Py_None) { + goto use_repr; + } + + // Looks like a class + if (PyUnicode_Check(module) && + _PyUnicode_EqualToASCIIString(module, "builtins")) + { + // builtins don't need a module name + r = PyObject_Str(qualname); + goto exit; + } + else { + r = PyUnicode_FromFormat("%S.%S", module, qualname); + goto exit; + } + +use_repr: + r = PyObject_Repr(p); +exit: + Py_XDECREF(qualname); + Py_XDECREF(module); + if (r == NULL) { + return -1; + } + err = _PyUnicodeWriter_WriteStr(writer, r); + Py_DECREF(r); + return err; +} + +static PyObject * +union_repr(PyObject *self) +{ + unionobject *alias = (unionobject *)self; + Py_ssize_t len = PyTuple_GET_SIZE(alias->args); + + _PyUnicodeWriter writer; + _PyUnicodeWriter_Init(&writer); + for (Py_ssize_t i = 0; i < len; i++) { + if (i > 0 && _PyUnicodeWriter_WriteASCIIString(&writer, " | ", 3) < 0) { + goto error; + } + PyObject *p = PyTuple_GET_ITEM(alias->args, i); + if (union_repr_item(&writer, p) < 0) { + goto error; + } + } + return _PyUnicodeWriter_Finish(&writer); +error: + _PyUnicodeWriter_Dealloc(&writer); + return NULL; +} + +static PyMemberDef union_members[] = { + {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY}, + {0} +}; + +static PyMethodDef union_methods[] = { + {"__instancecheck__", union_instancecheck, METH_O}, + {"__subclasscheck__", union_subclasscheck, METH_O}, + {0}}; + + +static PyObject * +union_getitem(PyObject *self, PyObject *item) +{ + unionobject *alias = (unionobject *)self; + // Populate __parameters__ if needed. + if (alias->parameters == NULL) { + alias->parameters = _Py_make_parameters(alias->args); + if (alias->parameters == NULL) { + return NULL; + } + } + + PyObject *newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item); + if (newargs == NULL) { + return NULL; + } + + PyObject *res; + Py_ssize_t nargs = PyTuple_GET_SIZE(newargs); + if (nargs == 0) { + res = make_union(newargs); + } + else { + res = PyTuple_GET_ITEM(newargs, 0); + Py_INCREF(res); + for (Py_ssize_t iarg = 1; iarg < nargs; iarg++) { + PyObject *arg = PyTuple_GET_ITEM(newargs, iarg); + Py_SETREF(res, PyNumber_Or(res, arg)); + if (res == NULL) { + break; + } + } + } + Py_DECREF(newargs); + return res; +} + +static PyMappingMethods union_as_mapping = { + .mp_subscript = union_getitem, +}; + +static PyObject * +union_parameters(PyObject *self, void *Py_UNUSED(unused)) +{ + unionobject *alias = (unionobject *)self; + if (alias->parameters == NULL) { + alias->parameters = _Py_make_parameters(alias->args); + if (alias->parameters == NULL) { + return NULL; + } + } + Py_INCREF(alias->parameters); + return alias->parameters; +} + +static PyGetSetDef union_properties[] = { + {"__parameters__", union_parameters, (setter)NULL, "Type variables in the types.UnionType.", NULL}, + {0} +}; + +static PyNumberMethods union_as_number = { + .nb_or = _Py_union_type_or, // Add __or__ function +}; + +static const char* const cls_attrs[] = { + "__module__", // Required for compatibility with typing module + NULL, +}; + +static PyObject * +union_getattro(PyObject *self, PyObject *name) +{ + unionobject *alias = (unionobject *)self; + if (PyUnicode_Check(name)) { + for (const char * const *p = cls_attrs; ; p++) { + if (*p == NULL) { + break; + } + if (_PyUnicode_EqualToASCIIString(name, *p)) { + return PyObject_GetAttr((PyObject *) Py_TYPE(alias), name); + } + } + } + return PyObject_GenericGetAttr(self, name); +} + +PyTypeObject _PyUnion_Type = { + PyVarObject_HEAD_INIT(&PyType_Type, 0) + .tp_name = "types.UnionType", + .tp_doc = PyDoc_STR("Represent a PEP 604 union type\n" + "\n" + "E.g. for int | str"), + .tp_basicsize = sizeof(unionobject), + .tp_dealloc = unionobject_dealloc, + .tp_alloc = PyType_GenericAlloc, + .tp_free = PyObject_GC_Del, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, + .tp_traverse = union_traverse, + .tp_hash = union_hash, + .tp_getattro = union_getattro, + .tp_members = union_members, + .tp_methods = union_methods, + .tp_richcompare = union_richcompare, + .tp_as_mapping = &union_as_mapping, + .tp_as_number = &union_as_number, + .tp_repr = union_repr, + .tp_getset = union_properties, +}; + +static PyObject * +make_union(PyObject *args) +{ + assert(PyTuple_CheckExact(args)); + + args = dedup_and_flatten_args(args); + if (args == NULL) { + return NULL; + } + if (PyTuple_GET_SIZE(args) == 1) { + PyObject *result1 = PyTuple_GET_ITEM(args, 0); + Py_INCREF(result1); + Py_DECREF(args); + return result1; + } + + unionobject *result = PyObject_GC_New(unionobject, &_PyUnion_Type); + if (result == NULL) { + Py_DECREF(args); + return NULL; + } + + result->parameters = NULL; + result->args = args; + _PyObject_GC_TRACK(result); + return (PyObject*)result; +} From 2085cf95acba4accb8698056ce54e9912dc4a713 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Tue, 19 Apr 2022 08:16:33 +0300 Subject: [PATCH 2/2] Remove spurious implementation of PEP-604 from the future --- Objects/unionobject.c | 492 ------------------------------------------ 1 file changed, 492 deletions(-) delete mode 100644 Objects/unionobject.c diff --git a/Objects/unionobject.c b/Objects/unionobject.c deleted file mode 100644 index 5432c6faa3f909..00000000000000 --- a/Objects/unionobject.c +++ /dev/null @@ -1,492 +0,0 @@ -// types.UnionType -- used to represent e.g. Union[int, str], int | str -#include "Python.h" -#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK -#include "pycore_unionobject.h" -#include "structmember.h" - - -static PyObject *make_union(PyObject *); - - -typedef struct { - PyObject_HEAD - PyObject *args; - PyObject *parameters; -} unionobject; - -static void -unionobject_dealloc(PyObject *self) -{ - unionobject *alias = (unionobject *)self; - - _PyObject_GC_UNTRACK(self); - - Py_XDECREF(alias->args); - Py_XDECREF(alias->parameters); - Py_TYPE(self)->tp_free(self); -} - -static int -union_traverse(PyObject *self, visitproc visit, void *arg) -{ - unionobject *alias = (unionobject *)self; - Py_VISIT(alias->args); - Py_VISIT(alias->parameters); - return 0; -} - -static Py_hash_t -union_hash(PyObject *self) -{ - unionobject *alias = (unionobject *)self; - PyObject *args = PyFrozenSet_New(alias->args); - if (args == NULL) { - return (Py_hash_t)-1; - } - Py_hash_t hash = PyObject_Hash(args); - Py_DECREF(args); - return hash; -} - -static int -is_generic_alias_in_args(PyObject *args) -{ - Py_ssize_t nargs = PyTuple_GET_SIZE(args); - for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { - PyObject *arg = PyTuple_GET_ITEM(args, iarg); - if (_PyGenericAlias_Check(arg)) { - return 0; - } - } - return 1; -} - -static PyObject * -union_instancecheck(PyObject *self, PyObject *instance) -{ - unionobject *alias = (unionobject *) self; - Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args); - if (!is_generic_alias_in_args(alias->args)) { - PyErr_SetString(PyExc_TypeError, - "isinstance() argument 2 cannot contain a parameterized generic"); - return NULL; - } - for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { - PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg); - if (PyType_Check(arg)) { - int res = PyObject_IsInstance(instance, arg); - if (res < 0) { - return NULL; - } - if (res) { - Py_RETURN_TRUE; - } - } - } - Py_RETURN_FALSE; -} - -static PyObject * -union_subclasscheck(PyObject *self, PyObject *instance) -{ - if (!PyType_Check(instance)) { - PyErr_SetString(PyExc_TypeError, "issubclass() arg 1 must be a class"); - return NULL; - } - unionobject *alias = (unionobject *)self; - if (!is_generic_alias_in_args(alias->args)) { - PyErr_SetString(PyExc_TypeError, - "issubclass() argument 2 cannot contain a parameterized generic"); - return NULL; - } - Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args); - for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { - PyObject *arg = PyTuple_GET_ITEM(alias->args, iarg); - if (PyType_Check(arg)) { - int res = PyObject_IsSubclass(instance, arg); - if (res < 0) { - return NULL; - } - if (res) { - Py_RETURN_TRUE; - } - } - } - Py_RETURN_FALSE; -} - -static PyObject * -union_richcompare(PyObject *a, PyObject *b, int op) -{ - if (!_PyUnion_Check(b) || (op != Py_EQ && op != Py_NE)) { - Py_RETURN_NOTIMPLEMENTED; - } - - PyObject *a_set = PySet_New(((unionobject*)a)->args); - if (a_set == NULL) { - return NULL; - } - PyObject *b_set = PySet_New(((unionobject*)b)->args); - if (b_set == NULL) { - Py_DECREF(a_set); - return NULL; - } - PyObject *result = PyObject_RichCompare(a_set, b_set, op); - Py_DECREF(b_set); - Py_DECREF(a_set); - return result; -} - -static PyObject* -flatten_args(PyObject* args) -{ - Py_ssize_t arg_length = PyTuple_GET_SIZE(args); - Py_ssize_t total_args = 0; - // Get number of total args once it's flattened. - for (Py_ssize_t i = 0; i < arg_length; i++) { - PyObject *arg = PyTuple_GET_ITEM(args, i); - if (_PyUnion_Check(arg)) { - total_args += PyTuple_GET_SIZE(((unionobject*) arg)->args); - } else { - total_args++; - } - } - // Create new tuple of flattened args. - PyObject *flattened_args = PyTuple_New(total_args); - if (flattened_args == NULL) { - return NULL; - } - Py_ssize_t pos = 0; - for (Py_ssize_t i = 0; i < arg_length; i++) { - PyObject *arg = PyTuple_GET_ITEM(args, i); - if (_PyUnion_Check(arg)) { - PyObject* nested_args = ((unionobject*)arg)->args; - Py_ssize_t nested_arg_length = PyTuple_GET_SIZE(nested_args); - for (Py_ssize_t j = 0; j < nested_arg_length; j++) { - PyObject* nested_arg = PyTuple_GET_ITEM(nested_args, j); - Py_INCREF(nested_arg); - PyTuple_SET_ITEM(flattened_args, pos, nested_arg); - pos++; - } - } else { - if (arg == Py_None) { - arg = (PyObject *)&_PyNone_Type; - } - Py_INCREF(arg); - PyTuple_SET_ITEM(flattened_args, pos, arg); - pos++; - } - } - assert(pos == total_args); - return flattened_args; -} - -static PyObject* -dedup_and_flatten_args(PyObject* args) -{ - args = flatten_args(args); - if (args == NULL) { - return NULL; - } - Py_ssize_t arg_length = PyTuple_GET_SIZE(args); - PyObject *new_args = PyTuple_New(arg_length); - if (new_args == NULL) { - Py_DECREF(args); - return NULL; - } - // Add unique elements to an array. - Py_ssize_t added_items = 0; - for (Py_ssize_t i = 0; i < arg_length; i++) { - int is_duplicate = 0; - PyObject* i_element = PyTuple_GET_ITEM(args, i); - for (Py_ssize_t j = 0; j < added_items; j++) { - PyObject* j_element = PyTuple_GET_ITEM(new_args, j); - int is_ga = _PyGenericAlias_Check(i_element) && - _PyGenericAlias_Check(j_element); - // RichCompare to also deduplicate GenericAlias types (slower) - is_duplicate = is_ga ? PyObject_RichCompareBool(i_element, j_element, Py_EQ) - : i_element == j_element; - // Should only happen if RichCompare fails - if (is_duplicate < 0) { - Py_DECREF(args); - Py_DECREF(new_args); - return NULL; - } - if (is_duplicate) - break; - } - if (!is_duplicate) { - Py_INCREF(i_element); - PyTuple_SET_ITEM(new_args, added_items, i_element); - added_items++; - } - } - Py_DECREF(args); - _PyTuple_Resize(&new_args, added_items); - return new_args; -} - -static int -is_unionable(PyObject *obj) -{ - return (obj == Py_None || - PyType_Check(obj) || - _PyGenericAlias_Check(obj) || - _PyUnion_Check(obj)); -} - -PyObject * -_Py_union_type_or(PyObject* self, PyObject* other) -{ - if (!is_unionable(self) || !is_unionable(other)) { - Py_RETURN_NOTIMPLEMENTED; - } - - PyObject *tuple = PyTuple_Pack(2, self, other); - if (tuple == NULL) { - return NULL; - } - - PyObject *new_union = make_union(tuple); - Py_DECREF(tuple); - return new_union; -} - -static int -union_repr_item(_PyUnicodeWriter *writer, PyObject *p) -{ - PyObject *qualname = NULL; - PyObject *module = NULL; - PyObject *tmp; - PyObject *r = NULL; - int err; - - if (p == (PyObject *)&_PyNone_Type) { - return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4); - } - - if (_PyObject_LookupAttr(p, &_Py_ID(__origin__), &tmp) < 0) { - goto exit; - } - - if (tmp) { - Py_DECREF(tmp); - if (_PyObject_LookupAttr(p, &_Py_ID(__args__), &tmp) < 0) { - goto exit; - } - if (tmp) { - // It looks like a GenericAlias - Py_DECREF(tmp); - goto use_repr; - } - } - - if (_PyObject_LookupAttr(p, &_Py_ID(__qualname__), &qualname) < 0) { - goto exit; - } - if (qualname == NULL) { - goto use_repr; - } - if (_PyObject_LookupAttr(p, &_Py_ID(__module__), &module) < 0) { - goto exit; - } - if (module == NULL || module == Py_None) { - goto use_repr; - } - - // Looks like a class - if (PyUnicode_Check(module) && - _PyUnicode_EqualToASCIIString(module, "builtins")) - { - // builtins don't need a module name - r = PyObject_Str(qualname); - goto exit; - } - else { - r = PyUnicode_FromFormat("%S.%S", module, qualname); - goto exit; - } - -use_repr: - r = PyObject_Repr(p); -exit: - Py_XDECREF(qualname); - Py_XDECREF(module); - if (r == NULL) { - return -1; - } - err = _PyUnicodeWriter_WriteStr(writer, r); - Py_DECREF(r); - return err; -} - -static PyObject * -union_repr(PyObject *self) -{ - unionobject *alias = (unionobject *)self; - Py_ssize_t len = PyTuple_GET_SIZE(alias->args); - - _PyUnicodeWriter writer; - _PyUnicodeWriter_Init(&writer); - for (Py_ssize_t i = 0; i < len; i++) { - if (i > 0 && _PyUnicodeWriter_WriteASCIIString(&writer, " | ", 3) < 0) { - goto error; - } - PyObject *p = PyTuple_GET_ITEM(alias->args, i); - if (union_repr_item(&writer, p) < 0) { - goto error; - } - } - return _PyUnicodeWriter_Finish(&writer); -error: - _PyUnicodeWriter_Dealloc(&writer); - return NULL; -} - -static PyMemberDef union_members[] = { - {"__args__", T_OBJECT, offsetof(unionobject, args), READONLY}, - {0} -}; - -static PyMethodDef union_methods[] = { - {"__instancecheck__", union_instancecheck, METH_O}, - {"__subclasscheck__", union_subclasscheck, METH_O}, - {0}}; - - -static PyObject * -union_getitem(PyObject *self, PyObject *item) -{ - unionobject *alias = (unionobject *)self; - // Populate __parameters__ if needed. - if (alias->parameters == NULL) { - alias->parameters = _Py_make_parameters(alias->args); - if (alias->parameters == NULL) { - return NULL; - } - } - - PyObject *newargs = _Py_subs_parameters(self, alias->args, alias->parameters, item); - if (newargs == NULL) { - return NULL; - } - - PyObject *res; - Py_ssize_t nargs = PyTuple_GET_SIZE(newargs); - if (nargs == 0) { - res = make_union(newargs); - } - else { - res = PyTuple_GET_ITEM(newargs, 0); - Py_INCREF(res); - for (Py_ssize_t iarg = 1; iarg < nargs; iarg++) { - PyObject *arg = PyTuple_GET_ITEM(newargs, iarg); - Py_SETREF(res, PyNumber_Or(res, arg)); - if (res == NULL) { - break; - } - } - } - Py_DECREF(newargs); - return res; -} - -static PyMappingMethods union_as_mapping = { - .mp_subscript = union_getitem, -}; - -static PyObject * -union_parameters(PyObject *self, void *Py_UNUSED(unused)) -{ - unionobject *alias = (unionobject *)self; - if (alias->parameters == NULL) { - alias->parameters = _Py_make_parameters(alias->args); - if (alias->parameters == NULL) { - return NULL; - } - } - Py_INCREF(alias->parameters); - return alias->parameters; -} - -static PyGetSetDef union_properties[] = { - {"__parameters__", union_parameters, (setter)NULL, "Type variables in the types.UnionType.", NULL}, - {0} -}; - -static PyNumberMethods union_as_number = { - .nb_or = _Py_union_type_or, // Add __or__ function -}; - -static const char* const cls_attrs[] = { - "__module__", // Required for compatibility with typing module - NULL, -}; - -static PyObject * -union_getattro(PyObject *self, PyObject *name) -{ - unionobject *alias = (unionobject *)self; - if (PyUnicode_Check(name)) { - for (const char * const *p = cls_attrs; ; p++) { - if (*p == NULL) { - break; - } - if (_PyUnicode_EqualToASCIIString(name, *p)) { - return PyObject_GetAttr((PyObject *) Py_TYPE(alias), name); - } - } - } - return PyObject_GenericGetAttr(self, name); -} - -PyTypeObject _PyUnion_Type = { - PyVarObject_HEAD_INIT(&PyType_Type, 0) - .tp_name = "types.UnionType", - .tp_doc = PyDoc_STR("Represent a PEP 604 union type\n" - "\n" - "E.g. for int | str"), - .tp_basicsize = sizeof(unionobject), - .tp_dealloc = unionobject_dealloc, - .tp_alloc = PyType_GenericAlloc, - .tp_free = PyObject_GC_Del, - .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC, - .tp_traverse = union_traverse, - .tp_hash = union_hash, - .tp_getattro = union_getattro, - .tp_members = union_members, - .tp_methods = union_methods, - .tp_richcompare = union_richcompare, - .tp_as_mapping = &union_as_mapping, - .tp_as_number = &union_as_number, - .tp_repr = union_repr, - .tp_getset = union_properties, -}; - -static PyObject * -make_union(PyObject *args) -{ - assert(PyTuple_CheckExact(args)); - - args = dedup_and_flatten_args(args); - if (args == NULL) { - return NULL; - } - if (PyTuple_GET_SIZE(args) == 1) { - PyObject *result1 = PyTuple_GET_ITEM(args, 0); - Py_INCREF(result1); - Py_DECREF(args); - return result1; - } - - unionobject *result = PyObject_GC_New(unionobject, &_PyUnion_Type); - if (result == NULL) { - Py_DECREF(args); - return NULL; - } - - result->parameters = NULL; - result->args = args; - _PyObject_GC_TRACK(result); - return (PyObject*)result; -}