create type hint stub files for module torch#12500
create type hint stub files for module torch#12500t-vi wants to merge 45 commits intopytorch:masterfrom
Conversation
This is more a basis for discussion that a ready solution, as it does lots of funny things, and for many of them a better solution will be found. - Initial stab at creating a type torch/__init__.pyi . - We only do this for Python 3 because we want to use type hint introspection. - So far, we only aim at doing this for torch functions and torch.Tensor. - We need to import the newly build torch. Thus we do this at the end of the build. We use os.fork to only import the module in a child process. - We use an annotate decorator to be able to put type hints on the native python functions in a way that a) they're available in the usual place for Python 3 b) we stay Python 2 compatible - Some annotatons in torch/functional.py are provided as examples, but the remaining ones still need to be done. This could end up fixing pytorch#7318
| 'double': 'float', | ||
| 'Generator *': 'Generator', | ||
| 'Generator*': 'Generator', | ||
| 'std::vector<Tensor>': 'Tuple[Tensor, ...]', |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| 'Layout': 'layout', | ||
| 'void*': 'int', # dataptr | ||
| 'std::string': 'str', | ||
| 'real': 'float', |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
- Add manual tensor() annotation, thank you, @elliotwite. - Create class stubs for device etc. - Improve type mapping. Thank you, Simon, for the review comments.
| 'std::string': 'str', | ||
| 'real': 'Union[float, int]', | ||
| 'accreal': 'Union[float, int]', | ||
| 'IntegerTensor': 'Tensor', |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
|
||
| def arg_to_type_hint(arg): | ||
| name = arg['name'] | ||
| if name == 'from': # keyword... |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
Question: what do you think is a good way to test this? Maybe pick some functions, and construct inputs basing on arg type, and then assert that it doesn't throw error from arg parser (it may still error in later stages due to things like shape mismatch)? |
|
We'll definitely need some sort of testing strategy for this, otherwise it will bitrot faster than you can say leroy jenkins. |
|
|
||
| # annotation decorator to get annotations in a way that is compatible | ||
| # with both Python 2 and 3 | ||
| def annotate(ret, **kwargs): |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| needed_modules = set() | ||
|
|
||
|
|
||
| def type_to_python(typename): |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
Re testing: So the type hints should ideally have two properties:
|
|
|
||
|
|
||
| def do_gen_py(build_lib_path): | ||
| while '' in sys.path: |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| decls = [d for d in decls if d['name'] != fname + '_out'] | ||
| for decl in decls: | ||
| skip = 'Type' in [a['dynamic_type'] for a in decl['arguments']] | ||
| if not skip: |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| return name + ': ' + typename + default | ||
|
|
||
|
|
||
| def generate_type_hints(fname, decls, is_tensor=False): |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
| python_returns_s = python_returns[0] | ||
| type_hint = "def {}({}) -> {}: ...".format(fname, python_args_s, python_returns_s) | ||
| numargs = len(decl['arguments']) | ||
| have_vararg_version = (numargs > 0 and decl['arguments'][0]['dynamic_type'] in {'IntList', 'TensorList'} and |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
OK, so I briefly skimmed the generation code, and I think the best thing we can do to make this presentable is to do some Declarations.yaml cleanup, which should simplify a bit of the hacking around you had to do here. #12562 records some overall directions we want to take here, but probably the most important prereqs for this patch are:
|
|
So what does the #12562 washlist mean for progressing here? |
Let me look again at the code and work out exactly what would be affected by the changes.
Yeah, I don't personally object to it. It seems like a quite nice way to add the annotation.
This sounds like a great idea. |
| return type_hints | ||
|
|
||
|
|
||
| def parameters_from_signature(sig): |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
These spots look like they would make Declarations.yaml refactoring harder Line 56 in a327e18 Line 188 in a327e18 Don't want to hard-code _out handling and inplace handling; should be centralized. Line 60 in a327e18 Type is going away. This one's pretty harmless though, easy enough to fix once Type is killed. Line 65 in a327e18 Hard-coded handling of self. Maybe this is OK and we'll keep this in the end. CC @zdevito Line 75 in a327e18 Hard-coded TensorOptions expansion Line 87 in a327e18 I actually have no idea what is going on with varargs. |
|
The |
| class create_pyi(distutils.command.build.build): | ||
| def run(self): | ||
| print("-- Building .pyi --") | ||
| if sys.version_info[0] == 3: |
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
This comment was marked as off-topic.
This comment was marked as off-topic.
Sorry, something went wrong.
|
After a lot of whacking, the type annotation script no longer requires |
|
Awesome stuff, @ezyang . ❤️ Probably would have been easier to not base it on my feeble attempts. 😕 |
|
Let's be clear now, your diff helped a lot :) Would have been 10x more work without it. |
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
|
Ugh, CI failure doesn't repro locally. Time to fire up the Docker image, I guess... |
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
So, in the end, you were right! The problem is specific to when you install PyTorch, because mypy treats installed packages differently from local packages. It will refuse to attempt to typecheck using installed packages unless they are registered on typeshed, which explains why the imports cannot be found. Your symlink trick solved the problem, because it made the package look local again. There doesn't seem to be a way to otherwise force mypy to treat the package as typecheckable when it's not registered on typeshed. I think I am just going to resurrect this code verbatim with a comment. |
Signed-off-by: Edward Z. Yang <ezyang@fb.com>
* create type hint stub files for module torch This is more a basis for discussion that a ready solution, as it does lots of funny things, and for many of them a better solution will be found. - Initial stab at creating a type torch/__init__.pyi . - We only do this for Python 3 because we want to use type hint introspection. - So far, we only aim at doing this for torch functions and torch.Tensor. - We need to import the newly build torch. Thus we do this at the end of the build. We use os.fork to only import the module in a child process. - We use an annotate decorator to be able to put type hints on the native python functions in a way that a) they're available in the usual place for Python 3 b) we stay Python 2 compatible - Some annotatons in torch/functional.py are provided as examples, but the remaining ones still need to be done. This could end up fixing #7318 This was backported from #12500 but with tests removed and the stub file checked in directly, so we don't have to also make sure all build shenanigans work. Master will be properly tested. The stub file was generated by running 'python setup.py create_pyi' and then copying the generated pyi file in the build directory (find -name *.pyi) to torch/ * Ignore pyi in flake8 Signed-off-by: Edward Z. Yang <ezyang@fb.com> * Real fix for lint error Signed-off-by: Edward Z. Yang <ezyang@fb.com>
|
Oh my god it passed all the tests LOL |
facebook-github-bot
left a comment
There was a problem hiding this comment.
@ezyang is landing this pull request. If you are a Facebook employee, you can view this diff on Phabricator.
|
I'm curious: why didn't you just annotate |
|
@vsiles For a pretty silly reason, actually: the original patch @t-vi wrote didn't do that, and when I tried it, PyCharm shows up the module as This isn't set in stone, esp. if people don't mind the originating module look ugly. But that's the reason. |
Summary: - Fix broken sparse_coo_examples, update output - Tensor(...) to tensor(...) - Fix arguments to math.log to be floats While the last might be debateable, mypy currently complains when passing an int to math.log. As it is not essential for our examples, let's be clean w.r.t. other people's expectations. These popped up while checking examples in the context of pytorch#12500 . Pull Request resolved: pytorch#12707 Differential Revision: D10415256 Pulled By: SsnL fbshipit-source-id: c907b576b02cb0f89d8f261173dbf4b3175b4b8d
Summary: We have: - This is an initial stab at creating a type stub `torch/__init__.pyi` . - This is only tested on Python 3, since that's the only Python version mypy works on. - So far, we only aim at doing this for torch functions and torch.Tensor. - Quite a few methods and functions have to be typed manually. These are done in `torch/__init__.pyi.in` For me, PyCharm (the non-paid one) didn't seem to indicate errors in the .pyi when opening and seemed to be able to get the type hint for the few functions I tried, but I don't use PyCharm for my usual PyTorch activities, so I didn't extensively try this out. An example of a generated PYI is at [this gist](https://gist.github.com/ezyang/bf9b6a5fa8827c52152858169bcb61b1). Pull Request resolved: pytorch#12500 Differential Revision: D13695553 Pulled By: ezyang fbshipit-source-id: 4566c71913ede4e4c23ebc4a72c17151f94e8e21
We have:
works on.
done in torch/init.pyi.in
For me, PyCharm (the non-paid one) didn't seem to indicate errors in the .pyi when opening and seemed to be able to get the type hint for the few functions I tried, but I don't use PyCharm for my usual PyTorch activities, so I didn't extensively try this out.
An example of a generated PYI is at this gist. https://gist.github.com/ezyang/bf9b6a5fa8827c52152858169bcb61b1
Original summary:
This is more a basis for discussion that a ready solution, as it does lots of funny things, and for many of them a better solution will be found, but to facilitate discussion, I'm putting this out.
After many improvements, this could contribute to fixing #7318.
We have
torch/__init__.pyi.We use os.fork to only import the module in a child process.
a) they're available in the usual place for Python 3
b) we stay Python 2 compatible
For me, PyCharm (the non-paid one) didn't seem to indicate errors in the .pyi when opening and seemed to be able to get the type hint for the few functions I tried, but I don't use PyCharm for my usual PyTorch activities, so I didn't extensively try this out.
An example of a generated PYI is at this gist.