Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
4c4c701
Add new type object for union types
maggiemoss Jul 10, 2020
07b5c29
Add test.
maggiemoss Jul 10, 2020
32c207b
Add basic tp_* fields to union object
maggiemoss Jul 15, 2020
50b33c2
Squash this commit.
maggiemoss Jul 15, 2020
8901145
Squash this commit.
maggiemoss Jul 15, 2020
6450321
Add union_traverse method.
maggiemoss Jul 15, 2020
8a09a92
Handle Py_None in union.
maggiemoss Jul 16, 2020
e3c80e9
Clean up syntax with Py_None
maggiemoss Jul 16, 2020
165c323
Update getattro method.
maggiemoss Jul 16, 2020
7f0018e
Reduce code duplication when adding union types together.
maggiemoss Jul 17, 2020
cd584d5
Add __or__ method to _SpecialGenericAlias
maggiemoss Jul 17, 2020
ba031c1
Add support for NewType with unions.
maggiemoss Jul 17, 2020
e62de73
Remove old code from abstract.c
maggiemoss Jul 17, 2020
f6ab05b
Fix warning for incompatible pointer type.
maggiemoss Jul 17, 2020
9c20fbc
Add support for union use in subclasses.
maggiemoss Jul 19, 2020
9f48989
Fix code style inconsistencies.
maggiemoss Jul 22, 2020
a0cd651
Change is_not_union -> is_union
maggiemoss Jul 22, 2020
a5c6850
Implement dedup and flatten.
maggiemoss Jul 24, 2020
5255f4f
Add basic tp_repr to unionobject.
maggiemoss Jul 24, 2020
1732fad
Update is_unionable.
maggiemoss Jul 24, 2020
31653dc
Clean up whitespace.
maggiemoss Jul 25, 2020
2ffb050
Update test_types.
maggiemoss Jul 25, 2020
3fed944
Fix style and grammar issues.
maggiemoss Jul 27, 2020
a1c703b
Implement NE for union richcompare.
maggiemoss Jul 27, 2020
c4acc73
Remove is_generic_alias helper method.
maggiemoss Jul 27, 2020
6a8dfe9
Fix negative ref count.
maggiemoss Jul 27, 2020
af59751
Small code style fixes.
maggiemoss Jul 27, 2020
cae1a38
Add unionobject to pythoncore.vcxproj.filters
maggiemoss Jul 27, 2020
0cb47c1
Remove semicolon after PyObject_HEAD
maggiemoss Jul 27, 2020
265be78
Use _PyTuple_Resize instead of variable length array.
maggiemoss Jul 28, 2020
6fddcd6
Add error handling.
maggiemoss Jul 28, 2020
60f023a
Add additional tests for union_richcompare.
maggiemoss Jul 28, 2020
99ba1fd
📜🤖 Added by blurb_it.
blurb-it[bot] Jul 28, 2020
ad945fd
Update error check for PySet_New call.
maggiemoss Jul 30, 2020
0586c41
Allow Any and other _SpecialForm
maggiemoss Jul 30, 2020
e44a657
Update repr to handle NoneType properly.
maggiemoss Jul 30, 2020
ba3110c
Add Union to types.py
maggiemoss Jul 30, 2020
7bec987
Merge branch 'PEP605-implementation' of github.com:MaggieMoss/cpython…
maggiemoss Jul 30, 2020
e052c53
Remove commented out code.
maggiemoss Jul 31, 2020
15e0d29
Update test with nested unions.
maggiemoss Jul 31, 2020
2e31288
Add additional tests for optionals.
maggiemoss Jul 31, 2020
2a12a43
Add support for int | int == int
maggiemoss Jul 31, 2020
7116cd9
Add more error handling to unionobject
maggiemoss Jul 31, 2020
2c0befb
Handle NoneType vs. None
maggiemoss Aug 3, 2020
8d59e44
Don't allow generics in isinstance calls.
maggiemoss Aug 4, 2020
9f014e2
Add errors for union subclass.
maggiemoss Aug 5, 2020
30bb872
Implement code review suggestions.
maggiemoss Aug 12, 2020
6fe4956
Increase code sharing between is_typing_name and is_new_type
maggiemoss Aug 12, 2020
bae3d2a
Add tests, fix reference leaks.
maggiemoss Aug 12, 2020
17a7277
Update tp_flags for unionobject.
maggiemoss Aug 12, 2020
f47bce0
Return int.
maggiemoss Aug 13, 2020
0735352
Fix NoneType comparison side effect.
maggiemoss Aug 13, 2020
b2910f0
Merge branch 'PEP605-implementation' of github.com:MaggieMoss/cpython…
maggiemoss Aug 14, 2020
e9e5a5e
Remove GC flag from unionobject.
maggiemoss Aug 14, 2020
385a338
Implement code review suggestions
maggiemoss Aug 18, 2020
dddc598
Refactor and remove PyUnion_New
maggiemoss Aug 18, 2020
8c06c1d
General cleanup, fix reference leaks, add new test for possible crashes
pablogsal Sep 8, 2020
37699f1
Add newline
pablogsal Sep 8, 2020
47a4cb3
Make unionobject APIs private
pablogsal Sep 9, 2020
7f89abd
Add minor test for GenericAlias
pablogsal Sep 9, 2020
d0be56f
Remove ellipsis from repr.
maggiemoss Sep 9, 2020
59f06f7
Remove commented out test code.
maggiemoss Sep 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Implement code review suggestions.
- Reorder checking valid arguments for isinstance and issubclass methods.
- Rename variables as suggested.
- Combine checks, where possible.
  • Loading branch information
