-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Adding minimal support for Cython functions #8631
Conversation
e29eeaf
to
139c80f
Compare
If people are curious what the full .pyi is that is outputted from the test, it's:
|
4d3dd92
to
35fc092
Compare
Thanks for the PR! Cython support for stubgen sounds like a useful thing. Before continuing with the code review, could you fix the CI failures? If something is unclear, feel free to ask here. |
8681f14
to
c00e677
Compare
Minimal support for Cython functions in stubgen for generating .pyi files from a C module. Based on python#7542.
c00e677
to
7e732c4
Compare
@JukkaL CI failures are now fixed; take a look if you have a chance. Thanks! |
ping @JukkaL @dgrunwald ? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the PR! Mostly looks good. Left some comments about testing and questions about PEP 484 type support in Cython.
Better Cython support would be quite useful.
@@ -15,3 +15,4 @@ py>=1.5.2 | |||
virtualenv<20 | |||
setuptools | |||
importlib-metadata==0.20 | |||
Cython |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd rather not add Cython as a dependency. Instead, it would be better to have the test automatically skipped if Cython is not installed.
def f(path: str, a: int = ..., b: bool = ...) -> typing.List[str]: ... | ||
""".strip() in outfile_txt or """ | ||
def f(path: str, a: int = ..., b: bool = ...) -> List: ... | ||
""".strip() in outfile_txt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are there two signatures that we accept?
This check was a bit confusing. I think that it would be easier to read if it was written along these lines:
sig1 = """..."""
sig2 = """..."""
assert sig1.strip() in outfile_txt or sig2.strip() in outfile_txt
(Also it would look nicer with textwrap.dedent()
.)
@@ -804,6 +809,52 @@ def __init__(self, arg0: str) -> None: | |||
'def __init__(*args, **kwargs) -> Any: ...']) | |||
assert_equal(set(imports), {'from typing import overload'}) | |||
|
|||
def test_cython(self) -> None: | |||
pyx_source = """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: What about indenting the source and using textwrap.dedent()
to dedent it?
if hasattr(annotation, '__str__'): | ||
return annotation.__str__() | ||
# Can't do anything here, so ignore | ||
return None |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder what sorts of things Cython might add to the signature. Have you tried that this does something reasonable with all types supported by Cython? It would be good to include all relevant sorts of types in the test case, in case something isn't covered yet.
|
||
import typing | ||
|
||
def f(path: str, a: int = 0, b: bool = True) -> typing.List[str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have some questions about how Cython supports PEP 484 types:
- What if some argument has no type annotation?
- Do you know how Cython supports optional types?
- What would happen if this function would return None (with a list return type)?
- What if
f
is called with aNone
argument forpath
, for example?
(If Cython doesn't properly support optional types, we may not be able to use the types in stubs, at least not without some tweaks.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if some argument has no type annotation?
Then it's an object
.
What would happen if this function would return None (with a list return type)?
That's ok currently. Not sure if it's going to stay that way forever, given that optional types exist, but it's at least unlikely to change all too soon.
What if f is called with a None argument for path, for example?
That's currently ok, too. None
is generally allowed for builtin and extension types, unless explicitly excluded. That's probably also going to change at some point.
If Cython doesn't properly support optional types
Cython currently only uses a subset of what PEP-484 allows, specifically: no container types, no optional types. Nothing that requires a []
or the typing
module, basically. That's a missing feature, though, and will change at some point.
Maybe Cython should just implement Optional[]
and apply not None
semantics to types that use PEP-484 annotations. Probably just a little change in the type parser (if it wasn't for the typing
import tracking, but we could ignore that, I guess).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If Cython doesn't yet ensure that optional types are used consistently, we can't easily know whether an argument should allow None
values in a stub, and whether a function might return None
. A conservative approximation would be to always use an optional type for an argument, and use Union[<type>, Any]
for the return type. The latter is the best way we have of saying that a function returns <type>
and potentially None
(but we don't know for sure). These are still better than using plain Any
or object
as the types.
Also, we don't usually want to use object
as a return type, and it will usually result in many false positives. Instead, it should probably be replaced with Any
.
Maybe Cython should just implement Optional[] and apply not None semantics to types that use PEP-484 annotations.
This would be great, since optional types tend to be used a lot in PEP 484 annotated code. Mypy also didn't properly support optional types in the early days (and we still allow disabling strict None checks, but this is mostly for legacy code).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, we don't usually want to use object as a return type, and it will usually result in many false positives. Instead, it should probably be replaced with Any.
For regular functions I don't think Cython enforces anything about the return type at all. Therefore, from a Cython point of view it should probably always be treated as Any
(although I guess you might chose to trust the user and use their annotation?).
For cdef
functions is does have specified return types, but those are only visible from C anyway so probably not relevant to mypy.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For regular functions …
You probably mean plain Python def
functions here. For cpdef
/ ccall
functions (and cdef
/ cfunc
, obviously), i.e. anything that has a C visible return type, the return types are enforced and checked like for an assignment.
I agree that mypy should believe the user annotations, with the above caveat regarding None
.
return inspect.isbuiltin(obj) or type(obj) is type(ord) | ||
return inspect.isbuiltin(obj) or \ | ||
type(obj) is type(ord) or \ | ||
type(obj).__name__ == 'cython_function_or_method' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably not going to work forever.
Why do these need to be processed as C functions rather than Python functions? I'd just use inspect
on the object and see if that produces something helpful. That wouldn't only work for Cython functions but also for Python functions in .pyc
files etc. (don't know how mypy handles those currently).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if there's any particular reason. If inspect
works, we should perhaps use it?
As long as Cython doesn't enforce the use of optional types, it would at least be useful to be able to detect which functions or modules were compiled by Cython, and generate wider types (as I described above) for them.
|
||
import typing | ||
|
||
def f(path: str, a: int = 0, b: bool = True) -> typing.List[str]: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if some argument has no type annotation?
Then it's an object
.
What would happen if this function would return None (with a list return type)?
That's ok currently. Not sure if it's going to stay that way forever, given that optional types exist, but it's at least unlikely to change all too soon.
What if f is called with a None argument for path, for example?
That's currently ok, too. None
is generally allowed for builtin and extension types, unless explicitly excluded. That's probably also going to change at some point.
If Cython doesn't properly support optional types
Cython currently only uses a subset of what PEP-484 allows, specifically: no container types, no optional types. Nothing that requires a []
or the typing
module, basically. That's a missing feature, though, and will change at some point.
Maybe Cython should just implement Optional[]
and apply not None
semantics to types that use PEP-484 annotations. Probably just a little change in the type parser (if it wasn't for the typing
import tracking, but we could ignore that, I guess).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Jukka had some questions/feedback
Closing, since this has been stale for a long time. |
Minimal support for Cython functions in stubgen for generating .pyi
files from a C module. Based on #7542.
@ilevkivskyi || @JukkaL (or others): PTAL? First time contributing to Mypy. Thanks!