-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
[Python] Add support for PEP 484 type hints #735
Comments
Well, there is the -py3 option that adds annotations to the proxy classes. But they are very useless for IDEs because they use the C/C type, and not the actual python type. |
IDE like VS code only support auto-completion based on PEP 484 type hints. |
These type annotations would be a great addition to SWIG. SWIG is an all volunteer project, but I don't know of anyone working on this. I encourage anyone interested in adding this support to discuss it here. |
Hi, For instance, when producing sphinx documentation with the
(the argument has been type-annotated with |
Currently the We need a consistent and flexible approach for the 3 different sort of types across docstrings, doxygen sourced comments and Python3 function annotations. Looks like these are the 3 types possible:
The doxygen sourced types come from one of the C type or the "doctype" typemap and autodoc uses one of the C type or "docstring:name". Needless to say this is bit of a mess. A proposal to clean this all up is most welcome, if not urgently required. I strongly suggest a clear proposal is put into a new issue for discussion. Unfortunately the merging in of the Doxygen module for SWIG-4.0.0 is about to go live, I'm wondering whether we should clean this up before pushing out SWIG-4.0.0, despite the long delays in releasing it. Any thoughts?
I'd take this to mean that whatever tools you are using should work without PEP-484 compliant type hints and they shouldn't require PEP-484 type hints. Nevertheless, SWIG should have more control over whether or not they should be generated, with or without |
In my project these type hints were not acceptable for users, so I whipped up a script to rename C/C names into interpretable Python types. Hope this is helpful to people out there! |
Python function annotations containing C/C types are no longer generated when using the -py3 option. Function annotations support has been moved to a feature to provide finer grained control. It can be turned on globally by adding: �ature("python:annotations", "c"); or by using the command line argument: -features python:annotations=c The implementation is designed to be expandable to support different annotations implementations. Future implementations could implement something like the following for generating pure Python types: �ature("python:annotations", "python"); or typing module types to conform to PEP-484: �ature("python:annotations", "typing"); Closes #1561 Issue #735
The first stage of this has been implemented in 2072ae1 by removing the current C/C type annotations from being generated with the -py3 option. The implementation has been designed to be expandable and configurable for alternative implementations. The three options in the list above could for example be implemented using:
with 2. now done. |
I think pure Python types and typing module types are the same. In Python 3.10 it is often possible to type everything without a single import from the typing module. Since PEP 585 (Python 3.8) generics can be type hinted in standard collections: a: list[str] = ["a", "b", "c"]
b: dict[str, int] = {"a": 1, "b": 2, "c": 3} Since PEP 604 (Python 3.10) union types can be written as a: str | int = "abc"
b: str | int = 123 It is currently also recommended to not use a: str | None = None This might be further improved by PEP 645 (Draft) which would allow writing optional types as a: str? = None |
I am interested in adding PEP 484 type hints to SWIG. Below is some relevant info and a proposal of how this could be achieved: Python CompatibilityInline AnnotationsAnnotations can be added to the Python source files themselves, this requires a new syntax and is not backwards compatible. The function annotation syntax was added to Python 3.0 in PEP 3107 and the variable annotation syntax was added to Python 3.6 in PEP 526. Valid since Python 3.0: def func(parameter: "parameter annotation") -> "return annotation":
pass Valid since Python 3.6: var: "variable annotation" = 123 Stub Files (
|
@bonqra, today I finished off the rest of what I wanted to contribute in this space by polishing off the variable annotations support in #1951. This basically implements your Phase 1. I suggest you tweak your plan slightly according to how this has been implemented. I'll come back with comments on the two remaining phases when done. |
@wsfulton there are still some parts missing but adding them in will be easy thanks to the new |
While I am not very familiar with PEP 484 typing hints, they don't look the same to me. Consider this: �ature("python:annotations", "c");
%include <std_vector.i>
%template(VectorInt) std::vector<int>;
void takeVector(std::vector<int> vi) {}
// [Edited to remove int <-> std::string confusion for the vector type] I would see the 3 types as this:
I'm not sure what the typing module hints would exactly look like, but the container/generic syntax along the lines of |
Phase 1 is indeed missing some variable wrappers, such as wrapping global variables. I'm not sure how this would be implemented without stub files as the implementation of 'cvar' is in C code. I'm not sure how this would be achieved as there is no obvious C api to add in annotations. |
Regarding Phase 2, is it not possible to have typing overloads implemented independent of stub files? |
The pure Python as well as the PEP 484 type hint for this would be
I will write up a detailed description but this might take some time. The gist of it is to use the correct fundamental type (
Type hinting the wrappers only is fine for consumers of libraries, they would get the correct type hints. Long term it would be desirable to type hint compiled modules with stub files so the SWIG generated Python files aren't full of type errors. But Phase 1 is only about adding as much type hints through inline annotations as possible, Phase 2 extends this through stub files to type hint everything so Phase 3 can enable full PEP 484 support in one go.
They can be inline, too, but that comes with compatibility concerns. It would definitely be worth it to add them to the inline type hints. I will look into what version would be required ( |
This can be done without stub files: /* Some global variable declarations */
%inline %{
extern int ivar;
extern char *strvar;
%} class CvarInterface:
ivar: int
strvar: str
cvar: CvarInterface = _example.cvar |
Ah yes, of course, my bad, Regarding typing overloads. Yes, does feel like it belongs in Phase 1. I suggest break each stage down into sub-stages and the typing overloads would be a final sub-stage. It will really help reviewing and subsequent merging into master if you have a number of clearly defined bite size pull requests rather than one monster sized pull request. |
Compatibility for typing overloads can indeed have their own feature flag to turn them off, how about |
This is currently
Presumably the stub file name does not need to be configurable, it will always be the same as the module name but with a .pyi extension. |
When you have your detailed proposal for PEP 484 type hints, I think it would be useful if took our classic example in Examples/python/class and hand wrote the final output and include in the proposal as an example. This example is remarkably similar to one discussed in Covariance and contravariance in PEP 484. Another one including the
I think I see this now. However to me, it still seems that there is a difference between pure Python type hints and the typing module type hints. For VectorInt, the class definition would remain as is for pure Python type hints: class VectorInt(object):
... but would be enhanced for the typing module type hints, something along the lines of (I've probably got some of this wrong, but I can see similar examples in the PEP 484 doc): from typing import List
class VectorInt(object, List[int]):
... or maybe from typing import Iterable, Container, Generic
class VectorInt(object, Iterable[int], Container[int], Generic[int]):
... noting that the following code works for my SWIG example above, that is, both lists and tuples can be used instead of VectorInt: takeVector([10, 20, 30])
takeVector((10, 20, 30))
takeVector(VectorInt((10, 20, 30))) |
Perfect, I'll add them to Phase 1.
Yeah, I planned on doing each phase as one PR but splitting them up further makes sense.
Sounds good, note that compiled modules will always be typed with a stub file.
Correct.
Great idea, the pyright playground doesn't support multiple files so I'll also set up a repo that can be explored with VSCode's "strict" Python analysis mode (which uses pyright under the hood).
I think I understand what you mean now, but I don't understand the need for this distinction. Why are pure Python type hints required? Couldn't all type hints be typing module type hints?
I didn't consider that any sequence can be passed, the corrected type hint is: def takeVector(vi: VectorInt | Sequence[int]) -> None: ... Note that this can't be typed with pure Python type hints. It would be possible to do: def takeVector(vi: VectorInt | list[int] | tuple[int]) -> None: ... But this doesn't allow custom sequences. And the typing module type hint of VectorInt would be: from typing import Iterable, MutableSequence
# since 3.9 these are aliases to
# from collections.abc import Iterable, MutableSequence
class VectorInt(MutableSequence[int]):
... |
Manually typed examples are now available at https://github.com/bonqra/swig-python-type-hint-example. The proposal isn't updated yet, I'll post again once that's done. |
Hi @bonqra, any news yet? |
here's what I did to get type annotations for return types. I'm on �ature(autodoc,0) but should more or less work for other levels too and I'm pretty sure it doesn't break stuff haha . maybe it helps someone
|
My shot at the renaming of the "C style" return types, with a basic support of the std::vector wrapped classes : #!/bin/bash
# This script convert "useless" C-style return types annotations created by Swig to ones pointing to the real python objects
# Supports "classic" objects and std::vector wrapped object
# Vector classes are expected to be defined as such:
# - %include "std_vector.i" present in the .i file
# - namespace std { %template(VectorClassname) vector<namespace::Classname>; }
# Tested with Swig v4.1.1:
# - with the �ature("python:annotations", "c"); parameter set in the .i file
# - "-c -python" command line arguments
# Usage:
# - update the namespace and vector_classes parameters
# - run swig on the <project>.i file
# - run "bash rename_swig_types.sh <project>.py"
namespace="whatever"
declare -A vector_classes
# vector_classes["Classname"]="VectorClassname"
vector_classes["Class"]="ClassVector"
# First rename "classic" return types from -> "namespace::ClassName" to -> ClassName
sed -i -E "s/\"$namespace::(\w ) *\"/ \1/g" $1
# Then loop on all vector classes and rename all the std::vector stuff
for key in "${!vector_classes[@]}"
do
sed -i "s/\"std::vector< $namespace::$key >::size_type\"/int/g" $1
sed -i "s/\"std::vector< $namespace::$key >::difference_type\"/int/g" $1
sed -i "s/\"std::vector< $namespace::$key,std::allocator< $namespace::$key > > *\"/${vector_classes[$key]}/g" $1
sed -i "s/\"std::vector< $namespace::$key >::value_type const &\"/$key/g" $1
sed -i "s/\"std::vector< $namespace::$key >::value_type\"/$key/g" $1
done
# Rename default types
sed -i 's/"int"/int/' $1
sed -i 's/"bool"/bool/' $1
sed -i 's/"float"/float/' $1
sed -i 's/"double"/float/' $1
sed -i 's/"std::string const &"/str/' $1
# insert annotations import to avoid errors with classes which return their own type
sed -i "1i\\from __future__ import annotations" $1 |
I'm using the For parameter type annotations I think I'll have to modify my C Doxygen comments to indicate type which is a shame but not really that big of a deal. After I do that, it'll again be some simple string parsing. I'm really only concerned with the annotations so I can get type hinting popups in IDEs. A single Python script that I can set to run automatically after I run my SWIG makefile isn't really a big deal, though having official support would be nice. If anyone has suggestions for a better way, I'm all ears. In the meantime, anyone looking for type hints with |
Python 3.5 and later have support for type hints/annotations, which help out in an IDE and are very useful for writing reliable code.
It would be great if SWIG could optionally generate type annotations for Python. Even better, support could be added for more complex constructs such as iterables (e.g.
std::vector
).The text was updated successfully, but these errors were encountered: