Repository: wntrblm/nox

Issue:
nox 2025.02.09 doesn't like `nox.options.envdir` to be a `pathlib.Path`
### Current Behavior

This used to work in older nox releases but breaks in `2025.02.09`:
```python
File "/__w/foobar/noxfile.py", line 66, in <module>
    nox.options.envdir = Path(".venv")
    ^^^^^^^^^^^^^^^^^^
  File "/github/home/.local/share/pipx/venvs/nox/lib/python3.12/site-packages/attr/_make.py", line 1119, in __setattr__
    nval = hook(self, a, val)
           ^^^^^^^^^^^^^^^^^^
  File "/github/home/.local/share/pipx/venvs/nox/lib/python3.12/site-packages/attr/setters.py", line 22, in wrapped_pipe
    rv = setter(instance, attrib, rv)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/github/home/.local/share/pipx/venvs/nox/lib/python3.12/site-packages/attr/setters.py", line [51](github_url/12345#step:17:52), in validate
    v(instance, attrib, new_value)
  File "/github/home/.local/share/pipx/venvs/nox/lib/python3.12/site-packages/attr/validators.py", line 206, in __call__
    self.validator(inst, attr, value)
  File "/github/home/.local/share/pipx/venvs/nox/lib/python3.12/site-packages/attr/validators.py", line 100, in __call__
    raise TypeError(
TypeError: ("'envdir' must be <class 'str'> (got PosixPath('.venv') that is a <class 'pathlib.PosixPath'>).", Attribute(name='envdir', default=NOTHING, validator=<optional validator for <instance_of validator for type <class 'str'>> or None>, repr=True, eq=True, eq_key=None, order=True, order_key=None, hash=None, init=True, metadata=mappingproxy({}), type='None | str', converter=None, kw_only=True, inherited=False, on_setattr=None, alias='envdir'), <class 'str'>, PosixPath('.venv'))
Error: Process completed with exit code 1.
```

### Expected Behavior

Accept `nox.options.envdir = Path(".venv")`

### Steps To Reproduce

_No response_

### Environment

```Markdown
- OS: Ubuntu 22.04
- Python: 3.12
- Nox: 2025.02.09
```

### Anything else?

_No response_

Discussion:
@henryiii: You can work around it by doing `os.fspath(Path(".venv"))` (or just `".venv"`), fixed in #932.

Solution patch:
diff --git a/nox/_option_set.py b/nox/_option_set.py
index 873a701..74d91a5 100644
--- a/nox/_option_set.py
+++ b/nox/_option_set.py
@@ -21,6 +21,7 @@ from __future__ import annotations
 import argparse
 import collections
 import functools
+import os
 from argparse import ArgumentError, ArgumentParser, Namespace
 from collections.abc import Callable, Iterable
 from typing import TYPE_CHECKING, Any, Literal
@@ -47,6 +48,7 @@ def __dir__() -> list[str]:
 
 
 av_opt_str = av.optional(av.instance_of(str))
+av_opt_path = av.optional(av.or_(av.instance_of(str), av.instance_of(os.PathLike)))
 av_opt_list_str = av.optional(
     av.deep_iterable(
         member_validator=av.instance_of(str),
@@ -59,7 +61,7 @@ av_bool = av.instance_of(bool)
 @attrs.define(slots=True, kw_only=True)
 class NoxOptions:
     default_venv_backend: None | str = attrs.field(validator=av_opt_str)
-    envdir: None | str = attrs.field(validator=av_opt_str)
+    envdir: None | str | os.PathLike[str] = attrs.field(validator=av_opt_path)
     error_on_external_run: bool = attrs.field(validator=av_bool)
     error_on_missing_interpreters: bool = attrs.field(validator=av_bool)
     force_venv_backend: None | str = attrs.field(validator=av_opt_str)
diff --git a/nox/_options.py b/nox/_options.py
index adb0e2a..7ce89ec 100644
--- a/nox/_options.py
+++ b/nox/_options.py
@@ -157,7 +157,7 @@ def _envdir_merge_func(
         noxfile_Args (NoxOptions): The options specified in the
             Noxfile.
     """
-    return command_args.envdir or noxfile_args.envdir or ".nox"
+    return os.fspath(command_args.envdir or noxfile_args.envdir or ".nox")
 
 
 def _reuse_venv_merge_func(


Test patch:
diff --git a/tests/test__option_set.py b/tests/test__option_set.py
index 3035d84..8d21a02 100644
--- a/tests/test__option_set.py
+++ b/tests/test__option_set.py
@@ -154,3 +154,6 @@ class TestOptionSet:
         options.sessions = ("testytest",)
         with pytest.raises(ValueError):  # noqa: PT011
             options.sessions = "testytest"
+
+        options.envdir = "envdir"
+        options.envdir = Path("envdir")