maggiemoss committed Aug 12, 2020
commit 30bb872a70f77805fa6d0672a2ce0518e579f3ca
3 changes: 3 additions & 0 deletions Lib/test/test_isinstance.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,13 @@ def test_isinstance_with_or_union(self):
self.assertTrue(isinstance(2, typing.List | int))
self.assertFalse(isinstance(2, typing.List | typing.Tuple))
self.assertTrue(isinstance(None, int | None))
self.assertFalse(isinstance(3.14, int | str))
with self.assertRaises(TypeError):
isinstance(2, list[int])
with self.assertRaises(TypeError):
isinstance(2, list[int] | int)
with self.assertRaises(TypeError):
isinstance(2, int | str | list[int] | float)



Expand Down
122 changes: 65 additions & 57 deletions Objects/unionobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,37 +43,42 @@ static PyMemberDef union_members[] = {
{0}
};

static PyObject *
union_getattro(PyObject *self, PyObject *name)
{
return PyObject_GenericGetAttr(self, name);
static int*
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
check_args(PyObject *args) {
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
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 (Py_TYPE(arg) == &Py_GenericAliasType) {
return 0;
}
}
return 1;
}

static PyObject *
union_instancecheck(PyObject *self, PyObject *instance)
Comment thread
gvanrossum marked this conversation as resolved.
Outdated
{
unionobject *alias = (unionobject *) self;
Py_ssize_t nargs = PyTuple_GET_SIZE(alias->args);
if (!check_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);
PyTypeObject* arg_type = Py_TYPE(arg);
PyTypeObject *arg_type = Py_TYPE(arg);
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
if (arg == Py_None) {
arg = (PyObject *)arg_type;
}
if (PyType_Check(arg)) {
if (PyObject_IsInstance(instance, arg) != 0)
{
return self;
}
} else if (arg_type == &Py_GenericAliasType) {
PyErr_SetString(PyExc_TypeError,
"isinstance() argument 2 cannot contain a parameterized generic");
return NULL;
if (PyType_Check(arg) && PyObject_IsInstance(instance, arg) != 0) {
Py_RETURN_TRUE;
}
}
return instance;
Py_RETURN_FALSE;
}


