Repository: theacodes/nox
Pull request #378 (2021-01-01 22:51:34)

Make ProcessEnv.bin always return a str
The property now never returns None. This simplifies user code containing
types that already knows a bin directory must exist. It avoids the need
to pepper calling code with:

    assert session.bin is not None

Or

    # type: ignore

For example, in pip:
https://github.com/pypa/pip/blob/062f0e54d99f58e53be36be5a45adad89e2429fb/tools/automation/release/__init__.py#L29

A noxfile that tries to access a bin directory that doesn't exist will
now raise an exception.


Patch:
diff --git a/nox/sessions.py b/nox/sessions.py
index 396a09f..96d80eb 100644
--- a/nox/sessions.py
+++ b/nox/sessions.py
@@ -167,10 +167,12 @@ class Session:
         return self.virtualenv.bin_paths
 
     @property
-    def bin(self) -> Optional[str]:
+    def bin(self) -> str:
         """The first bin directory for the virtualenv."""
         paths = self.bin_paths
-        return paths[0] if paths is not None else None
+        if paths is None:
+            raise ValueError("The environment does not have a bin directory.")
+        return paths[0]
 
     def create_tmp(self) -> str:
         """Create, and return, a temporary directory."""
@@ -184,7 +186,7 @@ class Session:
         """Returns True if Nox is being run in an interactive session or False otherwise."""
         return not self._runner.global_config.non_interactive and sys.stdin.isatty()
 
-    def chdir(self, dir: str) -> None:
+    def chdir(self, dir: Union[str, os.PathLike]) -> None:
         """Change the current working directory."""
         self.log("cd {}".format(dir))
         os.chdir(dir)
diff --git a/nox/virtualenv.py b/nox/virtualenv.py
index c32efaf..eba573e 100644
--- a/nox/virtualenv.py
+++ b/nox/virtualenv.py
@@ -69,10 +69,12 @@ class ProcessEnv:
         return self._bin_paths
 
     @property
-    def bin(self) -> Optional[str]:
+    def bin(self) -> str:
         """The first bin directory for the virtualenv."""
         paths = self.bin_paths
-        return paths[0] if paths is not None else None
+        if paths is None:
+            raise ValueError("The environment does not have a bin directory.")
+        return paths[0]
 
     def create(self) -> bool:
         raise NotImplementedError("ProcessEnv.create should be overwritten in subclass")


Test patch:
diff --git a/tests/test_sessions.py b/tests/test_sessions.py
index e1e19ea..2d4cf11 100644
--- a/tests/test_sessions.py
+++ b/tests/test_sessions.py
@@ -18,6 +18,7 @@ import operator
 import os
 import sys
 import tempfile
+from pathlib import Path
 from unittest import mock
 
 import nox.command
@@ -108,7 +109,10 @@ class TestSession:
         session, runner = self.make_session_and_runner()
 
         runner.venv.bin_paths = None
-        assert session.bin is None
+        with pytest.raises(
+            ValueError, match=r"^The environment does not have a bin directory\.$"
+        ):
+            session.bin
         assert session.bin_paths is None
 
     def test_virtualenv_as_none(self):
@@ -151,6 +155,17 @@ class TestSession:
         assert os.getcwd() == cdto
         os.chdir(current_cwd)
 
+    def test_chdir_pathlib(self, tmpdir):
+        cdto = str(tmpdir.join("cdbby").ensure(dir=True))
+        current_cwd = os.getcwd()
+
+        session, _ = self.make_session_and_runner()
+
+        session.chdir(Path(cdto))
+
+        assert os.getcwd() == cdto
+        os.chdir(current_cwd)
+
     def test_run_bad_args(self):
         session, _ = self.make_session_and_runner()
 
diff --git a/tests/test_virtualenv.py b/tests/test_virtualenv.py
index eb67694..7200d2d 100644
--- a/tests/test_virtualenv.py
+++ b/tests/test_virtualenv.py
@@ -121,10 +121,17 @@ def patch_sysfind(make_mocked_interpreter_path):
 def test_process_env_constructor():
     penv = nox.virtualenv.ProcessEnv()
     assert not penv.bin_paths
+    with pytest.raises(
+        ValueError, match=r"^The environment does not have a bin directory\.$"
+    ):
+        penv.bin
 
     penv = nox.virtualenv.ProcessEnv(env={"SIGIL": "123"})
     assert penv.env["SIGIL"] == "123"
 
+    penv = nox.virtualenv.ProcessEnv(bin_paths=["/bin"])
+    assert penv.bin == "/bin"
+
 
 def test_process_env_create():
     penv = nox.virtualenv.ProcessEnv()