Repository: theacodes/nox
Pull request #294 (2020-03-06 14:28:49)

Remove some type ignores
Hey, I have started work on #292. In this PR I have updated the code to be able to remove the `# type: ignore` from 1.1, 1.2, 1.4, 1.5 and I have fixed an incorrect typing for [`ProcessEnv.allowed_globals`](https://github.com/theacodes/nox/blob/8869bf2a20d51343e76459ca50ae2dc36d2b46e2/nox/virtualenv.py#L160).

Since 1.1 touches a lot of files and since 1.2 was a bit more complex than I originally thought, I've stopped here. The other changes should be fairly trivial.

This leaves 1.3, 1.6 and all of 2 to be done.

Peilonrayz: I think it's time I learnt how to install all the Conda versions on Windows.
Peilonrayz: I just noticed this doesn't work on 3.5.0. I will fix this.
Peilonrayz: Fixed the issue with 3.5.0

Patch:
diff --git a/nox/__main__.py b/nox/__main__.py
index ad39764..f81412c 100644
--- a/nox/__main__.py
+++ b/nox/__main__.py
@@ -25,9 +25,9 @@ from nox import _options, tasks, workflow
 from nox.logger import setup_logging
 
 try:
-    import importlib.metadata as metadata  # type: ignore
+    import importlib.metadata as metadata
 except ImportError:  # pragma: no cover
-    import importlib_metadata as metadata  # type: ignore
+    import importlib_metadata as metadata
 
 
 def main() -> None:
diff --git a/nox/_option_set.py b/nox/_option_set.py
index dead59c..79ae39b 100644
--- a/nox/_option_set.py
+++ b/nox/_option_set.py
@@ -23,7 +23,7 @@ import functools
 from argparse import ArgumentError, ArgumentParser, Namespace, _ArgumentGroup
 from typing import Any, Callable, Dict, List, Optional, Tuple, Union
 
-import argcomplete  # type: ignore
+import argcomplete
 
 
 class Option:
diff --git a/nox/_parametrize.py b/nox/_parametrize.py
index cd124d9..9f2cd63 100644
--- a/nox/_parametrize.py
+++ b/nox/_parametrize.py
@@ -120,17 +120,22 @@ def parametrize_decorator(
 
     # If there's only one arg_name, arg_values_list should be a single item
     # or list. Transform it so it'll work with the combine step.
+    _arg_values_list = []  # type: List[Union[Param, Iterable[Union[Any, ArgValue]]]]
     if len(arg_names) == 1:
         # In this case, the arg_values_list can also just be a single item.
-        if isinstance(arg_values_list, tuple):
-            # Must be mutable for the transformation steps
-            arg_values_list = list(arg_values_list)
-        if not isinstance(arg_values_list, list):
-            arg_values_list = [arg_values_list]
+        # Must be mutable for the transformation steps
+        if isinstance(arg_values_list, (tuple, list)):
+            _arg_values_list = list(arg_values_list)
+        else:
+            _arg_values_list = [arg_values_list]
 
-        for n, value in enumerate(arg_values_list):
+        for n, value in enumerate(_arg_values_list):
             if not isinstance(value, Param):
-                arg_values_list[n] = [value]
+                _arg_values_list[n] = [value]
+    elif isinstance(arg_values_list, Param):
+        _arg_values_list = [arg_values_list]
+    else:
+        _arg_values_list = list(arg_values_list)
 
     # if ids aren't specified at all, make them an empty list for zip.
     if not ids:
@@ -138,9 +143,7 @@ def parametrize_decorator(
 
     # Generate params for each item in the param_args_values list.
     param_specs = []  # type: List[Param]
-    for param_arg_values, param_id in itertools.zip_longest(
-        arg_values_list, ids  # type: ignore
-    ):
+    for param_arg_values, param_id in itertools.zip_longest(_arg_values_list, ids):
         if isinstance(param_arg_values, Param):
             param_spec = param_arg_values
             param_spec.arg_names = tuple(arg_names)
diff --git a/nox/_typing.py b/nox/_typing.py
index 46671ac..d323f04 100644
--- a/nox/_typing.py
+++ b/nox/_typing.py
@@ -1,4 +1,4 @@
-__all__ = ["TYPE_CHECKING", "NoReturn", "Python"]
+__all__ = ["TYPE_CHECKING", "ClassVar", "NoReturn", "Python"]
 
 import typing as _typing
 
@@ -18,4 +18,13 @@ except ImportError:
     except ImportError:
         pass
 
+
+try:
+    from typing import ClassVar
+except ImportError:
+    try:
+        from typing_extensions import ClassVar
+    except ImportError:
+        pass
+
 Python = _typing.Optional[_typing.Union[str, _typing.Sequence[str], bool]]
diff --git a/nox/command.py b/nox/command.py
index 9f3de29..766481e 100644
--- a/nox/command.py
+++ b/nox/command.py
@@ -16,7 +16,7 @@ import os
 import sys
 from typing import Any, Iterable, Optional, Sequence, Union
 
-import py  # type: ignore
+import py
 from nox.logger import logger
 from nox.popen import popen
 
diff --git a/nox/logger.py b/nox/logger.py
index 3d0ac6f..9d24105 100644
--- a/nox/logger.py
+++ b/nox/logger.py
@@ -15,7 +15,7 @@
 import logging
 from typing import Any, cast
 
-from colorlog import ColoredFormatter  # type: ignore
+from colorlog import ColoredFormatter
 
 SUCCESS = 25
 OUTPUT = logging.DEBUG - 1
diff --git a/nox/sessions.py b/nox/sessions.py
index eb523f0..add777b 100644
--- a/nox/sessions.py
+++ b/nox/sessions.py
@@ -32,7 +32,7 @@ from typing import (
 )
 
 import nox.command
-import py  # type: ignore
+import py
 from nox import _typing
 from nox._decorators import Func
 from nox.logger import logger
@@ -343,13 +343,13 @@ class Session:
         """Outputs a log during the session."""
         logger.info(*args, **kwargs)
 
-    def error(self, *args: Any, **kwargs: Any) -> "_typing.NoReturn":
+    def error(self, *args: Any) -> "_typing.NoReturn":
         """Immediately aborts the session and optionally logs an error."""
-        raise _SessionQuit(*args, **kwargs)  # type: ignore
+        raise _SessionQuit(*args)
 
-    def skip(self, *args: Any, **kwargs: Any) -> "_typing.NoReturn":
+    def skip(self, *args: Any) -> "_typing.NoReturn":
         """Immediately skips the session and optionally logs a warning."""
-        raise _SessionSkip(*args, **kwargs)  # type: ignore
+        raise _SessionSkip(*args)
 
 
 class SessionRunner:
@@ -423,7 +423,7 @@ class SessionRunner:
                 )
             )
 
-        self.venv.create()  # type: ignore
+        self.venv.create()
 
     def execute(self) -> "Result":
         logger.warning("Running session {}".format(self.friendly_name))
diff --git a/nox/tasks.py b/nox/tasks.py
index 30e4cda..7ff2362 100644
--- a/nox/tasks.py
+++ b/nox/tasks.py
@@ -21,7 +21,7 @@ from argparse import Namespace
 from typing import List, Union
 
 import nox
-from colorlog.escape_codes import parse_colors  # type: ignore
+from colorlog.escape_codes import parse_colors
 from nox import _options, registry
 from nox.logger import logger
 from nox.manifest import Manifest
diff --git a/nox/tox_to_nox.py b/nox/tox_to_nox.py
index b9d1b2e..222f5fb 100644
--- a/nox/tox_to_nox.py
+++ b/nox/tox_to_nox.py
@@ -20,7 +20,7 @@ import pkgutil
 from typing import Any, Iterator
 
 import jinja2