static PyObject *
union_subclasscheck(PyObject *self, PyObject *instance)
{
Expand All @@ -82,22 +87,19 @@ union_subclasscheck(PyObject *self, PyObject *instance)
return NULL;
}
unionobject *alias = (unionobject *)self;
if (!check_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)) {
if (PyType_IsSubtype((PyTypeObject *)instance, (PyTypeObject *)arg) != 0)
{
return self;
}
} else if (Py_TYPE(arg) == &Py_GenericAliasType) {
PyErr_SetString(PyExc_TypeError,
"issubclass() argument 2 cannot contain a parameterized generic");
return NULL;
if (PyType_Check(arg) && (PyType_IsSubtype((PyTypeObject *)instance, (PyTypeObject *)arg) != 0)) {
Py_RETURN_TRUE;
}
}
return instance;
Py_RETURN_FALSE;
}

static int
Expand Down Expand Up @@ -142,7 +144,7 @@ union_richcompare(PyObject *a, PyObject *b, int op)
if (is_typing_name(b, "_UnionGenericAlias")) {
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
PyObject* b_args = PyObject_GetAttrString(b, "__args__");
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
int b_arg_length = PyTuple_GET_SIZE(b_args);
for (int i = 0; i < b_arg_length; i++) {
for (Py_ssize_t i = 0; i < b_arg_length; i++) {
PyObject* arg = PyTuple_GET_ITEM(b_args, i);
if (arg == (PyObject *)&_PyNone_Type) {
PyTuple_SET_ITEM(b_args, i, Py_None);
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
Expand All @@ -152,14 +154,21 @@ union_richcompare(PyObject *a, PyObject *b, int op)
} else if (type == &Py_UnionType) {
unionobject *bb = (unionobject *)b;
b_set = PySet_New(bb->args);
if (b_set == NULL) {
return NULL;
}
} else {
PyObject * tup = PyTuple_Pack(1, b);
b_set = PySet_New(tup);
PyObject *tuple = PyTuple_Pack(1, b);
if (tuple == NULL ) {
return NULL;
}
b_set = PySet_New(tuple);
Py_DECREF(tuple);
if (b_set == NULL) {
return NULL;
}
}

if (b_set == NULL) {
Py_RETURN_FALSE;
}
return PyObject_RichCompare(a_set, b_set, op);
}

Expand All @@ -169,7 +178,7 @@ flatten_args(PyObject* args)
int arg_length = PyTuple_GET_SIZE(args);
int total_args = 0;
// Get number of total args once it's flattened.
for (int i = 0; i < arg_length; i++) {
for (Py_ssize_t i = 0; i < arg_length; i++) {
PyObject *arg = PyTuple_GET_ITEM(args, i);
PyTypeObject* arg_type = Py_TYPE(arg);
if (arg_type == &Py_UnionType) {
Expand All @@ -185,7 +194,7 @@ flatten_args(PyObject* args)
return NULL;
}
int pos = 0;
for (int i = 0; i < arg_length; i++) {
for (Py_ssize_t i = 0; i < arg_length; i++) {
PyObject *arg = PyTuple_GET_ITEM(args, i);
PyTypeObject* arg_type = Py_TYPE(arg);
if (arg_type == &Py_UnionType) {
Expand All @@ -204,7 +213,6 @@ flatten_args(PyObject* args)
pos++;
}
}
Py_INCREF(flattened_args);
return flattened_args;
}

Expand All @@ -221,23 +229,24 @@ dedup_and_flatten_args(PyObject* args)
return NULL;
}
// Add unique elements to an array.
int temp_count = 0;
for(int i = 0; i < arg_length; i++) {
int added_items = 0;
for(Py_ssize_t i = 0; i < arg_length; i++) {
int is_duplicate = 0;
PyObject* i_tuple = PyTuple_GET_ITEM(args, i);
for(int j = i + 1; j < arg_length; j++) {
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
PyObject* j_tuple = PyTuple_GET_ITEM(args, j);
if (i_tuple == j_tuple) is_duplicate = 1;
if (i_tuple == j_tuple) {
is_duplicate = 1;
}
}
if (!is_duplicate) {
Py_INCREF(i_tuple);
PyTuple_SET_ITEM(new_args, temp_count, i_tuple);
temp_count++;
PyTuple_SET_ITEM(new_args, added_items, i_tuple);
added_items++;
}
}

_PyTuple_Resize(&new_args, temp_count);
Py_INCREF(new_args);
Py_DECREF(args);
_PyTuple_Resize(&new_args, added_items);
return new_args;
}

Expand Down Expand Up @@ -297,10 +306,10 @@ union_new(PyTypeObject* self, PyObject* param)
return NULL;
}
PyObject *new_union = Py_Union(tuple);
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
Py_DECREF(tuple);
if (new_union == NULL) {
return NULL;
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
}
Py_INCREF(new_union);
return new_union;
Comment thread
maggiemoss marked this conversation as resolved.
}

Expand Down Expand Up @@ -331,16 +340,16 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
if (p == Py_Ellipsis) {
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
// The Ellipsis object
r = PyUnicode_FromString("...");
goto done;
goto exit;
}

if (_PyObject_LookupAttrId(p, &PyId___origin__, &tmp) < 0) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems that you are only checking that the attribute is there, so why not use something like _PyObject_HasAttrId ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I borrowed heavily from the repr function in generic alias. It seems to me like we're also updating the tmp variable here, and using it later in the function. https://github.com/python/cpython/blob/master/Objects/genericaliasobject.c#L37

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to me like we're also updating the tmp variable here, and using it later in the function.

But you throw it away inmediatele:

    if (tmp != NULL) {
        Py_DECREF(tmp);

and then you substitute it for another one:

	        if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) {
            goto exit;

that then you throw away immediately:

        if (tmp != NULL) {
            Py_DECREF(tmp);

goto done;
goto exit;
}
if (tmp != NULL) {
Py_DECREF(tmp);
if (_PyObject_LookupAttrId(p, &PyId___args__, &tmp) < 0) {
goto done;
goto exit;
}
if (tmp != NULL) {
Py_DECREF(tmp);
Expand All @@ -350,13 +359,13 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
}

if (_PyObject_LookupAttrId(p, &PyId___qualname__, &qualname) < 0) {
goto done;
goto exit;
}
if (qualname == NULL) {
goto use_repr;
}
if (_PyObject_LookupAttrId(p, &PyId___module__, &module) < 0) {
goto done;
goto exit;
}
if (module == NULL || module == Py_None) {
goto use_repr;
Expand All @@ -368,17 +377,17 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
{
// builtins don't need a module name
r = PyObject_Str(qualname);
goto done;
goto exit;
}
else {
r = PyUnicode_FromFormat("%S.%S", module, qualname);
goto done;
goto exit;
}

use_repr:
r = PyObject_Repr(p);

done:
exit:
Py_XDECREF(qualname);
Py_XDECREF(module);
if (r == NULL) {
Expand All @@ -401,10 +410,8 @@ union_repr(PyObject *self)
_PyUnicodeWriter writer;
_PyUnicodeWriter_Init(&writer);
for (Py_ssize_t i = 0; i < len; i++) {
if (i > 0) {
if (_PyUnicodeWriter_WriteASCIIString(&writer, " | ", 3) < 0) {
goto error;
}
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) {
Expand All @@ -430,7 +437,7 @@ PyTypeObject Py_UnionType = {
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
.tp_hash = union_hash,
.tp_traverse = union_traverse,
.tp_getattro = union_getattro,
.tp_getattro = PyObject_GenericGetAttr,
.tp_members = union_members,
.tp_methods = union_methods,
.tp_richcompare = union_richcompare,
Comment thread
gvanrossum marked this conversation as resolved.
Expand Down Expand Up @@ -458,6 +465,7 @@ Py_Union(PyObject *args)
}

PyObject* new_args = dedup_and_flatten_args(args);
Comment thread
maggiemoss marked this conversation as resolved.
Outdated
Py_DECREF(args);
if (new_args == NULL) {
return NULL;
Comment thread
maggiemoss marked this conversation as resolved.
}
Expand Down