Repository: onnx/onnx
Pull request: mypy: update to 0.782 and enable it with Python 3.8 and 3.9
**Description**
- Update to mypy 0.782
- Fix mypy failures for Python 3.8 and 3.9
- Add Python 3.8 and 3.9 in AzurePipeline to cover this mypy typecheck for Python 3.8 and 3.9

**Motivation**
Closes #3968. Current mypy version 0.760 cannot pass by Python 3.8 and Python 3.9

Problem statement:
mypy typecheck (mypy version now is 0.760 in main branch) cannot work with Python 3.8 and 3.9 
# Bug Report
mypy typecheck (mypy 0.760 in main branch) cannot work with Python 3.8 and 3.9. It will throw few check errors. It is not caught by CI (AzurePipeline) because it only uses Python 3.6 and 3.7 with mypy typecheck. We should add 3.8 and 3.9 in AzurePipeline to cover it.

### Reproduction instructions
```
python setup.py typecheck
...
```

### Expected behavior
Should not produce any error.

Hints from issue discussion:

Commit message: Merge branch 'main' into jcw/mypy3.9

Patch to review:
diff --git a/.azure-pipelines/Windows-CI.yml b/.azure-pipelines/Windows-CI.yml
index ef986a77140..7e2eca46595 100644
--- a/.azure-pipelines/Windows-CI.yml
+++ b/.azure-pipelines/Windows-CI.yml
@@ -8,6 +8,14 @@ jobs:
     vmImage: 'windows-2019'
   strategy:
     matrix:
+      py39:
+        python.version: '3.9'
+        onnx_ml: 0
+        onnx_verify_proto: 0
+      py38:
+        python.version: '3.8'
+        onnx_ml: 0
+        onnx_verify_proto: 0
       py37:
         python.version: '3.7'
         onnx_ml: 0
diff --git a/onnx/backend/test/report/coverage.py b/onnx/backend/test/report/coverage.py
index 2011eeb61b8..d7c33d2cc22 100644
--- a/onnx/backend/test/report/coverage.py
+++ b/onnx/backend/test/report/coverage.py
@@ -159,7 +159,7 @@ def report_csv(self, all_ops, passed, experimental):  # type: (List[Text], List[
         if os.path.isfile(nodes_path):
             with open(nodes_path, 'r') as nodes_file:
                 reader = csv.DictReader(nodes_file)
-                frameworks = list(reader.fieldnames)
+                frameworks = list(reader.fieldnames)  # type: ignore
                 for row in reader:
                     op = row[str('Op')]
                     del row[str('Op')]
diff --git a/onnx/helper.py b/onnx/helper.py
index 3757b824c9d..881ad3b04ba 100644
--- a/onnx/helper.py
+++ b/onnx/helper.py
@@ -797,10 +797,10 @@ def printable_node(node, prefix='', subgraphs=False):  # type: (NodeProto, Text,
     printed_attrs = []
     for attr in node.attribute:
         if subgraphs:
-            printed_attr, gs = printable_attribute(attr, subgraphs)
-            assert isinstance(gs, list)
-            graphs.extend(gs)
-            printed_attrs.append(printed_attr)
+            printed_attr_subgraphs = printable_attribute(attr, subgraphs)
+            assert isinstance(printed_attr_subgraphs[1], list)
+            graphs.extend(printed_attr_subgraphs[1])
+            printed_attrs.append(printed_attr_subgraphs[0])
         else:
             printed = printable_attribute(attr)
             assert isinstance(printed, Text)
@@ -865,10 +865,10 @@ def printable_graph(graph, prefix=''):  # type: (GraphProto, Text) -> Text
     graphs = []  # type: List[GraphProto]
     # body
     for node in graph.node:
-        pn, gs = printable_node(node, indent, subgraphs=True)
-        assert isinstance(gs, list)
-        content.append(pn)
-        graphs.extend(gs)
+        contents_subgraphs = printable_node(node, indent, subgraphs=True)
+        assert isinstance(contents_subgraphs[1], list)
+        content.append(contents_subgraphs[0])
+        graphs.extend(contents_subgraphs[1])
     # tail
     tail = ['return']
     if len(graph.output):
diff --git a/setup.py b/setup.py
index 541a540030f..a264c7808e3 100644
--- a/setup.py
+++ b/setup.py
@@ -327,7 +327,7 @@ def run(self):
 tests_require.append('nbval')
 tests_require.append('tabulate')
 
-extras_require['mypy'] = ['mypy==0.760', 'types-protobuf==3.18.4']
+extras_require['mypy'] = ['mypy==0.782', 'types-protobuf==3.18.4']
 
 ################################################################################
 # Final
diff --git a/tools/protoc-gen-mypy.py b/tools/protoc-gen-mypy.py
index 8da4fae3ccc..54387e47b4f 100755
--- a/tools/protoc-gen-mypy.py
+++ b/tools/protoc-gen-mypy.py
@@ -120,7 +120,7 @@ def _import_message(self, type_name):
 
     @contextmanager  # type: ignore
     def _indent(self):
-        # type: () -> Generator
+        # type: () -> Generator[None, None, None]
         self.indent = self.indent + "    "
         yield
         self.indent = self.indent[:-4]


Code review comments:
diff_hunk: @@ -159,7 +159,7 @@ def report_csv(self, all_ops, passed, experimental):  # type: (List[Text], List[
         if os.path.isfile(nodes_path):
             with open(nodes_path, 'r') as nodes_file:
                 reader = csv.DictReader(nodes_file)
-                frameworks = list(reader.fieldnames)
+                frameworks = list(reader.fieldnames)  # type: ignore
path: onnx/backend/test/report/coverage.py
text: @author:
The error is `Argument 1 to "list" has incompatible type "Optional[Sequence[str]]"; expected "List[str]"`.
I tried to make `reader.fieldnames` as `List Optional[Sequence[str]]` or others, but it still showed error anyway. Please let me know if there is a better way to solve it. Thanks!

@user1:
The problem is that `reader.fieldnames` may be None. We can assert that it's not to avoid the type error:

```suggestion
                assert reader.fieldnames
                frameworks = list(reader.fieldnames)
```

@author:
Nice trick. I have updated and it worked. Thank you for the suggestion!

diff_hunk: @@ -120,7 +120,7 @@ def _import_message(self, type_name):
 
     @contextmanager  # type: ignore
     def _indent(self):
-        # type: () -> Generator
+        # type: () -> Generator[None, None, None]
path: tools/protoc-gen-mypy.py
text: @author:
Fix the error: `Missing type parameters for generic type "Generator"`