Update GetItem to support __class_getitem__ for type objects#3518
Update GetItem to support __class_getitem__ for type objects#3518scoder merged 2 commits intocython:masterfrom
Conversation
|
Looking at it again, I think what I'd do is merge Obviously that may be contradicted by others, but this looks to me to be the way to do it with no changes to how well it runs. |
c963359 to
5c194be
Compare
Cython/Compiler/Nodes.py
Outdated
| if not self.is_classmethod and self.name in ('__class_getitem__', '__init_subclass__') and env.is_py_class_scope: | ||
| from .ExprNodes import NameNode | ||
| self.decorators = self.decorators or [] | ||
| self.decorators.append(DecoratorNode(self.pos, decorator=NameNode(self.pos, name='classmethod'))) |
There was a problem hiding this comment.
| self.decorators.append(DecoratorNode(self.pos, decorator=NameNode(self.pos, name='classmethod'))) | |
| self.decorators.append(DecoratorNode(self.pos, decorator=NameNode(self.pos, name=EncodedString('classmethod')))) |
Apart from that – what is this decorator needed for? Isn't setting self.is_classmethod enough?
There was a problem hiding this comment.
I borrowed this logic from another place in the same file where we add a staticmethod decorator. Should that spot likely change as well?
As for why we need the decorator at all - in my testing it seems like setting is_classmethod alone was not enough. I believe the issue is that we need the logic generated from https://github.com/cython/cython/blob/master/Cython/Compiler/Symtab.py#L2006. It probably makes sense for this logic to be generated based off of the is_classmethod setting and for the decorator to be ignored but that refactor is at least currently beyond my familiarity with this codebase.
e675883 to
2db6125
Compare
Cython/Compiler/Nodes.py
Outdated
| if sys.version_info[0:2] >= (3, 6): | ||
| IMPLICIT_CLASSMETHODS.add("__init_subclass__") | ||
| if sys.version_info[0:2] >= (3, 7): | ||
| IMPLICIT_CLASSMETHODS.add("__class_getitem__") |
There was a problem hiding this comment.
Open to suggestions on a better place for these definitions to exist and/or better way to express this.
There was a problem hiding this comment.
This is definitely the wrong way to check the version of Python - the version used to run Cython isn't necessarily the same as the version of Python the code will run in.
I still don't think this needs version checks - there's no harm in supporting this even on Python 2.7. Nobody should be using these attributes for anything else since I'm fairly sure Python reserves all unused special method names for its own use.
Cython/Utility/ObjectHandling.c
Outdated
|
|
||
| #if PY_VERSION_HEX >= 0x03070000 | ||
| if (likely(PyType_Check(obj))) { | ||
| PyObject *meth = __Pyx_PyObject_GetAttrStr(obj, PYIDENT("__class_getitem__")); |
There was a problem hiding this comment.
I just noticed that this may raise AttributeError, which the _PyObject_LookupAttr() in the original code does not. There's a __Pyx_PyObject_GetAttrStrNoError() for that purpose. And we should make sure this is covered by tests.
There was a problem hiding this comment.
True, I'll add a test to test behavior when __class_getitem__ does not exist however even with the current implementation it still later raises a TypeError which I guess overwrites the AttributeError. I'll update to use the suggested method, though.
| @@ -0,0 +1,135 @@ | |||
| # mode: run | |||
| # tag: pure3.7 | |||
There was a problem hiding this comment.
Looks like I'm failing CI from this test on python versions <3.7. Any idea what might be wrong here @da-woods?
There was a problem hiding this comment.
In Py2, the classes are old-style classes, not types. Let them inherit from (object).
And the _patched test depends on the __init_subclass__ protocol, which is Py3.6+, it seems. You can disable it in Py<3.5 with a @skipIf() decorator.
There was a problem hiding this comment.
I was actually under the impression that the "tag" comment would instruct the test runner to ignore this test for python < 3.7 which is what I really wanted to do.
There was a problem hiding this comment.
"pure3.7" runs the (Cython compiled) tests in all versions of Python, and also runs the tests in uncompiled-Python for Python>=3.7. I think this is correct because it should be possible for Cython to support __class_getitem__ all the time. Essentially it's just providing confirmation that Cython matches Python behaviour (for the versions of Python where this is supported)
The __init_subclass__ is probably something that has to be in the Python interpreter and Cython can't do on its own, so it makes sense to disable conditionally for some versions of Python.
There was a problem hiding this comment.
The old style classes are actually a bug in current master. With language_level=3, there shouldn't be any old style classes in Py2 any more. I pushed a fix as #3530, let's see if that breaks anything in the test suite.
There was a problem hiding this comment.
The
__init_subclass__is probably something that has to be in the Python interpreter and Cython can't do on its own
There's actually #2781 about this. PEP-487 Just needs to get implemented.
e0defa6 to
708fb11
Compare
|
@msg555 could you please merge in the latest master branch? That should fix the tests. |
I think the tests are automatically run on the PR merged with the master branch it might not be necessary to merge. I could be wrong but it'll probably be clear when the tests run. |
|
Rebased on master with the suggested changes |
|
Thanks @msg555 ! |
To address #2753
TODO: Write tests