• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import collections
2import io
3import os
4import errno
5import stat
6import unittest
7
8from pathlib._abc import UnsupportedOperation, ParserBase, PurePathBase, PathBase
9import posixpath
10
11from test.support import is_wasi
12from test.support.os_helper import TESTFN
13
14
15_tests_needing_posix = set()
16_tests_needing_windows = set()
17_tests_needing_symlinks = set()
18
19
20def needs_posix(fn):
21    """Decorator that marks a test as requiring a POSIX-flavoured path class."""
22    _tests_needing_posix.add(fn.__name__)
23    return fn
24
25def needs_windows(fn):
26    """Decorator that marks a test as requiring a Windows-flavoured path class."""
27    _tests_needing_windows.add(fn.__name__)
28    return fn
29
30def needs_symlinks(fn):
31    """Decorator that marks a test as requiring a path class that supports symlinks."""
32    _tests_needing_symlinks.add(fn.__name__)
33    return fn
34
35
36class UnsupportedOperationTest(unittest.TestCase):
37    def test_is_notimplemented(self):
38        self.assertTrue(issubclass(UnsupportedOperation, NotImplementedError))
39        self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError))
40
41
42class ParserBaseTest(unittest.TestCase):
43    cls = ParserBase
44
45    def test_unsupported_operation(self):
46        m = self.cls()
47        e = UnsupportedOperation
48        with self.assertRaises(e):
49            m.sep
50        self.assertRaises(e, m.join, 'foo')
51        self.assertRaises(e, m.split, 'foo')
52        self.assertRaises(e, m.splitdrive, 'foo')
53        self.assertRaises(e, m.normcase, 'foo')
54        self.assertRaises(e, m.isabs, 'foo')
55
56#
57# Tests for the pure classes.
58#
59
60
61class PurePathBaseTest(unittest.TestCase):
62    cls = PurePathBase
63
64    def test_unsupported_operation_pure(self):
65        p = self.cls('foo')
66        e = UnsupportedOperation
67        with self.assertRaises(e):
68            p.drive
69        with self.assertRaises(e):
70            p.root
71        with self.assertRaises(e):
72            p.anchor
73        with self.assertRaises(e):
74            p.parts
75        with self.assertRaises(e):
76            p.parent
77        with self.assertRaises(e):
78            p.parents
79        with self.assertRaises(e):
80            p.name
81        with self.assertRaises(e):
82            p.stem
83        with self.assertRaises(e):
84            p.suffix
85        with self.assertRaises(e):
86            p.suffixes
87        with self.assertRaises(e):
88            p / 'bar'
89        with self.assertRaises(e):
90            'bar' / p
91        self.assertRaises(e, p.joinpath, 'bar')
92        self.assertRaises(e, p.with_name, 'bar')
93        self.assertRaises(e, p.with_stem, 'bar')
94        self.assertRaises(e, p.with_suffix, '.txt')
95        self.assertRaises(e, p.relative_to, '')
96        self.assertRaises(e, p.is_relative_to, '')
97        self.assertRaises(e, p.is_absolute)
98        self.assertRaises(e, p.match, '*')
99
100    def test_magic_methods(self):
101        P = self.cls
102        self.assertFalse(hasattr(P, '__fspath__'))
103        self.assertFalse(hasattr(P, '__bytes__'))
104        self.assertIs(P.__reduce__, object.__reduce__)
105        self.assertIs(P.__repr__, object.__repr__)
106        self.assertIs(P.__hash__, object.__hash__)
107        self.assertIs(P.__eq__, object.__eq__)
108        self.assertIs(P.__lt__, object.__lt__)
109        self.assertIs(P.__le__, object.__le__)
110        self.assertIs(P.__gt__, object.__gt__)
111        self.assertIs(P.__ge__, object.__ge__)
112
113    def test_parser(self):
114        self.assertIsInstance(self.cls.parser, ParserBase)
115
116
117class DummyPurePath(PurePathBase):
118    __slots__ = ()
119    parser = posixpath
120
121    def __eq__(self, other):
122        if not isinstance(other, DummyPurePath):
123            return NotImplemented
124        return str(self) == str(other)
125
126    def __hash__(self):
127        return hash(str(self))
128
129    def __repr__(self):
130        return "{}({!r})".format(self.__class__.__name__, self.as_posix())
131
132
133class DummyPurePathTest(unittest.TestCase):
134    cls = DummyPurePath
135
136    # Use a base path that's unrelated to any real filesystem path.
137    base = f'/this/path/kills/fascists/{TESTFN}'
138
139    def setUp(self):
140        name = self.id().split('.')[-1]
141        if name in _tests_needing_posix and self.cls.parser is not posixpath:
142            self.skipTest('requires POSIX-flavoured path class')
143        if name in _tests_needing_windows and self.cls.parser is posixpath:
144            self.skipTest('requires Windows-flavoured path class')
145        p = self.cls('a')
146        self.parser = p.parser
147        self.sep = self.parser.sep
148        self.altsep = self.parser.altsep
149
150    def test_constructor_common(self):
151        P = self.cls
152        p = P('a')
153        self.assertIsInstance(p, P)
154        P('a', 'b', 'c')
155        P('/a', 'b', 'c')
156        P('a/b/c')
157        P('/a/b/c')
158
159    def test_bytes(self):
160        P = self.cls
161        with self.assertRaises(TypeError):
162            P(b'a')
163        with self.assertRaises(TypeError):
164            P(b'a', 'b')
165        with self.assertRaises(TypeError):
166            P('a', b'b')
167        with self.assertRaises(TypeError):
168            P('a').joinpath(b'b')
169        with self.assertRaises(TypeError):
170            P('a') / b'b'
171        with self.assertRaises(TypeError):
172            b'a' / P('b')
173        with self.assertRaises(TypeError):
174            P('a').match(b'b')
175        with self.assertRaises(TypeError):
176            P('a').relative_to(b'b')
177        with self.assertRaises(TypeError):
178            P('a').with_name(b'b')
179        with self.assertRaises(TypeError):
180            P('a').with_stem(b'b')
181        with self.assertRaises(TypeError):
182            P('a').with_suffix(b'b')
183
184    def _check_str_subclass(self, *args):
185        # Issue #21127: it should be possible to construct a PurePath object
186        # from a str subclass instance, and it then gets converted to
187        # a pure str object.
188        class StrSubclass(str):
189            pass
190        P = self.cls
191        p = P(*(StrSubclass(x) for x in args))
192        self.assertEqual(p, P(*args))
193        for part in p.parts:
194            self.assertIs(type(part), str)
195
196    def test_str_subclass_common(self):
197        self._check_str_subclass('')
198        self._check_str_subclass('.')
199        self._check_str_subclass('a')
200        self._check_str_subclass('a/b.txt')
201        self._check_str_subclass('/a/b.txt')
202
203    @needs_windows
204    def test_str_subclass_windows(self):
205        self._check_str_subclass('.\\a:b')
206        self._check_str_subclass('c:')
207        self._check_str_subclass('c:a')
208        self._check_str_subclass('c:a\\b.txt')
209        self._check_str_subclass('c:\\')
210        self._check_str_subclass('c:\\a')
211        self._check_str_subclass('c:\\a\\b.txt')
212        self._check_str_subclass('\\\\some\\share')
213        self._check_str_subclass('\\\\some\\share\\a')
214        self._check_str_subclass('\\\\some\\share\\a\\b.txt')
215
216    def test_with_segments_common(self):
217        class P(self.cls):
218            def __init__(self, *pathsegments, session_id):
219                super().__init__(*pathsegments)
220                self.session_id = session_id
221
222            def with_segments(self, *pathsegments):
223                return type(self)(*pathsegments, session_id=self.session_id)
224        p = P('foo', 'bar', session_id=42)
225        self.assertEqual(42, (p / 'foo').session_id)
226        self.assertEqual(42, ('foo' / p).session_id)
227        self.assertEqual(42, p.joinpath('foo').session_id)
228        self.assertEqual(42, p.with_name('foo').session_id)
229        self.assertEqual(42, p.with_stem('foo').session_id)
230        self.assertEqual(42, p.with_suffix('.foo').session_id)
231        self.assertEqual(42, p.with_segments('foo').session_id)
232        self.assertEqual(42, p.relative_to('foo').session_id)
233        self.assertEqual(42, p.parent.session_id)
234        for parent in p.parents:
235            self.assertEqual(42, parent.session_id)
236
237    def test_join_common(self):
238        P = self.cls
239        p = P('a/b')
240        pp = p.joinpath('c')
241        self.assertEqual(pp, P('a/b/c'))
242        self.assertIs(type(pp), type(p))
243        pp = p.joinpath('c', 'd')
244        self.assertEqual(pp, P('a/b/c/d'))
245        pp = p.joinpath('/c')
246        self.assertEqual(pp, P('/c'))
247
248    @needs_posix
249    def test_join_posix(self):
250        P = self.cls
251        p = P('//a')
252        pp = p.joinpath('b')
253        self.assertEqual(pp, P('//a/b'))
254        pp = P('/a').joinpath('//c')
255        self.assertEqual(pp, P('//c'))
256        pp = P('//a').joinpath('/c')
257        self.assertEqual(pp, P('/c'))
258
259    @needs_windows
260    def test_join_windows(self):
261        P = self.cls
262        p = P('C:/a/b')
263        pp = p.joinpath('x/y')
264        self.assertEqual(pp, P('C:/a/b/x/y'))
265        pp = p.joinpath('/x/y')
266        self.assertEqual(pp, P('C:/x/y'))
267        # Joining with a different drive => the first path is ignored, even
268        # if the second path is relative.
269        pp = p.joinpath('D:x/y')
270        self.assertEqual(pp, P('D:x/y'))
271        pp = p.joinpath('D:/x/y')
272        self.assertEqual(pp, P('D:/x/y'))
273        pp = p.joinpath('//host/share/x/y')
274        self.assertEqual(pp, P('//host/share/x/y'))
275        # Joining with the same drive => the first path is appended to if
276        # the second path is relative.
277        pp = p.joinpath('c:x/y')
278        self.assertEqual(pp, P('C:/a/b/x/y'))
279        pp = p.joinpath('c:/x/y')
280        self.assertEqual(pp, P('C:/x/y'))
281        # Joining with files with NTFS data streams => the filename should
282        # not be parsed as a drive letter
283        pp = p.joinpath(P('./d:s'))
284        self.assertEqual(pp, P('C:/a/b/d:s'))
285        pp = p.joinpath(P('./dd:s'))
286        self.assertEqual(pp, P('C:/a/b/dd:s'))
287        pp = p.joinpath(P('E:d:s'))
288        self.assertEqual(pp, P('E:d:s'))
289        # Joining onto a UNC path with no root
290        pp = P('//').joinpath('server')
291        self.assertEqual(pp, P('//server'))
292        pp = P('//server').joinpath('share')
293        self.assertEqual(pp, P('//server/share'))
294        pp = P('//./BootPartition').joinpath('Windows')
295        self.assertEqual(pp, P('//./BootPartition/Windows'))
296
297    def test_div_common(self):
298        # Basically the same as joinpath().
299        P = self.cls
300        p = P('a/b')
301        pp = p / 'c'
302        self.assertEqual(pp, P('a/b/c'))
303        self.assertIs(type(pp), type(p))
304        pp = p / 'c/d'
305        self.assertEqual(pp, P('a/b/c/d'))
306        pp = p / 'c' / 'd'
307        self.assertEqual(pp, P('a/b/c/d'))
308        pp = 'c' / p / 'd'
309        self.assertEqual(pp, P('c/a/b/d'))
310        pp = p/ '/c'
311        self.assertEqual(pp, P('/c'))
312
313    @needs_posix
314    def test_div_posix(self):
315        # Basically the same as joinpath().
316        P = self.cls
317        p = P('//a')
318        pp = p / 'b'
319        self.assertEqual(pp, P('//a/b'))
320        pp = P('/a') / '//c'
321        self.assertEqual(pp, P('//c'))
322        pp = P('//a') / '/c'
323        self.assertEqual(pp, P('/c'))
324
325    @needs_windows
326    def test_div_windows(self):
327        # Basically the same as joinpath().
328        P = self.cls
329        p = P('C:/a/b')
330        self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
331        self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
332        self.assertEqual(p / '/x/y', P('C:/x/y'))
333        self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
334        # Joining with a different drive => the first path is ignored, even
335        # if the second path is relative.
336        self.assertEqual(p / 'D:x/y', P('D:x/y'))
337        self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
338        self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
339        self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
340        self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
341        # Joining with the same drive => the first path is appended to if
342        # the second path is relative.
343        self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
344        self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
345        # Joining with files with NTFS data streams => the filename should
346        # not be parsed as a drive letter
347        self.assertEqual(p / P('./d:s'), P('C:/a/b/d:s'))
348        self.assertEqual(p / P('./dd:s'), P('C:/a/b/dd:s'))
349        self.assertEqual(p / P('E:d:s'), P('E:d:s'))
350
351    def _check_str(self, expected, args):
352        p = self.cls(*args)
353        self.assertEqual(str(p), expected.replace('/', self.sep))
354
355    def test_str_common(self):
356        # Canonicalized paths roundtrip.
357        for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
358            self._check_str(pathstr, (pathstr,))
359        # Other tests for str() are in test_equivalences().
360
361    @needs_windows
362    def test_str_windows(self):
363        p = self.cls('a/b/c')
364        self.assertEqual(str(p), 'a\\b\\c')
365        p = self.cls('c:/a/b/c')
366        self.assertEqual(str(p), 'c:\\a\\b\\c')
367        p = self.cls('//a/b')
368        self.assertEqual(str(p), '\\\\a\\b\\')
369        p = self.cls('//a/b/c')
370        self.assertEqual(str(p), '\\\\a\\b\\c')
371        p = self.cls('//a/b/c/d')
372        self.assertEqual(str(p), '\\\\a\\b\\c\\d')
373
374    def test_as_posix_common(self):
375        P = self.cls
376        for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
377            self.assertEqual(P(pathstr).as_posix(), pathstr)
378        # Other tests for as_posix() are in test_equivalences().
379
380    def test_match_empty(self):
381        P = self.cls
382        self.assertRaises(ValueError, P('a').match, '')
383
384    def test_match_common(self):
385        P = self.cls
386        # Simple relative pattern.
387        self.assertTrue(P('b.py').match('b.py'))
388        self.assertTrue(P('a/b.py').match('b.py'))
389        self.assertTrue(P('/a/b.py').match('b.py'))
390        self.assertFalse(P('a.py').match('b.py'))
391        self.assertFalse(P('b/py').match('b.py'))
392        self.assertFalse(P('/a.py').match('b.py'))
393        self.assertFalse(P('b.py/c').match('b.py'))
394        # Wildcard relative pattern.
395        self.assertTrue(P('b.py').match('*.py'))
396        self.assertTrue(P('a/b.py').match('*.py'))
397        self.assertTrue(P('/a/b.py').match('*.py'))
398        self.assertFalse(P('b.pyc').match('*.py'))
399        self.assertFalse(P('b./py').match('*.py'))
400        self.assertFalse(P('b.py/c').match('*.py'))
401        # Multi-part relative pattern.
402        self.assertTrue(P('ab/c.py').match('a*/*.py'))
403        self.assertTrue(P('/d/ab/c.py').match('a*/*.py'))
404        self.assertFalse(P('a.py').match('a*/*.py'))
405        self.assertFalse(P('/dab/c.py').match('a*/*.py'))
406        self.assertFalse(P('ab/c.py/d').match('a*/*.py'))
407        # Absolute pattern.
408        self.assertTrue(P('/b.py').match('/*.py'))
409        self.assertFalse(P('b.py').match('/*.py'))
410        self.assertFalse(P('a/b.py').match('/*.py'))
411        self.assertFalse(P('/a/b.py').match('/*.py'))
412        # Multi-part absolute pattern.
413        self.assertTrue(P('/a/b.py').match('/a/*.py'))
414        self.assertFalse(P('/ab.py').match('/a/*.py'))
415        self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
416        # Multi-part glob-style pattern.
417        self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
418        self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
419        # Case-sensitive flag
420        self.assertFalse(P('A.py').match('a.PY', case_sensitive=True))
421        self.assertTrue(P('A.py').match('a.PY', case_sensitive=False))
422        self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', case_sensitive=True))
423        self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))
424        # Matching against empty path
425        self.assertFalse(P('').match('*'))
426        self.assertFalse(P('').match('**'))
427        self.assertFalse(P('').match('**/*'))
428
429    @needs_posix
430    def test_match_posix(self):
431        P = self.cls
432        self.assertFalse(P('A.py').match('a.PY'))
433
434    @needs_windows
435    def test_match_windows(self):
436        P = self.cls
437        # Absolute patterns.
438        self.assertTrue(P('c:/b.py').match('*:/*.py'))
439        self.assertTrue(P('c:/b.py').match('c:/*.py'))
440        self.assertFalse(P('d:/b.py').match('c:/*.py'))  # wrong drive
441        self.assertFalse(P('b.py').match('/*.py'))
442        self.assertFalse(P('b.py').match('c:*.py'))
443        self.assertFalse(P('b.py').match('c:/*.py'))
444        self.assertFalse(P('c:b.py').match('/*.py'))
445        self.assertFalse(P('c:b.py').match('c:/*.py'))
446        self.assertFalse(P('/b.py').match('c:*.py'))
447        self.assertFalse(P('/b.py').match('c:/*.py'))
448        # UNC patterns.
449        self.assertTrue(P('//some/share/a.py').match('//*/*/*.py'))
450        self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
451        self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
452        self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
453        # Case-insensitivity.
454        self.assertTrue(P('B.py').match('b.PY'))
455        self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
456        self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
457        # Path anchor doesn't match pattern anchor
458        self.assertFalse(P('c:/b.py').match('/*.py'))  # 'c:/' vs '/'
459        self.assertFalse(P('c:/b.py').match('c:*.py'))  # 'c:/' vs 'c:'
460        self.assertFalse(P('//some/share/a.py').match('/*.py'))  # '//some/share/' vs '/'
461
462    def test_full_match_common(self):
463        P = self.cls
464        # Simple relative pattern.
465        self.assertTrue(P('b.py').full_match('b.py'))
466        self.assertFalse(P('a/b.py').full_match('b.py'))
467        self.assertFalse(P('/a/b.py').full_match('b.py'))
468        self.assertFalse(P('a.py').full_match('b.py'))
469        self.assertFalse(P('b/py').full_match('b.py'))
470        self.assertFalse(P('/a.py').full_match('b.py'))
471        self.assertFalse(P('b.py/c').full_match('b.py'))
472        # Wildcard relative pattern.
473        self.assertTrue(P('b.py').full_match('*.py'))
474        self.assertFalse(P('a/b.py').full_match('*.py'))
475        self.assertFalse(P('/a/b.py').full_match('*.py'))
476        self.assertFalse(P('b.pyc').full_match('*.py'))
477        self.assertFalse(P('b./py').full_match('*.py'))
478        self.assertFalse(P('b.py/c').full_match('*.py'))
479        # Multi-part relative pattern.
480        self.assertTrue(P('ab/c.py').full_match('a*/*.py'))
481        self.assertFalse(P('/d/ab/c.py').full_match('a*/*.py'))
482        self.assertFalse(P('a.py').full_match('a*/*.py'))
483        self.assertFalse(P('/dab/c.py').full_match('a*/*.py'))
484        self.assertFalse(P('ab/c.py/d').full_match('a*/*.py'))
485        # Absolute pattern.
486        self.assertTrue(P('/b.py').full_match('/*.py'))
487        self.assertFalse(P('b.py').full_match('/*.py'))
488        self.assertFalse(P('a/b.py').full_match('/*.py'))
489        self.assertFalse(P('/a/b.py').full_match('/*.py'))
490        # Multi-part absolute pattern.
491        self.assertTrue(P('/a/b.py').full_match('/a/*.py'))
492        self.assertFalse(P('/ab.py').full_match('/a/*.py'))
493        self.assertFalse(P('/a/b/c.py').full_match('/a/*.py'))
494        # Multi-part glob-style pattern.
495        self.assertTrue(P('a').full_match('**'))
496        self.assertTrue(P('c.py').full_match('**'))
497        self.assertTrue(P('a/b/c.py').full_match('**'))
498        self.assertTrue(P('/a/b/c.py').full_match('**'))
499        self.assertTrue(P('/a/b/c.py').full_match('/**'))
500        self.assertTrue(P('/a/b/c.py').full_match('/a/**'))
501        self.assertTrue(P('/a/b/c.py').full_match('**/*.py'))
502        self.assertTrue(P('/a/b/c.py').full_match('/**/*.py'))
503        self.assertTrue(P('/a/b/c.py').full_match('/a/**/*.py'))
504        self.assertTrue(P('/a/b/c.py').full_match('/a/b/**/*.py'))
505        self.assertTrue(P('/a/b/c.py').full_match('/**/**/**/**/*.py'))
506        self.assertFalse(P('c.py').full_match('**/a.py'))
507        self.assertFalse(P('c.py').full_match('c/**'))
508        self.assertFalse(P('a/b/c.py').full_match('**/a'))
509        self.assertFalse(P('a/b/c.py').full_match('**/a/b'))
510        self.assertFalse(P('a/b/c.py').full_match('**/a/b/c'))
511        self.assertFalse(P('a/b/c.py').full_match('**/a/b/c.'))
512        self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
513        self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
514        self.assertFalse(P('a/b/c.py').full_match('/a/b/c.py/**'))
515        self.assertFalse(P('a/b/c.py').full_match('/**/a/b/c.py'))
516        # Case-sensitive flag
517        self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
518        self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))
519        self.assertFalse(P('c:/a/B.Py').full_match('C:/A/*.pY', case_sensitive=True))
520        self.assertTrue(P('/a/b/c.py').full_match('/A/*/*.Py', case_sensitive=False))
521        # Matching against empty path
522        self.assertFalse(P('').full_match('*'))
523        self.assertTrue(P('').full_match('**'))
524        self.assertFalse(P('').full_match('**/*'))
525        # Matching with empty pattern
526        self.assertTrue(P('').full_match(''))
527        self.assertTrue(P('.').full_match('.'))
528        self.assertFalse(P('/').full_match(''))
529        self.assertFalse(P('/').full_match('.'))
530        self.assertFalse(P('foo').full_match(''))
531        self.assertFalse(P('foo').full_match('.'))
532
533    def test_parts_common(self):
534        # `parts` returns a tuple.
535        sep = self.sep
536        P = self.cls
537        p = P('a/b')
538        parts = p.parts
539        self.assertEqual(parts, ('a', 'b'))
540        # When the path is absolute, the anchor is a separate part.
541        p = P('/a/b')
542        parts = p.parts
543        self.assertEqual(parts, (sep, 'a', 'b'))
544
545    @needs_windows
546    def test_parts_windows(self):
547        P = self.cls
548        p = P('c:a/b')
549        parts = p.parts
550        self.assertEqual(parts, ('c:', 'a', 'b'))
551        p = P('c:/a/b')
552        parts = p.parts
553        self.assertEqual(parts, ('c:\\', 'a', 'b'))
554        p = P('//a/b/c/d')
555        parts = p.parts
556        self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
557
558    def test_parent_common(self):
559        # Relative
560        P = self.cls
561        p = P('a/b/c')
562        self.assertEqual(p.parent, P('a/b'))
563        self.assertEqual(p.parent.parent, P('a'))
564        self.assertEqual(p.parent.parent.parent, P(''))
565        self.assertEqual(p.parent.parent.parent.parent, P(''))
566        # Anchored
567        p = P('/a/b/c')
568        self.assertEqual(p.parent, P('/a/b'))
569        self.assertEqual(p.parent.parent, P('/a'))
570        self.assertEqual(p.parent.parent.parent, P('/'))
571        self.assertEqual(p.parent.parent.parent.parent, P('/'))
572
573    @needs_windows
574    def test_parent_windows(self):
575        # Anchored
576        P = self.cls
577        p = P('z:a/b/c')
578        self.assertEqual(p.parent, P('z:a/b'))
579        self.assertEqual(p.parent.parent, P('z:a'))
580        self.assertEqual(p.parent.parent.parent, P('z:'))
581        self.assertEqual(p.parent.parent.parent.parent, P('z:'))
582        p = P('z:/a/b/c')
583        self.assertEqual(p.parent, P('z:/a/b'))
584        self.assertEqual(p.parent.parent, P('z:/a'))
585        self.assertEqual(p.parent.parent.parent, P('z:/'))
586        self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
587        p = P('//a/b/c/d')
588        self.assertEqual(p.parent, P('//a/b/c'))
589        self.assertEqual(p.parent.parent, P('//a/b'))
590        self.assertEqual(p.parent.parent.parent, P('//a/b'))
591
592    def test_parents_common(self):
593        # Relative
594        P = self.cls
595        p = P('a/b/c')
596        par = p.parents
597        self.assertEqual(len(par), 3)
598        self.assertEqual(par[0], P('a/b'))
599        self.assertEqual(par[1], P('a'))
600        self.assertEqual(par[2], P(''))
601        self.assertEqual(par[-1], P(''))
602        self.assertEqual(par[-2], P('a'))
603        self.assertEqual(par[-3], P('a/b'))
604        self.assertEqual(par[0:1], (P('a/b'),))
605        self.assertEqual(par[:2], (P('a/b'), P('a')))
606        self.assertEqual(par[:-1], (P('a/b'), P('a')))
607        self.assertEqual(par[1:], (P('a'), P('')))
608        self.assertEqual(par[::2], (P('a/b'), P('')))
609        self.assertEqual(par[::-1], (P(''), P('a'), P('a/b')))
610        self.assertEqual(list(par), [P('a/b'), P('a'), P('')])
611        with self.assertRaises(IndexError):
612            par[-4]
613        with self.assertRaises(IndexError):
614            par[3]
615        with self.assertRaises(TypeError):
616            par[0] = p
617        # Anchored
618        p = P('/a/b/c')
619        par = p.parents
620        self.assertEqual(len(par), 3)
621        self.assertEqual(par[0], P('/a/b'))
622        self.assertEqual(par[1], P('/a'))
623        self.assertEqual(par[2], P('/'))
624        self.assertEqual(par[-1], P('/'))
625        self.assertEqual(par[-2], P('/a'))
626        self.assertEqual(par[-3], P('/a/b'))
627        self.assertEqual(par[0:1], (P('/a/b'),))
628        self.assertEqual(par[:2], (P('/a/b'), P('/a')))
629        self.assertEqual(par[:-1], (P('/a/b'), P('/a')))
630        self.assertEqual(par[1:], (P('/a'), P('/')))
631        self.assertEqual(par[::2], (P('/a/b'), P('/')))
632        self.assertEqual(par[::-1], (P('/'), P('/a'), P('/a/b')))
633        self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')])
634        with self.assertRaises(IndexError):
635            par[-4]
636        with self.assertRaises(IndexError):
637            par[3]
638
639    @needs_windows
640    def test_parents_windows(self):
641        # Anchored
642        P = self.cls
643        p = P('z:a/b/')
644        par = p.parents
645        self.assertEqual(len(par), 2)
646        self.assertEqual(par[0], P('z:a'))
647        self.assertEqual(par[1], P('z:'))
648        self.assertEqual(par[0:1], (P('z:a'),))
649        self.assertEqual(par[:-1], (P('z:a'),))
650        self.assertEqual(par[:2], (P('z:a'), P('z:')))
651        self.assertEqual(par[1:], (P('z:'),))
652        self.assertEqual(par[::2], (P('z:a'),))
653        self.assertEqual(par[::-1], (P('z:'), P('z:a')))
654        self.assertEqual(list(par), [P('z:a'), P('z:')])
655        with self.assertRaises(IndexError):
656            par[2]
657        p = P('z:/a/b/')
658        par = p.parents
659        self.assertEqual(len(par), 2)
660        self.assertEqual(par[0], P('z:/a'))
661        self.assertEqual(par[1], P('z:/'))
662        self.assertEqual(par[0:1], (P('z:/a'),))
663        self.assertEqual(par[0:-1], (P('z:/a'),))
664        self.assertEqual(par[:2], (P('z:/a'), P('z:/')))
665        self.assertEqual(par[1:], (P('z:/'),))
666        self.assertEqual(par[::2], (P('z:/a'),))
667        self.assertEqual(par[::-1], (P('z:/'), P('z:/a'),))
668        self.assertEqual(list(par), [P('z:/a'), P('z:/')])
669        with self.assertRaises(IndexError):
670            par[2]
671        p = P('//a/b/c/d')
672        par = p.parents
673        self.assertEqual(len(par), 2)
674        self.assertEqual(par[0], P('//a/b/c'))
675        self.assertEqual(par[1], P('//a/b'))
676        self.assertEqual(par[0:1], (P('//a/b/c'),))
677        self.assertEqual(par[0:-1], (P('//a/b/c'),))
678        self.assertEqual(par[:2], (P('//a/b/c'), P('//a/b')))
679        self.assertEqual(par[1:], (P('//a/b'),))
680        self.assertEqual(par[::2], (P('//a/b/c'),))
681        self.assertEqual(par[::-1], (P('//a/b'), P('//a/b/c')))
682        self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
683        with self.assertRaises(IndexError):
684            par[2]
685
686    def test_drive_common(self):
687        P = self.cls
688        self.assertEqual(P('a/b').drive, '')
689        self.assertEqual(P('/a/b').drive, '')
690        self.assertEqual(P('').drive, '')
691
692    @needs_windows
693    def test_drive_windows(self):
694        P = self.cls
695        self.assertEqual(P('c:').drive, 'c:')
696        self.assertEqual(P('c:a/b').drive, 'c:')
697        self.assertEqual(P('c:/').drive, 'c:')
698        self.assertEqual(P('c:/a/b/').drive, 'c:')
699        self.assertEqual(P('//a/b').drive, '\\\\a\\b')
700        self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
701        self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
702        self.assertEqual(P('./c:a').drive, '')
703
704    def test_root_common(self):
705        P = self.cls
706        sep = self.sep
707        self.assertEqual(P('').root, '')
708        self.assertEqual(P('a/b').root, '')
709        self.assertEqual(P('/').root, sep)
710        self.assertEqual(P('/a/b').root, sep)
711
712    @needs_posix
713    def test_root_posix(self):
714        P = self.cls
715        self.assertEqual(P('/a/b').root, '/')
716        # POSIX special case for two leading slashes.
717        self.assertEqual(P('//a/b').root, '//')
718
719    @needs_windows
720    def test_root_windows(self):
721        P = self.cls
722        self.assertEqual(P('c:').root, '')
723        self.assertEqual(P('c:a/b').root, '')
724        self.assertEqual(P('c:/').root, '\\')
725        self.assertEqual(P('c:/a/b/').root, '\\')
726        self.assertEqual(P('//a/b').root, '\\')
727        self.assertEqual(P('//a/b/').root, '\\')
728        self.assertEqual(P('//a/b/c/d').root, '\\')
729
730    def test_anchor_common(self):
731        P = self.cls
732        sep = self.sep
733        self.assertEqual(P('').anchor, '')
734        self.assertEqual(P('a/b').anchor, '')
735        self.assertEqual(P('/').anchor, sep)
736        self.assertEqual(P('/a/b').anchor, sep)
737
738    @needs_windows
739    def test_anchor_windows(self):
740        P = self.cls
741        self.assertEqual(P('c:').anchor, 'c:')
742        self.assertEqual(P('c:a/b').anchor, 'c:')
743        self.assertEqual(P('c:/').anchor, 'c:\\')
744        self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
745        self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
746        self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
747        self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
748
749    def test_name_empty(self):
750        P = self.cls
751        self.assertEqual(P('').name, '')
752        self.assertEqual(P('.').name, '.')
753        self.assertEqual(P('/a/b/.').name, '.')
754
755    def test_name_common(self):
756        P = self.cls
757        self.assertEqual(P('/').name, '')
758        self.assertEqual(P('a/b').name, 'b')
759        self.assertEqual(P('/a/b').name, 'b')
760        self.assertEqual(P('a/b.py').name, 'b.py')
761        self.assertEqual(P('/a/b.py').name, 'b.py')
762
763    @needs_windows
764    def test_name_windows(self):
765        P = self.cls
766        self.assertEqual(P('c:').name, '')
767        self.assertEqual(P('c:/').name, '')
768        self.assertEqual(P('c:a/b').name, 'b')
769        self.assertEqual(P('c:/a/b').name, 'b')
770        self.assertEqual(P('c:a/b.py').name, 'b.py')
771        self.assertEqual(P('c:/a/b.py').name, 'b.py')
772        self.assertEqual(P('//My.py/Share.php').name, '')
773        self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
774
775    def test_suffix_common(self):
776        P = self.cls
777        self.assertEqual(P('').suffix, '')
778        self.assertEqual(P('.').suffix, '')
779        self.assertEqual(P('..').suffix, '')
780        self.assertEqual(P('/').suffix, '')
781        self.assertEqual(P('a/b').suffix, '')
782        self.assertEqual(P('/a/b').suffix, '')
783        self.assertEqual(P('/a/b/.').suffix, '')
784        self.assertEqual(P('a/b.py').suffix, '.py')
785        self.assertEqual(P('/a/b.py').suffix, '.py')
786        self.assertEqual(P('a/.hgrc').suffix, '')
787        self.assertEqual(P('/a/.hgrc').suffix, '')
788        self.assertEqual(P('a/.hg.rc').suffix, '.rc')
789        self.assertEqual(P('/a/.hg.rc').suffix, '.rc')
790        self.assertEqual(P('a/b.tar.gz').suffix, '.gz')
791        self.assertEqual(P('/a/b.tar.gz').suffix, '.gz')
792        self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '')
793        self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '')
794
795    @needs_windows
796    def test_suffix_windows(self):
797        P = self.cls
798        self.assertEqual(P('c:').suffix, '')
799        self.assertEqual(P('c:/').suffix, '')
800        self.assertEqual(P('c:a/b').suffix, '')
801        self.assertEqual(P('c:/a/b').suffix, '')
802        self.assertEqual(P('c:a/b.py').suffix, '.py')
803        self.assertEqual(P('c:/a/b.py').suffix, '.py')
804        self.assertEqual(P('c:a/.hgrc').suffix, '')
805        self.assertEqual(P('c:/a/.hgrc').suffix, '')
806        self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
807        self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
808        self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
809        self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
810        self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
811        self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
812        self.assertEqual(P('//My.py/Share.php').suffix, '')
813        self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
814
815    def test_suffixes_common(self):
816        P = self.cls
817        self.assertEqual(P('').suffixes, [])
818        self.assertEqual(P('.').suffixes, [])
819        self.assertEqual(P('/').suffixes, [])
820        self.assertEqual(P('a/b').suffixes, [])
821        self.assertEqual(P('/a/b').suffixes, [])
822        self.assertEqual(P('/a/b/.').suffixes, [])
823        self.assertEqual(P('a/b.py').suffixes, ['.py'])
824        self.assertEqual(P('/a/b.py').suffixes, ['.py'])
825        self.assertEqual(P('a/.hgrc').suffixes, [])
826        self.assertEqual(P('/a/.hgrc').suffixes, [])
827        self.assertEqual(P('a/.hg.rc').suffixes, ['.rc'])
828        self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc'])
829        self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz'])
830        self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz'])
831        self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, [])
832        self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, [])
833
834    @needs_windows
835    def test_suffixes_windows(self):
836        P = self.cls
837        self.assertEqual(P('c:').suffixes, [])
838        self.assertEqual(P('c:/').suffixes, [])
839        self.assertEqual(P('c:a/b').suffixes, [])
840        self.assertEqual(P('c:/a/b').suffixes, [])
841        self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
842        self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
843        self.assertEqual(P('c:a/.hgrc').suffixes, [])
844        self.assertEqual(P('c:/a/.hgrc').suffixes, [])
845        self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
846        self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
847        self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
848        self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
849        self.assertEqual(P('//My.py/Share.php').suffixes, [])
850        self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
851        self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
852        self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
853
854    def test_stem_empty(self):
855        P = self.cls
856        self.assertEqual(P('').stem, '')
857        self.assertEqual(P('.').stem, '.')
858
859    def test_stem_common(self):
860        P = self.cls
861        self.assertEqual(P('..').stem, '..')
862        self.assertEqual(P('/').stem, '')
863        self.assertEqual(P('a/b').stem, 'b')
864        self.assertEqual(P('a/b.py').stem, 'b')
865        self.assertEqual(P('a/.hgrc').stem, '.hgrc')
866        self.assertEqual(P('a/.hg.rc').stem, '.hg')
867        self.assertEqual(P('a/b.tar.gz').stem, 'b.tar')
868        self.assertEqual(P('a/Some name. Ending with a dot.').stem,
869                         'Some name. Ending with a dot.')
870
871    @needs_windows
872    def test_stem_windows(self):
873        P = self.cls
874        self.assertEqual(P('c:').stem, '')
875        self.assertEqual(P('c:.').stem, '')
876        self.assertEqual(P('c:..').stem, '..')
877        self.assertEqual(P('c:/').stem, '')
878        self.assertEqual(P('c:a/b').stem, 'b')
879        self.assertEqual(P('c:a/b.py').stem, 'b')
880        self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
881        self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
882        self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
883        self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
884                         'Some name. Ending with a dot.')
885    def test_with_name_common(self):
886        P = self.cls
887        self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml'))
888        self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml'))
889        self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml'))
890        self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml'))
891        self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml'))
892        self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml'))
893
894    @needs_windows
895    def test_with_name_windows(self):
896        P = self.cls
897        self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
898        self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
899        self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
900        self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
901        self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
902        self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
903        self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
904        self.assertEqual(str(P('a').with_name('d:')), '.\\d:')
905        self.assertEqual(str(P('a').with_name('d:e')), '.\\d:e')
906        self.assertEqual(P('c:a/b').with_name('d:'), P('c:a/d:'))
907        self.assertEqual(P('c:a/b').with_name('d:e'), P('c:a/d:e'))
908        self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e')
909        self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share')
910
911    def test_with_name_empty(self):
912        P = self.cls
913        self.assertEqual(P('').with_name('d.xml'), P('d.xml'))
914        self.assertEqual(P('.').with_name('d.xml'), P('d.xml'))
915        self.assertEqual(P('/').with_name('d.xml'), P('/d.xml'))
916        self.assertEqual(P('a/b').with_name(''), P('a/'))
917        self.assertEqual(P('a/b').with_name('.'), P('a/.'))
918
919    def test_with_name_seps(self):
920        P = self.cls
921        self.assertRaises(ValueError, P('a/b').with_name, '/c')
922        self.assertRaises(ValueError, P('a/b').with_name, 'c/')
923        self.assertRaises(ValueError, P('a/b').with_name, 'c/d')
924
925    def test_with_stem_common(self):
926        P = self.cls
927        self.assertEqual(P('a/b').with_stem('d'), P('a/d'))
928        self.assertEqual(P('/a/b').with_stem('d'), P('/a/d'))
929        self.assertEqual(P('a/b.py').with_stem('d'), P('a/d.py'))
930        self.assertEqual(P('/a/b.py').with_stem('d'), P('/a/d.py'))
931        self.assertEqual(P('/a/b.tar.gz').with_stem('d'), P('/a/d.gz'))
932        self.assertEqual(P('a/Dot ending.').with_stem('d'), P('a/d'))
933        self.assertEqual(P('/a/Dot ending.').with_stem('d'), P('/a/d'))
934
935    @needs_windows
936    def test_with_stem_windows(self):
937        P = self.cls
938        self.assertEqual(P('c:a/b').with_stem('d'), P('c:a/d'))
939        self.assertEqual(P('c:/a/b').with_stem('d'), P('c:/a/d'))
940        self.assertEqual(P('c:a/Dot ending.').with_stem('d'), P('c:a/d'))
941        self.assertEqual(P('c:/a/Dot ending.').with_stem('d'), P('c:/a/d'))
942        self.assertRaises(ValueError, P('c:').with_stem, 'd')
943        self.assertRaises(ValueError, P('c:/').with_stem, 'd')
944        self.assertRaises(ValueError, P('//My/Share').with_stem, 'd')
945        self.assertEqual(str(P('a').with_stem('d:')), '.\\d:')
946        self.assertEqual(str(P('a').with_stem('d:e')), '.\\d:e')
947        self.assertEqual(P('c:a/b').with_stem('d:'), P('c:a/d:'))
948        self.assertEqual(P('c:a/b').with_stem('d:e'), P('c:a/d:e'))
949        self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:/e')
950        self.assertRaises(ValueError, P('c:a/b').with_stem, '//My/Share')
951
952    def test_with_stem_empty(self):
953        P = self.cls
954        self.assertEqual(P('').with_stem('d'), P('d'))
955        self.assertEqual(P('.').with_stem('d'), P('d'))
956        self.assertEqual(P('/').with_stem('d'), P('/d'))
957        self.assertEqual(P('a/b').with_stem(''), P('a/'))
958        self.assertEqual(P('a/b').with_stem('.'), P('a/.'))
959        self.assertRaises(ValueError, P('foo.gz').with_stem, '')
960        self.assertRaises(ValueError, P('/a/b/foo.gz').with_stem, '')
961
962    def test_with_stem_seps(self):
963        P = self.cls
964        self.assertRaises(ValueError, P('a/b').with_stem, '/c')
965        self.assertRaises(ValueError, P('a/b').with_stem, 'c/')
966        self.assertRaises(ValueError, P('a/b').with_stem, 'c/d')
967
968    def test_with_suffix_common(self):
969        P = self.cls
970        self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
971        self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
972        self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
973        self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
974        # Stripping suffix.
975        self.assertEqual(P('a/b.py').with_suffix(''), P('a/b'))
976        self.assertEqual(P('/a/b').with_suffix(''), P('/a/b'))
977
978    @needs_windows
979    def test_with_suffix_windows(self):
980        P = self.cls
981        self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
982        self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
983        self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
984        self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
985        # Path doesn't have a "filename" component.
986        self.assertRaises(ValueError, P('').with_suffix, '.gz')
987        self.assertRaises(ValueError, P('.').with_suffix, '.gz')
988        self.assertRaises(ValueError, P('/').with_suffix, '.gz')
989        self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
990        # Invalid suffix.
991        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
992        self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
993        self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
994        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
995        self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
996        self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
997        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
998        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
999        self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
1000        self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
1001        self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
1002        self.assertRaises(TypeError, P('c:a/b').with_suffix, None)
1003
1004    def test_with_suffix_empty(self):
1005        P = self.cls
1006        # Path doesn't have a "filename" component.
1007        self.assertRaises(ValueError, P('').with_suffix, '.gz')
1008        self.assertRaises(ValueError, P('/').with_suffix, '.gz')
1009
1010    def test_with_suffix_invalid(self):
1011        P = self.cls
1012        # Invalid suffix.
1013        self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
1014        self.assertRaises(ValueError, P('a/b').with_suffix, '/')
1015        self.assertRaises(ValueError, P('a/b').with_suffix, '.')
1016        self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
1017        self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
1018        self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
1019        self.assertRaises(ValueError, P('a/b').with_suffix, './.d')
1020        self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
1021        self.assertRaises(TypeError, P('a/b').with_suffix, None)
1022
1023    def test_relative_to_common(self):
1024        P = self.cls
1025        p = P('a/b')
1026        self.assertRaises(TypeError, p.relative_to)
1027        self.assertRaises(TypeError, p.relative_to, b'a')
1028        self.assertEqual(p.relative_to(P('')), P('a/b'))
1029        self.assertEqual(p.relative_to(''), P('a/b'))
1030        self.assertEqual(p.relative_to(P('a')), P('b'))
1031        self.assertEqual(p.relative_to('a'), P('b'))
1032        self.assertEqual(p.relative_to('a/'), P('b'))
1033        self.assertEqual(p.relative_to(P('a/b')), P(''))
1034        self.assertEqual(p.relative_to('a/b'), P(''))
1035        self.assertEqual(p.relative_to(P(''), walk_up=True), P('a/b'))
1036        self.assertEqual(p.relative_to('', walk_up=True), P('a/b'))
1037        self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b'))
1038        self.assertEqual(p.relative_to('a', walk_up=True), P('b'))
1039        self.assertEqual(p.relative_to('a/', walk_up=True), P('b'))
1040        self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P(''))
1041        self.assertEqual(p.relative_to('a/b', walk_up=True), P(''))
1042        self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('../b'))
1043        self.assertEqual(p.relative_to('a/c', walk_up=True), P('../b'))
1044        self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..'))
1045        self.assertEqual(p.relative_to('a/b/c', walk_up=True), P('..'))
1046        self.assertEqual(p.relative_to(P('c'), walk_up=True), P('../a/b'))
1047        self.assertEqual(p.relative_to('c', walk_up=True), P('../a/b'))
1048        # Unrelated paths.
1049        self.assertRaises(ValueError, p.relative_to, P('c'))
1050        self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
1051        self.assertRaises(ValueError, p.relative_to, P('a/c'))
1052        self.assertRaises(ValueError, p.relative_to, P('/a'))
1053        self.assertRaises(ValueError, p.relative_to, P("../a"))
1054        self.assertRaises(ValueError, p.relative_to, P("a/.."))
1055        self.assertRaises(ValueError, p.relative_to, P("/a/.."))
1056        self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
1057        self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True)
1058        self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True)
1059        self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True)
1060        self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True)
1061        p = P('/a/b')
1062        self.assertEqual(p.relative_to(P('/')), P('a/b'))
1063        self.assertEqual(p.relative_to('/'), P('a/b'))
1064        self.assertEqual(p.relative_to(P('/a')), P('b'))
1065        self.assertEqual(p.relative_to('/a'), P('b'))
1066        self.assertEqual(p.relative_to('/a/'), P('b'))
1067        self.assertEqual(p.relative_to(P('/a/b')), P(''))
1068        self.assertEqual(p.relative_to('/a/b'), P(''))
1069        self.assertEqual(p.relative_to(P('/'), walk_up=True), P('a/b'))
1070        self.assertEqual(p.relative_to('/', walk_up=True), P('a/b'))
1071        self.assertEqual(p.relative_to(P('/a'), walk_up=True), P('b'))
1072        self.assertEqual(p.relative_to('/a', walk_up=True), P('b'))
1073        self.assertEqual(p.relative_to('/a/', walk_up=True), P('b'))
1074        self.assertEqual(p.relative_to(P('/a/b'), walk_up=True), P(''))
1075        self.assertEqual(p.relative_to('/a/b', walk_up=True), P(''))
1076        self.assertEqual(p.relative_to(P('/a/c'), walk_up=True), P('../b'))
1077        self.assertEqual(p.relative_to('/a/c', walk_up=True), P('../b'))
1078        self.assertEqual(p.relative_to(P('/a/b/c'), walk_up=True), P('..'))
1079        self.assertEqual(p.relative_to('/a/b/c', walk_up=True), P('..'))
1080        self.assertEqual(p.relative_to(P('/c'), walk_up=True), P('../a/b'))
1081        self.assertEqual(p.relative_to('/c', walk_up=True), P('../a/b'))
1082        # Unrelated paths.
1083        self.assertRaises(ValueError, p.relative_to, P('/c'))
1084        self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
1085        self.assertRaises(ValueError, p.relative_to, P('/a/c'))
1086        self.assertRaises(ValueError, p.relative_to, P(''))
1087        self.assertRaises(ValueError, p.relative_to, '')
1088        self.assertRaises(ValueError, p.relative_to, P('a'))
1089        self.assertRaises(ValueError, p.relative_to, P("../a"))
1090        self.assertRaises(ValueError, p.relative_to, P("a/.."))
1091        self.assertRaises(ValueError, p.relative_to, P("/a/.."))
1092        self.assertRaises(ValueError, p.relative_to, P(''), walk_up=True)
1093        self.assertRaises(ValueError, p.relative_to, P('a'), walk_up=True)
1094        self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True)
1095        self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True)
1096        self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True)
1097
1098    @needs_windows
1099    def test_relative_to_windows(self):
1100        P = self.cls
1101        p = P('C:Foo/Bar')
1102        self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
1103        self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
1104        self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
1105        self.assertEqual(p.relative_to('c:foO'), P('Bar'))
1106        self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
1107        self.assertEqual(p.relative_to(P('c:foO/baR')), P())
1108        self.assertEqual(p.relative_to('c:foO/baR'), P())
1109        self.assertEqual(p.relative_to(P('c:'), walk_up=True), P('Foo/Bar'))
1110        self.assertEqual(p.relative_to('c:', walk_up=True), P('Foo/Bar'))
1111        self.assertEqual(p.relative_to(P('c:foO'), walk_up=True), P('Bar'))
1112        self.assertEqual(p.relative_to('c:foO', walk_up=True), P('Bar'))
1113        self.assertEqual(p.relative_to('c:foO/', walk_up=True), P('Bar'))
1114        self.assertEqual(p.relative_to(P('c:foO/baR'), walk_up=True), P())
1115        self.assertEqual(p.relative_to('c:foO/baR', walk_up=True), P())
1116        self.assertEqual(p.relative_to(P('C:Foo/Bar/Baz'), walk_up=True), P('..'))
1117        self.assertEqual(p.relative_to(P('C:Foo/Baz'), walk_up=True), P('../Bar'))
1118        self.assertEqual(p.relative_to(P('C:Baz/Bar'), walk_up=True), P('../../Foo/Bar'))
1119        # Unrelated paths.
1120        self.assertRaises(ValueError, p.relative_to, P())
1121        self.assertRaises(ValueError, p.relative_to, '')
1122        self.assertRaises(ValueError, p.relative_to, P('d:'))
1123        self.assertRaises(ValueError, p.relative_to, P('/'))
1124        self.assertRaises(ValueError, p.relative_to, P('Foo'))
1125        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1126        self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
1127        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
1128        self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
1129        self.assertRaises(ValueError, p.relative_to, P(), walk_up=True)
1130        self.assertRaises(ValueError, p.relative_to, '', walk_up=True)
1131        self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True)
1132        self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
1133        self.assertRaises(ValueError, p.relative_to, P('Foo'), walk_up=True)
1134        self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True)
1135        self.assertRaises(ValueError, p.relative_to, P('C:/Foo'), walk_up=True)
1136        p = P('C:/Foo/Bar')
1137        self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
1138        self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
1139        self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
1140        self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
1141        self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
1142        self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
1143        self.assertEqual(p.relative_to('c:/foO/baR'), P())
1144        self.assertEqual(p.relative_to(P('c:/'), walk_up=True), P('Foo/Bar'))
1145        self.assertEqual(p.relative_to('c:/', walk_up=True), P('Foo/Bar'))
1146        self.assertEqual(p.relative_to(P('c:/foO'), walk_up=True), P('Bar'))
1147        self.assertEqual(p.relative_to('c:/foO', walk_up=True), P('Bar'))
1148        self.assertEqual(p.relative_to('c:/foO/', walk_up=True), P('Bar'))
1149        self.assertEqual(p.relative_to(P('c:/foO/baR'), walk_up=True), P())
1150        self.assertEqual(p.relative_to('c:/foO/baR', walk_up=True), P())
1151        self.assertEqual(p.relative_to('C:/Baz', walk_up=True), P('../Foo/Bar'))
1152        self.assertEqual(p.relative_to('C:/Foo/Bar/Baz', walk_up=True), P('..'))
1153        self.assertEqual(p.relative_to('C:/Foo/Baz', walk_up=True), P('../Bar'))
1154        # Unrelated paths.
1155        self.assertRaises(ValueError, p.relative_to, 'c:')
1156        self.assertRaises(ValueError, p.relative_to, P('c:'))
1157        self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
1158        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
1159        self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
1160        self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
1161        self.assertRaises(ValueError, p.relative_to, P('d:'))
1162        self.assertRaises(ValueError, p.relative_to, P('d:/'))
1163        self.assertRaises(ValueError, p.relative_to, P('/'))
1164        self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1165        self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
1166        self.assertRaises(ValueError, p.relative_to, 'c:', walk_up=True)
1167        self.assertRaises(ValueError, p.relative_to, P('c:'), walk_up=True)
1168        self.assertRaises(ValueError, p.relative_to, P('C:Foo'), walk_up=True)
1169        self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True)
1170        self.assertRaises(ValueError, p.relative_to, P('d:/'), walk_up=True)
1171        self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
1172        self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True)
1173        self.assertRaises(ValueError, p.relative_to, P('//C/Foo'), walk_up=True)
1174        # UNC paths.
1175        p = P('//Server/Share/Foo/Bar')
1176        self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
1177        self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
1178        self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
1179        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
1180        self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
1181        self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
1182        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
1183        self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
1184        self.assertEqual(p.relative_to(P('//sErver/sHare'), walk_up=True), P('Foo/Bar'))
1185        self.assertEqual(p.relative_to('//sErver/sHare', walk_up=True), P('Foo/Bar'))
1186        self.assertEqual(p.relative_to('//sErver/sHare/', walk_up=True), P('Foo/Bar'))
1187        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo'), walk_up=True), P('Bar'))
1188        self.assertEqual(p.relative_to('//sErver/sHare/Foo', walk_up=True), P('Bar'))
1189        self.assertEqual(p.relative_to('//sErver/sHare/Foo/', walk_up=True), P('Bar'))
1190        self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar'), walk_up=True), P())
1191        self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar', walk_up=True), P())
1192        self.assertEqual(p.relative_to(P('//sErver/sHare/bar'), walk_up=True), P('../Foo/Bar'))
1193        self.assertEqual(p.relative_to('//sErver/sHare/bar', walk_up=True), P('../Foo/Bar'))
1194        # Unrelated paths.
1195        self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
1196        self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
1197        self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
1198        self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
1199        self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'), walk_up=True)
1200        self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'), walk_up=True)
1201        self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'), walk_up=True)
1202        self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'), walk_up=True)
1203
1204    def test_is_relative_to_common(self):
1205        P = self.cls
1206        p = P('a/b')
1207        self.assertRaises(TypeError, p.is_relative_to)
1208        self.assertRaises(TypeError, p.is_relative_to, b'a')
1209        self.assertTrue(p.is_relative_to(P('')))
1210        self.assertTrue(p.is_relative_to(''))
1211        self.assertTrue(p.is_relative_to(P('a')))
1212        self.assertTrue(p.is_relative_to('a/'))
1213        self.assertTrue(p.is_relative_to(P('a/b')))
1214        self.assertTrue(p.is_relative_to('a/b'))
1215        # Unrelated paths.
1216        self.assertFalse(p.is_relative_to(P('c')))
1217        self.assertFalse(p.is_relative_to(P('a/b/c')))
1218        self.assertFalse(p.is_relative_to(P('a/c')))
1219        self.assertFalse(p.is_relative_to(P('/a')))
1220        p = P('/a/b')
1221        self.assertTrue(p.is_relative_to(P('/')))
1222        self.assertTrue(p.is_relative_to('/'))
1223        self.assertTrue(p.is_relative_to(P('/a')))
1224        self.assertTrue(p.is_relative_to('/a'))
1225        self.assertTrue(p.is_relative_to('/a/'))
1226        self.assertTrue(p.is_relative_to(P('/a/b')))
1227        self.assertTrue(p.is_relative_to('/a/b'))
1228        # Unrelated paths.
1229        self.assertFalse(p.is_relative_to(P('/c')))
1230        self.assertFalse(p.is_relative_to(P('/a/b/c')))
1231        self.assertFalse(p.is_relative_to(P('/a/c')))
1232        self.assertFalse(p.is_relative_to(P('')))
1233        self.assertFalse(p.is_relative_to(''))
1234        self.assertFalse(p.is_relative_to(P('a')))
1235
1236    @needs_windows
1237    def test_is_relative_to_windows(self):
1238        P = self.cls
1239        p = P('C:Foo/Bar')
1240        self.assertTrue(p.is_relative_to(P('c:')))
1241        self.assertTrue(p.is_relative_to('c:'))
1242        self.assertTrue(p.is_relative_to(P('c:foO')))
1243        self.assertTrue(p.is_relative_to('c:foO'))
1244        self.assertTrue(p.is_relative_to('c:foO/'))
1245        self.assertTrue(p.is_relative_to(P('c:foO/baR')))
1246        self.assertTrue(p.is_relative_to('c:foO/baR'))
1247        # Unrelated paths.
1248        self.assertFalse(p.is_relative_to(P()))
1249        self.assertFalse(p.is_relative_to(''))
1250        self.assertFalse(p.is_relative_to(P('d:')))
1251        self.assertFalse(p.is_relative_to(P('/')))
1252        self.assertFalse(p.is_relative_to(P('Foo')))
1253        self.assertFalse(p.is_relative_to(P('/Foo')))
1254        self.assertFalse(p.is_relative_to(P('C:/Foo')))
1255        self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz')))
1256        self.assertFalse(p.is_relative_to(P('C:Foo/Baz')))
1257        p = P('C:/Foo/Bar')
1258        self.assertTrue(p.is_relative_to(P('c:/')))
1259        self.assertTrue(p.is_relative_to(P('c:/foO')))
1260        self.assertTrue(p.is_relative_to('c:/foO/'))
1261        self.assertTrue(p.is_relative_to(P('c:/foO/baR')))
1262        self.assertTrue(p.is_relative_to('c:/foO/baR'))
1263        # Unrelated paths.
1264        self.assertFalse(p.is_relative_to('c:'))
1265        self.assertFalse(p.is_relative_to(P('C:/Baz')))
1266        self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz')))
1267        self.assertFalse(p.is_relative_to(P('C:/Foo/Baz')))
1268        self.assertFalse(p.is_relative_to(P('C:Foo')))
1269        self.assertFalse(p.is_relative_to(P('d:')))
1270        self.assertFalse(p.is_relative_to(P('d:/')))
1271        self.assertFalse(p.is_relative_to(P('/')))
1272        self.assertFalse(p.is_relative_to(P('/Foo')))
1273        self.assertFalse(p.is_relative_to(P('//C/Foo')))
1274        # UNC paths.
1275        p = P('//Server/Share/Foo/Bar')
1276        self.assertTrue(p.is_relative_to(P('//sErver/sHare')))
1277        self.assertTrue(p.is_relative_to('//sErver/sHare'))
1278        self.assertTrue(p.is_relative_to('//sErver/sHare/'))
1279        self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo')))
1280        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo'))
1281        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/'))
1282        self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar')))
1283        self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar'))
1284        # Unrelated paths.
1285        self.assertFalse(p.is_relative_to(P('/Server/Share/Foo')))
1286        self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo')))
1287        self.assertFalse(p.is_relative_to(P('//z/Share/Foo')))
1288        self.assertFalse(p.is_relative_to(P('//Server/z/Foo')))
1289
1290    @needs_posix
1291    def test_is_absolute_posix(self):
1292        P = self.cls
1293        self.assertFalse(P('').is_absolute())
1294        self.assertFalse(P('a').is_absolute())
1295        self.assertFalse(P('a/b/').is_absolute())
1296        self.assertTrue(P('/').is_absolute())
1297        self.assertTrue(P('/a').is_absolute())
1298        self.assertTrue(P('/a/b/').is_absolute())
1299        self.assertTrue(P('//a').is_absolute())
1300        self.assertTrue(P('//a/b').is_absolute())
1301
1302    @needs_windows
1303    def test_is_absolute_windows(self):
1304        P = self.cls
1305        # Under NT, only paths with both a drive and a root are absolute.
1306        self.assertFalse(P().is_absolute())
1307        self.assertFalse(P('a').is_absolute())
1308        self.assertFalse(P('a/b/').is_absolute())
1309        self.assertFalse(P('/').is_absolute())
1310        self.assertFalse(P('/a').is_absolute())
1311        self.assertFalse(P('/a/b/').is_absolute())
1312        self.assertFalse(P('c:').is_absolute())
1313        self.assertFalse(P('c:a').is_absolute())
1314        self.assertFalse(P('c:a/b/').is_absolute())
1315        self.assertTrue(P('c:/').is_absolute())
1316        self.assertTrue(P('c:/a').is_absolute())
1317        self.assertTrue(P('c:/a/b/').is_absolute())
1318        # UNC paths are absolute by definition.
1319        self.assertTrue(P('//').is_absolute())
1320        self.assertTrue(P('//a').is_absolute())
1321        self.assertTrue(P('//a/b').is_absolute())
1322        self.assertTrue(P('//a/b/').is_absolute())
1323        self.assertTrue(P('//a/b/c').is_absolute())
1324        self.assertTrue(P('//a/b/c/d').is_absolute())
1325        self.assertTrue(P('//?/UNC/').is_absolute())
1326        self.assertTrue(P('//?/UNC/spam').is_absolute())
1327
1328
1329#
1330# Tests for the virtual classes.
1331#
1332
1333class PathBaseTest(PurePathBaseTest):
1334    cls = PathBase
1335
1336    def test_unsupported_operation(self):
1337        P = self.cls
1338        p = self.cls('')
1339        e = UnsupportedOperation
1340        self.assertRaises(e, p.stat)
1341        self.assertRaises(e, p.lstat)
1342        self.assertRaises(e, p.exists)
1343        self.assertRaises(e, p.samefile, 'foo')
1344        self.assertRaises(e, p.is_dir)
1345        self.assertRaises(e, p.is_file)
1346        self.assertRaises(e, p.is_mount)
1347        self.assertRaises(e, p.is_symlink)
1348        self.assertRaises(e, p.is_block_device)
1349        self.assertRaises(e, p.is_char_device)
1350        self.assertRaises(e, p.is_fifo)
1351        self.assertRaises(e, p.is_socket)
1352        self.assertRaises(e, p.open)
1353        self.assertRaises(e, p.read_bytes)
1354        self.assertRaises(e, p.read_text)
1355        self.assertRaises(e, p.write_bytes, b'foo')
1356        self.assertRaises(e, p.write_text, 'foo')
1357        self.assertRaises(e, p.iterdir)
1358        self.assertRaises(e, p.glob, '*')
1359        self.assertRaises(e, p.rglob, '*')
1360        self.assertRaises(e, lambda: list(p.walk()))
1361        self.assertRaises(e, p.absolute)
1362        self.assertRaises(e, P.cwd)
1363        self.assertRaises(e, p.expanduser)
1364        self.assertRaises(e, p.home)
1365        self.assertRaises(e, p.readlink)
1366        self.assertRaises(e, p.symlink_to, 'foo')
1367        self.assertRaises(e, p.hardlink_to, 'foo')
1368        self.assertRaises(e, p.mkdir)
1369        self.assertRaises(e, p.touch)
1370        self.assertRaises(e, p.rename, 'foo')
1371        self.assertRaises(e, p.replace, 'foo')
1372        self.assertRaises(e, p.chmod, 0o755)
1373        self.assertRaises(e, p.lchmod, 0o755)
1374        self.assertRaises(e, p.unlink)
1375        self.assertRaises(e, p.rmdir)
1376        self.assertRaises(e, p.owner)
1377        self.assertRaises(e, p.group)
1378        self.assertRaises(e, p.as_uri)
1379
1380    def test_as_uri_common(self):
1381        e = UnsupportedOperation
1382        self.assertRaises(e, self.cls('').as_uri)
1383
1384    def test_fspath_common(self):
1385        self.assertRaises(TypeError, os.fspath, self.cls(''))
1386
1387    def test_as_bytes_common(self):
1388        self.assertRaises(TypeError, bytes, self.cls(''))
1389
1390
1391class DummyPathIO(io.BytesIO):
1392    """
1393    Used by DummyPath to implement `open('w')`
1394    """
1395
1396    def __init__(self, files, path):
1397        super().__init__()
1398        self.files = files
1399        self.path = path
1400
1401    def close(self):
1402        self.files[self.path] = self.getvalue()
1403        super().close()
1404
1405
1406DummyPathStatResult = collections.namedtuple(
1407    'DummyPathStatResult',
1408    'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime')
1409
1410
1411class DummyPath(PathBase):
1412    """
1413    Simple implementation of PathBase that keeps files and directories in
1414    memory.
1415    """
1416    __slots__ = ()
1417    parser = posixpath
1418
1419    _files = {}
1420    _directories = {}
1421    _symlinks = {}
1422
1423    def __eq__(self, other):
1424        if not isinstance(other, DummyPath):
1425            return NotImplemented
1426        return str(self) == str(other)
1427
1428    def __hash__(self):
1429        return hash(str(self))
1430
1431    def __repr__(self):
1432        return "{}({!r})".format(self.__class__.__name__, self.as_posix())
1433
1434    def stat(self, *, follow_symlinks=True):
1435        if follow_symlinks or self.name in ('', '.', '..'):
1436            path = str(self.resolve(strict=True))
1437        else:
1438            path = str(self.parent.resolve(strict=True) / self.name)
1439        if path in self._files:
1440            st_mode = stat.S_IFREG
1441        elif path in self._directories:
1442            st_mode = stat.S_IFDIR
1443        elif path in self._symlinks:
1444            st_mode = stat.S_IFLNK
1445        else:
1446            raise FileNotFoundError(errno.ENOENT, "Not found", str(self))
1447        return DummyPathStatResult(st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0)
1448
1449    def open(self, mode='r', buffering=-1, encoding=None,
1450             errors=None, newline=None):
1451        if buffering != -1:
1452            raise NotImplementedError
1453        path_obj = self.resolve()
1454        path = str(path_obj)
1455        name = path_obj.name
1456        parent = str(path_obj.parent)
1457        if path in self._directories:
1458            raise IsADirectoryError(errno.EISDIR, "Is a directory", path)
1459
1460        text = 'b' not in mode
1461        mode = ''.join(c for c in mode if c not in 'btU')
1462        if mode == 'r':
1463            if path not in self._files:
1464                raise FileNotFoundError(errno.ENOENT, "File not found", path)
1465            stream = io.BytesIO(self._files[path])
1466        elif mode == 'w':
1467            if parent not in self._directories:
1468                raise FileNotFoundError(errno.ENOENT, "File not found", parent)
1469            stream = DummyPathIO(self._files, path)
1470            self._files[path] = b''
1471            self._directories[parent].add(name)
1472        else:
1473            raise NotImplementedError
1474        if text:
1475            stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline)
1476        return stream
1477
1478    def iterdir(self):
1479        path = str(self.resolve())
1480        if path in self._files:
1481            raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
1482        elif path in self._directories:
1483            return (self / name for name in self._directories[path])
1484        else:
1485            raise FileNotFoundError(errno.ENOENT, "File not found", path)
1486
1487    def mkdir(self, mode=0o777, parents=False, exist_ok=False):
1488        path = str(self.resolve())
1489        if path in self._directories:
1490            if exist_ok:
1491                return
1492            else:
1493                raise FileExistsError(errno.EEXIST, "File exists", path)
1494        try:
1495            if self.name:
1496                self._directories[str(self.parent)].add(self.name)
1497            self._directories[path] = set()
1498        except KeyError:
1499            if not parents:
1500                raise FileNotFoundError(errno.ENOENT, "File not found", str(self.parent)) from None
1501            self.parent.mkdir(parents=True, exist_ok=True)
1502            self.mkdir(mode, parents=False, exist_ok=exist_ok)
1503
1504
1505class DummyPathTest(DummyPurePathTest):
1506    """Tests for PathBase methods that use stat(), open() and iterdir()."""
1507
1508    cls = DummyPath
1509    can_symlink = False
1510
1511    # (self.base)
1512    #  |
1513    #  |-- brokenLink -> non-existing
1514    #  |-- dirA
1515    #  |   `-- linkC -> ../dirB
1516    #  |-- dirB
1517    #  |   |-- fileB
1518    #  |   `-- linkD -> ../dirB
1519    #  |-- dirC
1520    #  |   |-- dirD
1521    #  |   |   `-- fileD
1522    #  |   `-- fileC
1523    #  |   `-- novel.txt
1524    #  |-- dirE  # No permissions
1525    #  |-- fileA
1526    #  |-- linkA -> fileA
1527    #  |-- linkB -> dirB
1528    #  `-- brokenLinkLoop -> brokenLinkLoop
1529    #
1530
1531    def setUp(self):
1532        super().setUp()
1533        name = self.id().split('.')[-1]
1534        if name in _tests_needing_symlinks and not self.can_symlink:
1535            self.skipTest('requires symlinks')
1536        parser = self.cls.parser
1537        p = self.cls(self.base)
1538        p.mkdir(parents=True)
1539        p.joinpath('dirA').mkdir()
1540        p.joinpath('dirB').mkdir()
1541        p.joinpath('dirC').mkdir()
1542        p.joinpath('dirC', 'dirD').mkdir()
1543        p.joinpath('dirE').mkdir()
1544        with p.joinpath('fileA').open('wb') as f:
1545            f.write(b"this is file A\n")
1546        with p.joinpath('dirB', 'fileB').open('wb') as f:
1547            f.write(b"this is file B\n")
1548        with p.joinpath('dirC', 'fileC').open('wb') as f:
1549            f.write(b"this is file C\n")
1550        with p.joinpath('dirC', 'novel.txt').open('wb') as f:
1551            f.write(b"this is a novel\n")
1552        with p.joinpath('dirC', 'dirD', 'fileD').open('wb') as f:
1553            f.write(b"this is file D\n")
1554        if self.can_symlink:
1555            p.joinpath('linkA').symlink_to('fileA')
1556            p.joinpath('brokenLink').symlink_to('non-existing')
1557            p.joinpath('linkB').symlink_to('dirB')
1558            p.joinpath('dirA', 'linkC').symlink_to(parser.join('..', 'dirB'))
1559            p.joinpath('dirB', 'linkD').symlink_to(parser.join('..', 'dirB'))
1560            p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop')
1561
1562    def tearDown(self):
1563        cls = self.cls
1564        cls._files.clear()
1565        cls._directories.clear()
1566        cls._symlinks.clear()
1567
1568    def tempdir(self):
1569        path = self.cls(self.base).with_name('tmp-dirD')
1570        path.mkdir()
1571        return path
1572
1573    def assertFileNotFound(self, func, *args, **kwargs):
1574        with self.assertRaises(FileNotFoundError) as cm:
1575            func(*args, **kwargs)
1576        self.assertEqual(cm.exception.errno, errno.ENOENT)
1577
1578    def assertEqualNormCase(self, path_a, path_b):
1579        normcase = self.parser.normcase
1580        self.assertEqual(normcase(path_a), normcase(path_b))
1581
1582    def test_samefile(self):
1583        parser = self.parser
1584        fileA_path = parser.join(self.base, 'fileA')
1585        fileB_path = parser.join(self.base, 'dirB', 'fileB')
1586        p = self.cls(fileA_path)
1587        pp = self.cls(fileA_path)
1588        q = self.cls(fileB_path)
1589        self.assertTrue(p.samefile(fileA_path))
1590        self.assertTrue(p.samefile(pp))
1591        self.assertFalse(p.samefile(fileB_path))
1592        self.assertFalse(p.samefile(q))
1593        # Test the non-existent file case
1594        non_existent = parser.join(self.base, 'foo')
1595        r = self.cls(non_existent)
1596        self.assertRaises(FileNotFoundError, p.samefile, r)
1597        self.assertRaises(FileNotFoundError, p.samefile, non_existent)
1598        self.assertRaises(FileNotFoundError, r.samefile, p)
1599        self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1600        self.assertRaises(FileNotFoundError, r.samefile, r)
1601        self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1602
1603    def test_exists(self):
1604        P = self.cls
1605        p = P(self.base)
1606        self.assertIs(True, p.exists())
1607        self.assertIs(True, (p / 'dirA').exists())
1608        self.assertIs(True, (p / 'fileA').exists())
1609        self.assertIs(False, (p / 'fileA' / 'bah').exists())
1610        if self.can_symlink:
1611            self.assertIs(True, (p / 'linkA').exists())
1612            self.assertIs(True, (p / 'linkB').exists())
1613            self.assertIs(True, (p / 'linkB' / 'fileB').exists())
1614            self.assertIs(False, (p / 'linkA' / 'bah').exists())
1615            self.assertIs(False, (p / 'brokenLink').exists())
1616            self.assertIs(True, (p / 'brokenLink').exists(follow_symlinks=False))
1617        self.assertIs(False, (p / 'foo').exists())
1618        self.assertIs(False, P('/xyzzy').exists())
1619        self.assertIs(False, P(self.base + '\udfff').exists())
1620        self.assertIs(False, P(self.base + '\x00').exists())
1621
1622    def test_open_common(self):
1623        p = self.cls(self.base)
1624        with (p / 'fileA').open('r') as f:
1625            self.assertIsInstance(f, io.TextIOBase)
1626            self.assertEqual(f.read(), "this is file A\n")
1627        with (p / 'fileA').open('rb') as f:
1628            self.assertIsInstance(f, io.BufferedIOBase)
1629            self.assertEqual(f.read().strip(), b"this is file A")
1630
1631    def test_read_write_bytes(self):
1632        p = self.cls(self.base)
1633        (p / 'fileA').write_bytes(b'abcdefg')
1634        self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
1635        # Check that trying to write str does not truncate the file.
1636        self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr')
1637        self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
1638
1639    def test_read_write_text(self):
1640        p = self.cls(self.base)
1641        (p / 'fileA').write_text('äbcdefg', encoding='latin-1')
1642        self.assertEqual((p / 'fileA').read_text(
1643            encoding='utf-8', errors='ignore'), 'bcdefg')
1644        # Check that trying to write bytes does not truncate the file.
1645        self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes')
1646        self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg')
1647
1648    def test_read_text_with_newlines(self):
1649        p = self.cls(self.base)
1650        # Check that `\n` character change nothing
1651        (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
1652        self.assertEqual((p / 'fileA').read_text(newline='\n'),
1653                         'abcde\r\nfghlk\n\rmnopq')
1654        # Check that `\r` character replaces `\n`
1655        (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
1656        self.assertEqual((p / 'fileA').read_text(newline='\r'),
1657                         'abcde\r\nfghlk\n\rmnopq')
1658        # Check that `\r\n` character replaces `\n`
1659        (p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
1660        self.assertEqual((p / 'fileA').read_text(newline='\r\n'),
1661                             'abcde\r\nfghlk\n\rmnopq')
1662
1663    def test_write_text_with_newlines(self):
1664        p = self.cls(self.base)
1665        # Check that `\n` character change nothing
1666        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n')
1667        self.assertEqual((p / 'fileA').read_bytes(),
1668                         b'abcde\r\nfghlk\n\rmnopq')
1669        # Check that `\r` character replaces `\n`
1670        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r')
1671        self.assertEqual((p / 'fileA').read_bytes(),
1672                         b'abcde\r\rfghlk\r\rmnopq')
1673        # Check that `\r\n` character replaces `\n`
1674        (p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n')
1675        self.assertEqual((p / 'fileA').read_bytes(),
1676                         b'abcde\r\r\nfghlk\r\n\rmnopq')
1677        # Check that no argument passed will change `\n` to `os.linesep`
1678        os_linesep_byte = bytes(os.linesep, encoding='ascii')
1679        (p / 'fileA').write_text('abcde\nfghlk\n\rmnopq')
1680        self.assertEqual((p / 'fileA').read_bytes(),
1681                          b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq')
1682
1683    def test_iterdir(self):
1684        P = self.cls
1685        p = P(self.base)
1686        it = p.iterdir()
1687        paths = set(it)
1688        expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA']
1689        if self.can_symlink:
1690            expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop']
1691        self.assertEqual(paths, { P(self.base, q) for q in expected })
1692
1693    @needs_symlinks
1694    def test_iterdir_symlink(self):
1695        # __iter__ on a symlink to a directory.
1696        P = self.cls
1697        p = P(self.base, 'linkB')
1698        paths = set(p.iterdir())
1699        expected = { P(self.base, 'linkB', q) for q in ['fileB', 'linkD'] }
1700        self.assertEqual(paths, expected)
1701
1702    def test_iterdir_nodir(self):
1703        # __iter__ on something that is not a directory.
1704        p = self.cls(self.base, 'fileA')
1705        with self.assertRaises(OSError) as cm:
1706            p.iterdir()
1707        # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1708        # (see issue #12802).
1709        self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1710                                           errno.ENOENT, errno.EINVAL))
1711
1712    def test_glob_common(self):
1713        def _check(glob, expected):
1714            self.assertEqual(set(glob), { P(self.base, q) for q in expected })
1715        P = self.cls
1716        p = P(self.base)
1717        it = p.glob("fileA")
1718        self.assertIsInstance(it, collections.abc.Iterator)
1719        _check(it, ["fileA"])
1720        _check(p.glob("fileB"), [])
1721        _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1722        if not self.can_symlink:
1723            _check(p.glob("*A"), ['dirA', 'fileA'])
1724        else:
1725            _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1726        if not self.can_symlink:
1727            _check(p.glob("*B/*"), ['dirB/fileB'])
1728        else:
1729            _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1730                                    'linkB/fileB', 'linkB/linkD'])
1731        if not self.can_symlink:
1732            _check(p.glob("*/fileB"), ['dirB/fileB'])
1733        else:
1734            _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1735        if self.can_symlink:
1736            _check(p.glob("brokenLink"), ['brokenLink'])
1737
1738        if not self.can_symlink:
1739            _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/"])
1740        else:
1741            _check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
1742
1743    @needs_posix
1744    def test_glob_posix(self):
1745        P = self.cls
1746        p = P(self.base)
1747        q = p / "FILEa"
1748        given = set(p.glob("FILEa"))
1749        expect = {q} if q.exists() else set()
1750        self.assertEqual(given, expect)
1751        self.assertEqual(set(p.glob("FILEa*")), set())
1752
1753    @needs_windows
1754    def test_glob_windows(self):
1755        P = self.cls
1756        p = P(self.base)
1757        self.assertEqual(set(p.glob("FILEa")), { P(self.base, "fileA") })
1758        self.assertEqual(set(p.glob("*a\\")), { P(self.base, "dirA/") })
1759        self.assertEqual(set(p.glob("F*a")), { P(self.base, "fileA") })
1760
1761    def test_glob_empty_pattern(self):
1762        P = self.cls
1763        p = P(self.base)
1764        self.assertEqual(list(p.glob("")), [p])
1765        self.assertEqual(list(p.glob(".")), [p / "."])
1766        self.assertEqual(list(p.glob("./")), [p / "./"])
1767
1768    def test_glob_case_sensitive(self):
1769        P = self.cls
1770        def _check(path, pattern, case_sensitive, expected):
1771            actual = {str(q) for q in path.glob(pattern, case_sensitive=case_sensitive)}
1772            expected = {str(P(self.base, q)) for q in expected}
1773            self.assertEqual(actual, expected)
1774        path = P(self.base)
1775        _check(path, "DIRB/FILE*", True, [])
1776        _check(path, "DIRB/FILE*", False, ["dirB/fileB"])
1777        _check(path, "dirb/file*", True, [])
1778        _check(path, "dirb/file*", False, ["dirB/fileB"])
1779
1780    @needs_symlinks
1781    def test_glob_recurse_symlinks_common(self):
1782        def _check(path, glob, expected):
1783            actual = {path for path in path.glob(glob, recurse_symlinks=True)
1784                      if path.parts.count("linkD") <= 1}  # exclude symlink loop.
1785            self.assertEqual(actual, { P(self.base, q) for q in expected })
1786        P = self.cls
1787        p = P(self.base)
1788        _check(p, "fileB", [])
1789        _check(p, "dir*/file*", ["dirB/fileB", "dirC/fileC"])
1790        _check(p, "*A", ["dirA", "fileA", "linkA"])
1791        _check(p, "*B/*", ["dirB/fileB", "dirB/linkD", "linkB/fileB", "linkB/linkD"])
1792        _check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"])
1793        _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
1794        _check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/..", "dirB/linkD/.."])
1795        _check(p, "dir*/**", [
1796            "dirA/", "dirA/linkC", "dirA/linkC/fileB", "dirA/linkC/linkD", "dirA/linkC/linkD/fileB",
1797            "dirB/", "dirB/fileB", "dirB/linkD", "dirB/linkD/fileB",
1798            "dirC/", "dirC/fileC", "dirC/dirD",  "dirC/dirD/fileD", "dirC/novel.txt",
1799            "dirE/"])
1800        _check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
1801                               "dirC/", "dirC/dirD/", "dirE/"])
1802        _check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..",
1803                                 "dirB/linkD/..", "dirA/linkC/linkD/..",
1804                                 "dirC/..", "dirC/dirD/..", "dirE/.."])
1805        _check(p, "dir*/*/**", [
1806            "dirA/linkC/", "dirA/linkC/linkD", "dirA/linkC/fileB", "dirA/linkC/linkD/fileB",
1807            "dirB/linkD/", "dirB/linkD/fileB",
1808            "dirC/dirD/", "dirC/dirD/fileD"])
1809        _check(p, "dir*/*/**/", ["dirA/linkC/", "dirA/linkC/linkD/", "dirB/linkD/", "dirC/dirD/"])
1810        _check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirA/linkC/linkD/..",
1811                                   "dirB/linkD/..", "dirC/dirD/.."])
1812        _check(p, "dir*/**/fileC", ["dirC/fileC"])
1813        _check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
1814        _check(p, "*/dirD/**", ["dirC/dirD/", "dirC/dirD/fileD"])
1815        _check(p, "*/dirD/**/", ["dirC/dirD/"])
1816
1817    def test_rglob_recurse_symlinks_false(self):
1818        def _check(path, glob, expected):
1819            actual = set(path.rglob(glob, recurse_symlinks=False))
1820            self.assertEqual(actual, { P(self.base, q) for q in expected })
1821        P = self.cls
1822        p = P(self.base)
1823        it = p.rglob("fileA")
1824        self.assertIsInstance(it, collections.abc.Iterator)
1825        _check(p, "fileA", ["fileA"])
1826        _check(p, "fileB", ["dirB/fileB"])
1827        _check(p, "**/fileB", ["dirB/fileB"])
1828        _check(p, "*/fileA", [])
1829
1830        if self.can_symlink:
1831            _check(p, "*/fileB", ["dirB/fileB", "dirB/linkD/fileB",
1832                                  "linkB/fileB", "dirA/linkC/fileB"])
1833            _check(p, "*/", [
1834                "dirA/", "dirA/linkC/", "dirB/", "dirB/linkD/", "dirC/",
1835                "dirC/dirD/", "dirE/", "linkB/"])
1836        else:
1837            _check(p, "*/fileB", ["dirB/fileB"])
1838            _check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"])
1839
1840        _check(p, "file*", ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD"])
1841        _check(p, "", ["", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"])
1842        p = P(self.base, "dirC")
1843        _check(p, "*", ["dirC/fileC", "dirC/novel.txt",
1844                              "dirC/dirD", "dirC/dirD/fileD"])
1845        _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"])
1846        _check(p, "**/file*", ["dirC/fileC", "dirC/dirD/fileD"])
1847        _check(p, "dir*/**", ["dirC/dirD/", "dirC/dirD/fileD"])
1848        _check(p, "dir*/**/", ["dirC/dirD/"])
1849        _check(p, "*/*", ["dirC/dirD/fileD"])
1850        _check(p, "*/", ["dirC/dirD/"])
1851        _check(p, "", ["dirC/", "dirC/dirD/"])
1852        _check(p, "**", ["dirC/", "dirC/fileC", "dirC/dirD", "dirC/dirD/fileD", "dirC/novel.txt"])
1853        _check(p, "**/", ["dirC/", "dirC/dirD/"])
1854        # gh-91616, a re module regression
1855        _check(p, "*.txt", ["dirC/novel.txt"])
1856        _check(p, "*.*", ["dirC/novel.txt"])
1857
1858    @needs_posix
1859    def test_rglob_posix(self):
1860        P = self.cls
1861        p = P(self.base, "dirC")
1862        q = p / "dirD" / "FILEd"
1863        given = set(p.rglob("FILEd"))
1864        expect = {q} if q.exists() else set()
1865        self.assertEqual(given, expect)
1866        self.assertEqual(set(p.rglob("FILEd*")), set())
1867
1868    @needs_windows
1869    def test_rglob_windows(self):
1870        P = self.cls
1871        p = P(self.base, "dirC")
1872        self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") })
1873        self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") })
1874
1875    @needs_symlinks
1876    def test_rglob_recurse_symlinks_common(self):
1877        def _check(path, glob, expected):
1878            actual = {path for path in path.rglob(glob, recurse_symlinks=True)
1879                      if path.parts.count("linkD") <= 1}  # exclude symlink loop.
1880            self.assertEqual(actual, { P(self.base, q) for q in expected })
1881        P = self.cls
1882        p = P(self.base)
1883        _check(p, "fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB",
1884                            "dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB"])
1885        _check(p, "*/fileA", [])
1886        _check(p, "*/fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB",
1887                              "dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB"])
1888        _check(p, "file*", ["fileA", "dirA/linkC/fileB", "dirB/fileB",
1889                            "dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB",
1890                            "dirC/fileC", "dirC/dirD/fileD", "linkB/fileB"])
1891        _check(p, "*/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
1892                         "dirC/", "dirC/dirD/", "dirE/", "linkB/", "linkB/linkD/"])
1893        _check(p, "", ["", "dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
1894                       "dirC/", "dirE/", "dirC/dirD/", "linkB/", "linkB/linkD/"])
1895
1896        p = P(self.base, "dirC")
1897        _check(p, "*", ["dirC/fileC", "dirC/novel.txt",
1898                        "dirC/dirD", "dirC/dirD/fileD"])
1899        _check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"])
1900        _check(p, "*/*", ["dirC/dirD/fileD"])
1901        _check(p, "*/", ["dirC/dirD/"])
1902        _check(p, "", ["dirC/", "dirC/dirD/"])
1903        # gh-91616, a re module regression
1904        _check(p, "*.txt", ["dirC/novel.txt"])
1905        _check(p, "*.*", ["dirC/novel.txt"])
1906
1907    @needs_symlinks
1908    def test_rglob_symlink_loop(self):
1909        # Don't get fooled by symlink loops (Issue #26012).
1910        P = self.cls
1911        p = P(self.base)
1912        given = set(p.rglob('*', recurse_symlinks=False))
1913        expect = {'brokenLink',
1914                  'dirA', 'dirA/linkC',
1915                  'dirB', 'dirB/fileB', 'dirB/linkD',
1916                  'dirC', 'dirC/dirD', 'dirC/dirD/fileD',
1917                  'dirC/fileC', 'dirC/novel.txt',
1918                  'dirE',
1919                  'fileA',
1920                  'linkA',
1921                  'linkB',
1922                  'brokenLinkLoop',
1923                  }
1924        self.assertEqual(given, {p / x for x in expect})
1925
1926    # See https://github.com/WebAssembly/wasi-filesystem/issues/26
1927    @unittest.skipIf(is_wasi, "WASI resolution of '..' parts doesn't match POSIX")
1928    def test_glob_dotdot(self):
1929        # ".." is not special in globs.
1930        P = self.cls
1931        p = P(self.base)
1932        self.assertEqual(set(p.glob("..")), { P(self.base, "..") })
1933        self.assertEqual(set(p.glob("../..")), { P(self.base, "..", "..") })
1934        self.assertEqual(set(p.glob("dirA/..")), { P(self.base, "dirA", "..") })
1935        self.assertEqual(set(p.glob("dirA/../file*")), { P(self.base, "dirA/../fileA") })
1936        self.assertEqual(set(p.glob("dirA/../file*/..")), set())
1937        self.assertEqual(set(p.glob("../xyzzy")), set())
1938        if self.cls.parser is posixpath:
1939            self.assertEqual(set(p.glob("xyzzy/..")), set())
1940        else:
1941            # ".." segments are normalized first on Windows, so this path is stat()able.
1942            self.assertEqual(set(p.glob("xyzzy/..")), { P(self.base, "xyzzy", "..") })
1943        self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(self.base, *[".."] * 50)})
1944
1945    @needs_symlinks
1946    def test_glob_permissions(self):
1947        # See bpo-38894
1948        P = self.cls
1949        base = P(self.base) / 'permissions'
1950        base.mkdir()
1951
1952        for i in range(100):
1953            link = base / f"link{i}"
1954            if i % 2:
1955                link.symlink_to(P(self.base, "dirE", "nonexistent"))
1956            else:
1957                link.symlink_to(P(self.base, "dirC"))
1958
1959        self.assertEqual(len(set(base.glob("*"))), 100)
1960        self.assertEqual(len(set(base.glob("*/"))), 50)
1961        self.assertEqual(len(set(base.glob("*/fileC"))), 50)
1962        self.assertEqual(len(set(base.glob("*/file*"))), 50)
1963
1964    @needs_symlinks
1965    def test_glob_long_symlink(self):
1966        # See gh-87695
1967        base = self.cls(self.base) / 'long_symlink'
1968        base.mkdir()
1969        bad_link = base / 'bad_link'
1970        bad_link.symlink_to("bad" * 200)
1971        self.assertEqual(sorted(base.glob('**/*')), [bad_link])
1972
1973    @needs_symlinks
1974    def test_readlink(self):
1975        P = self.cls(self.base)
1976        self.assertEqual((P / 'linkA').readlink(), self.cls('fileA'))
1977        self.assertEqual((P / 'brokenLink').readlink(),
1978                         self.cls('non-existing'))
1979        self.assertEqual((P / 'linkB').readlink(), self.cls('dirB'))
1980        self.assertEqual((P / 'linkB' / 'linkD').readlink(), self.cls('../dirB'))
1981        with self.assertRaises(OSError):
1982            (P / 'fileA').readlink()
1983
1984    @unittest.skipIf(hasattr(os, "readlink"), "os.readlink() is present")
1985    def test_readlink_unsupported(self):
1986        P = self.cls(self.base)
1987        p = P / 'fileA'
1988        with self.assertRaises(UnsupportedOperation):
1989            q.readlink(p)
1990
1991    def _check_resolve(self, p, expected, strict=True):
1992        q = p.resolve(strict)
1993        self.assertEqual(q, expected)
1994
1995    # This can be used to check both relative and absolute resolutions.
1996    _check_resolve_relative = _check_resolve_absolute = _check_resolve
1997
1998    @needs_symlinks
1999    def test_resolve_common(self):
2000        P = self.cls
2001        p = P(self.base, 'foo')
2002        with self.assertRaises(OSError) as cm:
2003            p.resolve(strict=True)
2004        self.assertEqual(cm.exception.errno, errno.ENOENT)
2005        # Non-strict
2006        parser = self.parser
2007        self.assertEqualNormCase(str(p.resolve(strict=False)),
2008                                 parser.join(self.base, 'foo'))
2009        p = P(self.base, 'foo', 'in', 'spam')
2010        self.assertEqualNormCase(str(p.resolve(strict=False)),
2011                                 parser.join(self.base, 'foo', 'in', 'spam'))
2012        p = P(self.base, '..', 'foo', 'in', 'spam')
2013        self.assertEqualNormCase(str(p.resolve(strict=False)),
2014                                 parser.join(parser.dirname(self.base), 'foo', 'in', 'spam'))
2015        # These are all relative symlinks.
2016        p = P(self.base, 'dirB', 'fileB')
2017        self._check_resolve_relative(p, p)
2018        p = P(self.base, 'linkA')
2019        self._check_resolve_relative(p, P(self.base, 'fileA'))
2020        p = P(self.base, 'dirA', 'linkC', 'fileB')
2021        self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB'))
2022        p = P(self.base, 'dirB', 'linkD', 'fileB')
2023        self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB'))
2024        # Non-strict
2025        p = P(self.base, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam')
2026        self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB', 'foo', 'in',
2027                                          'spam'), False)
2028        p = P(self.base, 'dirA', 'linkC', '..', 'foo', 'in', 'spam')
2029        if self.cls.parser is not posixpath:
2030            # In Windows, if linkY points to dirB, 'dirA\linkY\..'
2031            # resolves to 'dirA' without resolving linkY first.
2032            self._check_resolve_relative(p, P(self.base, 'dirA', 'foo', 'in',
2033                                              'spam'), False)
2034        else:
2035            # In Posix, if linkY points to dirB, 'dirA/linkY/..'
2036            # resolves to 'dirB/..' first before resolving to parent of dirB.
2037            self._check_resolve_relative(p, P(self.base, 'foo', 'in', 'spam'), False)
2038        # Now create absolute symlinks.
2039        d = self.tempdir()
2040        P(self.base, 'dirA', 'linkX').symlink_to(d)
2041        P(self.base, str(d), 'linkY').symlink_to(self.parser.join(self.base, 'dirB'))
2042        p = P(self.base, 'dirA', 'linkX', 'linkY', 'fileB')
2043        self._check_resolve_absolute(p, P(self.base, 'dirB', 'fileB'))
2044        # Non-strict
2045        p = P(self.base, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam')
2046        self._check_resolve_relative(p, P(self.base, 'dirB', 'foo', 'in', 'spam'),
2047                                     False)
2048        p = P(self.base, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam')
2049        if self.cls.parser is not posixpath:
2050            # In Windows, if linkY points to dirB, 'dirA\linkY\..'
2051            # resolves to 'dirA' without resolving linkY first.
2052            self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False)
2053        else:
2054            # In Posix, if linkY points to dirB, 'dirA/linkY/..'
2055            # resolves to 'dirB/..' first before resolving to parent of dirB.
2056            self._check_resolve_relative(p, P(self.base, 'foo', 'in', 'spam'), False)
2057
2058    @needs_symlinks
2059    def test_resolve_dot(self):
2060        # See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
2061        parser = self.parser
2062        p = self.cls(self.base)
2063        p.joinpath('0').symlink_to('.', target_is_directory=True)
2064        p.joinpath('1').symlink_to(parser.join('0', '0'), target_is_directory=True)
2065        p.joinpath('2').symlink_to(parser.join('1', '1'), target_is_directory=True)
2066        q = p / '2'
2067        self.assertEqual(q.resolve(strict=True), p)
2068        r = q / '3' / '4'
2069        self.assertRaises(FileNotFoundError, r.resolve, strict=True)
2070        # Non-strict
2071        self.assertEqual(r.resolve(strict=False), p / '3' / '4')
2072
2073    def _check_symlink_loop(self, *args):
2074        path = self.cls(*args)
2075        with self.assertRaises(OSError) as cm:
2076            path.resolve(strict=True)
2077        self.assertEqual(cm.exception.errno, errno.ELOOP)
2078
2079    @needs_posix
2080    @needs_symlinks
2081    def test_resolve_loop(self):
2082        # Loops with relative symlinks.
2083        self.cls(self.base, 'linkX').symlink_to('linkX/inside')
2084        self._check_symlink_loop(self.base, 'linkX')
2085        self.cls(self.base, 'linkY').symlink_to('linkY')
2086        self._check_symlink_loop(self.base, 'linkY')
2087        self.cls(self.base, 'linkZ').symlink_to('linkZ/../linkZ')
2088        self._check_symlink_loop(self.base, 'linkZ')
2089        # Non-strict
2090        p = self.cls(self.base, 'linkZ', 'foo')
2091        self.assertEqual(p.resolve(strict=False), p)
2092        # Loops with absolute symlinks.
2093        self.cls(self.base, 'linkU').symlink_to(self.parser.join(self.base, 'linkU/inside'))
2094        self._check_symlink_loop(self.base, 'linkU')
2095        self.cls(self.base, 'linkV').symlink_to(self.parser.join(self.base, 'linkV'))
2096        self._check_symlink_loop(self.base, 'linkV')
2097        self.cls(self.base, 'linkW').symlink_to(self.parser.join(self.base, 'linkW/../linkW'))
2098        self._check_symlink_loop(self.base, 'linkW')
2099        # Non-strict
2100        q = self.cls(self.base, 'linkW', 'foo')
2101        self.assertEqual(q.resolve(strict=False), q)
2102
2103    def test_stat(self):
2104        statA = self.cls(self.base).joinpath('fileA').stat()
2105        statB = self.cls(self.base).joinpath('dirB', 'fileB').stat()
2106        statC = self.cls(self.base).joinpath('dirC').stat()
2107        # st_mode: files are the same, directory differs.
2108        self.assertIsInstance(statA.st_mode, int)
2109        self.assertEqual(statA.st_mode, statB.st_mode)
2110        self.assertNotEqual(statA.st_mode, statC.st_mode)
2111        self.assertNotEqual(statB.st_mode, statC.st_mode)
2112        # st_ino: all different,
2113        self.assertIsInstance(statA.st_ino, int)
2114        self.assertNotEqual(statA.st_ino, statB.st_ino)
2115        self.assertNotEqual(statA.st_ino, statC.st_ino)
2116        self.assertNotEqual(statB.st_ino, statC.st_ino)
2117        # st_dev: all the same.
2118        self.assertIsInstance(statA.st_dev, int)
2119        self.assertEqual(statA.st_dev, statB.st_dev)
2120        self.assertEqual(statA.st_dev, statC.st_dev)
2121        # other attributes not used by pathlib.
2122
2123    @needs_symlinks
2124    def test_stat_no_follow_symlinks(self):
2125        p = self.cls(self.base) / 'linkA'
2126        st = p.stat()
2127        self.assertNotEqual(st, p.stat(follow_symlinks=False))
2128
2129    def test_stat_no_follow_symlinks_nosymlink(self):
2130        p = self.cls(self.base) / 'fileA'
2131        st = p.stat()
2132        self.assertEqual(st, p.stat(follow_symlinks=False))
2133
2134    @needs_symlinks
2135    def test_lstat(self):
2136        p = self.cls(self.base)/ 'linkA'
2137        st = p.stat()
2138        self.assertNotEqual(st, p.lstat())
2139
2140    def test_lstat_nosymlink(self):
2141        p = self.cls(self.base) / 'fileA'
2142        st = p.stat()
2143        self.assertEqual(st, p.lstat())
2144
2145    def test_is_dir(self):
2146        P = self.cls(self.base)
2147        self.assertTrue((P / 'dirA').is_dir())
2148        self.assertFalse((P / 'fileA').is_dir())
2149        self.assertFalse((P / 'non-existing').is_dir())
2150        self.assertFalse((P / 'fileA' / 'bah').is_dir())
2151        if self.can_symlink:
2152            self.assertFalse((P / 'linkA').is_dir())
2153            self.assertTrue((P / 'linkB').is_dir())
2154            self.assertFalse((P/ 'brokenLink').is_dir())
2155        self.assertFalse((P / 'dirA\udfff').is_dir())
2156        self.assertFalse((P / 'dirA\x00').is_dir())
2157
2158    def test_is_dir_no_follow_symlinks(self):
2159        P = self.cls(self.base)
2160        self.assertTrue((P / 'dirA').is_dir(follow_symlinks=False))
2161        self.assertFalse((P / 'fileA').is_dir(follow_symlinks=False))
2162        self.assertFalse((P / 'non-existing').is_dir(follow_symlinks=False))
2163        self.assertFalse((P / 'fileA' / 'bah').is_dir(follow_symlinks=False))
2164        if self.can_symlink:
2165            self.assertFalse((P / 'linkA').is_dir(follow_symlinks=False))
2166            self.assertFalse((P / 'linkB').is_dir(follow_symlinks=False))
2167            self.assertFalse((P/ 'brokenLink').is_dir(follow_symlinks=False))
2168        self.assertFalse((P / 'dirA\udfff').is_dir(follow_symlinks=False))
2169        self.assertFalse((P / 'dirA\x00').is_dir(follow_symlinks=False))
2170
2171    def test_is_file(self):
2172        P = self.cls(self.base)
2173        self.assertTrue((P / 'fileA').is_file())
2174        self.assertFalse((P / 'dirA').is_file())
2175        self.assertFalse((P / 'non-existing').is_file())
2176        self.assertFalse((P / 'fileA' / 'bah').is_file())
2177        if self.can_symlink:
2178            self.assertTrue((P / 'linkA').is_file())
2179            self.assertFalse((P / 'linkB').is_file())
2180            self.assertFalse((P/ 'brokenLink').is_file())
2181        self.assertFalse((P / 'fileA\udfff').is_file())
2182        self.assertFalse((P / 'fileA\x00').is_file())
2183
2184    def test_is_file_no_follow_symlinks(self):
2185        P = self.cls(self.base)
2186        self.assertTrue((P / 'fileA').is_file(follow_symlinks=False))
2187        self.assertFalse((P / 'dirA').is_file(follow_symlinks=False))
2188        self.assertFalse((P / 'non-existing').is_file(follow_symlinks=False))
2189        self.assertFalse((P / 'fileA' / 'bah').is_file(follow_symlinks=False))
2190        if self.can_symlink:
2191            self.assertFalse((P / 'linkA').is_file(follow_symlinks=False))
2192            self.assertFalse((P / 'linkB').is_file(follow_symlinks=False))
2193            self.assertFalse((P/ 'brokenLink').is_file(follow_symlinks=False))
2194        self.assertFalse((P / 'fileA\udfff').is_file(follow_symlinks=False))
2195        self.assertFalse((P / 'fileA\x00').is_file(follow_symlinks=False))
2196
2197    def test_is_mount(self):
2198        P = self.cls(self.base)
2199        self.assertFalse((P / 'fileA').is_mount())
2200        self.assertFalse((P / 'dirA').is_mount())
2201        self.assertFalse((P / 'non-existing').is_mount())
2202        self.assertFalse((P / 'fileA' / 'bah').is_mount())
2203        if self.can_symlink:
2204            self.assertFalse((P / 'linkA').is_mount())
2205
2206    def test_is_symlink(self):
2207        P = self.cls(self.base)
2208        self.assertFalse((P / 'fileA').is_symlink())
2209        self.assertFalse((P / 'dirA').is_symlink())
2210        self.assertFalse((P / 'non-existing').is_symlink())
2211        self.assertFalse((P / 'fileA' / 'bah').is_symlink())
2212        if self.can_symlink:
2213            self.assertTrue((P / 'linkA').is_symlink())
2214            self.assertTrue((P / 'linkB').is_symlink())
2215            self.assertTrue((P/ 'brokenLink').is_symlink())
2216        self.assertIs((P / 'fileA\udfff').is_file(), False)
2217        self.assertIs((P / 'fileA\x00').is_file(), False)
2218        if self.can_symlink:
2219            self.assertIs((P / 'linkA\udfff').is_file(), False)
2220            self.assertIs((P / 'linkA\x00').is_file(), False)
2221
2222    def test_is_junction_false(self):
2223        P = self.cls(self.base)
2224        self.assertFalse((P / 'fileA').is_junction())
2225        self.assertFalse((P / 'dirA').is_junction())
2226        self.assertFalse((P / 'non-existing').is_junction())
2227        self.assertFalse((P / 'fileA' / 'bah').is_junction())
2228        self.assertFalse((P / 'fileA\udfff').is_junction())
2229        self.assertFalse((P / 'fileA\x00').is_junction())
2230
2231    def test_is_fifo_false(self):
2232        P = self.cls(self.base)
2233        self.assertFalse((P / 'fileA').is_fifo())
2234        self.assertFalse((P / 'dirA').is_fifo())
2235        self.assertFalse((P / 'non-existing').is_fifo())
2236        self.assertFalse((P / 'fileA' / 'bah').is_fifo())
2237        self.assertIs((P / 'fileA\udfff').is_fifo(), False)
2238        self.assertIs((P / 'fileA\x00').is_fifo(), False)
2239
2240    def test_is_socket_false(self):
2241        P = self.cls(self.base)
2242        self.assertFalse((P / 'fileA').is_socket())
2243        self.assertFalse((P / 'dirA').is_socket())
2244        self.assertFalse((P / 'non-existing').is_socket())
2245        self.assertFalse((P / 'fileA' / 'bah').is_socket())
2246        self.assertIs((P / 'fileA\udfff').is_socket(), False)
2247        self.assertIs((P / 'fileA\x00').is_socket(), False)
2248
2249    def test_is_block_device_false(self):
2250        P = self.cls(self.base)
2251        self.assertFalse((P / 'fileA').is_block_device())
2252        self.assertFalse((P / 'dirA').is_block_device())
2253        self.assertFalse((P / 'non-existing').is_block_device())
2254        self.assertFalse((P / 'fileA' / 'bah').is_block_device())
2255        self.assertIs((P / 'fileA\udfff').is_block_device(), False)
2256        self.assertIs((P / 'fileA\x00').is_block_device(), False)
2257
2258    def test_is_char_device_false(self):
2259        P = self.cls(self.base)
2260        self.assertFalse((P / 'fileA').is_char_device())
2261        self.assertFalse((P / 'dirA').is_char_device())
2262        self.assertFalse((P / 'non-existing').is_char_device())
2263        self.assertFalse((P / 'fileA' / 'bah').is_char_device())
2264        self.assertIs((P / 'fileA\udfff').is_char_device(), False)
2265        self.assertIs((P / 'fileA\x00').is_char_device(), False)
2266
2267    def _check_complex_symlinks(self, link0_target):
2268        # Test solving a non-looping chain of symlinks (issue #19887).
2269        parser = self.parser
2270        P = self.cls(self.base)
2271        P.joinpath('link1').symlink_to(parser.join('link0', 'link0'), target_is_directory=True)
2272        P.joinpath('link2').symlink_to(parser.join('link1', 'link1'), target_is_directory=True)
2273        P.joinpath('link3').symlink_to(parser.join('link2', 'link2'), target_is_directory=True)
2274        P.joinpath('link0').symlink_to(link0_target, target_is_directory=True)
2275
2276        # Resolve absolute paths.
2277        p = (P / 'link0').resolve()
2278        self.assertEqual(p, P)
2279        self.assertEqualNormCase(str(p), self.base)
2280        p = (P / 'link1').resolve()
2281        self.assertEqual(p, P)
2282        self.assertEqualNormCase(str(p), self.base)
2283        p = (P / 'link2').resolve()
2284        self.assertEqual(p, P)
2285        self.assertEqualNormCase(str(p), self.base)
2286        p = (P / 'link3').resolve()
2287        self.assertEqual(p, P)
2288        self.assertEqualNormCase(str(p), self.base)
2289
2290        # Resolve relative paths.
2291        try:
2292            self.cls('').absolute()
2293        except UnsupportedOperation:
2294            return
2295        old_path = os.getcwd()
2296        os.chdir(self.base)
2297        try:
2298            p = self.cls('link0').resolve()
2299            self.assertEqual(p, P)
2300            self.assertEqualNormCase(str(p), self.base)
2301            p = self.cls('link1').resolve()
2302            self.assertEqual(p, P)
2303            self.assertEqualNormCase(str(p), self.base)
2304            p = self.cls('link2').resolve()
2305            self.assertEqual(p, P)
2306            self.assertEqualNormCase(str(p), self.base)
2307            p = self.cls('link3').resolve()
2308            self.assertEqual(p, P)
2309            self.assertEqualNormCase(str(p), self.base)
2310        finally:
2311            os.chdir(old_path)
2312
2313    @needs_symlinks
2314    def test_complex_symlinks_absolute(self):
2315        self._check_complex_symlinks(self.base)
2316
2317    @needs_symlinks
2318    def test_complex_symlinks_relative(self):
2319        self._check_complex_symlinks('.')
2320
2321    @needs_symlinks
2322    def test_complex_symlinks_relative_dot_dot(self):
2323        self._check_complex_symlinks(self.parser.join('dirA', '..'))
2324
2325    def setUpWalk(self):
2326        # Build:
2327        #     TESTFN/
2328        #       TEST1/              a file kid and two directory kids
2329        #         tmp1
2330        #         SUB1/             a file kid and a directory kid
2331        #           tmp2
2332        #           SUB11/          no kids
2333        #         SUB2/             a file kid and a dirsymlink kid
2334        #           tmp3
2335        #           link/           a symlink to TEST2
2336        #           broken_link
2337        #           broken_link2
2338        #       TEST2/
2339        #         tmp4              a lone file
2340        self.walk_path = self.cls(self.base, "TEST1")
2341        self.sub1_path = self.walk_path / "SUB1"
2342        self.sub11_path = self.sub1_path / "SUB11"
2343        self.sub2_path = self.walk_path / "SUB2"
2344        tmp1_path = self.walk_path / "tmp1"
2345        tmp2_path = self.sub1_path / "tmp2"
2346        tmp3_path = self.sub2_path / "tmp3"
2347        self.link_path = self.sub2_path / "link"
2348        t2_path = self.cls(self.base, "TEST2")
2349        tmp4_path = self.cls(self.base, "TEST2", "tmp4")
2350        broken_link_path = self.sub2_path / "broken_link"
2351        broken_link2_path = self.sub2_path / "broken_link2"
2352
2353        self.sub11_path.mkdir(parents=True)
2354        self.sub2_path.mkdir(parents=True)
2355        t2_path.mkdir(parents=True)
2356
2357        for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path:
2358            with path.open("w", encoding='utf-8') as f:
2359                f.write(f"I'm {path} and proud of it.  Blame test_pathlib.\n")
2360
2361        if self.can_symlink:
2362            self.link_path.symlink_to(t2_path)
2363            broken_link_path.symlink_to('broken')
2364            broken_link2_path.symlink_to(self.cls('tmp3', 'broken'))
2365            self.sub2_tree = (self.sub2_path, [], ["broken_link", "broken_link2", "link", "tmp3"])
2366        else:
2367            self.sub2_tree = (self.sub2_path, [], ["tmp3"])
2368
2369    def test_walk_topdown(self):
2370        self.setUpWalk()
2371        walker = self.walk_path.walk()
2372        entry = next(walker)
2373        entry[1].sort()  # Ensure we visit SUB1 before SUB2
2374        self.assertEqual(entry, (self.walk_path, ["SUB1", "SUB2"], ["tmp1"]))
2375        entry = next(walker)
2376        self.assertEqual(entry, (self.sub1_path, ["SUB11"], ["tmp2"]))
2377        entry = next(walker)
2378        self.assertEqual(entry, (self.sub11_path, [], []))
2379        entry = next(walker)
2380        entry[1].sort()
2381        entry[2].sort()
2382        self.assertEqual(entry, self.sub2_tree)
2383        with self.assertRaises(StopIteration):
2384            next(walker)
2385
2386    def test_walk_prune(self):
2387        self.setUpWalk()
2388        # Prune the search.
2389        all = []
2390        for root, dirs, files in self.walk_path.walk():
2391            all.append((root, dirs, files))
2392            if 'SUB1' in dirs:
2393                # Note that this also mutates the dirs we appended to all!
2394                dirs.remove('SUB1')
2395
2396        self.assertEqual(len(all), 2)
2397        self.assertEqual(all[0], (self.walk_path, ["SUB2"], ["tmp1"]))
2398
2399        all[1][-1].sort()
2400        all[1][1].sort()
2401        self.assertEqual(all[1], self.sub2_tree)
2402
2403    def test_walk_bottom_up(self):
2404        self.setUpWalk()
2405        seen_testfn = seen_sub1 = seen_sub11 = seen_sub2 = False
2406        for path, dirnames, filenames in self.walk_path.walk(top_down=False):
2407            if path == self.walk_path:
2408                self.assertFalse(seen_testfn)
2409                self.assertTrue(seen_sub1)
2410                self.assertTrue(seen_sub2)
2411                self.assertEqual(sorted(dirnames), ["SUB1", "SUB2"])
2412                self.assertEqual(filenames, ["tmp1"])
2413                seen_testfn = True
2414            elif path == self.sub1_path:
2415                self.assertFalse(seen_testfn)
2416                self.assertFalse(seen_sub1)
2417                self.assertTrue(seen_sub11)
2418                self.assertEqual(dirnames, ["SUB11"])
2419                self.assertEqual(filenames, ["tmp2"])
2420                seen_sub1 = True
2421            elif path == self.sub11_path:
2422                self.assertFalse(seen_sub1)
2423                self.assertFalse(seen_sub11)
2424                self.assertEqual(dirnames, [])
2425                self.assertEqual(filenames, [])
2426                seen_sub11 = True
2427            elif path == self.sub2_path:
2428                self.assertFalse(seen_testfn)
2429                self.assertFalse(seen_sub2)
2430                self.assertEqual(sorted(dirnames), sorted(self.sub2_tree[1]))
2431                self.assertEqual(sorted(filenames), sorted(self.sub2_tree[2]))
2432                seen_sub2 = True
2433            else:
2434                raise AssertionError(f"Unexpected path: {path}")
2435        self.assertTrue(seen_testfn)
2436
2437    @needs_symlinks
2438    def test_walk_follow_symlinks(self):
2439        self.setUpWalk()
2440        walk_it = self.walk_path.walk(follow_symlinks=True)
2441        for root, dirs, files in walk_it:
2442            if root == self.link_path:
2443                self.assertEqual(dirs, [])
2444                self.assertEqual(files, ["tmp4"])
2445                break
2446        else:
2447            self.fail("Didn't follow symlink with follow_symlinks=True")
2448
2449    @needs_symlinks
2450    def test_walk_symlink_location(self):
2451        self.setUpWalk()
2452        # Tests whether symlinks end up in filenames or dirnames depending
2453        # on the `follow_symlinks` argument.
2454        walk_it = self.walk_path.walk(follow_symlinks=False)
2455        for root, dirs, files in walk_it:
2456            if root == self.sub2_path:
2457                self.assertIn("link", files)
2458                break
2459        else:
2460            self.fail("symlink not found")
2461
2462        walk_it = self.walk_path.walk(follow_symlinks=True)
2463        for root, dirs, files in walk_it:
2464            if root == self.sub2_path:
2465                self.assertIn("link", dirs)
2466                break
2467        else:
2468            self.fail("symlink not found")
2469
2470
2471class DummyPathWithSymlinks(DummyPath):
2472    __slots__ = ()
2473
2474    # Reduce symlink traversal limit to make tests run faster.
2475    _max_symlinks = 20
2476
2477    def readlink(self):
2478        path = str(self.parent.resolve() / self.name)
2479        if path in self._symlinks:
2480            return self.with_segments(self._symlinks[path])
2481        elif path in self._files or path in self._directories:
2482            raise OSError(errno.EINVAL, "Not a symlink", path)
2483        else:
2484            raise FileNotFoundError(errno.ENOENT, "File not found", path)
2485
2486    def symlink_to(self, target, target_is_directory=False):
2487        self._directories[str(self.parent)].add(self.name)
2488        self._symlinks[str(self)] = str(target)
2489
2490
2491class DummyPathWithSymlinksTest(DummyPathTest):
2492    cls = DummyPathWithSymlinks
2493    can_symlink = True
2494
2495
2496if __name__ == "__main__":
2497    unittest.main()
2498