-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
Conversation
tc.initialize_trainers(bad_config) | ||
assert ( |
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.
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]: |
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.
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! |
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.
All of these .pyi
files are stubs generated with https://github.com/dropbox/mypy-protobuf. I updated the directions with how to generate them.
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 use stub files instead of putting the type annotation directly onto the python source file?
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.
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
.
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.
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: |
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.
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: |
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.
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): |
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.
Unfortunately that's not valid type annotation syntax, you have to use Tuple[]
@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] |
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 are --ignore-missing-imports, --disallow-incomplete-defs used for?
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.
--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) |
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 think this is still a style guide, we just enforce our style through CI, so maybe still keep this as Style Guide?
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.
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.
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.
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]: |
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 this is Optional here?
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.
The method can potentially return None
(line 105), so we have to change the return type. Optional[UnityOutput]
is shorthand for Union[None, UnityOutput]
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.
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 |
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 does #type:ignore mean here?
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.
_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.
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 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 |
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.
Have you tested this?
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.
Yes, that was how I generated the .pyi
files.
This enables mypy (python type annotation checks) for
ml-agents
,ml-agents-env
, andgym-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:
--ignore-missing-imports
on each. Will try to remove those in subsequent PRs_pb2.py
files are flagged as incorrect because the stubs https://github.com/python/typeshed/tree/master/third_party/2and3/google/protobuf don't have theserialized_options
field that the current code does https://github.com/protocolbuffers/protobuf/blob/master/python/google/protobuf/descriptor.py#L539. Need to see if we can fix the stubs or if they're missing for a reason.