-
-
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
Adds support for typing.Union
#6206
Conversation
As I understand, this can be considered work towards some of the union semantics outlined in #4631. I don't think Cython fused types should be expressed by Python unions. Constrained type variables would be a more natural fit. Consider the following example, which fails to compile in Cython: ctypedef fused char_or_float:
char
double
cpdef char_or_float plus_one(char_or_float var):
return 0.0 cpdef char_or_float plus_one(char_or_float var):
return 0.0
^
error: Cannot assign type 'double' to 'char' In Python, if import typing as t
char_or_float_union: t.TypeAlias = str | float
char_or_float_typevar = t.TypeVar("char_or_float_typevar", str, float)
def plus_one_union(var: char_or_float_union) -> char_or_float_union:
return 0.0 # No errors
def plus_one_typevar(var: char_or_float_typevar) -> char_or_float_typevar:
return 0.0 # mypy: Incompatible return value type (got "float", expected "str") [return-value] |
I see your point and I agree with your example. But I think this should be more optimisation technique than expression of type. I don't see any wrong to optimise def expensive_test(x: Union[cython.int, cython.float]) -> cython.bint:
# Optimised Implementation of time consuming test. Of course we cannot optimise/translate But maybe I am missing something that's why i created draft to get feedback. |
Yeah it's a fair point and we don't want to break too much working code by using type-hints in an odd way. The only thing about fused return types is that they're ignored in a Just thinking about other places it can break:
Reading the documentation, that looks like a better fit for what we're trying to write. I'm personally pretty sceptical of the more advanced features of So I'm a little unsure of what's best to do here. |
Me too. I think implementing more advanced typing features seems to be complex task. My personal motivation of this PR is to implement following optional type using
I would optimise what does make sense and has reasonable complexity. Rest I would ignore as we ignore types now. Currently, I don't know what is exact scope of optimisation but hopefully we will figure out in time. |
If the purpose is to optimise inline Python unions into anonymous fused types behind-the-hood, then I agree that in this example a union could be fine under standard Python typing (but only if the body of the function [doesn't pass this argument to another function which uses a named fused type appearing 2 or more times in the function signature*]). More generally speaking, I believe that Python union types are compatible with a fused type in a function signature only if the union type is (1) declared as a type alias which only appears once in the function signature* or (2) declared inline as an argument annotation. I initially understood this PR as a general way of declaring functions taking and returning fused types - if that's the case, the same subtleties for named fused types as explained in the Cython docs apply to constrained type variables, which would be a much better fit. *Function signature here includes all parameter types and the return type. |
What's Cython's general direction of Pure Python mode, with regards to annotations and utilisation of items from the
|
It's somewhere between the three.
|
That sounds like it matches (2), which is fairly straightforward to work with. I was mainly afraid that it might be (3), which is a lot harder to work with in terms of 3rd party static analysis tools. For future developments of Python unions in Cython, then, I would not expect Cython to compile this into C functions taking and returning each member of a fused type, because that goes against what a Python union type means: import typing as t
import cython
char_or_float: t.TypeAlias = cython.char | cython.float
@cython.cfunc
def f(arg1: char_or_float, arg2: char_or_float) -> char_or_float:
...
if t.TYPE_CHECKING:
# Standard Python type-checker behaviour
reveal_type(f(0.0, 1.0)) # revealed type is "int | float" As for this example, an implicit optimisation with a fused type is fine: import typing as t
import cython
@cython.cfunc
def f(arg1: cython.char | cython.float, arg2: cython.char | cython.float) -> cython.char | cython.float:
"""
# OK for Cython to see this as:
import cython
from typing import Any
fused_type1 = cython.fused_type(cython.char, cython.float)
fused_type2 = cython.fused_type(cython.char, cython.float)
def f(arg1: fused_type_1, arg2: fused_type_2) -> Any: ...
""" |
My current view is that the |
OK I will close this PR then. It was still just an experiment. Any any case, I would like to implement following optimisation though (since it is pretty commonly used):
|
This PR adds support of
typing.Union
. It is optimised to fused type.Note
This PR does not support
Union
ofNone
. HenceUnion[int, None]
will trigger an error.Currently only as a draft. The test suite is passing but needs #6204 merged to master.