Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit5dbd27d

Browse files
authored
GH-128520: pathlib ABCs: addJoinablePath.__vfspath__() (#133437)
In the abstract interface of `JoinablePath`, replace `__str__()` with`__vfspath__()`. This frees user implementations of `JoinablePath` toimplement `__str__()` however they like (or not at all.)Also add `pathlib._os.vfspath()`, which calls `__fspath__()` or`__vfspath__()`.
1 parent9f69a58 commit5dbd27d

File tree

8 files changed

+118
-81
lines changed

8 files changed

+118
-81
lines changed

‎Lib/glob.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ def concat_path(path, text):
358358
"""
359359
raiseNotImplementedError
360360

361+
@staticmethod
362+
defstringify_path(path):
363+
"""Converts the path to a string object
364+
"""
365+
raiseNotImplementedError
366+
361367
# High-level methods
362368

363369
defcompile(self,pat,altsep=None):
@@ -466,8 +472,9 @@ def recursive_selector(self, part, parts):
466472
select_next=self.selector(parts)
467473

468474
defselect_recursive(path,exists=False):
469-
match_pos=len(str(path))
470-
ifmatchisNoneormatch(str(path),match_pos):
475+
path_str=self.stringify_path(path)
476+
match_pos=len(path_str)
477+
ifmatchisNoneormatch(path_str,match_pos):
471478
yieldfromselect_next(path,exists)
472479
stack= [path]
473480
whilestack:
@@ -489,7 +496,7 @@ def select_recursive_step(stack, match_pos):
489496
pass
490497

491498
ifis_dirornotdir_only:
492-
entry_path_str=str(entry_path)
499+
entry_path_str=self.stringify_path(entry_path)
493500
ifdir_only:
494501
entry_path=self.concat_path(entry_path,self.sep)
495502
ifmatchisNoneormatch(entry_path_str,match_pos):
@@ -529,19 +536,6 @@ def scandir(path):
529536
entries=list(scandir_it)
530537
return ((entry,entry.name,entry.path)forentryinentries)
531538

532-
533-
class_PathGlobber(_GlobberBase):
534-
"""Provides shell-style pattern matching and globbing for pathlib paths.
535-
"""
536-
537539
@staticmethod
538-
deflexists(path):
539-
returnpath.info.exists(follow_symlinks=False)
540-
541-
@staticmethod
542-
defscandir(path):
543-
return ((child.info,child.name,child)forchildinpath.iterdir())
544-
545-
@staticmethod
546-
defconcat_path(path,text):
547-
returnpath.with_segments(str(path)+text)
540+
defstringify_path(path):
541+
returnpath# Already a string.

‎Lib/pathlib/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828

2929
frompathlib._osimport (
3030
PathInfo,DirEntryInfo,
31+
magic_open,vfspath,
3132
ensure_different_files,ensure_distinct_paths,
32-
copyfile2,copyfileobj,magic_open,copy_info,
33+
copyfile2,copyfileobj,copy_info,
3334
)
3435

3536

@@ -1164,12 +1165,12 @@ def _copy_from_file(self, source, preserve_metadata=False):
11641165
# os.symlink() incorrectly creates a file-symlink on Windows. Avoid
11651166
# this by passing *target_is_dir* to os.symlink() on Windows.
11661167
def_copy_from_symlink(self,source,preserve_metadata=False):
1167-
os.symlink(str(source.readlink()),self,source.info.is_dir())
1168+
os.symlink(vfspath(source.readlink()),self,source.info.is_dir())
11681169
ifpreserve_metadata:
11691170
copy_info(source.info,self,follow_symlinks=False)
11701171
else:
11711172
def_copy_from_symlink(self,source,preserve_metadata=False):
1172-
os.symlink(str(source.readlink()),self)
1173+
os.symlink(vfspath(source.readlink()),self)
11731174
ifpreserve_metadata:
11741175
copy_info(source.info,self,follow_symlinks=False)
11751176

‎Lib/pathlib/_os.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,26 @@ def magic_open(path, mode='r', buffering=-1, encoding=None, errors=None,
210210
raiseTypeError(f"{cls.__name__} can't be opened with mode{mode!r}")
211211

212212

213+
defvfspath(path):
214+
"""
215+
Return the string representation of a virtual path object.
216+
"""
217+
try:
218+
returnos.fsdecode(path)
219+
exceptTypeError:
220+
pass
221+
222+
path_type=type(path)
223+
try:
224+
returnpath_type.__vfspath__(path)
225+
exceptAttributeError:
226+
ifhasattr(path_type,'__vfspath__'):
227+
raise
228+
229+
raiseTypeError("expected str, bytes, os.PathLike or JoinablePath "
230+
"object, not "+path_type.__name__)
231+
232+
213233
defensure_distinct_paths(source,target):
214234
"""
215235
Raise OSError(EINVAL) if the other path is within this path.
@@ -225,8 +245,8 @@ def ensure_distinct_paths(source, target):
225245
err=OSError(EINVAL,"Source path is a parent of target path")
226246
else:
227247
return
228-
err.filename=str(source)
229-
err.filename2=str(target)
248+
err.filename=vfspath(source)
249+
err.filename2=vfspath(target)
230250
raiseerr
231251

232252

@@ -247,8 +267,8 @@ def ensure_different_files(source, target):
247267
except (OSError,ValueError):
248268
return
249269
err=OSError(EINVAL,"Source and target are the same file")
250-
err.filename=str(source)
251-
err.filename2=str(target)
270+
err.filename=vfspath(source)
271+
err.filename2=vfspath(target)
252272
raiseerr
253273

254274

‎Lib/pathlib/types.py

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@
1111

1212

1313
fromabcimportABC,abstractmethod
14-
fromglobimport_PathGlobber
14+
fromglobimport_GlobberBase
1515
fromioimporttext_encoding
16-
frompathlib._osimportmagic_open,ensure_distinct_paths,ensure_different_files,copyfileobj
16+
frompathlib._osimport (magic_open,vfspath,ensure_distinct_paths,
17+
ensure_different_files,copyfileobj)
1718
frompathlibimportPurePath,Path
1819
fromtypingimportOptional,Protocol,runtime_checkable
1920

@@ -60,6 +61,25 @@ def is_file(self, *, follow_symlinks: bool = True) -> bool: ...
6061
defis_symlink(self)->bool: ...
6162

6263

64+
class_PathGlobber(_GlobberBase):
65+
"""Provides shell-style pattern matching and globbing for ReadablePath.
66+
"""
67+
68+
@staticmethod
69+
deflexists(path):
70+
returnpath.info.exists(follow_symlinks=False)
71+
72+
@staticmethod
73+
defscandir(path):
74+
return ((child.info,child.name,child)forchildinpath.iterdir())
75+
76+
@staticmethod
77+
defconcat_path(path,text):
78+
returnpath.with_segments(vfspath(path)+text)
79+
80+
stringify_path=staticmethod(vfspath)
81+
82+
6383
class_JoinablePath(ABC):
6484
"""Abstract base class for pure path objects.
6585
@@ -86,20 +106,19 @@ def with_segments(self, *pathsegments):
86106
raiseNotImplementedError
87107

88108
@abstractmethod
89-
def__str__(self):
90-
"""Return the string representation of the path, suitable for
91-
passing to system calls."""
109+
def__vfspath__(self):
110+
"""Return the string representation of the path."""
92111
raiseNotImplementedError
93112

94113
@property
95114
defanchor(self):
96115
"""The concatenation of the drive and root, or ''."""
97-
return_explode_path(str(self),self.parser.split)[0]
116+
return_explode_path(vfspath(self),self.parser.split)[0]
98117

99118
@property
100119
defname(self):
101120
"""The final path component, if any."""
102-
returnself.parser.split(str(self))[1]
121+
returnself.parser.split(vfspath(self))[1]
103122

104123
@property
105124
defsuffix(self):
@@ -135,7 +154,7 @@ def with_name(self, name):
135154
split=self.parser.split
136155
ifsplit(name)[0]:
137156
raiseValueError(f"Invalid name{name!r}")
138-
path=str(self)
157+
path=vfspath(self)
139158
path=path.removesuffix(split(path)[1])+name
140159
returnself.with_segments(path)
141160

@@ -168,7 +187,7 @@ def with_suffix(self, suffix):
168187
defparts(self):
169188
"""An object providing sequence-like access to the
170189
components in the filesystem path."""
171-
anchor,parts=_explode_path(str(self),self.parser.split)
190+
anchor,parts=_explode_path(vfspath(self),self.parser.split)
172191
ifanchor:
173192
parts.append(anchor)
174193
returntuple(reversed(parts))
@@ -179,24 +198,24 @@ def joinpath(self, *pathsegments):
179198
paths) or a totally different path (if one of the arguments is
180199
anchored).
181200
"""
182-
returnself.with_segments(str(self),*pathsegments)
201+
returnself.with_segments(vfspath(self),*pathsegments)
183202

184203
def__truediv__(self,key):
185204
try:
186-
returnself.with_segments(str(self),key)
205+
returnself.with_segments(vfspath(self),key)
187206
exceptTypeError:
188207
returnNotImplemented
189208

190209
def__rtruediv__(self,key):
191210
try:
192-
returnself.with_segments(key,str(self))
211+
returnself.with_segments(key,vfspath(self))
193212
exceptTypeError:
194213
returnNotImplemented
195214

196215
@property
197216
defparent(self):
198217
"""The logical parent of the path."""
199-
path=str(self)
218+
path=vfspath(self)
200219
parent=self.parser.split(path)[0]
201220
ifpath!=parent:
202221
returnself.with_segments(parent)
@@ -206,7 +225,7 @@ def parent(self):
206225
defparents(self):
207226
"""A sequence of this path's logical parents."""
208227
split=self.parser.split
209-
path=str(self)
228+
path=vfspath(self)
210229
parent=split(path)[0]
211230
parents= []
212231
whilepath!=parent:
@@ -223,7 +242,7 @@ def full_match(self, pattern):
223242
case_sensitive=self.parser.normcase('Aa')=='Aa'
224243
globber=_PathGlobber(self.parser.sep,case_sensitive,recursive=True)
225244
match=globber.compile(pattern,altsep=self.parser.altsep)
226-
returnmatch(str(self))isnotNone
245+
returnmatch(vfspath(self))isnotNone
227246

228247

229248
class_ReadablePath(_JoinablePath):
@@ -412,7 +431,7 @@ def _copy_from(self, source, follow_symlinks=True):
412431
whilestack:
413432
src,dst=stack.pop()
414433
ifnotfollow_symlinksandsrc.info.is_symlink():
415-
dst.symlink_to(str(src.readlink()),src.info.is_dir())
434+
dst.symlink_to(vfspath(src.readlink()),src.info.is_dir())
416435
elifsrc.info.is_dir():
417436
children=src.iterdir()
418437
dst.mkdir()

‎Lib/test/test_pathlib/support/lexical_path.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@
99
from .importis_pypi
1010

1111
ifis_pypi:
12-
frompathlib_abcimport_JoinablePath
12+
frompathlib_abcimportvfspath,_JoinablePath
1313
else:
1414
frompathlib.typesimport_JoinablePath
15+
frompathlib._osimportvfspath
1516

1617

1718
classLexicalPath(_JoinablePath):
@@ -22,20 +23,20 @@ def __init__(self, *pathsegments):
2223
self._segments=pathsegments
2324

2425
def__hash__(self):
25-
returnhash(str(self))
26+
returnhash(vfspath(self))
2627

2728
def__eq__(self,other):
2829
ifnotisinstance(other,LexicalPath):
2930
returnNotImplemented
30-
returnstr(self)==str(other)
31+
returnvfspath(self)==vfspath(other)
3132

32-
def__str__(self):
33+
def__vfspath__(self):
3334
ifnotself._segments:
3435
return''
3536
returnself.parser.join(*self._segments)
3637

3738
def__repr__(self):
38-
returnf'{type(self).__name__}({str(self)!r})'
39+
returnf'{type(self).__name__}({vfspath(self)!r})'
3940

4041
defwith_segments(self,*pathsegments):
4142
returntype(self)(*pathsegments)

‎Lib/test/test_pathlib/support/local_path.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ class LocalPathInfo(PathInfo):
9797
__slots__= ('_path','_exists','_is_dir','_is_file','_is_symlink')
9898

9999
def__init__(self,path):
100-
self._path=str(path)
100+
self._path=os.fspath(path)
101101
self._exists=None
102102
self._is_dir=None
103103
self._is_file=None
@@ -139,14 +139,12 @@ class ReadableLocalPath(_ReadablePath, LexicalPath):
139139
Simple implementation of a ReadablePath class for local filesystem paths.
140140
"""
141141
__slots__= ('info',)
142+
__fspath__=LexicalPath.__vfspath__
142143

143144
def__init__(self,*pathsegments):
144145
super().__init__(*pathsegments)
145146
self.info=LocalPathInfo(self)
146147

147-
def__fspath__(self):
148-
returnstr(self)
149-
150148
def__open_rb__(self,buffering=-1):
151149
returnopen(self,'rb')
152150

@@ -163,9 +161,7 @@ class WritableLocalPath(_WritablePath, LexicalPath):
163161
"""
164162

165163
__slots__= ()
166-
167-
def__fspath__(self):
168-
returnstr(self)
164+
__fspath__=LexicalPath.__vfspath__
169165

170166
def__open_wb__(self,buffering=-1):
171167
returnopen(self,'wb')

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp