From 9804ec5614dfe68125261fdea957c442db7bc641 Mon Sep 17 00:00:00 2001 From: Sai Asish Y Date: Mon, 18 May 2026 23:40:33 -0700 Subject: [PATCH] feat: add Path trait for pathlib.Path values --- docs/source/trait_types.rst | 2 ++ tests/test_traitlets.py | 16 ++++++++++++++++ traitlets/traitlets.py | 21 +++++++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/docs/source/trait_types.rst b/docs/source/trait_types.rst index cb63d66c..c7fb785f 100644 --- a/docs/source/trait_types.rst +++ b/docs/source/trait_types.rst @@ -109,6 +109,8 @@ Miscellaneous .. autoclass:: CRegExp +.. autoclass:: Path + .. autoclass:: Union :members: __init__ diff --git a/tests/test_traitlets.py b/tests/test_traitlets.py index 665422cd..60b790a9 100644 --- a/tests/test_traitlets.py +++ b/tests/test_traitlets.py @@ -8,6 +8,7 @@ from __future__ import annotations import decimal +import pathlib import pickle import re import typing as t @@ -44,6 +45,7 @@ Long, MetaHasTraits, ObjectName, + Path, Set, TCPAddress, This, @@ -1643,6 +1645,20 @@ class TestTCPAddress(TraitTestBase): _bad_values = [(0, 0), ("localhost", 10.0), ("localhost", -1), None] +class PathTrait(HasTraits): + value = Path() + + +class TestPath(TraitTestBase): + obj = PathTrait() + + _good_values = ["foo", "foo/bar", pathlib.Path("foo"), pathlib.PurePath("foo")] + _bad_values = [0, 10.0, None, ["foo"]] + + def coerce(self, value): + return pathlib.Path(value) + + class ListTrait(HasTraits): value = List(Int()) diff --git a/traitlets/traitlets.py b/traitlets/traitlets.py index 4aa08cf3..9c33d2f3 100644 --- a/traitlets/traitlets.py +++ b/traitlets/traitlets.py @@ -46,6 +46,7 @@ import inspect import numbers import os +import pathlib import re import sys import types @@ -111,6 +112,7 @@ "MetaHasTraits", "ObjectName", "ObserveHandler", + "Path", "Set", "TCPAddress", "This", @@ -4208,6 +4210,25 @@ def validate(self, obj: t.Any, value: t.Any) -> re.Pattern[t.Any] | None: self.error(obj, value) +class Path(TraitType["pathlib.Path", t.Union["pathlib.Path", str, "os.PathLike[str]"]]): + """A trait for filesystem paths. + + Accepts strings and :class:`os.PathLike` objects. The resulting + attribute will be a :class:`pathlib.Path` instance.""" + + info_text = "a filesystem path" + + def validate(self, obj: t.Any, value: t.Any) -> pathlib.Path | None: + if isinstance(value, (str, os.PathLike)): + return pathlib.Path(value) + self.error(obj, value) + + def from_string(self, s: str) -> pathlib.Path | None: + if self.allow_none and s == "None": + return None + return pathlib.Path(s) + + class UseEnum(TraitType[t.Any, t.Any]): """Use a Enum class as model for the data type description. Note that if no default-value is provided, the first enum-value is used