blob: fd9cf232a1d967993102e5a0857d3cf1ab53a5a9 [file] [log] [blame]
Antoine Pitrou31119e42013-11-22 17:38:12 +01001import collections
2import io
3import os
4import errno
5import pathlib
6import pickle
7import shutil
8import socket
9import stat
10import sys
11import tempfile
12import unittest
13from contextlib import contextmanager
14
15from test import support
16TESTFN = support.TESTFN
17
18try:
19 import grp, pwd
20except ImportError:
21 grp = pwd = None
22
23
24class _BaseFlavourTest(object):
25
26 def _check_parse_parts(self, arg, expected):
27 f = self.flavour.parse_parts
28 sep = self.flavour.sep
29 altsep = self.flavour.altsep
30 actual = f([x.replace('/', sep) for x in arg])
31 self.assertEqual(actual, expected)
32 if altsep:
33 actual = f([x.replace('/', altsep) for x in arg])
34 self.assertEqual(actual, expected)
35
36 def test_parse_parts_common(self):
37 check = self._check_parse_parts
38 sep = self.flavour.sep
39 # Unanchored parts
40 check([], ('', '', []))
41 check(['a'], ('', '', ['a']))
42 check(['a/'], ('', '', ['a']))
43 check(['a', 'b'], ('', '', ['a', 'b']))
44 # Expansion
45 check(['a/b'], ('', '', ['a', 'b']))
46 check(['a/b/'], ('', '', ['a', 'b']))
47 check(['a', 'b/c', 'd'], ('', '', ['a', 'b', 'c', 'd']))
48 # Collapsing and stripping excess slashes
49 check(['a', 'b//c', 'd'], ('', '', ['a', 'b', 'c', 'd']))
50 check(['a', 'b/c/', 'd'], ('', '', ['a', 'b', 'c', 'd']))
51 # Eliminating standalone dots
52 check(['.'], ('', '', []))
53 check(['.', '.', 'b'], ('', '', ['b']))
54 check(['a', '.', 'b'], ('', '', ['a', 'b']))
55 check(['a', '.', '.'], ('', '', ['a']))
56 # The first part is anchored
57 check(['/a/b'], ('', sep, [sep, 'a', 'b']))
58 check(['/a', 'b'], ('', sep, [sep, 'a', 'b']))
59 check(['/a/', 'b'], ('', sep, [sep, 'a', 'b']))
60 # Ignoring parts before an anchored part
61 check(['a', '/b', 'c'], ('', sep, [sep, 'b', 'c']))
62 check(['a', '/b', '/c'], ('', sep, [sep, 'c']))
63
64
65class PosixFlavourTest(_BaseFlavourTest, unittest.TestCase):
66 flavour = pathlib._posix_flavour
67
68 def test_parse_parts(self):
69 check = self._check_parse_parts
70 # Collapsing of excess leading slashes, except for the double-slash
71 # special case.
72 check(['//a', 'b'], ('', '//', ['//', 'a', 'b']))
73 check(['///a', 'b'], ('', '/', ['/', 'a', 'b']))
74 check(['////a', 'b'], ('', '/', ['/', 'a', 'b']))
75 # Paths which look like NT paths aren't treated specially
76 check(['c:a'], ('', '', ['c:a']))
77 check(['c:\\a'], ('', '', ['c:\\a']))
78 check(['\\a'], ('', '', ['\\a']))
79
80 def test_splitroot(self):
81 f = self.flavour.splitroot
82 self.assertEqual(f(''), ('', '', ''))
83 self.assertEqual(f('a'), ('', '', 'a'))
84 self.assertEqual(f('a/b'), ('', '', 'a/b'))
85 self.assertEqual(f('a/b/'), ('', '', 'a/b/'))
86 self.assertEqual(f('/a'), ('', '/', 'a'))
87 self.assertEqual(f('/a/b'), ('', '/', 'a/b'))
88 self.assertEqual(f('/a/b/'), ('', '/', 'a/b/'))
89 # The root is collapsed when there are redundant slashes
90 # except when there are exactly two leading slashes, which
91 # is a special case in POSIX.
92 self.assertEqual(f('//a'), ('', '//', 'a'))
93 self.assertEqual(f('///a'), ('', '/', 'a'))
94 self.assertEqual(f('///a/b'), ('', '/', 'a/b'))
95 # Paths which look like NT paths aren't treated specially
96 self.assertEqual(f('c:/a/b'), ('', '', 'c:/a/b'))
97 self.assertEqual(f('\\/a/b'), ('', '', '\\/a/b'))
98 self.assertEqual(f('\\a\\b'), ('', '', '\\a\\b'))
99
100
101class NTFlavourTest(_BaseFlavourTest, unittest.TestCase):
102 flavour = pathlib._windows_flavour
103
104 def test_parse_parts(self):
105 check = self._check_parse_parts
106 # First part is anchored
107 check(['c:'], ('c:', '', ['c:']))
108 check(['c:\\'], ('c:', '\\', ['c:\\']))
109 check(['\\'], ('', '\\', ['\\']))
110 check(['c:a'], ('c:', '', ['c:', 'a']))
111 check(['c:\\a'], ('c:', '\\', ['c:\\', 'a']))
112 check(['\\a'], ('', '\\', ['\\', 'a']))
113 # UNC paths
114 check(['\\\\a\\b'], ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
115 check(['\\\\a\\b\\'], ('\\\\a\\b', '\\', ['\\\\a\\b\\']))
116 check(['\\\\a\\b\\c'], ('\\\\a\\b', '\\', ['\\\\a\\b\\', 'c']))
117 # Second part is anchored, so that the first part is ignored
118 check(['a', 'Z:b', 'c'], ('Z:', '', ['Z:', 'b', 'c']))
119 check(['a', 'Z:\\b', 'c'], ('Z:', '\\', ['Z:\\', 'b', 'c']))
120 check(['a', '\\b', 'c'], ('', '\\', ['\\', 'b', 'c']))
121 # UNC paths
122 check(['a', '\\\\b\\c', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
123 # Collapsing and stripping excess slashes
124 check(['a', 'Z:\\\\b\\\\c\\', 'd\\'], ('Z:', '\\', ['Z:\\', 'b', 'c', 'd']))
125 # UNC paths
126 check(['a', '\\\\b\\c\\\\', 'd'], ('\\\\b\\c', '\\', ['\\\\b\\c\\', 'd']))
127 # Extended paths
128 check(['\\\\?\\c:\\'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\']))
129 check(['\\\\?\\c:\\a'], ('\\\\?\\c:', '\\', ['\\\\?\\c:\\', 'a']))
130 # Extended UNC paths (format is "\\?\UNC\server\share")
131 check(['\\\\?\\UNC\\b\\c'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\']))
132 check(['\\\\?\\UNC\\b\\c\\d'], ('\\\\?\\UNC\\b\\c', '\\', ['\\\\?\\UNC\\b\\c\\', 'd']))
133
134 def test_splitroot(self):
135 f = self.flavour.splitroot
136 self.assertEqual(f(''), ('', '', ''))
137 self.assertEqual(f('a'), ('', '', 'a'))
138 self.assertEqual(f('a\\b'), ('', '', 'a\\b'))
139 self.assertEqual(f('\\a'), ('', '\\', 'a'))
140 self.assertEqual(f('\\a\\b'), ('', '\\', 'a\\b'))
141 self.assertEqual(f('c:a\\b'), ('c:', '', 'a\\b'))
142 self.assertEqual(f('c:\\a\\b'), ('c:', '\\', 'a\\b'))
143 # Redundant slashes in the root are collapsed
144 self.assertEqual(f('\\\\a'), ('', '\\', 'a'))
145 self.assertEqual(f('\\\\\\a/b'), ('', '\\', 'a/b'))
146 self.assertEqual(f('c:\\\\a'), ('c:', '\\', 'a'))
147 self.assertEqual(f('c:\\\\\\a/b'), ('c:', '\\', 'a/b'))
148 # Valid UNC paths
149 self.assertEqual(f('\\\\a\\b'), ('\\\\a\\b', '\\', ''))
150 self.assertEqual(f('\\\\a\\b\\'), ('\\\\a\\b', '\\', ''))
151 self.assertEqual(f('\\\\a\\b\\c\\d'), ('\\\\a\\b', '\\', 'c\\d'))
152 # These are non-UNC paths (according to ntpath.py and test_ntpath)
153 # However, command.com says such paths are invalid, so it's
154 # difficult to know what the right semantics are
155 self.assertEqual(f('\\\\\\a\\b'), ('', '\\', 'a\\b'))
156 self.assertEqual(f('\\\\a'), ('', '\\', 'a'))
157
158
159#
160# Tests for the pure classes
161#
162
163class _BasePurePathTest(object):
164
165 # keys are canonical paths, values are list of tuples of arguments
166 # supposed to produce equal paths
167 equivalences = {
168 'a/b': [
169 ('a', 'b'), ('a/', 'b'), ('a', 'b/'), ('a/', 'b/'),
170 ('a/b/',), ('a//b',), ('a//b//',),
171 # empty components get removed
172 ('', 'a', 'b'), ('a', '', 'b'), ('a', 'b', ''),
173 ],
174 '/b/c/d': [
175 ('a', '/b/c', 'd'), ('a', '///b//c', 'd/'),
176 ('/a', '/b/c', 'd'),
177 # empty components get removed
178 ('/', 'b', '', 'c/d'), ('/', '', 'b/c/d'), ('', '/b/c/d'),
179 ],
180 }
181
182 def setUp(self):
183 p = self.cls('a')
184 self.flavour = p._flavour
185 self.sep = self.flavour.sep
186 self.altsep = self.flavour.altsep
187
188 def test_constructor_common(self):
189 P = self.cls
190 p = P('a')
191 self.assertIsInstance(p, P)
192 P('a', 'b', 'c')
193 P('/a', 'b', 'c')
194 P('a/b/c')
195 P('/a/b/c')
196 self.assertEqual(P(P('a')), P('a'))
197 self.assertEqual(P(P('a'), 'b'), P('a/b'))
198 self.assertEqual(P(P('a'), P('b')), P('a/b'))
199
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200200 def _check_str_subclass(self, *args):
201 # Issue #21127: it should be possible to construct a PurePath object
202 # from an str subclass instance, and it then gets converted to
203 # a pure str object.
204 class StrSubclass(str):
205 pass
206 P = self.cls
207 p = P(*(StrSubclass(x) for x in args))
208 self.assertEqual(p, P(*args))
209 for part in p.parts:
210 self.assertIs(type(part), str)
211
212 def test_str_subclass_common(self):
213 self._check_str_subclass('')
214 self._check_str_subclass('.')
215 self._check_str_subclass('a')
216 self._check_str_subclass('a/b.txt')
217 self._check_str_subclass('/a/b.txt')
218
Antoine Pitrou31119e42013-11-22 17:38:12 +0100219 def test_join_common(self):
220 P = self.cls
221 p = P('a/b')
222 pp = p.joinpath('c')
223 self.assertEqual(pp, P('a/b/c'))
224 self.assertIs(type(pp), type(p))
225 pp = p.joinpath('c', 'd')
226 self.assertEqual(pp, P('a/b/c/d'))
227 pp = p.joinpath(P('c'))
228 self.assertEqual(pp, P('a/b/c'))
229 pp = p.joinpath('/c')
230 self.assertEqual(pp, P('/c'))
231
232 def test_div_common(self):
233 # Basically the same as joinpath()
234 P = self.cls
235 p = P('a/b')
236 pp = p / 'c'
237 self.assertEqual(pp, P('a/b/c'))
238 self.assertIs(type(pp), type(p))
239 pp = p / 'c/d'
240 self.assertEqual(pp, P('a/b/c/d'))
241 pp = p / 'c' / 'd'
242 self.assertEqual(pp, P('a/b/c/d'))
243 pp = 'c' / p / 'd'
244 self.assertEqual(pp, P('c/a/b/d'))
245 pp = p / P('c')
246 self.assertEqual(pp, P('a/b/c'))
247 pp = p/ '/c'
248 self.assertEqual(pp, P('/c'))
249
250 def _check_str(self, expected, args):
251 p = self.cls(*args)
252 self.assertEqual(str(p), expected.replace('/', self.sep))
253
254 def test_str_common(self):
255 # Canonicalized paths roundtrip
256 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
257 self._check_str(pathstr, (pathstr,))
258 # Special case for the empty path
259 self._check_str('.', ('',))
260 # Other tests for str() are in test_equivalences()
261
262 def test_as_posix_common(self):
263 P = self.cls
264 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
265 self.assertEqual(P(pathstr).as_posix(), pathstr)
266 # Other tests for as_posix() are in test_equivalences()
267
268 def test_as_bytes_common(self):
269 sep = os.fsencode(self.sep)
270 P = self.cls
271 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b')
272
273 def test_as_uri_common(self):
274 P = self.cls
275 with self.assertRaises(ValueError):
276 P('a').as_uri()
277 with self.assertRaises(ValueError):
278 P().as_uri()
279
280 def test_repr_common(self):
281 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
282 p = self.cls(pathstr)
283 clsname = p.__class__.__name__
284 r = repr(p)
285 # The repr() is in the form ClassName("forward-slashes path")
286 self.assertTrue(r.startswith(clsname + '('), r)
287 self.assertTrue(r.endswith(')'), r)
288 inner = r[len(clsname) + 1 : -1]
289 self.assertEqual(eval(inner), p.as_posix())
290 # The repr() roundtrips
291 q = eval(r, pathlib.__dict__)
292 self.assertIs(q.__class__, p.__class__)
293 self.assertEqual(q, p)
294 self.assertEqual(repr(q), r)
295
296 def test_eq_common(self):
297 P = self.cls
298 self.assertEqual(P('a/b'), P('a/b'))
299 self.assertEqual(P('a/b'), P('a', 'b'))
300 self.assertNotEqual(P('a/b'), P('a'))
301 self.assertNotEqual(P('a/b'), P('/a/b'))
302 self.assertNotEqual(P('a/b'), P())
303 self.assertNotEqual(P('/a/b'), P('/'))
304 self.assertNotEqual(P(), P('/'))
305 self.assertNotEqual(P(), "")
306 self.assertNotEqual(P(), {})
307 self.assertNotEqual(P(), int)
308
309 def test_match_common(self):
310 P = self.cls
311 self.assertRaises(ValueError, P('a').match, '')
312 self.assertRaises(ValueError, P('a').match, '.')
313 # Simple relative pattern
314 self.assertTrue(P('b.py').match('b.py'))
315 self.assertTrue(P('a/b.py').match('b.py'))
316 self.assertTrue(P('/a/b.py').match('b.py'))
317 self.assertFalse(P('a.py').match('b.py'))
318 self.assertFalse(P('b/py').match('b.py'))
319 self.assertFalse(P('/a.py').match('b.py'))
320 self.assertFalse(P('b.py/c').match('b.py'))
321 # Wilcard relative pattern
322 self.assertTrue(P('b.py').match('*.py'))
323 self.assertTrue(P('a/b.py').match('*.py'))
324 self.assertTrue(P('/a/b.py').match('*.py'))
325 self.assertFalse(P('b.pyc').match('*.py'))
326 self.assertFalse(P('b./py').match('*.py'))
327 self.assertFalse(P('b.py/c').match('*.py'))
328 # Multi-part relative pattern
329 self.assertTrue(P('ab/c.py').match('a*/*.py'))
330 self.assertTrue(P('/d/ab/c.py').match('a*/*.py'))
331 self.assertFalse(P('a.py').match('a*/*.py'))
332 self.assertFalse(P('/dab/c.py').match('a*/*.py'))
333 self.assertFalse(P('ab/c.py/d').match('a*/*.py'))
334 # Absolute pattern
335 self.assertTrue(P('/b.py').match('/*.py'))
336 self.assertFalse(P('b.py').match('/*.py'))
337 self.assertFalse(P('a/b.py').match('/*.py'))
338 self.assertFalse(P('/a/b.py').match('/*.py'))
339 # Multi-part absolute pattern
340 self.assertTrue(P('/a/b.py').match('/a/*.py'))
341 self.assertFalse(P('/ab.py').match('/a/*.py'))
342 self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
343
344 def test_ordering_common(self):
345 # Ordering is tuple-alike
346 def assertLess(a, b):
347 self.assertLess(a, b)
348 self.assertGreater(b, a)
349 P = self.cls
350 a = P('a')
351 b = P('a/b')
352 c = P('abc')
353 d = P('b')
354 assertLess(a, b)
355 assertLess(a, c)
356 assertLess(a, d)
357 assertLess(b, c)
358 assertLess(c, d)
359 P = self.cls
360 a = P('/a')
361 b = P('/a/b')
362 c = P('/abc')
363 d = P('/b')
364 assertLess(a, b)
365 assertLess(a, c)
366 assertLess(a, d)
367 assertLess(b, c)
368 assertLess(c, d)
369 with self.assertRaises(TypeError):
370 P() < {}
371
372 def test_parts_common(self):
373 # `parts` returns a tuple
374 sep = self.sep
375 P = self.cls
376 p = P('a/b')
377 parts = p.parts
378 self.assertEqual(parts, ('a', 'b'))
379 # The object gets reused
380 self.assertIs(parts, p.parts)
381 # When the path is absolute, the anchor is a separate part
382 p = P('/a/b')
383 parts = p.parts
384 self.assertEqual(parts, (sep, 'a', 'b'))
385
386 def test_equivalences(self):
387 for k, tuples in self.equivalences.items():
388 canon = k.replace('/', self.sep)
389 posix = k.replace(self.sep, '/')
390 if canon != posix:
391 tuples = tuples + [
392 tuple(part.replace('/', self.sep) for part in t)
393 for t in tuples
394 ]
395 tuples.append((posix, ))
396 pcanon = self.cls(canon)
397 for t in tuples:
398 p = self.cls(*t)
399 self.assertEqual(p, pcanon, "failed with args {}".format(t))
400 self.assertEqual(hash(p), hash(pcanon))
401 self.assertEqual(str(p), canon)
402 self.assertEqual(p.as_posix(), posix)
403
404 def test_parent_common(self):
405 # Relative
406 P = self.cls
407 p = P('a/b/c')
408 self.assertEqual(p.parent, P('a/b'))
409 self.assertEqual(p.parent.parent, P('a'))
410 self.assertEqual(p.parent.parent.parent, P())
411 self.assertEqual(p.parent.parent.parent.parent, P())
412 # Anchored
413 p = P('/a/b/c')
414 self.assertEqual(p.parent, P('/a/b'))
415 self.assertEqual(p.parent.parent, P('/a'))
416 self.assertEqual(p.parent.parent.parent, P('/'))
417 self.assertEqual(p.parent.parent.parent.parent, P('/'))
418
419 def test_parents_common(self):
420 # Relative
421 P = self.cls
422 p = P('a/b/c')
423 par = p.parents
424 self.assertEqual(len(par), 3)
425 self.assertEqual(par[0], P('a/b'))
426 self.assertEqual(par[1], P('a'))
427 self.assertEqual(par[2], P('.'))
428 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')])
429 with self.assertRaises(IndexError):
430 par[-1]
431 with self.assertRaises(IndexError):
432 par[3]
433 with self.assertRaises(TypeError):
434 par[0] = p
435 # Anchored
436 p = P('/a/b/c')
437 par = p.parents
438 self.assertEqual(len(par), 3)
439 self.assertEqual(par[0], P('/a/b'))
440 self.assertEqual(par[1], P('/a'))
441 self.assertEqual(par[2], P('/'))
442 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')])
443 with self.assertRaises(IndexError):
444 par[3]
445
446 def test_drive_common(self):
447 P = self.cls
448 self.assertEqual(P('a/b').drive, '')
449 self.assertEqual(P('/a/b').drive, '')
450 self.assertEqual(P('').drive, '')
451
452 def test_root_common(self):
453 P = self.cls
454 sep = self.sep
455 self.assertEqual(P('').root, '')
456 self.assertEqual(P('a/b').root, '')
457 self.assertEqual(P('/').root, sep)
458 self.assertEqual(P('/a/b').root, sep)
459
460 def test_anchor_common(self):
461 P = self.cls
462 sep = self.sep
463 self.assertEqual(P('').anchor, '')
464 self.assertEqual(P('a/b').anchor, '')
465 self.assertEqual(P('/').anchor, sep)
466 self.assertEqual(P('/a/b').anchor, sep)
467
468 def test_name_common(self):
469 P = self.cls
470 self.assertEqual(P('').name, '')
471 self.assertEqual(P('.').name, '')
472 self.assertEqual(P('/').name, '')
473 self.assertEqual(P('a/b').name, 'b')
474 self.assertEqual(P('/a/b').name, 'b')
475 self.assertEqual(P('/a/b/.').name, 'b')
476 self.assertEqual(P('a/b.py').name, 'b.py')
477 self.assertEqual(P('/a/b.py').name, 'b.py')
478
479 def test_suffix_common(self):
480 P = self.cls
481 self.assertEqual(P('').suffix, '')
482 self.assertEqual(P('.').suffix, '')
483 self.assertEqual(P('..').suffix, '')
484 self.assertEqual(P('/').suffix, '')
485 self.assertEqual(P('a/b').suffix, '')
486 self.assertEqual(P('/a/b').suffix, '')
487 self.assertEqual(P('/a/b/.').suffix, '')
488 self.assertEqual(P('a/b.py').suffix, '.py')
489 self.assertEqual(P('/a/b.py').suffix, '.py')
490 self.assertEqual(P('a/.hgrc').suffix, '')
491 self.assertEqual(P('/a/.hgrc').suffix, '')
492 self.assertEqual(P('a/.hg.rc').suffix, '.rc')
493 self.assertEqual(P('/a/.hg.rc').suffix, '.rc')
494 self.assertEqual(P('a/b.tar.gz').suffix, '.gz')
495 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz')
496 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '')
497 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '')
498
499 def test_suffixes_common(self):
500 P = self.cls
501 self.assertEqual(P('').suffixes, [])
502 self.assertEqual(P('.').suffixes, [])
503 self.assertEqual(P('/').suffixes, [])
504 self.assertEqual(P('a/b').suffixes, [])
505 self.assertEqual(P('/a/b').suffixes, [])
506 self.assertEqual(P('/a/b/.').suffixes, [])
507 self.assertEqual(P('a/b.py').suffixes, ['.py'])
508 self.assertEqual(P('/a/b.py').suffixes, ['.py'])
509 self.assertEqual(P('a/.hgrc').suffixes, [])
510 self.assertEqual(P('/a/.hgrc').suffixes, [])
511 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc'])
512 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc'])
513 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz'])
514 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz'])
515 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, [])
516 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, [])
517
518 def test_stem_common(self):
519 P = self.cls
520 self.assertEqual(P('').stem, '')
521 self.assertEqual(P('.').stem, '')
522 self.assertEqual(P('..').stem, '..')
523 self.assertEqual(P('/').stem, '')
524 self.assertEqual(P('a/b').stem, 'b')
525 self.assertEqual(P('a/b.py').stem, 'b')
526 self.assertEqual(P('a/.hgrc').stem, '.hgrc')
527 self.assertEqual(P('a/.hg.rc').stem, '.hg')
528 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar')
529 self.assertEqual(P('a/Some name. Ending with a dot.').stem,
530 'Some name. Ending with a dot.')
531
532 def test_with_name_common(self):
533 P = self.cls
534 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml'))
535 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml'))
536 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml'))
537 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml'))
538 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml'))
539 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml'))
540 self.assertRaises(ValueError, P('').with_name, 'd.xml')
541 self.assertRaises(ValueError, P('.').with_name, 'd.xml')
542 self.assertRaises(ValueError, P('/').with_name, 'd.xml')
543
544 def test_with_suffix_common(self):
545 P = self.cls
546 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
547 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
548 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
549 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100550 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100551 self.assertRaises(ValueError, P('').with_suffix, '.gz')
552 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
553 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100554 # Invalid suffix
555 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
556 self.assertRaises(ValueError, P('a/b').with_suffix, '/')
557 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
558 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
559 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100560
561 def test_relative_to_common(self):
562 P = self.cls
563 p = P('a/b')
564 self.assertRaises(TypeError, p.relative_to)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100565 self.assertRaises(TypeError, p.relative_to, b'a')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100566 self.assertEqual(p.relative_to(P()), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100567 self.assertEqual(p.relative_to(''), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100568 self.assertEqual(p.relative_to(P('a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100569 self.assertEqual(p.relative_to('a'), P('b'))
570 self.assertEqual(p.relative_to('a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100571 self.assertEqual(p.relative_to(P('a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100572 self.assertEqual(p.relative_to('a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100573 # With several args
574 self.assertEqual(p.relative_to('a', 'b'), P())
575 # Unrelated paths
576 self.assertRaises(ValueError, p.relative_to, P('c'))
577 self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
578 self.assertRaises(ValueError, p.relative_to, P('a/c'))
579 self.assertRaises(ValueError, p.relative_to, P('/a'))
580 p = P('/a/b')
581 self.assertEqual(p.relative_to(P('/')), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100582 self.assertEqual(p.relative_to('/'), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100583 self.assertEqual(p.relative_to(P('/a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100584 self.assertEqual(p.relative_to('/a'), P('b'))
585 self.assertEqual(p.relative_to('/a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100586 self.assertEqual(p.relative_to(P('/a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100587 self.assertEqual(p.relative_to('/a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100588 # Unrelated paths
589 self.assertRaises(ValueError, p.relative_to, P('/c'))
590 self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
591 self.assertRaises(ValueError, p.relative_to, P('/a/c'))
592 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100593 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100594 self.assertRaises(ValueError, p.relative_to, P('a'))
595
596 def test_pickling_common(self):
597 P = self.cls
598 p = P('/a/b')
599 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
600 dumped = pickle.dumps(p, proto)
601 pp = pickle.loads(dumped)
602 self.assertIs(pp.__class__, p.__class__)
603 self.assertEqual(pp, p)
604 self.assertEqual(hash(pp), hash(p))
605 self.assertEqual(str(pp), str(p))
606
607
608class PurePosixPathTest(_BasePurePathTest, unittest.TestCase):
609 cls = pathlib.PurePosixPath
610
611 def test_root(self):
612 P = self.cls
613 self.assertEqual(P('/a/b').root, '/')
614 self.assertEqual(P('///a/b').root, '/')
615 # POSIX special case for two leading slashes
616 self.assertEqual(P('//a/b').root, '//')
617
618 def test_eq(self):
619 P = self.cls
620 self.assertNotEqual(P('a/b'), P('A/b'))
621 self.assertEqual(P('/a'), P('///a'))
622 self.assertNotEqual(P('/a'), P('//a'))
623
624 def test_as_uri(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100625 P = self.cls
626 self.assertEqual(P('/').as_uri(), 'file:///')
627 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c')
628 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c')
Antoine Pitrou29eac422013-11-22 17:57:03 +0100629
630 def test_as_uri_non_ascii(self):
631 from urllib.parse import quote_from_bytes
632 P = self.cls
633 try:
634 os.fsencode('\xe9')
635 except UnicodeEncodeError:
636 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100637 self.assertEqual(P('/a/b\xe9').as_uri(),
638 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9')))
639
640 def test_match(self):
641 P = self.cls
642 self.assertFalse(P('A.py').match('a.PY'))
643
644 def test_is_absolute(self):
645 P = self.cls
646 self.assertFalse(P().is_absolute())
647 self.assertFalse(P('a').is_absolute())
648 self.assertFalse(P('a/b/').is_absolute())
649 self.assertTrue(P('/').is_absolute())
650 self.assertTrue(P('/a').is_absolute())
651 self.assertTrue(P('/a/b/').is_absolute())
652 self.assertTrue(P('//a').is_absolute())
653 self.assertTrue(P('//a/b').is_absolute())
654
655 def test_is_reserved(self):
656 P = self.cls
657 self.assertIs(False, P('').is_reserved())
658 self.assertIs(False, P('/').is_reserved())
659 self.assertIs(False, P('/foo/bar').is_reserved())
660 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved())
661
662 def test_join(self):
663 P = self.cls
664 p = P('//a')
665 pp = p.joinpath('b')
666 self.assertEqual(pp, P('//a/b'))
667 pp = P('/a').joinpath('//c')
668 self.assertEqual(pp, P('//c'))
669 pp = P('//a').joinpath('/c')
670 self.assertEqual(pp, P('/c'))
671
672 def test_div(self):
673 # Basically the same as joinpath()
674 P = self.cls
675 p = P('//a')
676 pp = p / 'b'
677 self.assertEqual(pp, P('//a/b'))
678 pp = P('/a') / '//c'
679 self.assertEqual(pp, P('//c'))
680 pp = P('//a') / '/c'
681 self.assertEqual(pp, P('/c'))
682
683
684class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
685 cls = pathlib.PureWindowsPath
686
687 equivalences = _BasePurePathTest.equivalences.copy()
688 equivalences.update({
689 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ],
690 'c:/a': [
691 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'),
692 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'),
693 ],
694 '//a/b/': [ ('//a/b',) ],
695 '//a/b/c': [
696 ('//a/b', 'c'), ('//a/b/', 'c'),
697 ],
698 })
699
700 def test_str(self):
701 p = self.cls('a/b/c')
702 self.assertEqual(str(p), 'a\\b\\c')
703 p = self.cls('c:/a/b/c')
704 self.assertEqual(str(p), 'c:\\a\\b\\c')
705 p = self.cls('//a/b')
706 self.assertEqual(str(p), '\\\\a\\b\\')
707 p = self.cls('//a/b/c')
708 self.assertEqual(str(p), '\\\\a\\b\\c')
709 p = self.cls('//a/b/c/d')
710 self.assertEqual(str(p), '\\\\a\\b\\c\\d')
711
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200712 def test_str_subclass(self):
713 self._check_str_subclass('c:')
714 self._check_str_subclass('c:a')
715 self._check_str_subclass('c:a\\b.txt')
716 self._check_str_subclass('c:\\')
717 self._check_str_subclass('c:\\a')
718 self._check_str_subclass('c:\\a\\b.txt')
719 self._check_str_subclass('\\\\some\\share')
720 self._check_str_subclass('\\\\some\\share\\a')
721 self._check_str_subclass('\\\\some\\share\\a\\b.txt')
722
Antoine Pitrou31119e42013-11-22 17:38:12 +0100723 def test_eq(self):
724 P = self.cls
725 self.assertEqual(P('c:a/b'), P('c:a/b'))
726 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b'))
727 self.assertNotEqual(P('c:a/b'), P('d:a/b'))
728 self.assertNotEqual(P('c:a/b'), P('c:/a/b'))
729 self.assertNotEqual(P('/a/b'), P('c:/a/b'))
730 # Case-insensitivity
731 self.assertEqual(P('a/B'), P('A/b'))
732 self.assertEqual(P('C:a/B'), P('c:A/b'))
733 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b'))
734
735 def test_as_uri(self):
736 from urllib.parse import quote_from_bytes
737 P = self.cls
738 with self.assertRaises(ValueError):
739 P('/a/b').as_uri()
740 with self.assertRaises(ValueError):
741 P('c:a/b').as_uri()
742 self.assertEqual(P('c:/').as_uri(), 'file:///c:/')
743 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c')
744 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c')
745 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9')
746 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/')
747 self.assertEqual(P('//some/share/a/b.c').as_uri(),
748 'file://some/share/a/b.c')
749 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
750 'file://some/share/a/b%25%23c%C3%A9')
751
752 def test_match_common(self):
753 P = self.cls
754 # Absolute patterns
755 self.assertTrue(P('c:/b.py').match('/*.py'))
756 self.assertTrue(P('c:/b.py').match('c:*.py'))
757 self.assertTrue(P('c:/b.py').match('c:/*.py'))
758 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive
759 self.assertFalse(P('b.py').match('/*.py'))
760 self.assertFalse(P('b.py').match('c:*.py'))
761 self.assertFalse(P('b.py').match('c:/*.py'))
762 self.assertFalse(P('c:b.py').match('/*.py'))
763 self.assertFalse(P('c:b.py').match('c:/*.py'))
764 self.assertFalse(P('/b.py').match('c:*.py'))
765 self.assertFalse(P('/b.py').match('c:/*.py'))
766 # UNC patterns
767 self.assertTrue(P('//some/share/a.py').match('/*.py'))
768 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
769 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
770 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
771 # Case-insensitivity
772 self.assertTrue(P('B.py').match('b.PY'))
773 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
774 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
775
776 def test_ordering_common(self):
777 # Case-insensitivity
778 def assertOrderedEqual(a, b):
779 self.assertLessEqual(a, b)
780 self.assertGreaterEqual(b, a)
781 P = self.cls
782 p = P('c:A/b')
783 q = P('C:a/B')
784 assertOrderedEqual(p, q)
785 self.assertFalse(p < q)
786 self.assertFalse(p > q)
787 p = P('//some/Share/A/b')
788 q = P('//Some/SHARE/a/B')
789 assertOrderedEqual(p, q)
790 self.assertFalse(p < q)
791 self.assertFalse(p > q)
792
793 def test_parts(self):
794 P = self.cls
795 p = P('c:a/b')
796 parts = p.parts
797 self.assertEqual(parts, ('c:', 'a', 'b'))
798 p = P('c:/a/b')
799 parts = p.parts
800 self.assertEqual(parts, ('c:\\', 'a', 'b'))
801 p = P('//a/b/c/d')
802 parts = p.parts
803 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
804
805 def test_parent(self):
806 # Anchored
807 P = self.cls
808 p = P('z:a/b/c')
809 self.assertEqual(p.parent, P('z:a/b'))
810 self.assertEqual(p.parent.parent, P('z:a'))
811 self.assertEqual(p.parent.parent.parent, P('z:'))
812 self.assertEqual(p.parent.parent.parent.parent, P('z:'))
813 p = P('z:/a/b/c')
814 self.assertEqual(p.parent, P('z:/a/b'))
815 self.assertEqual(p.parent.parent, P('z:/a'))
816 self.assertEqual(p.parent.parent.parent, P('z:/'))
817 self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
818 p = P('//a/b/c/d')
819 self.assertEqual(p.parent, P('//a/b/c'))
820 self.assertEqual(p.parent.parent, P('//a/b'))
821 self.assertEqual(p.parent.parent.parent, P('//a/b'))
822
823 def test_parents(self):
824 # Anchored
825 P = self.cls
826 p = P('z:a/b/')
827 par = p.parents
828 self.assertEqual(len(par), 2)
829 self.assertEqual(par[0], P('z:a'))
830 self.assertEqual(par[1], P('z:'))
831 self.assertEqual(list(par), [P('z:a'), P('z:')])
832 with self.assertRaises(IndexError):
833 par[2]
834 p = P('z:/a/b/')
835 par = p.parents
836 self.assertEqual(len(par), 2)
837 self.assertEqual(par[0], P('z:/a'))
838 self.assertEqual(par[1], P('z:/'))
839 self.assertEqual(list(par), [P('z:/a'), P('z:/')])
840 with self.assertRaises(IndexError):
841 par[2]
842 p = P('//a/b/c/d')
843 par = p.parents
844 self.assertEqual(len(par), 2)
845 self.assertEqual(par[0], P('//a/b/c'))
846 self.assertEqual(par[1], P('//a/b'))
847 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
848 with self.assertRaises(IndexError):
849 par[2]
850
851 def test_drive(self):
852 P = self.cls
853 self.assertEqual(P('c:').drive, 'c:')
854 self.assertEqual(P('c:a/b').drive, 'c:')
855 self.assertEqual(P('c:/').drive, 'c:')
856 self.assertEqual(P('c:/a/b/').drive, 'c:')
857 self.assertEqual(P('//a/b').drive, '\\\\a\\b')
858 self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
859 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
860
861 def test_root(self):
862 P = self.cls
863 self.assertEqual(P('c:').root, '')
864 self.assertEqual(P('c:a/b').root, '')
865 self.assertEqual(P('c:/').root, '\\')
866 self.assertEqual(P('c:/a/b/').root, '\\')
867 self.assertEqual(P('//a/b').root, '\\')
868 self.assertEqual(P('//a/b/').root, '\\')
869 self.assertEqual(P('//a/b/c/d').root, '\\')
870
871 def test_anchor(self):
872 P = self.cls
873 self.assertEqual(P('c:').anchor, 'c:')
874 self.assertEqual(P('c:a/b').anchor, 'c:')
875 self.assertEqual(P('c:/').anchor, 'c:\\')
876 self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
877 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
878 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
879 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
880
881 def test_name(self):
882 P = self.cls
883 self.assertEqual(P('c:').name, '')
884 self.assertEqual(P('c:/').name, '')
885 self.assertEqual(P('c:a/b').name, 'b')
886 self.assertEqual(P('c:/a/b').name, 'b')
887 self.assertEqual(P('c:a/b.py').name, 'b.py')
888 self.assertEqual(P('c:/a/b.py').name, 'b.py')
889 self.assertEqual(P('//My.py/Share.php').name, '')
890 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
891
892 def test_suffix(self):
893 P = self.cls
894 self.assertEqual(P('c:').suffix, '')
895 self.assertEqual(P('c:/').suffix, '')
896 self.assertEqual(P('c:a/b').suffix, '')
897 self.assertEqual(P('c:/a/b').suffix, '')
898 self.assertEqual(P('c:a/b.py').suffix, '.py')
899 self.assertEqual(P('c:/a/b.py').suffix, '.py')
900 self.assertEqual(P('c:a/.hgrc').suffix, '')
901 self.assertEqual(P('c:/a/.hgrc').suffix, '')
902 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
903 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
904 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
905 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
906 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
907 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
908 self.assertEqual(P('//My.py/Share.php').suffix, '')
909 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
910
911 def test_suffixes(self):
912 P = self.cls
913 self.assertEqual(P('c:').suffixes, [])
914 self.assertEqual(P('c:/').suffixes, [])
915 self.assertEqual(P('c:a/b').suffixes, [])
916 self.assertEqual(P('c:/a/b').suffixes, [])
917 self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
918 self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
919 self.assertEqual(P('c:a/.hgrc').suffixes, [])
920 self.assertEqual(P('c:/a/.hgrc').suffixes, [])
921 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
922 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
923 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
924 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
925 self.assertEqual(P('//My.py/Share.php').suffixes, [])
926 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
927 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
928 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
929
930 def test_stem(self):
931 P = self.cls
932 self.assertEqual(P('c:').stem, '')
933 self.assertEqual(P('c:.').stem, '')
934 self.assertEqual(P('c:..').stem, '..')
935 self.assertEqual(P('c:/').stem, '')
936 self.assertEqual(P('c:a/b').stem, 'b')
937 self.assertEqual(P('c:a/b.py').stem, 'b')
938 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
939 self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
940 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
941 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
942 'Some name. Ending with a dot.')
943
944 def test_with_name(self):
945 P = self.cls
946 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
947 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
948 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
949 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
950 self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
951 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
952 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
953
954 def test_with_suffix(self):
955 P = self.cls
956 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
957 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
958 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
959 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100960 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100961 self.assertRaises(ValueError, P('').with_suffix, '.gz')
962 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
963 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
964 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100965 # Invalid suffix
966 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
967 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
968 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
969 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
970 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
971 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
972 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
973 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
974 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
975 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
976 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100977
978 def test_relative_to(self):
979 P = self.cls
Antoine Pitrou156b3612013-12-28 19:49:04 +0100980 p = P('C:Foo/Bar')
981 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
982 self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
983 self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
984 self.assertEqual(p.relative_to('c:foO'), P('Bar'))
985 self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
986 self.assertEqual(p.relative_to(P('c:foO/baR')), P())
987 self.assertEqual(p.relative_to('c:foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100988 # Unrelated paths
989 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100990 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100991 self.assertRaises(ValueError, p.relative_to, P('d:'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100992 self.assertRaises(ValueError, p.relative_to, P('/'))
993 self.assertRaises(ValueError, p.relative_to, P('Foo'))
994 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
995 self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
996 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
997 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
998 p = P('C:/Foo/Bar')
999 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar'))
1000 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar'))
1001 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar')
1002 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar')
1003 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
1004 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
1005 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
1006 self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
1007 self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
1008 self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
1009 self.assertEqual(p.relative_to('c:/foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001010 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001011 self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
1012 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
1013 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
1014 self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001015 self.assertRaises(ValueError, p.relative_to, P('d:'))
1016 self.assertRaises(ValueError, p.relative_to, P('d:/'))
Antoine Pitrou156b3612013-12-28 19:49:04 +01001017 self.assertRaises(ValueError, p.relative_to, P('/'))
1018 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1019 self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001020 # UNC paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001021 p = P('//Server/Share/Foo/Bar')
1022 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
1023 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
1024 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
1025 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
1026 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
1027 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
1028 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
1029 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001030 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001031 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
1032 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
1033 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
1034 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001035
1036 def test_is_absolute(self):
1037 P = self.cls
1038 # Under NT, only paths with both a drive and a root are absolute
1039 self.assertFalse(P().is_absolute())
1040 self.assertFalse(P('a').is_absolute())
1041 self.assertFalse(P('a/b/').is_absolute())
1042 self.assertFalse(P('/').is_absolute())
1043 self.assertFalse(P('/a').is_absolute())
1044 self.assertFalse(P('/a/b/').is_absolute())
1045 self.assertFalse(P('c:').is_absolute())
1046 self.assertFalse(P('c:a').is_absolute())
1047 self.assertFalse(P('c:a/b/').is_absolute())
1048 self.assertTrue(P('c:/').is_absolute())
1049 self.assertTrue(P('c:/a').is_absolute())
1050 self.assertTrue(P('c:/a/b/').is_absolute())
1051 # UNC paths are absolute by definition
1052 self.assertTrue(P('//a/b').is_absolute())
1053 self.assertTrue(P('//a/b/').is_absolute())
1054 self.assertTrue(P('//a/b/c').is_absolute())
1055 self.assertTrue(P('//a/b/c/d').is_absolute())
1056
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001057 def test_join(self):
1058 P = self.cls
1059 p = P('C:/a/b')
1060 pp = p.joinpath('x/y')
1061 self.assertEqual(pp, P('C:/a/b/x/y'))
1062 pp = p.joinpath('/x/y')
1063 self.assertEqual(pp, P('C:/x/y'))
1064 # Joining with a different drive => the first path is ignored, even
1065 # if the second path is relative.
1066 pp = p.joinpath('D:x/y')
1067 self.assertEqual(pp, P('D:x/y'))
1068 pp = p.joinpath('D:/x/y')
1069 self.assertEqual(pp, P('D:/x/y'))
1070 pp = p.joinpath('//host/share/x/y')
1071 self.assertEqual(pp, P('//host/share/x/y'))
1072 # Joining with the same drive => the first path is appended to if
1073 # the second path is relative.
1074 pp = p.joinpath('c:x/y')
1075 self.assertEqual(pp, P('C:/a/b/x/y'))
1076 pp = p.joinpath('c:/x/y')
1077 self.assertEqual(pp, P('C:/x/y'))
1078
1079 def test_div(self):
1080 # Basically the same as joinpath()
1081 P = self.cls
1082 p = P('C:/a/b')
1083 self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
1084 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
1085 self.assertEqual(p / '/x/y', P('C:/x/y'))
1086 self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
1087 # Joining with a different drive => the first path is ignored, even
1088 # if the second path is relative.
1089 self.assertEqual(p / 'D:x/y', P('D:x/y'))
1090 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
1091 self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
1092 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
1093 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
1094 # Joining with the same drive => the first path is appended to if
1095 # the second path is relative.
Serhiy Storchaka010ff582013-12-06 17:25:51 +02001096 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
1097 self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001098
Antoine Pitrou31119e42013-11-22 17:38:12 +01001099 def test_is_reserved(self):
1100 P = self.cls
1101 self.assertIs(False, P('').is_reserved())
1102 self.assertIs(False, P('/').is_reserved())
1103 self.assertIs(False, P('/foo/bar').is_reserved())
1104 self.assertIs(True, P('con').is_reserved())
1105 self.assertIs(True, P('NUL').is_reserved())
1106 self.assertIs(True, P('NUL.txt').is_reserved())
1107 self.assertIs(True, P('com1').is_reserved())
1108 self.assertIs(True, P('com9.bar').is_reserved())
1109 self.assertIs(False, P('bar.com9').is_reserved())
1110 self.assertIs(True, P('lpt1').is_reserved())
1111 self.assertIs(True, P('lpt9.bar').is_reserved())
1112 self.assertIs(False, P('bar.lpt9').is_reserved())
1113 # Only the last component matters
1114 self.assertIs(False, P('c:/NUL/con/baz').is_reserved())
1115 # UNC paths are never reserved
1116 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved())
1117
1118
1119class PurePathTest(_BasePurePathTest, unittest.TestCase):
1120 cls = pathlib.PurePath
1121
1122 def test_concrete_class(self):
1123 p = self.cls('a')
1124 self.assertIs(type(p),
1125 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath)
1126
1127 def test_different_flavours_unequal(self):
1128 p = pathlib.PurePosixPath('a')
1129 q = pathlib.PureWindowsPath('a')
1130 self.assertNotEqual(p, q)
1131
1132 def test_different_flavours_unordered(self):
1133 p = pathlib.PurePosixPath('a')
1134 q = pathlib.PureWindowsPath('a')
1135 with self.assertRaises(TypeError):
1136 p < q
1137 with self.assertRaises(TypeError):
1138 p <= q
1139 with self.assertRaises(TypeError):
1140 p > q
1141 with self.assertRaises(TypeError):
1142 p >= q
1143
1144
1145#
1146# Tests for the concrete classes
1147#
1148
1149# Make sure any symbolic links in the base test path are resolved
1150BASE = os.path.realpath(TESTFN)
1151join = lambda *x: os.path.join(BASE, *x)
1152rel_join = lambda *x: os.path.join(TESTFN, *x)
1153
1154def symlink_skip_reason():
1155 if not pathlib.supports_symlinks:
1156 return "no system support for symlinks"
1157 try:
1158 os.symlink(__file__, BASE)
1159 except OSError as e:
1160 return str(e)
1161 else:
1162 support.unlink(BASE)
1163 return None
1164
1165symlink_skip_reason = symlink_skip_reason()
1166
1167only_nt = unittest.skipIf(os.name != 'nt',
1168 'test requires a Windows-compatible system')
1169only_posix = unittest.skipIf(os.name == 'nt',
1170 'test requires a POSIX-compatible system')
1171with_symlinks = unittest.skipIf(symlink_skip_reason, symlink_skip_reason)
1172
1173
1174@only_posix
1175class PosixPathAsPureTest(PurePosixPathTest):
1176 cls = pathlib.PosixPath
1177
1178@only_nt
1179class WindowsPathAsPureTest(PureWindowsPathTest):
1180 cls = pathlib.WindowsPath
1181
1182
1183class _BasePathTest(object):
1184 """Tests for the FS-accessing functionalities of the Path classes."""
1185
1186 # (BASE)
1187 # |
1188 # |-- dirA/
1189 # |-- linkC -> "../dirB"
1190 # |-- dirB/
1191 # | |-- fileB
1192 # |-- linkD -> "../dirB"
1193 # |-- dirC/
1194 # | |-- fileC
1195 # | |-- fileD
1196 # |-- fileA
1197 # |-- linkA -> "fileA"
1198 # |-- linkB -> "dirB"
1199 #
1200
1201 def setUp(self):
1202 os.mkdir(BASE)
1203 self.addCleanup(shutil.rmtree, BASE)
1204 os.mkdir(join('dirA'))
1205 os.mkdir(join('dirB'))
1206 os.mkdir(join('dirC'))
1207 os.mkdir(join('dirC', 'dirD'))
1208 with open(join('fileA'), 'wb') as f:
1209 f.write(b"this is file A\n")
1210 with open(join('dirB', 'fileB'), 'wb') as f:
1211 f.write(b"this is file B\n")
1212 with open(join('dirC', 'fileC'), 'wb') as f:
1213 f.write(b"this is file C\n")
1214 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
1215 f.write(b"this is file D\n")
1216 if not symlink_skip_reason:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001217 # Relative symlinks
1218 os.symlink('fileA', join('linkA'))
1219 os.symlink('non-existing', join('brokenLink'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001220 self.dirlink('dirB', join('linkB'))
1221 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001222 # This one goes upwards but doesn't create a loop
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001223 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1224
1225 if os.name == 'nt':
1226 # Workaround for http://bugs.python.org/issue13772
1227 def dirlink(self, src, dest):
1228 os.symlink(src, dest, target_is_directory=True)
1229 else:
1230 def dirlink(self, src, dest):
1231 os.symlink(src, dest)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001232
1233 def assertSame(self, path_a, path_b):
1234 self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1235 "%r and %r don't point to the same file" %
1236 (path_a, path_b))
1237
1238 def assertFileNotFound(self, func, *args, **kwargs):
1239 with self.assertRaises(FileNotFoundError) as cm:
1240 func(*args, **kwargs)
1241 self.assertEqual(cm.exception.errno, errno.ENOENT)
1242
1243 def _test_cwd(self, p):
1244 q = self.cls(os.getcwd())
1245 self.assertEqual(p, q)
1246 self.assertEqual(str(p), str(q))
1247 self.assertIs(type(p), type(q))
1248 self.assertTrue(p.is_absolute())
1249
1250 def test_cwd(self):
1251 p = self.cls.cwd()
1252 self._test_cwd(p)
1253
Antoine Pitrou43e3d942014-05-13 10:50:15 +02001254 def test_samefile(self):
1255 fileA_path = os.path.join(BASE, 'fileA')
1256 fileB_path = os.path.join(BASE, 'dirB', 'fileB')
1257 p = self.cls(fileA_path)
1258 pp = self.cls(fileA_path)
1259 q = self.cls(fileB_path)
1260 self.assertTrue(p.samefile(fileA_path))
1261 self.assertTrue(p.samefile(pp))
1262 self.assertFalse(p.samefile(fileB_path))
1263 self.assertFalse(p.samefile(q))
1264 # Test the non-existent file case
1265 non_existent = os.path.join(BASE, 'foo')
1266 r = self.cls(non_existent)
1267 self.assertRaises(FileNotFoundError, p.samefile, r)
1268 self.assertRaises(FileNotFoundError, p.samefile, non_existent)
1269 self.assertRaises(FileNotFoundError, r.samefile, p)
1270 self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1271 self.assertRaises(FileNotFoundError, r.samefile, r)
1272 self.assertRaises(FileNotFoundError, r.samefile, non_existent)
1273
Antoine Pitrou31119e42013-11-22 17:38:12 +01001274 def test_empty_path(self):
1275 # The empty path points to '.'
1276 p = self.cls('')
1277 self.assertEqual(p.stat(), os.stat('.'))
1278
1279 def test_exists(self):
1280 P = self.cls
1281 p = P(BASE)
1282 self.assertIs(True, p.exists())
1283 self.assertIs(True, (p / 'dirA').exists())
1284 self.assertIs(True, (p / 'fileA').exists())
1285 if not symlink_skip_reason:
1286 self.assertIs(True, (p / 'linkA').exists())
1287 self.assertIs(True, (p / 'linkB').exists())
1288 self.assertIs(False, (p / 'foo').exists())
1289 self.assertIs(False, P('/xyzzy').exists())
1290
1291 def test_open_common(self):
1292 p = self.cls(BASE)
1293 with (p / 'fileA').open('r') as f:
1294 self.assertIsInstance(f, io.TextIOBase)
1295 self.assertEqual(f.read(), "this is file A\n")
1296 with (p / 'fileA').open('rb') as f:
1297 self.assertIsInstance(f, io.BufferedIOBase)
1298 self.assertEqual(f.read().strip(), b"this is file A")
1299 with (p / 'fileA').open('rb', buffering=0) as f:
1300 self.assertIsInstance(f, io.RawIOBase)
1301 self.assertEqual(f.read().strip(), b"this is file A")
1302
1303 def test_iterdir(self):
1304 P = self.cls
1305 p = P(BASE)
1306 it = p.iterdir()
1307 paths = set(it)
1308 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1309 if not symlink_skip_reason:
1310 expected += ['linkA', 'linkB', 'brokenLink']
1311 self.assertEqual(paths, { P(BASE, q) for q in expected })
1312
1313 @with_symlinks
1314 def test_iterdir_symlink(self):
1315 # __iter__ on a symlink to a directory
1316 P = self.cls
1317 p = P(BASE, 'linkB')
1318 paths = set(p.iterdir())
1319 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1320 self.assertEqual(paths, expected)
1321
1322 def test_iterdir_nodir(self):
1323 # __iter__ on something that is not a directory
1324 p = self.cls(BASE, 'fileA')
1325 with self.assertRaises(OSError) as cm:
1326 next(p.iterdir())
1327 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1328 # (see issue #12802)
1329 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1330 errno.ENOENT, errno.EINVAL))
1331
1332 def test_glob_common(self):
1333 def _check(glob, expected):
1334 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1335 P = self.cls
1336 p = P(BASE)
1337 it = p.glob("fileA")
1338 self.assertIsInstance(it, collections.Iterator)
1339 _check(it, ["fileA"])
1340 _check(p.glob("fileB"), [])
1341 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1342 if symlink_skip_reason:
1343 _check(p.glob("*A"), ['dirA', 'fileA'])
1344 else:
1345 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1346 if symlink_skip_reason:
1347 _check(p.glob("*B/*"), ['dirB/fileB'])
1348 else:
1349 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1350 'linkB/fileB', 'linkB/linkD'])
1351 if symlink_skip_reason:
1352 _check(p.glob("*/fileB"), ['dirB/fileB'])
1353 else:
1354 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1355
1356 def test_rglob_common(self):
1357 def _check(glob, expected):
1358 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1359 P = self.cls
1360 p = P(BASE)
1361 it = p.rglob("fileA")
1362 self.assertIsInstance(it, collections.Iterator)
1363 # XXX cannot test because of symlink loops in the test setup
1364 #_check(it, ["fileA"])
1365 #_check(p.rglob("fileB"), ["dirB/fileB"])
1366 #_check(p.rglob("*/fileA"), [""])
1367 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1368 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1369 # No symlink loops here
1370 p = P(BASE, "dirC")
1371 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1372 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1373
1374 def test_glob_dotdot(self):
1375 # ".." is not special in globs
1376 P = self.cls
1377 p = P(BASE)
1378 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1379 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1380 self.assertEqual(set(p.glob("../xyzzy")), set())
1381
1382 def _check_resolve_relative(self, p, expected):
1383 q = p.resolve()
1384 self.assertEqual(q, expected)
1385
1386 def _check_resolve_absolute(self, p, expected):
1387 q = p.resolve()
1388 self.assertEqual(q, expected)
1389
1390 @with_symlinks
1391 def test_resolve_common(self):
1392 P = self.cls
1393 p = P(BASE, 'foo')
1394 with self.assertRaises(OSError) as cm:
1395 p.resolve()
1396 self.assertEqual(cm.exception.errno, errno.ENOENT)
1397 # These are all relative symlinks
1398 p = P(BASE, 'dirB', 'fileB')
1399 self._check_resolve_relative(p, p)
1400 p = P(BASE, 'linkA')
1401 self._check_resolve_relative(p, P(BASE, 'fileA'))
1402 p = P(BASE, 'dirA', 'linkC', 'fileB')
1403 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1404 p = P(BASE, 'dirB', 'linkD', 'fileB')
1405 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1406 # Now create absolute symlinks
1407 d = tempfile.mkdtemp(suffix='-dirD')
1408 self.addCleanup(shutil.rmtree, d)
1409 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1410 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1411 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1412 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1413
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001414 @with_symlinks
1415 def test_resolve_dot(self):
1416 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1417 p = self.cls(BASE)
1418 self.dirlink('.', join('0'))
Antoine Pitroucc157512013-12-03 17:13:13 +01001419 self.dirlink(os.path.join('0', '0'), join('1'))
1420 self.dirlink(os.path.join('1', '1'), join('2'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001421 q = p / '2'
1422 self.assertEqual(q.resolve(), p)
1423
Antoine Pitrou31119e42013-11-22 17:38:12 +01001424 def test_with(self):
1425 p = self.cls(BASE)
1426 it = p.iterdir()
1427 it2 = p.iterdir()
1428 next(it2)
1429 with p:
1430 pass
1431 # I/O operation on closed path
1432 self.assertRaises(ValueError, next, it)
1433 self.assertRaises(ValueError, next, it2)
1434 self.assertRaises(ValueError, p.open)
1435 self.assertRaises(ValueError, p.resolve)
1436 self.assertRaises(ValueError, p.absolute)
1437 self.assertRaises(ValueError, p.__enter__)
1438
1439 def test_chmod(self):
1440 p = self.cls(BASE) / 'fileA'
1441 mode = p.stat().st_mode
1442 # Clear writable bit
1443 new_mode = mode & ~0o222
1444 p.chmod(new_mode)
1445 self.assertEqual(p.stat().st_mode, new_mode)
1446 # Set writable bit
1447 new_mode = mode | 0o222
1448 p.chmod(new_mode)
1449 self.assertEqual(p.stat().st_mode, new_mode)
1450
1451 # XXX also need a test for lchmod
1452
1453 def test_stat(self):
1454 p = self.cls(BASE) / 'fileA'
1455 st = p.stat()
1456 self.assertEqual(p.stat(), st)
1457 # Change file mode by flipping write bit
1458 p.chmod(st.st_mode ^ 0o222)
1459 self.addCleanup(p.chmod, st.st_mode)
1460 self.assertNotEqual(p.stat(), st)
1461
1462 @with_symlinks
1463 def test_lstat(self):
1464 p = self.cls(BASE)/ 'linkA'
1465 st = p.stat()
1466 self.assertNotEqual(st, p.lstat())
1467
1468 def test_lstat_nosymlink(self):
1469 p = self.cls(BASE) / 'fileA'
1470 st = p.stat()
1471 self.assertEqual(st, p.lstat())
1472
1473 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1474 def test_owner(self):
1475 p = self.cls(BASE) / 'fileA'
1476 uid = p.stat().st_uid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001477 try:
1478 name = pwd.getpwuid(uid).pw_name
1479 except KeyError:
1480 self.skipTest(
1481 "user %d doesn't have an entry in the system database" % uid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001482 self.assertEqual(name, p.owner())
1483
1484 @unittest.skipUnless(grp, "the grp module is needed for this test")
1485 def test_group(self):
1486 p = self.cls(BASE) / 'fileA'
1487 gid = p.stat().st_gid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001488 try:
1489 name = grp.getgrgid(gid).gr_name
1490 except KeyError:
1491 self.skipTest(
1492 "group %d doesn't have an entry in the system database" % gid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001493 self.assertEqual(name, p.group())
1494
1495 def test_unlink(self):
1496 p = self.cls(BASE) / 'fileA'
1497 p.unlink()
1498 self.assertFileNotFound(p.stat)
1499 self.assertFileNotFound(p.unlink)
1500
1501 def test_rmdir(self):
1502 p = self.cls(BASE) / 'dirA'
1503 for q in p.iterdir():
1504 q.unlink()
1505 p.rmdir()
1506 self.assertFileNotFound(p.stat)
1507 self.assertFileNotFound(p.unlink)
1508
1509 def test_rename(self):
1510 P = self.cls(BASE)
1511 p = P / 'fileA'
1512 size = p.stat().st_size
1513 # Renaming to another path
1514 q = P / 'dirA' / 'fileAA'
1515 p.rename(q)
1516 self.assertEqual(q.stat().st_size, size)
1517 self.assertFileNotFound(p.stat)
1518 # Renaming to a str of a relative path
1519 r = rel_join('fileAAA')
1520 q.rename(r)
1521 self.assertEqual(os.stat(r).st_size, size)
1522 self.assertFileNotFound(q.stat)
1523
1524 def test_replace(self):
1525 P = self.cls(BASE)
1526 p = P / 'fileA'
1527 size = p.stat().st_size
1528 # Replacing a non-existing path
1529 q = P / 'dirA' / 'fileAA'
1530 p.replace(q)
1531 self.assertEqual(q.stat().st_size, size)
1532 self.assertFileNotFound(p.stat)
1533 # Replacing another (existing) path
1534 r = rel_join('dirB', 'fileB')
1535 q.replace(r)
1536 self.assertEqual(os.stat(r).st_size, size)
1537 self.assertFileNotFound(q.stat)
1538
1539 def test_touch_common(self):
1540 P = self.cls(BASE)
1541 p = P / 'newfileA'
1542 self.assertFalse(p.exists())
1543 p.touch()
1544 self.assertTrue(p.exists())
Antoine Pitrou0f575642013-11-22 23:20:08 +01001545 st = p.stat()
1546 old_mtime = st.st_mtime
1547 old_mtime_ns = st.st_mtime_ns
Antoine Pitrou31119e42013-11-22 17:38:12 +01001548 # Rewind the mtime sufficiently far in the past to work around
1549 # filesystem-specific timestamp granularity.
1550 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001551 # The file mtime should be refreshed by calling touch() again
Antoine Pitrou31119e42013-11-22 17:38:12 +01001552 p.touch()
Antoine Pitrou0f575642013-11-22 23:20:08 +01001553 st = p.stat()
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001554 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
1555 self.assertGreaterEqual(st.st_mtime, old_mtime)
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001556 # Now with exist_ok=False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001557 p = P / 'newfileB'
1558 self.assertFalse(p.exists())
1559 p.touch(mode=0o700, exist_ok=False)
1560 self.assertTrue(p.exists())
1561 self.assertRaises(OSError, p.touch, exist_ok=False)
1562
Antoine Pitrou8b784932013-11-23 14:52:39 +01001563 def test_touch_nochange(self):
1564 P = self.cls(BASE)
1565 p = P / 'fileA'
1566 p.touch()
1567 with p.open('rb') as f:
1568 self.assertEqual(f.read().strip(), b"this is file A")
1569
Antoine Pitrou31119e42013-11-22 17:38:12 +01001570 def test_mkdir(self):
1571 P = self.cls(BASE)
1572 p = P / 'newdirA'
1573 self.assertFalse(p.exists())
1574 p.mkdir()
1575 self.assertTrue(p.exists())
1576 self.assertTrue(p.is_dir())
1577 with self.assertRaises(OSError) as cm:
1578 p.mkdir()
1579 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001580
1581 def test_mkdir_parents(self):
1582 # Creating a chain of directories
1583 p = self.cls(BASE, 'newdirB', 'newdirC')
1584 self.assertFalse(p.exists())
1585 with self.assertRaises(OSError) as cm:
1586 p.mkdir()
1587 self.assertEqual(cm.exception.errno, errno.ENOENT)
1588 p.mkdir(parents=True)
1589 self.assertTrue(p.exists())
1590 self.assertTrue(p.is_dir())
1591 with self.assertRaises(OSError) as cm:
1592 p.mkdir(parents=True)
1593 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001594 # test `mode` arg
1595 mode = stat.S_IMODE(p.stat().st_mode) # default mode
1596 p = self.cls(BASE, 'newdirD', 'newdirE')
1597 p.mkdir(0o555, parents=True)
1598 self.assertTrue(p.exists())
1599 self.assertTrue(p.is_dir())
1600 if os.name != 'nt':
1601 # the directory's permissions follow the mode argument
Gregory P. Smithb599c612014-01-20 01:10:33 -08001602 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001603 # the parent's permissions follow the default process settings
1604 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001605
1606 @with_symlinks
1607 def test_symlink_to(self):
1608 P = self.cls(BASE)
1609 target = P / 'fileA'
1610 # Symlinking a path target
1611 link = P / 'dirA' / 'linkAA'
1612 link.symlink_to(target)
1613 self.assertEqual(link.stat(), target.stat())
1614 self.assertNotEqual(link.lstat(), target.stat())
1615 # Symlinking a str target
1616 link = P / 'dirA' / 'linkAAA'
1617 link.symlink_to(str(target))
1618 self.assertEqual(link.stat(), target.stat())
1619 self.assertNotEqual(link.lstat(), target.stat())
1620 self.assertFalse(link.is_dir())
1621 # Symlinking to a directory
1622 target = P / 'dirB'
1623 link = P / 'dirA' / 'linkAAAA'
1624 link.symlink_to(target, target_is_directory=True)
1625 self.assertEqual(link.stat(), target.stat())
1626 self.assertNotEqual(link.lstat(), target.stat())
1627 self.assertTrue(link.is_dir())
1628 self.assertTrue(list(link.iterdir()))
1629
1630 def test_is_dir(self):
1631 P = self.cls(BASE)
1632 self.assertTrue((P / 'dirA').is_dir())
1633 self.assertFalse((P / 'fileA').is_dir())
1634 self.assertFalse((P / 'non-existing').is_dir())
1635 if not symlink_skip_reason:
1636 self.assertFalse((P / 'linkA').is_dir())
1637 self.assertTrue((P / 'linkB').is_dir())
1638 self.assertFalse((P/ 'brokenLink').is_dir())
1639
1640 def test_is_file(self):
1641 P = self.cls(BASE)
1642 self.assertTrue((P / 'fileA').is_file())
1643 self.assertFalse((P / 'dirA').is_file())
1644 self.assertFalse((P / 'non-existing').is_file())
1645 if not symlink_skip_reason:
1646 self.assertTrue((P / 'linkA').is_file())
1647 self.assertFalse((P / 'linkB').is_file())
1648 self.assertFalse((P/ 'brokenLink').is_file())
1649
1650 def test_is_symlink(self):
1651 P = self.cls(BASE)
1652 self.assertFalse((P / 'fileA').is_symlink())
1653 self.assertFalse((P / 'dirA').is_symlink())
1654 self.assertFalse((P / 'non-existing').is_symlink())
1655 if not symlink_skip_reason:
1656 self.assertTrue((P / 'linkA').is_symlink())
1657 self.assertTrue((P / 'linkB').is_symlink())
1658 self.assertTrue((P/ 'brokenLink').is_symlink())
1659
1660 def test_is_fifo_false(self):
1661 P = self.cls(BASE)
1662 self.assertFalse((P / 'fileA').is_fifo())
1663 self.assertFalse((P / 'dirA').is_fifo())
1664 self.assertFalse((P / 'non-existing').is_fifo())
1665
1666 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1667 def test_is_fifo_true(self):
1668 P = self.cls(BASE, 'myfifo')
1669 os.mkfifo(str(P))
1670 self.assertTrue(P.is_fifo())
1671 self.assertFalse(P.is_socket())
1672 self.assertFalse(P.is_file())
1673
1674 def test_is_socket_false(self):
1675 P = self.cls(BASE)
1676 self.assertFalse((P / 'fileA').is_socket())
1677 self.assertFalse((P / 'dirA').is_socket())
1678 self.assertFalse((P / 'non-existing').is_socket())
1679
1680 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1681 def test_is_socket_true(self):
1682 P = self.cls(BASE, 'mysock')
Antoine Pitrou330ce592013-11-22 18:05:06 +01001683 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001684 self.addCleanup(sock.close)
Antoine Pitrou330ce592013-11-22 18:05:06 +01001685 try:
1686 sock.bind(str(P))
1687 except OSError as e:
1688 if "AF_UNIX path too long" in str(e):
1689 self.skipTest("cannot bind Unix socket: " + str(e))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001690 self.assertTrue(P.is_socket())
1691 self.assertFalse(P.is_fifo())
1692 self.assertFalse(P.is_file())
1693
1694 def test_is_block_device_false(self):
1695 P = self.cls(BASE)
1696 self.assertFalse((P / 'fileA').is_block_device())
1697 self.assertFalse((P / 'dirA').is_block_device())
1698 self.assertFalse((P / 'non-existing').is_block_device())
1699
1700 def test_is_char_device_false(self):
1701 P = self.cls(BASE)
1702 self.assertFalse((P / 'fileA').is_char_device())
1703 self.assertFalse((P / 'dirA').is_char_device())
1704 self.assertFalse((P / 'non-existing').is_char_device())
1705
1706 def test_is_char_device_true(self):
1707 # Under Unix, /dev/null should generally be a char device
1708 P = self.cls('/dev/null')
1709 if not P.exists():
1710 self.skipTest("/dev/null required")
1711 self.assertTrue(P.is_char_device())
1712 self.assertFalse(P.is_block_device())
1713 self.assertFalse(P.is_file())
1714
1715 def test_pickling_common(self):
1716 p = self.cls(BASE, 'fileA')
1717 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1718 dumped = pickle.dumps(p, proto)
1719 pp = pickle.loads(dumped)
1720 self.assertEqual(pp.stat(), p.stat())
1721
1722 def test_parts_interning(self):
1723 P = self.cls
1724 p = P('/usr/bin/foo')
1725 q = P('/usr/local/bin')
1726 # 'usr'
1727 self.assertIs(p.parts[1], q.parts[1])
1728 # 'bin'
1729 self.assertIs(p.parts[2], q.parts[3])
1730
Antoine Pitrouc274fd22013-12-16 19:57:41 +01001731 def _check_complex_symlinks(self, link0_target):
1732 # Test solving a non-looping chain of symlinks (issue #19887)
1733 P = self.cls(BASE)
1734 self.dirlink(os.path.join('link0', 'link0'), join('link1'))
1735 self.dirlink(os.path.join('link1', 'link1'), join('link2'))
1736 self.dirlink(os.path.join('link2', 'link2'), join('link3'))
1737 self.dirlink(link0_target, join('link0'))
1738
1739 # Resolve absolute paths
1740 p = (P / 'link0').resolve()
1741 self.assertEqual(p, P)
1742 self.assertEqual(str(p), BASE)
1743 p = (P / 'link1').resolve()
1744 self.assertEqual(p, P)
1745 self.assertEqual(str(p), BASE)
1746 p = (P / 'link2').resolve()
1747 self.assertEqual(p, P)
1748 self.assertEqual(str(p), BASE)
1749 p = (P / 'link3').resolve()
1750 self.assertEqual(p, P)
1751 self.assertEqual(str(p), BASE)
1752
1753 # Resolve relative paths
1754 old_path = os.getcwd()
1755 os.chdir(BASE)
1756 try:
1757 p = self.cls('link0').resolve()
1758 self.assertEqual(p, P)
1759 self.assertEqual(str(p), BASE)
1760 p = self.cls('link1').resolve()
1761 self.assertEqual(p, P)
1762 self.assertEqual(str(p), BASE)
1763 p = self.cls('link2').resolve()
1764 self.assertEqual(p, P)
1765 self.assertEqual(str(p), BASE)
1766 p = self.cls('link3').resolve()
1767 self.assertEqual(p, P)
1768 self.assertEqual(str(p), BASE)
1769 finally:
1770 os.chdir(old_path)
1771
1772 @with_symlinks
1773 def test_complex_symlinks_absolute(self):
1774 self._check_complex_symlinks(BASE)
1775
1776 @with_symlinks
1777 def test_complex_symlinks_relative(self):
1778 self._check_complex_symlinks('.')
1779
1780 @with_symlinks
1781 def test_complex_symlinks_relative_dot_dot(self):
1782 self._check_complex_symlinks(os.path.join('dirA', '..'))
1783
Antoine Pitrou31119e42013-11-22 17:38:12 +01001784
1785class PathTest(_BasePathTest, unittest.TestCase):
1786 cls = pathlib.Path
1787
1788 def test_concrete_class(self):
1789 p = self.cls('a')
1790 self.assertIs(type(p),
1791 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1792
1793 def test_unsupported_flavour(self):
1794 if os.name == 'nt':
1795 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1796 else:
1797 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1798
1799
1800@only_posix
1801class PosixPathTest(_BasePathTest, unittest.TestCase):
1802 cls = pathlib.PosixPath
1803
1804 def _check_symlink_loop(self, *args):
1805 path = self.cls(*args)
1806 with self.assertRaises(RuntimeError):
1807 print(path.resolve())
1808
1809 def test_open_mode(self):
1810 old_mask = os.umask(0)
1811 self.addCleanup(os.umask, old_mask)
1812 p = self.cls(BASE)
1813 with (p / 'new_file').open('wb'):
1814 pass
1815 st = os.stat(join('new_file'))
1816 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1817 os.umask(0o022)
1818 with (p / 'other_new_file').open('wb'):
1819 pass
1820 st = os.stat(join('other_new_file'))
1821 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1822
1823 def test_touch_mode(self):
1824 old_mask = os.umask(0)
1825 self.addCleanup(os.umask, old_mask)
1826 p = self.cls(BASE)
1827 (p / 'new_file').touch()
1828 st = os.stat(join('new_file'))
1829 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1830 os.umask(0o022)
1831 (p / 'other_new_file').touch()
1832 st = os.stat(join('other_new_file'))
1833 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1834 (p / 'masked_new_file').touch(mode=0o750)
1835 st = os.stat(join('masked_new_file'))
1836 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1837
1838 @with_symlinks
1839 def test_resolve_loop(self):
1840 # Loop detection for broken symlinks under POSIX
1841 P = self.cls
1842 # Loops with relative symlinks
1843 os.symlink('linkX/inside', join('linkX'))
1844 self._check_symlink_loop(BASE, 'linkX')
1845 os.symlink('linkY', join('linkY'))
1846 self._check_symlink_loop(BASE, 'linkY')
1847 os.symlink('linkZ/../linkZ', join('linkZ'))
1848 self._check_symlink_loop(BASE, 'linkZ')
1849 # Loops with absolute symlinks
1850 os.symlink(join('linkU/inside'), join('linkU'))
1851 self._check_symlink_loop(BASE, 'linkU')
1852 os.symlink(join('linkV'), join('linkV'))
1853 self._check_symlink_loop(BASE, 'linkV')
1854 os.symlink(join('linkW/../linkW'), join('linkW'))
1855 self._check_symlink_loop(BASE, 'linkW')
1856
1857 def test_glob(self):
1858 P = self.cls
1859 p = P(BASE)
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001860 given = set(p.glob("FILEa"))
1861 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1862 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001863 self.assertEqual(set(p.glob("FILEa*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001864
1865 def test_rglob(self):
1866 P = self.cls
1867 p = P(BASE, "dirC")
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001868 given = set(p.rglob("FILEd"))
1869 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1870 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001871 self.assertEqual(set(p.rglob("FILEd*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001872
1873
1874@only_nt
1875class WindowsPathTest(_BasePathTest, unittest.TestCase):
1876 cls = pathlib.WindowsPath
1877
1878 def test_glob(self):
1879 P = self.cls
1880 p = P(BASE)
1881 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1882
1883 def test_rglob(self):
1884 P = self.cls
1885 p = P(BASE, "dirC")
1886 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1887
1888
1889if __name__ == "__main__":
1890 unittest.main()