-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[BUG] Cython doesn't handle mixed imports and cimports of enums cleanly #5609
Comments
CC @shwina |
The issue here seems to be an incorrect determination of whether or not the enum is an lvalue, which then blocks assignment. I'm not sure what the correct change to make here is, though, because it's not clear to me exactly how to access the necessary information. AFAICT what we need is to update
However, it seems like even though |
Well, an enum is not an lvalue. It's a type. It cannot be assigned to.
This sounds wrong, but I'm not sure I've fully understood the problem. (Besides, if you really need both, you can rename one of them on import with So, you are trying to write a That sounds like you should use the C++ enum type as argument type and that's it. You should not need the Python wrapper type at all in your code. Also because from the point of view of Cython, that's not actually a special enum type but an arbitrary Python type like any other. If you try to use only the C++ enum as argument type, what doesn't work then? |
Let me try to explain a bit better. We can first focus on what I see as the inconsistency in Cython's current behavior, then go back to my use case (which is a bit more complex, but I'll explain that too to avoid the XY problem here in case there's a different solution to that problem). Consider the following example:
In this example, I am able to both cimport and import (using the same name) a Cython module, a cdef class, and a cpdef function from the declarations module into the definitions module. My first expectation is therefore that I should be able to do the same thing with a cpdef enum. However, that triggers the error originally noted in issue. Therefore, my conclusion was that it should be possible to treat the enum as an lvalue, at least in the context of an import being considered an "assignment" to a name that is already reserved by a cimport.
This appears to be true as shown in the example above since the class
That is absolutely true, and it is how I'm working around this issue at present. My actual use case is roughly the following. I have one pxd file declarations.pxd that exports C++ definitions to Cython. One of those definitions is an enum class E. I have another Cython module (definitions.pyx + definitions.pxd) that establishes my public API. In definitions I have a cpdef function
In order for this to be possible, I need E to be part of the public API of definitions, which means it needs to be both imported in definitions.pyx and cimported in definitions.pxd so that client code can either import or cimport E. Does this make sense/seem possible? |
So, basically, what you are saying is that you want to write The cimport makes |
Yup that is exactly correct (see the original example, specifically the comments "Works" and "Doesn't work" in the definitions.pxd).
I hadn't considered this option at all. Since declarations.pxd is currently exporting functions defined in C++ that accept the enum as a parameter, it seemed natural to export the enum in that file as well, and when I wanted to make it available in Python I just changed it from a cdef to a cpdef. I think exporting the enum in definitions.pyx would work, though, since in that case I would only need to cimport it into declarations.pxd and I wouldn't need declarations.pyx at all. That seems like a pretty reasonable workaround for my use case, let me give it a shot. |
That does indeed work for the toy example above. Unfortunately it wouldn't translate into my real use case because switching around the dependency structure like that would introduce a circular import dependency. I'm not sure that there is a way to resolve that since in my real use case all the functions in definitions.pyx are just wrappers around C++ functions. Perhaps this example will help illustrate the real use case more clearly (apologies for cluttering this issue with so many code snippets, let me know if there's different information that you think would help more):
|
@scoder given that #5657 was closed, I'd like to reopen this discussion. I didn't do a great job of clarifying my intent in the various messages above, so let me try and start over with a simpler example. I think this statement above is the clearest argument:
Let's illustrate that with this simplified example using only two files (assume
If we run
I see two issues with consistency here:
Therefore, I would argue that the Cython compiler is producing unexpected behavior for the two reasons above. Would you agree? |
Describe the bug
I would like to use Cython to make a C++ scoped enum accessible in Cython and Python code. I'm using a
cpdef enum class
declaration to accomplish this in one pxd file. In another file I would like to define a cpdef function that accepts this enum as a parameter. The cpdef function should use the C++ enum directly when invoked from typed Cython code, while it should use the Python wrapper enum when called in pure Python code. In order to support this, the enum needs to be both imported and cimported.What I observe is that imports in the second module are confused when there is both a pyx and a pxd file, leading to conflicts that are not easily resolved.
Code to reproduce the behaviour:
This MWE requires four separate files:
If I run
cythonize -3 -i declarations.pyx && cythonize -3 -i -f definitions.pyx
with these files, everything compiles successfully. However, if I comment out the two lines below "Works" in definitions.pxd and instead include the two lines below "Doesn't work", I get the following error:Note that the error is coming from the definitions.pyx file, not the pxd file. What appears to be happening is that the line
from declarations cimport my_enum
in definitions.pxd makes Cython treatmy_enum
as a strongly typed C object, and therefore in Python it forbids the import becausemy_enum
is no longer a valid PyObject name.The workaround above isn't too bad, but it does mean that
my_enum
is not cimportable from definitions, only pure Python importable. It can be cimported from declarations in this example, but in my production use case declarations is an internal module not intended for public consumption and I'm looking to export the enum into the public API (trying to solve the same problem as intended in #5602). I could equivalently remove the import from definitions.pyx and only include the cimport, which would create the inverse problem wheremy_enum
is only visible in Cython, not Python.Expected behaviour
I expect to be able to cimport the module into the pxd file with the same name as in the pyx file without any name collisions.
OS
Linux
Python version
3.11.4
Cython version
3.0.0
Additional context
An aside: declarations.pyx is an empty file that's required to trigger compilation of the Python version of
my_enum
. It makes sense why this file is necessary, but it's a bit of a footgun that simply changingmy_enum
from acdef enum class
to acpdef
triggers the requirement that this class exist. It would be nice if the Cython compiler could detect that a pxd file contains a cpdef and errors if a corresponding pyx file doesn't exists. That's orthogonal to the current issue though, and not something new in Cython, just a bit extra surprising in this case IMHO.The text was updated successfully, but these errors were encountered: