@@ -163,10 +163,15 @@ def with_segments(self, *pathsegments):
163
163
"""
164
164
return type (self )(* pathsegments )
165
165
166
+ @property
167
+ def _raw_path (self ):
168
+ """The joined but unnormalized path."""
169
+ return self .pathmod .join (* self ._raw_paths )
170
+
166
171
def __str__ (self ):
167
172
"""Return the string representation of the path, suitable for
168
173
passing to system calls."""
169
- return self .pathmod . join ( * self . _raw_paths )
174
+ return self ._raw_path
170
175
171
176
def as_posix (self ):
172
177
"""Return the string representation of the path with forward (/)
@@ -176,23 +181,23 @@ def as_posix(self):
176
181
@property
177
182
def drive (self ):
178
183
"""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 ]
180
185
181
186
@property
182
187
def root (self ):
183
188
"""The root of the path, if any."""
184
- return self .pathmod .splitroot (str ( self ) )[1 ]
189
+ return self .pathmod .splitroot (self . _raw_path )[1 ]
185
190
186
191
@property
187
192
def anchor (self ):
188
193
"""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 )
190
195
return drive + root
191
196
192
197
@property
193
198
def name (self ):
194
199
"""The final path component, if any."""
195
- return self .pathmod .basename (str ( self ) )
200
+ return self .pathmod .basename (self . _raw_path )
196
201
197
202
@property
198
203
def suffix (self ):
@@ -236,7 +241,7 @@ def with_name(self, name):
236
241
dirname = self .pathmod .dirname
237
242
if dirname (name ):
238
243
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 )
240
245
241
246
def with_stem (self , stem ):
242
247
"""Return a new path with the stem changed."""
@@ -266,18 +271,20 @@ def relative_to(self, other, *, walk_up=False):
266
271
other = self .with_segments (other )
267
272
anchor0 , parts0 = self ._stack
268
273
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" )
269
276
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" )
271
278
while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
272
279
parts0 .pop ()
273
280
parts1 .pop ()
274
281
for part in parts1 :
275
282
if not part or part == '.' :
276
283
pass
277
284
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} " )
279
286
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" )
281
288
else :
282
289
parts0 .append ('..' )
283
290
return self .with_segments ('' , * reversed (parts0 ))
@@ -289,6 +296,8 @@ def is_relative_to(self, other):
289
296
other = self .with_segments (other )
290
297
anchor0 , parts0 = self ._stack
291
298
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" )
292
301
if anchor0 != anchor1 :
293
302
return False
294
303
while parts0 and parts1 and parts0 [- 1 ] == parts1 [- 1 ]:
@@ -336,7 +345,7 @@ def _stack(self):
336
345
*parts* is a reversed list of parts following the anchor.
337
346
"""
338
347
split = self .pathmod .split
339
- path = str ( self )
348
+ path = self . _raw_path
340
349
parent , name = split (path )
341
350
names = []
342
351
while path != parent :
@@ -348,7 +357,7 @@ def _stack(self):
348
357
@property
349
358
def parent (self ):
350
359
"""The logical parent of the path."""
351
- path = str ( self )
360
+ path = self . _raw_path
352
361
parent = self .pathmod .dirname (path )
353
362
if path != parent :
354
363
parent = self .with_segments (parent )
@@ -360,7 +369,7 @@ def parent(self):
360
369
def parents (self ):
361
370
"""A sequence of this path's logical parents."""
362
371
dirname = self .pathmod .dirname
363
- path = str ( self )
372
+ path = self . _raw_path
364
373
parent = dirname (path )
365
374
parents = []
366
375
while path != parent :
@@ -379,7 +388,7 @@ def is_absolute(self):
379
388
return True
380
389
return False
381
390
else :
382
- return self .pathmod .isabs (str ( self ) )
391
+ return self .pathmod .isabs (self . _raw_path )
383
392
384
393
def is_reserved (self ):
385
394
"""Return True if the path contains one of the special names reserved
@@ -894,7 +903,7 @@ def resolve(self, strict=False):
894
903
# encountered during resolution.
895
904
link_count += 1
896
905
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 )
898
907
target_root , target_parts = path .readlink ()._stack
899
908
# If the symlink target is absolute (like '/etc/hosts'), set the current
900
909
# path to its uppermost parent (like '/').
@@ -908,7 +917,7 @@ def resolve(self, strict=False):
908
917
parts .extend (target_parts )
909
918
continue
910
919
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 )
912
921
except OSError :
913
922
if strict :
914
923
raise
0 commit comments