@@ -163,10 +163,15 @@ def with_segments(self, *pathsegments):
163163 """
164164 return type (self )(* pathsegments )
165165
166+ @property
167+ def _raw_path (self ):
168+ """The joined but unnormalized path."""
169+ return self .pathmod .join (* self ._raw_paths )
170+
166171 def __str__ (self ):
167172 """Return the string representation of the path, suitable for
168173 passing to system calls."""
169- return self .pathmod . join ( * self . _raw_paths )
174+ return self ._raw_path
170175
171176 def as_posix (self ):
172177 """Return the string representation of the path with forward (/)
@@ -176,23 +181,23 @@ def as_posix(self):
176181 @property
177182 def drive (self ):
178183 """The drive prefix (letter or UNC path), if any."""
179- return self .pathmod .splitdrive (str ( self ) )[0 ]
184+ return self .pathmod .splitdrive (self . _raw_path )[0 ]
180185
181186 @property
182187 def root (self ):
183188 """The root of the path, if any."""
184- return self .pathmod .splitroot (str ( self ) )[1 ]
189+ return self .pathmod .splitroot (self . _raw_path )[1 ]
185190
186191 @property
187192 def anchor (self ):
188193 """The concatenation of the drive and root, or ''."""
189- drive , root , _ = self .pathmod .splitroot (str ( self ) )
194+ drive , root , _ = self .pathmod .splitroot (self . _raw_path )
190195 return drive + root
191196
192197 @property
193198 def name (self ):
194199 """The final path component, if any."""
195- return self .pathmod .basename (str ( self ) )
200+ return self .pathmod .basename (self . _raw_path )
196201
197202 @property
198203 def suffix (self ):
@@ -236,7 +241,7 @@ def with_name(self, name):
236241 dirname = self .pathmod .dirname
237242 if dirname (name ):
238243 raise ValueError (f"Invalid name { name !r} " )
239- return self .with_segments (dirname (str ( self ) ), name )
244+ return self .with_segments (dirname (self . _raw_path ), name )
240245
241246 def with_stem (self , stem ):
242247 """Return a new path with the stem changed."""
@@ -266,18 +271,20 @@ def relative_to(self, other, *, walk_up=False):
266271 other = self .with_segments (other )
267272 anchor0 , parts0 = self ._stack
268273 anchor1 , parts1 = other ._stack
274+ if isinstance (anchor0 , str ) != isinstance (anchor1 , str ):
275+ raise TypeError (f"{ self ._raw_path !r} and { other ._raw_path !r} have different types" )
269276 if anchor0 != anchor1 :
270- raise ValueError (f"{ str ( self ) !r} and { str ( other ) !r} have different anchors" )
277+ raise ValueError (f"{ self . _raw_path !r} and { other . _raw_path !r} have different anchors" )
271278 while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
272279 parts0 .pop ()
273280 parts1 .pop ()
274281 for part in parts1 :
275282 if not part or part == '.' :
276283 pass
277284 elif not walk_up :
278- raise ValueError (f"{ str ( self ) !r} is not in the subpath of { str ( other ) !r} " )
285+ raise ValueError (f"{ self . _raw_path !r} is not in the subpath of { other . _raw_path !r} " )
279286 elif part == '..' :
280- raise ValueError (f"'..' segment in { str ( other ) !r} cannot be walked" )
287+ raise ValueError (f"'..' segment in { other . _raw_path !r} cannot be walked" )
281288 else :
282289 parts0 .append ('..' )
283290 return self .with_segments ('' , * reversed (parts0 ))
@@ -289,6 +296,8 @@ def is_relative_to(self, other):
289296 other = self .with_segments (other )
290297 anchor0 , parts0 = self ._stack
291298 anchor1 , parts1 = other ._stack
299+ if isinstance (anchor0 , str ) != isinstance (anchor1 , str ):
300+ raise TypeError (f"{ self ._raw_path !r} and { other ._raw_path !r} have different types" )
292301 if anchor0 != anchor1 :
293302 return False
294303 while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
@@ -336,7 +345,7 @@ def _stack(self):
336345 *parts* is a reversed list of parts following the anchor.
337346 """
338347 split = self .pathmod .split
339- path = str ( self )
348+ path = self . _raw_path
340349 parent , name = split (path )
341350 names = []
342351 while path != parent :
@@ -348,7 +357,7 @@ def _stack(self):
348357 @property
349358 def parent (self ):
350359 """The logical parent of the path."""
351- path = str ( self )
360+ path = self . _raw_path
352361 parent = self .pathmod .dirname (path )
353362 if path != parent :
354363 parent = self .with_segments (parent )
@@ -360,7 +369,7 @@ def parent(self):
360369 def parents (self ):
361370 """A sequence of this path's logical parents."""
362371 dirname = self .pathmod .dirname
363- path = str ( self )
372+ path = self . _raw_path
364373 parent = dirname (path )
365374 parents = []
366375 while path != parent :
@@ -379,7 +388,7 @@ def is_absolute(self):
379388 return True
380389 return False
381390 else :
382- return self .pathmod .isabs (str ( self ) )
391+ return self .pathmod .isabs (self . _raw_path )
383392
384393 def is_reserved (self ):
385394 """Return True if the path contains one of the special names reserved
@@ -894,7 +903,7 @@ def resolve(self, strict=False):
894903 # encountered during resolution.
895904 link_count += 1
896905 if link_count >= self ._max_symlinks :
897- raise OSError (ELOOP , "Too many symbolic links in path" , str ( self ) )
906+ raise OSError (ELOOP , "Too many symbolic links in path" , self . _raw_path )
898907 target_root , target_parts = path .readlink ()._stack
899908 # If the symlink target is absolute (like '/etc/hosts'), set the current
900909 # path to its uppermost parent (like '/').
@@ -908,7 +917,7 @@ def resolve(self, strict=False):
908917 parts .extend (target_parts )
909918 continue
910919 elif parts and not S_ISDIR (st .st_mode ):
911- raise NotADirectoryError (ENOTDIR , "Not a directory" , str ( self ) )
920+ raise NotADirectoryError (ENOTDIR , "Not a directory" , self . _raw_path )
912921 except OSError :
913922 if strict :
914923 raise
0 commit comments