Repository: theacodes/nox
Pull request #297 (2020-03-14 23:55:05)

Remove more type ignore comments
Hey, here's another PR removing more of these comments. From #292 I have fixed 1.3, 2.1, 2.3 and many from 2.2.  
This leaves 1.6 and 2.2 to be removed.


Patch:
diff --git a/nox/_options.py b/nox/_options.py
index 096c681..c7ebd4d 100644
--- a/nox/_options.py
+++ b/nox/_options.py
@@ -134,7 +134,7 @@ def _session_completer(
 ) -> List[str]:
     global_config = parsed_args
     module = load_nox_module(global_config)
-    manifest = discover_manifest(module, global_config)  # type: ignore
+    manifest = discover_manifest(module, global_config)
     filtered_manifest = filter_manifest(manifest, global_config)
     if isinstance(filtered_manifest, int):
         return []
diff --git a/nox/manifest.py b/nox/manifest.py
index 0e8994b..16ce01a 100644
--- a/nox/manifest.py
+++ b/nox/manifest.py
@@ -13,6 +13,7 @@
 # limitations under the License.
 
 import argparse
+import collections.abc
 import itertools
 from typing import Any, Iterable, Iterator, List, Mapping, Set, Tuple, Union
 
@@ -243,7 +244,7 @@ class Manifest:
         raise ValueError("Session {} not found.".format(session))
 
 
-class KeywordLocals:
+class KeywordLocals(collections.abc.Mapping):
     """Eval locals using keywords.
 
     When looking up a local variable the variable name is compared against
@@ -261,11 +262,17 @@ class KeywordLocals:
                 return True
         return False
 
+    def __iter__(self) -> Iterator[str]:
+        return iter(self._keywords)
+
+    def __len__(self) -> int:
+        return len(self._keywords)
+
 
 def keyword_match(expression: str, keywords: Iterable[str]) -> Any:
     """See if an expression matches the given set of keywords."""
     locals = KeywordLocals(set(keywords))
-    return eval(expression, {}, locals)  # type: ignore
+    return eval(expression, {}, locals)
 
 
 def _null_session_func_(session: Session) -> None:
diff --git a/nox/sessions.py b/nox/sessions.py
index add777b..59a5cf4 100644
--- a/nox/sessions.py
+++ b/nox/sessions.py
@@ -109,7 +109,7 @@ class Session:
     @property
     def env(self) -> dict:
         """A dictionary of environment variables to pass into all commands."""
-        return self._runner.venv.env  # type: ignore
+        return self.virtualenv.env
 
     @property
     def posargs(self) -> List[str]:
@@ -118,9 +118,12 @@ class Session:
         return self._runner.global_config.posargs
 
     @property
-    def virtualenv(self) -> Optional[ProcessEnv]:
+    def virtualenv(self) -> ProcessEnv:
         """The virtualenv that all commands are run in."""
-        return self._runner.venv
+        venv = self._runner.venv
+        if venv is None:
+            raise ValueError("A virtualenv has not been created for this session")
+        return venv
 
     @property
     def python(self) -> Optional[Union[str, Sequence[str], bool]]:
@@ -128,9 +131,9 @@ class Session:
         return self._runner.func.python
 
     @property
-    def bin(self) -> str:
+    def bin(self) -> Optional[str]:
         """The bin directory for the virtualenv."""
-        return self._runner.venv.bin  # type: ignore
+        return self.virtualenv.bin
 
     @property
     def interactive(self) -> bool:
@@ -233,10 +236,10 @@ class Session:
             kwargs.setdefault("external", "error")
 
         # Allow all external programs when running outside a sandbox.
-        if not self.virtualenv.is_sandboxed:  # type: ignore
+        if not self.virtualenv.is_sandboxed:
             kwargs["external"] = True
 
-        if args[0] in self.virtualenv.allowed_globals:  # type: ignore
+        if args[0] in self.virtualenv.allowed_globals:
             kwargs["external"] = True
 
         # Run a shell command.
@@ -268,7 +271,8 @@ class Session:
 
         .. _conda install:
         """
-        if not isinstance(self.virtualenv, CondaEnv):
+        venv = self._runner.venv
+        if not isinstance(venv, CondaEnv):
             raise ValueError(
                 "A session without a conda environment can not install dependencies from conda."
             )
@@ -283,7 +287,7 @@ class Session:
             "install",
             "--yes",
             "--prefix",
-            self.virtualenv.location,
+            venv.location,
             *args,
             external="error",
             **kwargs
@@ -314,7 +318,7 @@ class Session:
 
         .. _pip: https://pip.readthedocs.org
         """
-        if not isinstance(self.virtualenv, (CondaEnv, VirtualEnv)):
+        if not isinstance(self._runner.venv, (CondaEnv, VirtualEnv)):
             raise ValueError(
                 "A session without a virtualenv can not install dependencies."
             )
@@ -337,7 +341,7 @@ class Session:
                 may be specified as the appropriate string (same as used for
                 ``nox -s``) or using the function object.
         """
-        self._runner.manifest.notify(target)  # type: ignore
+        self._runner.manifest.notify(target)
 
     def log(self, *args: Any, **kwargs: Any) -> None:
         """Outputs a log during the session."""
@@ -359,7 +363,7 @@ class SessionRunner:
         signatures: List[str],
         func: Func,
         global_config: argparse.Namespace,
-        manifest: "Optional[Manifest]" = None,
+        manifest: "Manifest",
     ) -> None:
         self.name = name
         self.signatures = signatures
diff --git a/nox/tasks.py b/nox/tasks.py
index 7ff2362..41f1fce 100644
--- a/nox/tasks.py
+++ b/nox/tasks.py
@@ -79,7 +79,9 @@ def merge_noxfile_options(
     return module
 
 
-def discover_manifest(module: types.ModuleType, global_config: Namespace) -> Manifest:
+def discover_manifest(
+    module: Union[types.ModuleType, int], global_config: Namespace
+) -> Manifest:
     """Discover all session functions in the noxfile module.
 
     Args:


Test patch:
diff --git a/tests/test_manifest.py b/tests/test_manifest.py
index 62b1664..cd29a5e 100644
--- a/tests/test_manifest.py
+++ b/tests/test_manifest.py
@@ -18,7 +18,7 @@ from unittest import mock
 import nox
 import pytest
 from nox._decorators import Func
-from nox.manifest import Manifest, _null_session_func
+from nox.manifest import KeywordLocals, Manifest, _null_session_func
 
 
 def create_mock_sessions():
@@ -301,3 +301,14 @@ def test_null_session_function():
     session = mock.Mock(spec=("skip",))
     _null_session_func(session)
     assert session.skip.called
+
+
+def test_keyword_locals_length():
+    kw = KeywordLocals({"foo", "bar"})
+    assert len(kw) == 2
+
+
+def test_keyword_locals_iter():
+    values = ["foo", "bar"]
+    kw = KeywordLocals(values)
+    assert list(kw) == values
diff --git a/tests/test_sessions.py b/tests/test_sessions.py
index 86a229b..8af3f85 100644
--- a/tests/test_sessions.py
+++ b/tests/test_sessions.py
@@ -83,6 +83,14 @@ class TestSession:
         assert session.bin is runner.venv.bin
         assert session.python is runner.func.python
 
+    def test_virtualenv_as_none(self):
+        session, runner = self.make_session_and_runner()
+
+        runner.venv = None
+
+        with pytest.raises(ValueError, match="virtualenv"):
+            _ = session.virtualenv
+
     def test_interactive(self):
         session, runner = self.make_session_and_runner()