cython: Classes lack __annotations__ (PEP-526)

With python 3.7 dataclasses were introduced. They work just fine in Cython except for their standard init function.

How to reproduce: test.pyx

from dataclasses import dataclass

@dataclass
class TestClass:
	x: int
	y: int
	some_string: str

This file compiles to Cython code just fine. However, when ran from the python it raises an error: test2.py

import test

test_class = test.TestClass(1, 2, 'string')

This raises the following:

Traceback (most recent call last):
  File ".\test2.py", line 3, in <module>
    test_class = test.TestClass(1, 2, 'string')
TypeError: __init__() takes 1 positional argument but 4 were given

if you rename test.pyx to test.py and do not compile it, no such error occurs.

my current workaround: setting each property after initialization separately: test.pyx remains unchanged. test.py

import test

test_class = test.TestClass()
test_class.x = 1
test_class.y = 2
test_class.some_string = 'string'

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 18
  • Comments: 24 (16 by maintainers)

Commits related to this issue

Most upvoted comments

awesome news that this is implemented 🎉 thanks a lot

thanks for the quick reply. i will try to get some proper time to work on this. in the meantime i have tried a little workaround:

class Annotated:
    x: int
    y: int 

    __annotations__ = {'x': int, 'y': int}

this seems to work properly. is this what you meant by "get away with building the dict in Python and adding it to the class dict "?

As always, the most important bit are tests. I would add new ones in a file tests/run/pyclass_annotations_pep526.py (with a # tag: pure36 to prevent Py<3.6 from running in uncompiled). Look at the other pyclass tests to see how it’s done, we basically use doctests (which are executed in Python and not compiled, as opposed to the rest of the file).

I first thought that this could be implemented after executing the class body, but it turns out that PEP 526 wants the __annotations__ dict to be available before that, so creating the empty dict and assigning it in __Pyx_Py3MetaclassPrepare() would be the right way.

Then each NameNode inside of the class body (entry.is_pyclass_attr) which holds a self.annotation will have to generate C code (in generate_assignment_code()) to look up the __annotations__ dict in the class scope and assign its name to its annotation .py_result() on an assignment (or creation of the name). I don’t actually know if that happens before or after the assignment to the name (which might fail, but could be shadowed by a try-except). Probably something to test in CPython. Thus: tests.

I’m also stumbling over this currently, especially Python 3.6-style NamedTuples not working.

I don’t have any experience with how Cython works under the hood, but I’d be willing to give fixing this a try if someone could point me where to start. Is there some relevant documentation somewhere?