Skip to content

Commit d4b1500

Browse files
authored
Resolve optional Path correctly (#197)
I was adding types to beetsplug/fetchart.py and found that this ```py @cached_property def fallback(self) -> Path | None: return self.config["fallback"].get( confuse.Optional(confuse.templates.Path()) ) ``` fails with ``` beetsplug/fetchart.py:1370: error: Incompatible return value type (got "PurePath | None", expected "Path | None") [return-value] ```
2 parents e5fea14 + e24ce6e commit d4b1500

4 files changed

Lines changed: 20 additions & 8 deletions

File tree

confuse/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
from .exceptions import * # noqa: F403
55
from .sources import * # noqa: F403
66
from .templates import * # noqa: F403
7+
from .templates import Path as Path
78
from .util import * # type: ignore[no-redef] # noqa: F403
89
from .yaml_util import * # noqa: F403

confuse/templates.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
T = TypeVar("T")
2525
T_co = TypeVar("T_co", covariant=True)
2626
K = TypeVar("K", bound=Hashable, default=str)
27-
P = TypeVar("P", bound=pathlib.PurePath | str, default=str)
27+
P = TypeVar("P", bound=pathlib.Path | str, default=str)
2828
V = TypeVar("V", default=object)
2929
ConfigKey = int | str | bytes
3030
ConfigKeyT = TypeVar("ConfigKeyT", bound=ConfigKey, default=str)
@@ -738,7 +738,7 @@ def value(
738738
return os.path.abspath(path_str)
739739

740740

741-
class Path(Filename[pathlib.PurePath]):
741+
class Path(Filename[pathlib.Path]):
742742
"""A template that validates strings as `pathlib.Path` objects.
743743
744744
Filenames are parsed equivalent to the `Filename` template and then
@@ -863,7 +863,7 @@ def as_template(value: set[T]) -> Choice[T, T]: ...
863863
@overload
864864
def as_template(value: list[T]) -> OneOf[T]: ...
865865
@overload
866-
def as_template(value: pathlib.PurePath) -> Path: ...
866+
def as_template(value: pathlib.Path) -> Path: ...
867867
@overload
868868
def as_template(value: None) -> Template[None]: ...
869869
@overload
@@ -897,7 +897,7 @@ def as_template(value: Any) -> Template[Any]:
897897
return Number()
898898
elif isinstance(value, float):
899899
return Number(value)
900-
elif isinstance(value, pathlib.PurePath):
900+
elif isinstance(value, pathlib.Path):
901901
return Path(value)
902902
elif value is None:
903903
return Template(None)

docs/changelog.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ Unreleased
66

77
- Require `typing_extensions` on older Python versions (and use `typing` on
88
newer versions). [#189](https://github.com/beetbox/confuse/issues/189)
9+
- Narrow `Path` template shorthand handling to concrete `pathlib.Path` values
10+
for more accurate type checking.
11+
- Fix `confuse.Path` which now points to `confuse.templates.Path` instead of
12+
`pathlib.Path`.
913

1014
v2.2.0
1115
------

test/test_valid.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
11
import enum
22
import os
3+
import pathlib
34
import unittest
45
from collections.abc import Mapping, Sequence
56
from typing import Any
67

78
import pytest
89

10+
try:
11+
from typing import assert_type # type: ignore[attr-defined]
12+
except ImportError: # Python < 3.11
13+
from typing_extensions import assert_type
14+
915
import confuse
1016

1117
from . import _root
@@ -461,19 +467,20 @@ def test_filename_wrong_type(self):
461467

462468
class PathTest(unittest.TestCase):
463469
def test_path_value(self):
464-
import pathlib
465-
466470
config = _root({"foo": "foo/bar"})
467471
valid = config["foo"].get(confuse.Path())
468472
assert valid == pathlib.Path(os.path.abspath("foo/bar"))
469473

470474
def test_default_value(self):
471-
import pathlib
472-
473475
config = _root({})
474476
valid = config["foo"].get(confuse.Path("foo/bar"))
475477
assert valid == pathlib.Path("foo/bar")
476478

479+
def test_optional_value_type(self):
480+
config = _root({"foo": "foo/bar"})
481+
valid = config["foo"].get(confuse.Optional(confuse.Path()))
482+
assert_type(valid, pathlib.Path | None)
483+
477484
def test_default_none(self):
478485
config = _root({})
479486
valid = config["foo"].get(confuse.Path(None))

0 commit comments

Comments
 (0)