-import tox.config  # type: ignore
+import tox.config
 
 _TEMPLATE = jinja2.Template(
     pkgutil.get_data(__name__, "tox_to_nox.jinja2").decode("utf-8"),  # type: ignore
diff --git a/nox/virtualenv.py b/nox/virtualenv.py
index 87d8ca4..918525e 100644
--- a/nox/virtualenv.py
+++ b/nox/virtualenv.py
@@ -17,12 +17,14 @@ import platform
 import re
 import shutil
 import sys
-from typing import Any, Mapping, Optional, Union
+from typing import Any, Mapping, Optional, Tuple, Union
 
 import nox.command
-import py  # type: ignore
+import py
 from nox.logger import logger
 
+from . import _typing
+
 # Problematic environment variables that are stripped from all commands inside
 # of a virtualenv. See https://github.com/theacodes/nox/issues/44
 _BLACKLISTED_ENV_VARS = frozenset(
@@ -44,7 +46,7 @@ class ProcessEnv:
     is_sandboxed = False
 
     # Special programs that aren't included in the environment.
-    allowed_globals = ()
+    allowed_globals = ()  # type: _typing.ClassVar[Tuple[Any, ...]]
 
     def __init__(self, bin: None = None, env: Mapping[str, str] = None) -> None:
         self._bin = bin
@@ -63,6 +65,9 @@ class ProcessEnv:
     def bin(self) -> Optional[str]:
         return self._bin
 
+    def create(self) -> bool:
+        raise NotImplementedError("ProcessEnv.create should be overwitten in subclass")
+
 
 def locate_via_py(version: str) -> Optional[str]:
     """Find the Python executable using the Windows Launcher.
@@ -157,7 +162,7 @@ class CondaEnv(ProcessEnv):
     """
 
     is_sandboxed = True
-    allowed_globals = ("conda",)  # type: ignore
+    allowed_globals = ("conda",)
 
     def __init__(
         self,
diff --git a/noxfile.py b/noxfile.py
index ec43aa7..540de2f 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -67,7 +67,13 @@ def blacken(session):
 @nox.session(python="3.8")
 def lint(session):
     session.install("flake8==3.7.8", "black==19.3b0", "mypy==0.720")
-    session.run("mypy", "--disallow-untyped-defs", "--warn-unused-ignores", "nox")
+    session.run(
+        "mypy",
+        "--disallow-untyped-defs",
+        "--warn-unused-ignores",
+        "--ignore-missing-imports",
+        "nox",
+    )
     files = ["nox", "tests", "noxfile.py", "setup.py"]
     session.run("black", "--check", *files)
     session.run("flake8", "nox", *files)


Test patch:
diff --git a/tests/test__parametrize.py b/tests/test__parametrize.py
index 9e2d9a7..2a7f69b 100644
--- a/tests/test__parametrize.py
+++ b/tests/test__parametrize.py
@@ -63,6 +63,15 @@ def test_parametrize_decorator_one_with_args():
     assert f.parametrize == [{"abc": 1}, {"abc": 2}, {"abc": 3}]
 
 
+def test_parametrize_decorator_param():
+    def f():
+        pass
+
+    _parametrize.parametrize_decorator(["abc", "def"], _parametrize.Param(1))(f)
+
+    assert f.parametrize == [_parametrize.Param(1, arg_names=("abc", "def"))]
+
+
 def test_parametrize_decorator_id_list():
     def f():
         pass
diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py
index 53b304d..543d66f 100644
--- a/tests/test_virtualenv.py
+++ b/tests/test_virtualenv.py
@@ -126,6 +126,12 @@ def test_process_env_constructor():
     assert penv.env["SIGIL"] == "123"
 
 
+def test_process_env_create():
+    penv = nox.virtualenv.ProcessEnv()
+    with pytest.raises(NotImplementedError):
+        penv.create()
+
+
 def test_condaenv_constructor_defaults(make_conda):
     venv, _ = make_conda()
     assert venv.location