33import os
44from pathlib import Path
55from typing import Any , Self
6+
67from pydantic import GetCoreSchemaHandler
7- from pydantic_core import core_schema
8+ from pydantic_core import core_schema
89
910
1011class EnsureWritableDirectory :
11- """
12- A Pydantic custom type that ensures a directory exists and is writable.
12+ """A Pydantic custom type that ensures a directory exists and is writable.
1313
1414 Behavior:
1515 1. Expands user paths (e.g., "~/data" -> "/home/user/data").
1616 2. Validates input is a path.
1717 3. If path DOES NOT exist: It creates it (including parents).
18- 4. If path DOES exist (or was just created): It checks is_dir() and R/W/X permissions.
18+ 4. If path DOES exist (or was just created): It checks is_dir()
19+ and R/W/X permissions.
1920 """
2021
21-
2222 def __init__ (self , path : str | Path ) -> None :
23- """
24- Initializes the instance with the fully resolved and expanded path.
23+ """Initialize the instance with the fully resolved and expanded path.
24+
2525 Assumes the validation step (validate_and_create) has already handled
2626 creation and permission checks.
2727 """
28-
2928 self ._path : Path = Path (path ).expanduser ().resolve ()
3029
3130 # --- Pydantic V2 Core Schema ---
@@ -59,10 +58,10 @@ def __get_pydantic_core_schema__(
5958
6059 @classmethod
6160 def validate_and_create (cls , path : Path ) -> Self :
62- """
63- Expands user, checks if path exists. If not, creates it. Then checks permissions.
64- """
61+ """Expand user, checks if path exists.
6562
63+ If not, creates it. Then checks permissions.
64+ """
6665 # Ensure user expansion happens before any filesystem operations
6766 path = path .expanduser ()
6867
@@ -73,7 +72,7 @@ def validate_and_create(cls, path: Path) -> Self:
7372 # exist_ok=True: prevents race conditions
7473 path .mkdir (parents = True , exist_ok = True )
7574 except OSError as e :
76- raise ValueError (f"Could not create directory '{ path } ': { e } " )
75+ raise ValueError (f"Could not create directory '{ path } ': { e } " ) from e
7776
7877 # 2. Type Check
7978 if not path .is_dir ():
@@ -95,13 +94,24 @@ def validate_and_create(cls, path: Path) -> Self:
9594 return cls (path )
9695
9796 # --- Usability Methods ---
98-
9997 def __str__ (self ) -> str :
98+ """Return the string representation of the path."""
10099 return str (self ._path )
101-
100+
102101 def __repr__ (self ) -> str :
102+ """Return the developer-friendly representation of the object."""
103103 return f"{ self .__class__ .__name__ } ('{ self ._path } ')"
104-
105- # Allows access to methods/attributes of the underlying Path object (e.g., .joinpath)
104+
105+ def __truediv__ (self , other : str ) -> Path :
106+ """Implement the / operator to delegate to the underlying Path object."""
107+ return self ._path / other
108+
109+ # Allows access to methods/attributes of the underlying Path object
110+ # (e.g., .joinpath)
106111 def __getattr__ (self , name : str ) -> Any :
112+ """Delegate attribute access to the underlying Path object."""
107113 return getattr (self ._path , name )
114+
115+ def __fspath__ (self ) -> str :
116+ """Return the string path for os.PathLike compatibility."""
117+ return str (self ._path )
0 commit comments