Skip to content

Enable mypy in precommit checks #2177

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

Merged
merged 22 commits into from
Jun 25, 2019
Merged

Enable mypy in precommit checks #2177

merged 22 commits into from
Jun 25, 2019

Conversation

chriselion
Copy link
Contributor

@chriselion chriselion commented Jun 24, 2019

This enables mypy (python type annotation checks) for ml-agents, ml-agents-env, and gym-unity. The number of lines changed is a little intimidating, but it's mostly due to adding new stub files. I can break this into a PR per library though.

The repo layout added a few complications:

tc.initialize_trainers(bad_config)
assert (
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mypy correctly complained that the assert was always true. pytest.raises is a cleaner way to test that an exception is raised

return l2.copy()
if l1 is not None and l2 is None:
return l1.copy()
def safe_concat_lists(l1: Optional[List], l2: Optional[List]) -> Optional[List]:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mypy was getting confused about the null-ness of the inputs. Personally I think this is a little nicer anyway.

@@ -0,0 +1,51 @@
# @generated by generate_proto_mypy_stubs.py. Do not edit!
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of these .pyi files are stubs generated with https://github.com/dropbox/mypy-protobuf. I updated the directions with how to generate them.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why use stub files instead of putting the type annotation directly onto the python source file?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The python source here is generated by protoc and it's not generating traditional python classes. In theory we might be able to do it, but it would involve wrestling with both protoc and mypy-protobuf.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, make sense.

@@ -373,7 +373,18 @@ def step(
custom_action = {} if custom_action is None else custom_action

# Check that environment is loaded, and episode is currently running.
if self._loaded and not self._global_done and self._global_done is not None:
if not self._loaded:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It wasn't obvious that this block either raises or returns; I think this is a little cleaner.
Could also unindent the "main" block but trying to keep the diff smaller.

@@ -623,7 +623,7 @@ def _get_state(self, output: UnityRLOutput) -> (AllBrainInfo, bool):

def _generate_step_input(
self, vector_action, memory, text_action, value, custom_action
) -> UnityRLInput:
) -> UnityInput:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were just wrong :)

@@ -607,7 +607,7 @@ def _flatten(cls, arr) -> List[float]:
arr = [float(x) for x in arr]
return arr

def _get_state(self, output: UnityRLOutput) -> (AllBrainInfo, bool):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately that's not valid type annotation syntax, you have to use Tuple[]

@chriselion chriselion changed the title Develop precommit mypy Enable mypy in precommit checks Jun 25, 2019
@chriselion chriselion requested a review from xiaomaogy June 25, 2019 19:04
@chriselion
Copy link
Contributor Author

@ervteng just a heads up. I don't think this conflicts much with your rewards refactor but there might be a bit of overlap.

- id: mypy
name: mypy-ml-agents
files: "ml-agents/.*"
args: [--ignore-missing-imports, --disallow-incomplete-defs]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are --ignore-missing-imports, --disallow-incomplete-defs used for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--disallow-incomplete-defs means that the checker will complain about functions that have some arguments and return annotated, but not others. For example

def foo(x: int, y) -> int:  # bad, no type hint on y

def bar(x: int, y: int):  # bad, no return type

would be treated as errors. I didn't apply it to ml-agents-env because there's still some code that I'm not sure what the types should be.

--ignore-missing-imports is a little more complicated; without it the checker will complain about importing numpy and tensorflow. It also complains about some imports in the mlagents repo; I think is related to our directory structure (similar to the package problems we had during the release) but I'll admit that I don't fully understand it yet. I would like to remove these in the future but it's the only practical way to get checks passing today.

@@ -48,6 +48,18 @@ the platform, and provide a unique non-trivial challenge to modern
machine learning algorithms. Feel free to submit these environments with a
PR explaining the nature of the environment and task.

## Style Guide
## Continuous Integration (CI)
Copy link
Contributor

@xiaomaogy xiaomaogy Jun 25, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is still a style guide, we just enforce our style through CI, so maybe still keep this as Style Guide?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a "Code Style" section below. I wanted to give a little more explanation about how the checks (black, mypy, and anything else we add later) were run. I can change this back and move it to another doc if you think it was better before.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think everything is fine here. I don't have a strong opinion on this.

@@ -94,7 +95,7 @@ def initialize(self, inputs: UnityInput) -> UnityOutput:
self.unity_to_external.parent_conn.recv()
return aca_param

def exchange(self, inputs: UnityInput) -> UnityOutput:
def exchange(self, inputs: UnityInput) -> Optional[UnityOutput]:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why this is Optional here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The method can potentially return None (line 105), so we have to change the return type. Optional[UnityOutput] is shorthand for Union[None, UnityOutput]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I missed that part of the logic.

@@ -4,7 +4,8 @@
from mlagents.trainers.buffer import Buffer
from mlagents.envs.brain import BrainParameters, BrainInfo
from mlagents.envs.communicator_objects import *
from google.protobuf.internal.decoder import _DecodeVarint32
from google.protobuf.internal.decoder import _DecodeVarint32 # type: ignore
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does #type:ignore mean here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_DecodeVarint32 isn't declared in the protobuf library stubs here: https://github.com/python/typeshed/blob/master/third_party/2and3/google/protobuf/internal/decoder.pyi
I'm not sure if this is an oversight, or because they don't want to provide type info for internal functions (it starts with a _), or because it's not available in all versions of protobuf.

# type: ignore just tells mypy to ignore errors on that line; it will treat _DecodeVarint32 as an Any. We can try to fix this up, but it didn't seem worth the effort.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, this is good.

@@ -22,7 +22,7 @@ mkdir -p $DST_DIR_P/$PYTHON_PACKAGE
# generate proto objects in python and C#

protoc --proto_path=proto --csharp_out=$DST_DIR_C $SRC_DIR/*.proto
protoc --proto_path=proto --python_out=$DST_DIR_P $SRC_DIR/*.proto
protoc --proto_path=proto --python_out=$DST_DIR_P --mypy_out=$DST_DIR_P $SRC_DIR/*.proto
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you tested this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that was how I generated the .pyi files.

@chriselion chriselion merged commit 8b77f11 into develop Jun 25, 2019
@chriselion chriselion deleted the develop-precommit-mypy branch July 11, 2019 22:26
@github-actions github-actions bot locked as resolved and limited conversation to collaborators May 18, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants