@@ -50,103 +50,15 @@ class _Flavour(object):
5050 """A flavour implements a particular (platform-specific) set of path
5151 semantics."""
5252
53- def parse_parts (self , parts ):
54- parsed = []
55- sep = self .pathmod .sep
56- altsep = self .pathmod .altsep
57- drv = root = ''
58- it = reversed (parts )
59- for part in it :
60- if not part :
61- continue
62- if altsep :
63- part = part .replace (altsep , sep )
64- drv , root , rel = self .splitroot (part )
65- if sep in rel :
66- for x in reversed (rel .split (sep )):
67- if x and x != '.' :
68- parsed .append (sys .intern (x ))
69- else :
70- if rel and rel != '.' :
71- parsed .append (sys .intern (rel ))
72- if drv or root :
73- if not drv :
74- # If no drive is present, try to find one in the previous
75- # parts. This makes the result of parsing e.g.
76- # ("C:", "/", "a") reasonably intuitive.
77- for part in it :
78- if not part :
79- continue
80- if altsep :
81- part = part .replace (altsep , sep )
82- drv = self .splitroot (part )[0 ]
83- if drv :
84- break
85- break
86- if drv or root :
87- parsed .append (drv + root )
88- parsed .reverse ()
89- return drv , root , parsed
90-
9153
9254class _WindowsFlavour (_Flavour ):
9355 # Reference for Windows paths can be found at
9456 # http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx
9557
9658 has_drv = True
97- pathmod = ntpath
9859
9960 is_supported = (os .name == 'nt' )
10061
101- drive_letters = set ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' )
102- ext_namespace_prefix = '\\ \\ ?\\ '
103-
104- # Interesting findings about extended paths:
105- # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
106- # but '\\?\c:/a' is not
107- # - extended paths are always absolute; "relative" extended paths will
108- # fail.
109-
110- def splitroot (self , part ):
111- sep = self .pathmod .sep
112- first = part [0 :1 ]
113- second = part [1 :2 ]
114- if (second == sep and first == sep ):
115- # XXX extended paths should also disable the collapsing of "."
116- # components (according to MSDN docs).
117- prefix , part = self ._split_extended_path (part )
118- first = part [0 :1 ]
119- second = part [1 :2 ]
120- else :
121- prefix = ''
122- third = part [2 :3 ]
123- if (second == sep and first == sep and third != sep ):
124- # is a UNC path:
125- # vvvvvvvvvvvvvvvvvvvvv root
126- # \\machine\mountpoint\directory\etc\...
127- # directory ^^^^^^^^^^^^^^
128- index = part .find (sep , 2 )
129- if index != - 1 :
130- index2 = part .find (sep , index + 1 )
131- # a UNC path can't have two slashes in a row
132- # (after the initial two)
133- if index2 != index + 1 :
134- if index2 == - 1 :
135- index2 = len (part )
136- if prefix :
137- return prefix + part [1 :index2 ], sep , part [index2 + 1 :]
138- else :
139- return part [:index2 ], sep , part [index2 + 1 :]
140- drv = root = ''
141- if second == ':' and first in self .drive_letters :
142- drv = part [:2 ]
143- part = part [2 :]
144- first = third
145- if first == sep :
146- root = first
147- part = part .lstrip (sep )
148- return prefix + drv , root , part
149-
15062 def casefold (self , s ):
15163 return s .lower ()
15264
@@ -156,39 +68,12 @@ def casefold_parts(self, parts):
15668 def compile_pattern (self , pattern ):
15769 return re .compile (fnmatch .translate (pattern ), re .IGNORECASE ).fullmatch
15870
159- def _split_extended_path (self , s , ext_prefix = ext_namespace_prefix ):
160- prefix = ''
161- if s .startswith (ext_prefix ):
162- prefix = s [:4 ]
163- s = s [4 :]
164- if s .startswith ('UNC\\ ' ):
165- prefix += s [:3 ]
166- s = '\\ ' + s [3 :]
167- return prefix , s
168-
16971
17072class _PosixFlavour (_Flavour ):
17173 has_drv = False
172- pathmod = posixpath
17374
17475 is_supported = (os .name != 'nt' )
17576
176- def splitroot (self , part ):
177- sep = self .pathmod .sep
178- if part and part [0 ] == sep :
179- stripped_part = part .lstrip (sep )
180- # According to POSIX path resolution:
181- # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
182- # "A pathname that begins with two successive slashes may be
183- # interpreted in an implementation-defined manner, although more
184- # than two leading slashes shall be treated as a single slash".
185- if len (part ) - len (stripped_part ) == 2 :
186- return '' , sep * 2 , stripped_part
187- else :
188- return '' , sep , stripped_part
189- else :
190- return '' , '' , part
191-
19277 def casefold (self , s ):
19378 return s
19479
@@ -511,7 +396,46 @@ def _parse_args(cls, args):
511396 "argument should be a str object or an os.PathLike "
512397 "object returning str, not %r"
513398 % type (a ))
514- return cls ._flavour .parse_parts (parts )
399+ return cls ._parse_parts (parts )
400+
401+ @classmethod
402+ def _parse_parts (cls , parts ):
403+ parsed = []
404+ sep = cls ._pathmod .sep
405+ altsep = cls ._pathmod .altsep
406+ drv = root = ''
407+ it = reversed (parts )
408+ for part in it :
409+ if not part :
410+ continue
411+ if altsep :
412+ part = part .replace (altsep , sep )
413+ drv , root , rel = cls ._splitroot (part )
414+ if sep in rel :
415+ for x in reversed (rel .split (sep )):
416+ if x and x != '.' :
417+ parsed .append (sys .intern (x ))
418+ else :
419+ if rel and rel != '.' :
420+ parsed .append (sys .intern (rel ))
421+ if drv or root :
422+ if not drv :
423+ # If no drive is present, try to find one in the previous
424+ # parts. This makes the result of parsing e.g.
425+ # ("C:", "/", "a") reasonably intuitive.
426+ for part in it :
427+ if not part :
428+ continue
429+ if altsep :
430+ part = part .replace (altsep , sep )
431+ drv = cls ._splitroot (part )[0 ]
432+ if drv :
433+ break
434+ break
435+ if drv or root :
436+ parsed .append (drv + root )
437+ parsed .reverse ()
438+ return drv , root , parsed
515439
516440 @classmethod
517441 def _from_parts (cls , args ):
@@ -535,9 +459,9 @@ def _from_parsed_parts(cls, drv, root, parts):
535459 @classmethod
536460 def _format_parsed_parts (cls , drv , root , parts ):
537461 if drv or root :
538- return drv + root + cls ._flavour . pathmod .sep .join (parts [1 :])
462+ return drv + root + cls ._pathmod .sep .join (parts [1 :])
539463 else :
540- return cls ._flavour . pathmod .sep .join (parts )
464+ return cls ._pathmod .sep .join (parts )
541465
542466 def _make_child (self , args ):
543467 drv , root , parts = self ._parse_args (args )
@@ -578,8 +502,7 @@ def __fspath__(self):
578502 def as_posix (self ):
579503 """Return the string representation of the path with forward (/)
580504 slashes."""
581- f = self ._flavour
582- return str (self ).replace (f .pathmod .sep , '/' )
505+ return str (self ).replace (self ._pathmod .sep , '/' )
583506
584507 def __bytes__ (self ):
585508 """Return the bytes representation of the path. This is only
@@ -698,8 +621,8 @@ def with_name(self, name):
698621 """Return a new path with the file name changed."""
699622 if not self .name :
700623 raise ValueError ("%r has an empty name" % (self ,))
701- drv , root , parts = self ._flavour . parse_parts ((name ,))
702- m = self ._flavour . pathmod
624+ drv , root , parts = self ._parse_parts ((name ,))
625+ m = self ._pathmod
703626 if (not name or name [- 1 ] in [m .sep , m .altsep ]
704627 or drv or root or len (parts ) != 1 ):
705628 raise ValueError ("Invalid name %r" % (name ))
@@ -715,7 +638,7 @@ def with_suffix(self, suffix):
715638 has no suffix, add given suffix. If the given suffix is an empty
716639 string, remove the suffix from the path.
717640 """
718- m = self ._flavour . pathmod
641+ m = self ._pathmod
719642 if m .sep in suffix or m .altsep and m .altsep in suffix :
720643 raise ValueError ("Invalid suffix %r" % (suffix ,))
721644 if suffix and not suffix .startswith ('.' ) or suffix == '.' :
@@ -838,7 +761,7 @@ def match(self, path_pattern):
838761 """
839762 cf = self ._flavour .casefold
840763 path_pattern = cf (path_pattern )
841- drv , root , pat_parts = self ._flavour . parse_parts ((path_pattern ,))
764+ drv , root , pat_parts = self ._parse_parts ((path_pattern ,))
842765 if not pat_parts :
843766 raise ValueError ("empty pattern" )
844767 if drv and drv != cf (self ._drv ):
@@ -869,8 +792,26 @@ class PurePosixPath(PurePath):
869792 However, you can also instantiate it directly on any system.
870793 """
871794 _flavour = _posix_flavour
795+ _pathmod = posixpath
872796 __slots__ = ()
873797
798+ @classmethod
799+ def _splitroot (cls , part ):
800+ sep = cls ._pathmod .sep
801+ if part and part [0 ] == sep :
802+ stripped_part = part .lstrip (sep )
803+ # According to POSIX path resolution:
804+ # http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap04.html#tag_04_11
805+ # "A pathname that begins with two successive slashes may be
806+ # interpreted in an implementation-defined manner, although more
807+ # than two leading slashes shall be treated as a single slash".
808+ if len (part ) - len (stripped_part ) == 2 :
809+ return '' , sep * 2 , stripped_part
810+ else :
811+ return '' , sep , stripped_part
812+ else :
813+ return '' , '' , part
814+
874815 def is_reserved (self ):
875816 return False
876817
@@ -890,13 +831,74 @@ class PureWindowsPath(PurePath):
890831 However, you can also instantiate it directly on any system.
891832 """
892833 _flavour = _windows_flavour
834+ _pathmod = ntpath
835+ _drive_letters = set ('abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' )
836+ _ext_namespace_prefix = '\\ \\ ?\\ '
893837 _reserved_names = (
894838 {'CON' , 'PRN' , 'AUX' , 'NUL' } |
895839 {'COM%d' % i for i in range (1 , 10 )} |
896840 {'LPT%d' % i for i in range (1 , 10 )}
897841 )
898842 __slots__ = ()
899843
844+ # Interesting findings about extended paths:
845+ # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
846+ # but '\\?\c:/a' is not
847+ # - extended paths are always absolute; "relative" extended paths will
848+ # fail.
849+
850+ @classmethod
851+ def _splitroot (cls , part ):
852+ sep = cls ._pathmod .sep
853+ first = part [0 :1 ]
854+ second = part [1 :2 ]
855+ if (second == sep and first == sep ):
856+ # XXX extended paths should also disable the collapsing of "."
857+ # components (according to MSDN docs).
858+ prefix , part = cls ._split_extended_path (part )
859+ first = part [0 :1 ]
860+ second = part [1 :2 ]
861+ else :
862+ prefix = ''
863+ third = part [2 :3 ]
864+ if (second == sep and first == sep and third != sep ):
865+ # is a UNC path:
866+ # vvvvvvvvvvvvvvvvvvvvv root
867+ # \\machine\mountpoint\directory\etc\...
868+ # directory ^^^^^^^^^^^^^^
869+ index = part .find (sep , 2 )
870+ if index != - 1 :
871+ index2 = part .find (sep , index + 1 )
872+ # a UNC path can't have two slashes in a row
873+ # (after the initial two)
874+ if index2 != index + 1 :
875+ if index2 == - 1 :
876+ index2 = len (part )
877+ if prefix :
878+ return prefix + part [1 :index2 ], sep , part [index2 + 1 :]
879+ else :
880+ return part [:index2 ], sep , part [index2 + 1 :]
881+ drv = root = ''
882+ if second == ':' and first in cls ._drive_letters :
883+ drv = part [:2 ]
884+ part = part [2 :]
885+ first = third
886+ if first == sep :
887+ root = first
888+ part = part .lstrip (sep )
889+ return prefix + drv , root , part
890+
891+ @classmethod
892+ def _split_extended_path (cls , s , ext_prefix = _ext_namespace_prefix ):
893+ prefix = ''
894+ if s .startswith (ext_prefix ):
895+ prefix = s [:4 ]
896+ s = s [4 :]
897+ if s .startswith ('UNC\\ ' ):
898+ prefix += s [:3 ]
899+ s = '\\ ' + s [3 :]
900+ return prefix , s
901+
900902 def is_reserved (self ):
901903 # NOTE: the rules for reserved names seem somewhat complicated
902904 # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
@@ -1011,7 +1013,7 @@ def glob(self, pattern):
10111013 sys .audit ("pathlib.Path.glob" , self , pattern )
10121014 if not pattern :
10131015 raise ValueError ("Unacceptable pattern: {!r}" .format (pattern ))
1014- drv , root , pattern_parts = self ._flavour . parse_parts ((pattern ,))
1016+ drv , root , pattern_parts = self ._parse_parts ((pattern ,))
10151017 if drv or root :
10161018 raise NotImplementedError ("Non-relative patterns are unsupported" )
10171019 selector = _make_selector (tuple (pattern_parts ), self ._flavour )
@@ -1024,7 +1026,7 @@ def rglob(self, pattern):
10241026 this subtree.
10251027 """
10261028 sys .audit ("pathlib.Path.rglob" , self , pattern )
1027- drv , root , pattern_parts = self ._flavour . parse_parts ((pattern ,))
1029+ drv , root , pattern_parts = self ._parse_parts ((pattern ,))
10281030 if drv or root :
10291031 raise NotImplementedError ("Non-relative patterns are unsupported" )
10301032 selector = _make_selector (("**" ,) + tuple (pattern_parts ), self ._flavour )
0 commit comments