blob: 7f6a66dc82d934cb2170c6da64e7a9cf8c1cc869 [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')
Antoine Pitrou7084e732014-07-06 21:31:12 -0400543 self.assertRaises(ValueError, P('a/b').with_name, '')
544 self.assertRaises(ValueError, P('a/b').with_name, '/c')
545 self.assertRaises(ValueError, P('a/b').with_name, 'c/')
546 self.assertRaises(ValueError, P('a/b').with_name, 'c/d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100547
548 def test_with_suffix_common(self):
549 P = self.cls
550 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
551 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
552 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
553 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400554 # Stripping suffix
555 self.assertEqual(P('a/b.py').with_suffix(''), P('a/b'))
556 self.assertEqual(P('/a/b').with_suffix(''), P('/a/b'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100557 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100558 self.assertRaises(ValueError, P('').with_suffix, '.gz')
559 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
560 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100561 # Invalid suffix
562 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
563 self.assertRaises(ValueError, P('a/b').with_suffix, '/')
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400564 self.assertRaises(ValueError, P('a/b').with_suffix, '.')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100565 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
566 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
567 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
Antoine Pitroue50dafc2014-07-06 21:37:15 -0400568 self.assertRaises(ValueError, P('a/b').with_suffix, './.d')
569 self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100570
571 def test_relative_to_common(self):
572 P = self.cls
573 p = P('a/b')
574 self.assertRaises(TypeError, p.relative_to)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100575 self.assertRaises(TypeError, p.relative_to, b'a')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100576 self.assertEqual(p.relative_to(P()), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100577 self.assertEqual(p.relative_to(''), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100578 self.assertEqual(p.relative_to(P('a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100579 self.assertEqual(p.relative_to('a'), P('b'))
580 self.assertEqual(p.relative_to('a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100581 self.assertEqual(p.relative_to(P('a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100582 self.assertEqual(p.relative_to('a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100583 # With several args
584 self.assertEqual(p.relative_to('a', 'b'), P())
585 # Unrelated paths
586 self.assertRaises(ValueError, p.relative_to, P('c'))
587 self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
588 self.assertRaises(ValueError, p.relative_to, P('a/c'))
589 self.assertRaises(ValueError, p.relative_to, P('/a'))
590 p = P('/a/b')
591 self.assertEqual(p.relative_to(P('/')), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100592 self.assertEqual(p.relative_to('/'), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100593 self.assertEqual(p.relative_to(P('/a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100594 self.assertEqual(p.relative_to('/a'), P('b'))
595 self.assertEqual(p.relative_to('/a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100596 self.assertEqual(p.relative_to(P('/a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100597 self.assertEqual(p.relative_to('/a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100598 # Unrelated paths
599 self.assertRaises(ValueError, p.relative_to, P('/c'))
600 self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
601 self.assertRaises(ValueError, p.relative_to, P('/a/c'))
602 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100603 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100604 self.assertRaises(ValueError, p.relative_to, P('a'))
605
606 def test_pickling_common(self):
607 P = self.cls
608 p = P('/a/b')
609 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
610 dumped = pickle.dumps(p, proto)
611 pp = pickle.loads(dumped)
612 self.assertIs(pp.__class__, p.__class__)
613 self.assertEqual(pp, p)
614 self.assertEqual(hash(pp), hash(p))
615 self.assertEqual(str(pp), str(p))
616
617
618class PurePosixPathTest(_BasePurePathTest, unittest.TestCase):
619 cls = pathlib.PurePosixPath
620
621 def test_root(self):
622 P = self.cls
623 self.assertEqual(P('/a/b').root, '/')
624 self.assertEqual(P('///a/b').root, '/')
625 # POSIX special case for two leading slashes
626 self.assertEqual(P('//a/b').root, '//')
627
628 def test_eq(self):
629 P = self.cls
630 self.assertNotEqual(P('a/b'), P('A/b'))
631 self.assertEqual(P('/a'), P('///a'))
632 self.assertNotEqual(P('/a'), P('//a'))
633
634 def test_as_uri(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100635 P = self.cls
636 self.assertEqual(P('/').as_uri(), 'file:///')
637 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c')
638 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c')
Antoine Pitrou29eac422013-11-22 17:57:03 +0100639
640 def test_as_uri_non_ascii(self):
641 from urllib.parse import quote_from_bytes
642 P = self.cls
643 try:
644 os.fsencode('\xe9')
645 except UnicodeEncodeError:
646 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100647 self.assertEqual(P('/a/b\xe9').as_uri(),
648 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9')))
649
650 def test_match(self):
651 P = self.cls
652 self.assertFalse(P('A.py').match('a.PY'))
653
654 def test_is_absolute(self):
655 P = self.cls
656 self.assertFalse(P().is_absolute())
657 self.assertFalse(P('a').is_absolute())
658 self.assertFalse(P('a/b/').is_absolute())
659 self.assertTrue(P('/').is_absolute())
660 self.assertTrue(P('/a').is_absolute())
661 self.assertTrue(P('/a/b/').is_absolute())
662 self.assertTrue(P('//a').is_absolute())
663 self.assertTrue(P('//a/b').is_absolute())
664
665 def test_is_reserved(self):
666 P = self.cls
667 self.assertIs(False, P('').is_reserved())
668 self.assertIs(False, P('/').is_reserved())
669 self.assertIs(False, P('/foo/bar').is_reserved())
670 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved())
671
672 def test_join(self):
673 P = self.cls
674 p = P('//a')
675 pp = p.joinpath('b')
676 self.assertEqual(pp, P('//a/b'))
677 pp = P('/a').joinpath('//c')
678 self.assertEqual(pp, P('//c'))
679 pp = P('//a').joinpath('/c')
680 self.assertEqual(pp, P('/c'))
681
682 def test_div(self):
683 # Basically the same as joinpath()
684 P = self.cls
685 p = P('//a')
686 pp = p / 'b'
687 self.assertEqual(pp, P('//a/b'))
688 pp = P('/a') / '//c'
689 self.assertEqual(pp, P('//c'))
690 pp = P('//a') / '/c'
691 self.assertEqual(pp, P('/c'))
692
693
694class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
695 cls = pathlib.PureWindowsPath
696
697 equivalences = _BasePurePathTest.equivalences.copy()
698 equivalences.update({
699 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ],
700 'c:/a': [
701 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'),
702 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'),
703 ],
704 '//a/b/': [ ('//a/b',) ],
705 '//a/b/c': [
706 ('//a/b', 'c'), ('//a/b/', 'c'),
707 ],
708 })
709
710 def test_str(self):
711 p = self.cls('a/b/c')
712 self.assertEqual(str(p), 'a\\b\\c')
713 p = self.cls('c:/a/b/c')
714 self.assertEqual(str(p), 'c:\\a\\b\\c')
715 p = self.cls('//a/b')
716 self.assertEqual(str(p), '\\\\a\\b\\')
717 p = self.cls('//a/b/c')
718 self.assertEqual(str(p), '\\\\a\\b\\c')
719 p = self.cls('//a/b/c/d')
720 self.assertEqual(str(p), '\\\\a\\b\\c\\d')
721
Antoine Pitroucb5ec772014-04-23 00:34:15 +0200722 def test_str_subclass(self):
723 self._check_str_subclass('c:')
724 self._check_str_subclass('c:a')
725 self._check_str_subclass('c:a\\b.txt')
726 self._check_str_subclass('c:\\')
727 self._check_str_subclass('c:\\a')
728 self._check_str_subclass('c:\\a\\b.txt')
729 self._check_str_subclass('\\\\some\\share')
730 self._check_str_subclass('\\\\some\\share\\a')
731 self._check_str_subclass('\\\\some\\share\\a\\b.txt')
732
Antoine Pitrou31119e42013-11-22 17:38:12 +0100733 def test_eq(self):
734 P = self.cls
735 self.assertEqual(P('c:a/b'), P('c:a/b'))
736 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b'))
737 self.assertNotEqual(P('c:a/b'), P('d:a/b'))
738 self.assertNotEqual(P('c:a/b'), P('c:/a/b'))
739 self.assertNotEqual(P('/a/b'), P('c:/a/b'))
740 # Case-insensitivity
741 self.assertEqual(P('a/B'), P('A/b'))
742 self.assertEqual(P('C:a/B'), P('c:A/b'))
743 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b'))
744
745 def test_as_uri(self):
746 from urllib.parse import quote_from_bytes
747 P = self.cls
748 with self.assertRaises(ValueError):
749 P('/a/b').as_uri()
750 with self.assertRaises(ValueError):
751 P('c:a/b').as_uri()
752 self.assertEqual(P('c:/').as_uri(), 'file:///c:/')
753 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c')
754 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c')
755 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9')
756 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/')
757 self.assertEqual(P('//some/share/a/b.c').as_uri(),
758 'file://some/share/a/b.c')
759 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
760 'file://some/share/a/b%25%23c%C3%A9')
761
762 def test_match_common(self):
763 P = self.cls
764 # Absolute patterns
765 self.assertTrue(P('c:/b.py').match('/*.py'))
766 self.assertTrue(P('c:/b.py').match('c:*.py'))
767 self.assertTrue(P('c:/b.py').match('c:/*.py'))
768 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive
769 self.assertFalse(P('b.py').match('/*.py'))
770 self.assertFalse(P('b.py').match('c:*.py'))
771 self.assertFalse(P('b.py').match('c:/*.py'))
772 self.assertFalse(P('c:b.py').match('/*.py'))
773 self.assertFalse(P('c:b.py').match('c:/*.py'))
774 self.assertFalse(P('/b.py').match('c:*.py'))
775 self.assertFalse(P('/b.py').match('c:/*.py'))
776 # UNC patterns
777 self.assertTrue(P('//some/share/a.py').match('/*.py'))
778 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
779 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
780 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
781 # Case-insensitivity
782 self.assertTrue(P('B.py').match('b.PY'))
783 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
784 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
785
786 def test_ordering_common(self):
787 # Case-insensitivity
788 def assertOrderedEqual(a, b):
789 self.assertLessEqual(a, b)
790 self.assertGreaterEqual(b, a)
791 P = self.cls
792 p = P('c:A/b')
793 q = P('C:a/B')
794 assertOrderedEqual(p, q)
795 self.assertFalse(p < q)
796 self.assertFalse(p > q)
797 p = P('//some/Share/A/b')
798 q = P('//Some/SHARE/a/B')
799 assertOrderedEqual(p, q)
800 self.assertFalse(p < q)
801 self.assertFalse(p > q)
802
803 def test_parts(self):
804 P = self.cls
805 p = P('c:a/b')
806 parts = p.parts
807 self.assertEqual(parts, ('c:', 'a', 'b'))
808 p = P('c:/a/b')
809 parts = p.parts
810 self.assertEqual(parts, ('c:\\', 'a', 'b'))
811 p = P('//a/b/c/d')
812 parts = p.parts
813 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
814
815 def test_parent(self):
816 # Anchored
817 P = self.cls
818 p = P('z:a/b/c')
819 self.assertEqual(p.parent, P('z:a/b'))
820 self.assertEqual(p.parent.parent, P('z:a'))
821 self.assertEqual(p.parent.parent.parent, P('z:'))
822 self.assertEqual(p.parent.parent.parent.parent, P('z:'))
823 p = P('z:/a/b/c')
824 self.assertEqual(p.parent, P('z:/a/b'))
825 self.assertEqual(p.parent.parent, P('z:/a'))
826 self.assertEqual(p.parent.parent.parent, P('z:/'))
827 self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
828 p = P('//a/b/c/d')
829 self.assertEqual(p.parent, P('//a/b/c'))
830 self.assertEqual(p.parent.parent, P('//a/b'))
831 self.assertEqual(p.parent.parent.parent, P('//a/b'))
832
833 def test_parents(self):
834 # Anchored
835 P = self.cls
836 p = P('z:a/b/')
837 par = p.parents
838 self.assertEqual(len(par), 2)
839 self.assertEqual(par[0], P('z:a'))
840 self.assertEqual(par[1], P('z:'))
841 self.assertEqual(list(par), [P('z:a'), P('z:')])
842 with self.assertRaises(IndexError):
843 par[2]
844 p = P('z:/a/b/')
845 par = p.parents
846 self.assertEqual(len(par), 2)
847 self.assertEqual(par[0], P('z:/a'))
848 self.assertEqual(par[1], P('z:/'))
849 self.assertEqual(list(par), [P('z:/a'), P('z:/')])
850 with self.assertRaises(IndexError):
851 par[2]
852 p = P('//a/b/c/d')
853 par = p.parents
854 self.assertEqual(len(par), 2)
855 self.assertEqual(par[0], P('//a/b/c'))
856 self.assertEqual(par[1], P('//a/b'))
857 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
858 with self.assertRaises(IndexError):
859 par[2]
860
861 def test_drive(self):
862 P = self.cls
863 self.assertEqual(P('c:').drive, 'c:')
864 self.assertEqual(P('c:a/b').drive, 'c:')
865 self.assertEqual(P('c:/').drive, 'c:')
866 self.assertEqual(P('c:/a/b/').drive, 'c:')
867 self.assertEqual(P('//a/b').drive, '\\\\a\\b')
868 self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
869 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
870
871 def test_root(self):
872 P = self.cls
873 self.assertEqual(P('c:').root, '')
874 self.assertEqual(P('c:a/b').root, '')
875 self.assertEqual(P('c:/').root, '\\')
876 self.assertEqual(P('c:/a/b/').root, '\\')
877 self.assertEqual(P('//a/b').root, '\\')
878 self.assertEqual(P('//a/b/').root, '\\')
879 self.assertEqual(P('//a/b/c/d').root, '\\')
880
881 def test_anchor(self):
882 P = self.cls
883 self.assertEqual(P('c:').anchor, 'c:')
884 self.assertEqual(P('c:a/b').anchor, 'c:')
885 self.assertEqual(P('c:/').anchor, 'c:\\')
886 self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
887 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
888 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
889 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
890
891 def test_name(self):
892 P = self.cls
893 self.assertEqual(P('c:').name, '')
894 self.assertEqual(P('c:/').name, '')
895 self.assertEqual(P('c:a/b').name, 'b')
896 self.assertEqual(P('c:/a/b').name, 'b')
897 self.assertEqual(P('c:a/b.py').name, 'b.py')
898 self.assertEqual(P('c:/a/b.py').name, 'b.py')
899 self.assertEqual(P('//My.py/Share.php').name, '')
900 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
901
902 def test_suffix(self):
903 P = self.cls
904 self.assertEqual(P('c:').suffix, '')
905 self.assertEqual(P('c:/').suffix, '')
906 self.assertEqual(P('c:a/b').suffix, '')
907 self.assertEqual(P('c:/a/b').suffix, '')
908 self.assertEqual(P('c:a/b.py').suffix, '.py')
909 self.assertEqual(P('c:/a/b.py').suffix, '.py')
910 self.assertEqual(P('c:a/.hgrc').suffix, '')
911 self.assertEqual(P('c:/a/.hgrc').suffix, '')
912 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
913 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
914 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
915 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
916 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
917 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
918 self.assertEqual(P('//My.py/Share.php').suffix, '')
919 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
920
921 def test_suffixes(self):
922 P = self.cls
923 self.assertEqual(P('c:').suffixes, [])
924 self.assertEqual(P('c:/').suffixes, [])
925 self.assertEqual(P('c:a/b').suffixes, [])
926 self.assertEqual(P('c:/a/b').suffixes, [])
927 self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
928 self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
929 self.assertEqual(P('c:a/.hgrc').suffixes, [])
930 self.assertEqual(P('c:/a/.hgrc').suffixes, [])
931 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
932 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
933 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
934 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
935 self.assertEqual(P('//My.py/Share.php').suffixes, [])
936 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
937 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
938 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
939
940 def test_stem(self):
941 P = self.cls
942 self.assertEqual(P('c:').stem, '')
943 self.assertEqual(P('c:.').stem, '')
944 self.assertEqual(P('c:..').stem, '..')
945 self.assertEqual(P('c:/').stem, '')
946 self.assertEqual(P('c:a/b').stem, 'b')
947 self.assertEqual(P('c:a/b.py').stem, 'b')
948 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
949 self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
950 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
951 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
952 'Some name. Ending with a dot.')
953
954 def test_with_name(self):
955 P = self.cls
956 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
957 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
958 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
959 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
960 self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
961 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
962 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
Antoine Pitrou7084e732014-07-06 21:31:12 -0400963 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:')
964 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:e')
965 self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e')
966 self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100967
968 def test_with_suffix(self):
969 P = self.cls
970 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
971 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
972 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
973 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100974 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100975 self.assertRaises(ValueError, P('').with_suffix, '.gz')
976 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
977 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
978 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100979 # Invalid suffix
980 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
981 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
982 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
983 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
984 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
985 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
986 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
987 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
988 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
989 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
990 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100991
992 def test_relative_to(self):
993 P = self.cls
Antoine Pitrou156b3612013-12-28 19:49:04 +0100994 p = P('C:Foo/Bar')
995 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
996 self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
997 self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
998 self.assertEqual(p.relative_to('c:foO'), P('Bar'))
999 self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
1000 self.assertEqual(p.relative_to(P('c:foO/baR')), P())
1001 self.assertEqual(p.relative_to('c:foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001002 # Unrelated paths
1003 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +01001004 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +01001005 self.assertRaises(ValueError, p.relative_to, P('d:'))
Antoine Pitrou156b3612013-12-28 19:49:04 +01001006 self.assertRaises(ValueError, p.relative_to, P('/'))
1007 self.assertRaises(ValueError, p.relative_to, P('Foo'))
1008 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1009 self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
1010 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
1011 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
1012 p = P('C:/Foo/Bar')
1013 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar'))
1014 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar'))
1015 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar')
1016 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar')
1017 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
1018 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
1019 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
1020 self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
1021 self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
1022 self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
1023 self.assertEqual(p.relative_to('c:/foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001024 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001025 self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
1026 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
1027 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
1028 self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001029 self.assertRaises(ValueError, p.relative_to, P('d:'))
1030 self.assertRaises(ValueError, p.relative_to, P('d:/'))
Antoine Pitrou156b3612013-12-28 19:49:04 +01001031 self.assertRaises(ValueError, p.relative_to, P('/'))
1032 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
1033 self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001034 # UNC paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001035 p = P('//Server/Share/Foo/Bar')
1036 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
1037 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
1038 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
1039 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
1040 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
1041 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
1042 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
1043 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001044 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001045 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
1046 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
1047 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
1048 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001049
1050 def test_is_absolute(self):
1051 P = self.cls
1052 # Under NT, only paths with both a drive and a root are absolute
1053 self.assertFalse(P().is_absolute())
1054 self.assertFalse(P('a').is_absolute())
1055 self.assertFalse(P('a/b/').is_absolute())
1056 self.assertFalse(P('/').is_absolute())
1057 self.assertFalse(P('/a').is_absolute())
1058 self.assertFalse(P('/a/b/').is_absolute())
1059 self.assertFalse(P('c:').is_absolute())
1060 self.assertFalse(P('c:a').is_absolute())
1061 self.assertFalse(P('c:a/b/').is_absolute())
1062 self.assertTrue(P('c:/').is_absolute())
1063 self.assertTrue(P('c:/a').is_absolute())
1064 self.assertTrue(P('c:/a/b/').is_absolute())
1065 # UNC paths are absolute by definition
1066 self.assertTrue(P('//a/b').is_absolute())
1067 self.assertTrue(P('//a/b/').is_absolute())
1068 self.assertTrue(P('//a/b/c').is_absolute())
1069 self.assertTrue(P('//a/b/c/d').is_absolute())
1070
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001071 def test_join(self):
1072 P = self.cls
1073 p = P('C:/a/b')
1074 pp = p.joinpath('x/y')
1075 self.assertEqual(pp, P('C:/a/b/x/y'))
1076 pp = p.joinpath('/x/y')
1077 self.assertEqual(pp, P('C:/x/y'))
1078 # Joining with a different drive => the first path is ignored, even
1079 # if the second path is relative.
1080 pp = p.joinpath('D:x/y')
1081 self.assertEqual(pp, P('D:x/y'))
1082 pp = p.joinpath('D:/x/y')
1083 self.assertEqual(pp, P('D:/x/y'))
1084 pp = p.joinpath('//host/share/x/y')
1085 self.assertEqual(pp, P('//host/share/x/y'))
1086 # Joining with the same drive => the first path is appended to if
1087 # the second path is relative.
1088 pp = p.joinpath('c:x/y')
1089 self.assertEqual(pp, P('C:/a/b/x/y'))
1090 pp = p.joinpath('c:/x/y')
1091 self.assertEqual(pp, P('C:/x/y'))
1092
1093 def test_div(self):
1094 # Basically the same as joinpath()
1095 P = self.cls
1096 p = P('C:/a/b')
1097 self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
1098 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
1099 self.assertEqual(p / '/x/y', P('C:/x/y'))
1100 self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
1101 # Joining with a different drive => the first path is ignored, even
1102 # if the second path is relative.
1103 self.assertEqual(p / 'D:x/y', P('D:x/y'))
1104 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
1105 self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
1106 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
1107 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
1108 # Joining with the same drive => the first path is appended to if
1109 # the second path is relative.
Serhiy Storchaka010ff582013-12-06 17:25:51 +02001110 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
1111 self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001112
Antoine Pitrou31119e42013-11-22 17:38:12 +01001113 def test_is_reserved(self):
1114 P = self.cls
1115 self.assertIs(False, P('').is_reserved())
1116 self.assertIs(False, P('/').is_reserved())
1117 self.assertIs(False, P('/foo/bar').is_reserved())
1118 self.assertIs(True, P('con').is_reserved())
1119 self.assertIs(True, P('NUL').is_reserved())
1120 self.assertIs(True, P('NUL.txt').is_reserved())
1121 self.assertIs(True, P('com1').is_reserved())
1122 self.assertIs(True, P('com9.bar').is_reserved())
1123 self.assertIs(False, P('bar.com9').is_reserved())
1124 self.assertIs(True, P('lpt1').is_reserved())
1125 self.assertIs(True, P('lpt9.bar').is_reserved())
1126 self.assertIs(False, P('bar.lpt9').is_reserved())
1127 # Only the last component matters
1128 self.assertIs(False, P('c:/NUL/con/baz').is_reserved())
1129 # UNC paths are never reserved
1130 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved())
1131
1132
1133class PurePathTest(_BasePurePathTest, unittest.TestCase):
1134 cls = pathlib.PurePath
1135
1136 def test_concrete_class(self):
1137 p = self.cls('a')
1138 self.assertIs(type(p),
1139 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath)
1140
1141 def test_different_flavours_unequal(self):
1142 p = pathlib.PurePosixPath('a')
1143 q = pathlib.PureWindowsPath('a')
1144 self.assertNotEqual(p, q)
1145
1146 def test_different_flavours_unordered(self):
1147 p = pathlib.PurePosixPath('a')
1148 q = pathlib.PureWindowsPath('a')
1149 with self.assertRaises(TypeError):
1150 p < q
1151 with self.assertRaises(TypeError):
1152 p <= q
1153 with self.assertRaises(TypeError):
1154 p > q
1155 with self.assertRaises(TypeError):
1156 p >= q
1157
1158
1159#
1160# Tests for the concrete classes
1161#
1162
1163# Make sure any symbolic links in the base test path are resolved
1164BASE = os.path.realpath(TESTFN)
1165join = lambda *x: os.path.join(BASE, *x)
1166rel_join = lambda *x: os.path.join(TESTFN, *x)
1167
1168def symlink_skip_reason():
1169 if not pathlib.supports_symlinks:
1170 return "no system support for symlinks"
1171 try:
1172 os.symlink(__file__, BASE)
1173 except OSError as e:
1174 return str(e)
1175 else:
1176 support.unlink(BASE)
1177 return None
1178
1179symlink_skip_reason = symlink_skip_reason()
1180
1181only_nt = unittest.skipIf(os.name != 'nt',
1182 'test requires a Windows-compatible system')
1183only_posix = unittest.skipIf(os.name == 'nt',
1184 'test requires a POSIX-compatible system')
1185with_symlinks = unittest.skipIf(symlink_skip_reason, symlink_skip_reason)
1186
1187
1188@only_posix
1189class PosixPathAsPureTest(PurePosixPathTest):
1190 cls = pathlib.PosixPath
1191
1192@only_nt
1193class WindowsPathAsPureTest(PureWindowsPathTest):
1194 cls = pathlib.WindowsPath
1195
1196
1197class _BasePathTest(object):
1198 """Tests for the FS-accessing functionalities of the Path classes."""
1199
1200 # (BASE)
1201 # |
1202 # |-- dirA/
1203 # |-- linkC -> "../dirB"
1204 # |-- dirB/
1205 # | |-- fileB
1206 # |-- linkD -> "../dirB"
1207 # |-- dirC/
1208 # | |-- fileC
1209 # | |-- fileD
1210 # |-- fileA
1211 # |-- linkA -> "fileA"
1212 # |-- linkB -> "dirB"
1213 #
1214
1215 def setUp(self):
1216 os.mkdir(BASE)
Victor Stinnerec864692014-07-21 19:19:05 +02001217 self.addCleanup(support.rmtree, BASE)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001218 os.mkdir(join('dirA'))
1219 os.mkdir(join('dirB'))
1220 os.mkdir(join('dirC'))
1221 os.mkdir(join('dirC', 'dirD'))
1222 with open(join('fileA'), 'wb') as f:
1223 f.write(b"this is file A\n")
1224 with open(join('dirB', 'fileB'), 'wb') as f:
1225 f.write(b"this is file B\n")
1226 with open(join('dirC', 'fileC'), 'wb') as f:
1227 f.write(b"this is file C\n")
1228 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
1229 f.write(b"this is file D\n")
1230 if not symlink_skip_reason:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001231 # Relative symlinks
1232 os.symlink('fileA', join('linkA'))
1233 os.symlink('non-existing', join('brokenLink'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001234 self.dirlink('dirB', join('linkB'))
1235 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001236 # This one goes upwards but doesn't create a loop
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001237 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1238
1239 if os.name == 'nt':
1240 # Workaround for http://bugs.python.org/issue13772
1241 def dirlink(self, src, dest):
1242 os.symlink(src, dest, target_is_directory=True)
1243 else:
1244 def dirlink(self, src, dest):
1245 os.symlink(src, dest)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001246
1247 def assertSame(self, path_a, path_b):
1248 self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1249 "%r and %r don't point to the same file" %
1250 (path_a, path_b))
1251
1252 def assertFileNotFound(self, func, *args, **kwargs):
1253 with self.assertRaises(FileNotFoundError) as cm:
1254 func(*args, **kwargs)
1255 self.assertEqual(cm.exception.errno, errno.ENOENT)
1256
1257 def _test_cwd(self, p):
1258 q = self.cls(os.getcwd())
1259 self.assertEqual(p, q)
1260 self.assertEqual(str(p), str(q))
1261 self.assertIs(type(p), type(q))
1262 self.assertTrue(p.is_absolute())
1263
1264 def test_cwd(self):
1265 p = self.cls.cwd()
1266 self._test_cwd(p)
1267
1268 def test_empty_path(self):
1269 # The empty path points to '.'
1270 p = self.cls('')
1271 self.assertEqual(p.stat(), os.stat('.'))
1272
1273 def test_exists(self):
1274 P = self.cls
1275 p = P(BASE)
1276 self.assertIs(True, p.exists())
1277 self.assertIs(True, (p / 'dirA').exists())
1278 self.assertIs(True, (p / 'fileA').exists())
1279 if not symlink_skip_reason:
1280 self.assertIs(True, (p / 'linkA').exists())
1281 self.assertIs(True, (p / 'linkB').exists())
1282 self.assertIs(False, (p / 'foo').exists())
1283 self.assertIs(False, P('/xyzzy').exists())
1284
1285 def test_open_common(self):
1286 p = self.cls(BASE)
1287 with (p / 'fileA').open('r') as f:
1288 self.assertIsInstance(f, io.TextIOBase)
1289 self.assertEqual(f.read(), "this is file A\n")
1290 with (p / 'fileA').open('rb') as f:
1291 self.assertIsInstance(f, io.BufferedIOBase)
1292 self.assertEqual(f.read().strip(), b"this is file A")
1293 with (p / 'fileA').open('rb', buffering=0) as f:
1294 self.assertIsInstance(f, io.RawIOBase)
1295 self.assertEqual(f.read().strip(), b"this is file A")
1296
1297 def test_iterdir(self):
1298 P = self.cls
1299 p = P(BASE)
1300 it = p.iterdir()
1301 paths = set(it)
1302 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1303 if not symlink_skip_reason:
1304 expected += ['linkA', 'linkB', 'brokenLink']
1305 self.assertEqual(paths, { P(BASE, q) for q in expected })
1306
1307 @with_symlinks
1308 def test_iterdir_symlink(self):
1309 # __iter__ on a symlink to a directory
1310 P = self.cls
1311 p = P(BASE, 'linkB')
1312 paths = set(p.iterdir())
1313 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1314 self.assertEqual(paths, expected)
1315
1316 def test_iterdir_nodir(self):
1317 # __iter__ on something that is not a directory
1318 p = self.cls(BASE, 'fileA')
1319 with self.assertRaises(OSError) as cm:
1320 next(p.iterdir())
1321 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1322 # (see issue #12802)
1323 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1324 errno.ENOENT, errno.EINVAL))
1325
1326 def test_glob_common(self):
1327 def _check(glob, expected):
1328 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1329 P = self.cls
1330 p = P(BASE)
1331 it = p.glob("fileA")
1332 self.assertIsInstance(it, collections.Iterator)
1333 _check(it, ["fileA"])
1334 _check(p.glob("fileB"), [])
1335 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1336 if symlink_skip_reason:
1337 _check(p.glob("*A"), ['dirA', 'fileA'])
1338 else:
1339 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1340 if symlink_skip_reason:
1341 _check(p.glob("*B/*"), ['dirB/fileB'])
1342 else:
1343 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1344 'linkB/fileB', 'linkB/linkD'])
1345 if symlink_skip_reason:
1346 _check(p.glob("*/fileB"), ['dirB/fileB'])
1347 else:
1348 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1349
1350 def test_rglob_common(self):
1351 def _check(glob, expected):
1352 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1353 P = self.cls
1354 p = P(BASE)
1355 it = p.rglob("fileA")
1356 self.assertIsInstance(it, collections.Iterator)
1357 # XXX cannot test because of symlink loops in the test setup
1358 #_check(it, ["fileA"])
1359 #_check(p.rglob("fileB"), ["dirB/fileB"])
1360 #_check(p.rglob("*/fileA"), [""])
1361 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1362 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1363 # No symlink loops here
1364 p = P(BASE, "dirC")
1365 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1366 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1367
1368 def test_glob_dotdot(self):
1369 # ".." is not special in globs
1370 P = self.cls
1371 p = P(BASE)
1372 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1373 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1374 self.assertEqual(set(p.glob("../xyzzy")), set())
1375
1376 def _check_resolve_relative(self, p, expected):
1377 q = p.resolve()
1378 self.assertEqual(q, expected)
1379
1380 def _check_resolve_absolute(self, p, expected):
1381 q = p.resolve()
1382 self.assertEqual(q, expected)
1383
1384 @with_symlinks
1385 def test_resolve_common(self):
1386 P = self.cls
1387 p = P(BASE, 'foo')
1388 with self.assertRaises(OSError) as cm:
1389 p.resolve()
1390 self.assertEqual(cm.exception.errno, errno.ENOENT)
1391 # These are all relative symlinks
1392 p = P(BASE, 'dirB', 'fileB')
1393 self._check_resolve_relative(p, p)
1394 p = P(BASE, 'linkA')
1395 self._check_resolve_relative(p, P(BASE, 'fileA'))
1396 p = P(BASE, 'dirA', 'linkC', 'fileB')
1397 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1398 p = P(BASE, 'dirB', 'linkD', 'fileB')
1399 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1400 # Now create absolute symlinks
1401 d = tempfile.mkdtemp(suffix='-dirD')
Victor Stinnerec864692014-07-21 19:19:05 +02001402 self.addCleanup(support.rmtree, d)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001403 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1404 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1405 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1406 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1407
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001408 @with_symlinks
1409 def test_resolve_dot(self):
1410 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1411 p = self.cls(BASE)
1412 self.dirlink('.', join('0'))
Antoine Pitroucc157512013-12-03 17:13:13 +01001413 self.dirlink(os.path.join('0', '0'), join('1'))
1414 self.dirlink(os.path.join('1', '1'), join('2'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001415 q = p / '2'
1416 self.assertEqual(q.resolve(), p)
1417
Antoine Pitrou31119e42013-11-22 17:38:12 +01001418 def test_with(self):
1419 p = self.cls(BASE)
1420 it = p.iterdir()
1421 it2 = p.iterdir()
1422 next(it2)
1423 with p:
1424 pass
1425 # I/O operation on closed path
1426 self.assertRaises(ValueError, next, it)
1427 self.assertRaises(ValueError, next, it2)
1428 self.assertRaises(ValueError, p.open)
1429 self.assertRaises(ValueError, p.resolve)
1430 self.assertRaises(ValueError, p.absolute)
1431 self.assertRaises(ValueError, p.__enter__)
1432
1433 def test_chmod(self):
1434 p = self.cls(BASE) / 'fileA'
1435 mode = p.stat().st_mode
1436 # Clear writable bit
1437 new_mode = mode & ~0o222
1438 p.chmod(new_mode)
1439 self.assertEqual(p.stat().st_mode, new_mode)
1440 # Set writable bit
1441 new_mode = mode | 0o222
1442 p.chmod(new_mode)
1443 self.assertEqual(p.stat().st_mode, new_mode)
1444
1445 # XXX also need a test for lchmod
1446
1447 def test_stat(self):
1448 p = self.cls(BASE) / 'fileA'
1449 st = p.stat()
1450 self.assertEqual(p.stat(), st)
1451 # Change file mode by flipping write bit
1452 p.chmod(st.st_mode ^ 0o222)
1453 self.addCleanup(p.chmod, st.st_mode)
1454 self.assertNotEqual(p.stat(), st)
1455
1456 @with_symlinks
1457 def test_lstat(self):
1458 p = self.cls(BASE)/ 'linkA'
1459 st = p.stat()
1460 self.assertNotEqual(st, p.lstat())
1461
1462 def test_lstat_nosymlink(self):
1463 p = self.cls(BASE) / 'fileA'
1464 st = p.stat()
1465 self.assertEqual(st, p.lstat())
1466
1467 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1468 def test_owner(self):
1469 p = self.cls(BASE) / 'fileA'
1470 uid = p.stat().st_uid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001471 try:
1472 name = pwd.getpwuid(uid).pw_name
1473 except KeyError:
1474 self.skipTest(
1475 "user %d doesn't have an entry in the system database" % uid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001476 self.assertEqual(name, p.owner())
1477
1478 @unittest.skipUnless(grp, "the grp module is needed for this test")
1479 def test_group(self):
1480 p = self.cls(BASE) / 'fileA'
1481 gid = p.stat().st_gid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001482 try:
1483 name = grp.getgrgid(gid).gr_name
1484 except KeyError:
1485 self.skipTest(
1486 "group %d doesn't have an entry in the system database" % gid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001487 self.assertEqual(name, p.group())
1488
1489 def test_unlink(self):
1490 p = self.cls(BASE) / 'fileA'
1491 p.unlink()
1492 self.assertFileNotFound(p.stat)
1493 self.assertFileNotFound(p.unlink)
1494
1495 def test_rmdir(self):
1496 p = self.cls(BASE) / 'dirA'
1497 for q in p.iterdir():
1498 q.unlink()
1499 p.rmdir()
1500 self.assertFileNotFound(p.stat)
1501 self.assertFileNotFound(p.unlink)
1502
1503 def test_rename(self):
1504 P = self.cls(BASE)
1505 p = P / 'fileA'
1506 size = p.stat().st_size
1507 # Renaming to another path
1508 q = P / 'dirA' / 'fileAA'
1509 p.rename(q)
1510 self.assertEqual(q.stat().st_size, size)
1511 self.assertFileNotFound(p.stat)
1512 # Renaming to a str of a relative path
1513 r = rel_join('fileAAA')
1514 q.rename(r)
1515 self.assertEqual(os.stat(r).st_size, size)
1516 self.assertFileNotFound(q.stat)
1517
1518 def test_replace(self):
1519 P = self.cls(BASE)
1520 p = P / 'fileA'
1521 size = p.stat().st_size
1522 # Replacing a non-existing path
1523 q = P / 'dirA' / 'fileAA'
1524 p.replace(q)
1525 self.assertEqual(q.stat().st_size, size)
1526 self.assertFileNotFound(p.stat)
1527 # Replacing another (existing) path
1528 r = rel_join('dirB', 'fileB')
1529 q.replace(r)
1530 self.assertEqual(os.stat(r).st_size, size)
1531 self.assertFileNotFound(q.stat)
1532
1533 def test_touch_common(self):
1534 P = self.cls(BASE)
1535 p = P / 'newfileA'
1536 self.assertFalse(p.exists())
1537 p.touch()
1538 self.assertTrue(p.exists())
Antoine Pitrou0f575642013-11-22 23:20:08 +01001539 st = p.stat()
1540 old_mtime = st.st_mtime
1541 old_mtime_ns = st.st_mtime_ns
Antoine Pitrou31119e42013-11-22 17:38:12 +01001542 # Rewind the mtime sufficiently far in the past to work around
1543 # filesystem-specific timestamp granularity.
1544 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001545 # The file mtime should be refreshed by calling touch() again
Antoine Pitrou31119e42013-11-22 17:38:12 +01001546 p.touch()
Antoine Pitrou0f575642013-11-22 23:20:08 +01001547 st = p.stat()
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001548 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
1549 self.assertGreaterEqual(st.st_mtime, old_mtime)
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001550 # Now with exist_ok=False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001551 p = P / 'newfileB'
1552 self.assertFalse(p.exists())
1553 p.touch(mode=0o700, exist_ok=False)
1554 self.assertTrue(p.exists())
1555 self.assertRaises(OSError, p.touch, exist_ok=False)
1556
Antoine Pitrou8b784932013-11-23 14:52:39 +01001557 def test_touch_nochange(self):
1558 P = self.cls(BASE)
1559 p = P / 'fileA'
1560 p.touch()
1561 with p.open('rb') as f:
1562 self.assertEqual(f.read().strip(), b"this is file A")
1563
Antoine Pitrou31119e42013-11-22 17:38:12 +01001564 def test_mkdir(self):
1565 P = self.cls(BASE)
1566 p = P / 'newdirA'
1567 self.assertFalse(p.exists())
1568 p.mkdir()
1569 self.assertTrue(p.exists())
1570 self.assertTrue(p.is_dir())
1571 with self.assertRaises(OSError) as cm:
1572 p.mkdir()
1573 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001574
1575 def test_mkdir_parents(self):
1576 # Creating a chain of directories
1577 p = self.cls(BASE, 'newdirB', 'newdirC')
1578 self.assertFalse(p.exists())
1579 with self.assertRaises(OSError) as cm:
1580 p.mkdir()
1581 self.assertEqual(cm.exception.errno, errno.ENOENT)
1582 p.mkdir(parents=True)
1583 self.assertTrue(p.exists())
1584 self.assertTrue(p.is_dir())
1585 with self.assertRaises(OSError) as cm:
1586 p.mkdir(parents=True)
1587 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001588 # test `mode` arg
1589 mode = stat.S_IMODE(p.stat().st_mode) # default mode
1590 p = self.cls(BASE, 'newdirD', 'newdirE')
1591 p.mkdir(0o555, parents=True)
1592 self.assertTrue(p.exists())
1593 self.assertTrue(p.is_dir())
1594 if os.name != 'nt':
1595 # the directory's permissions follow the mode argument
Gregory P. Smithb599c612014-01-20 01:10:33 -08001596 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001597 # the parent's permissions follow the default process settings
1598 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001599
1600 @with_symlinks
1601 def test_symlink_to(self):
1602 P = self.cls(BASE)
1603 target = P / 'fileA'
1604 # Symlinking a path target
1605 link = P / 'dirA' / 'linkAA'
1606 link.symlink_to(target)
1607 self.assertEqual(link.stat(), target.stat())
1608 self.assertNotEqual(link.lstat(), target.stat())
1609 # Symlinking a str target
1610 link = P / 'dirA' / 'linkAAA'
1611 link.symlink_to(str(target))
1612 self.assertEqual(link.stat(), target.stat())
1613 self.assertNotEqual(link.lstat(), target.stat())
1614 self.assertFalse(link.is_dir())
1615 # Symlinking to a directory
1616 target = P / 'dirB'
1617 link = P / 'dirA' / 'linkAAAA'
1618 link.symlink_to(target, target_is_directory=True)
1619 self.assertEqual(link.stat(), target.stat())
1620 self.assertNotEqual(link.lstat(), target.stat())
1621 self.assertTrue(link.is_dir())
1622 self.assertTrue(list(link.iterdir()))
1623
1624 def test_is_dir(self):
1625 P = self.cls(BASE)
1626 self.assertTrue((P / 'dirA').is_dir())
1627 self.assertFalse((P / 'fileA').is_dir())
1628 self.assertFalse((P / 'non-existing').is_dir())
1629 if not symlink_skip_reason:
1630 self.assertFalse((P / 'linkA').is_dir())
1631 self.assertTrue((P / 'linkB').is_dir())
1632 self.assertFalse((P/ 'brokenLink').is_dir())
1633
1634 def test_is_file(self):
1635 P = self.cls(BASE)
1636 self.assertTrue((P / 'fileA').is_file())
1637 self.assertFalse((P / 'dirA').is_file())
1638 self.assertFalse((P / 'non-existing').is_file())
1639 if not symlink_skip_reason:
1640 self.assertTrue((P / 'linkA').is_file())
1641 self.assertFalse((P / 'linkB').is_file())
1642 self.assertFalse((P/ 'brokenLink').is_file())
1643
1644 def test_is_symlink(self):
1645 P = self.cls(BASE)
1646 self.assertFalse((P / 'fileA').is_symlink())
1647 self.assertFalse((P / 'dirA').is_symlink())
1648 self.assertFalse((P / 'non-existing').is_symlink())
1649 if not symlink_skip_reason:
1650 self.assertTrue((P / 'linkA').is_symlink())
1651 self.assertTrue((P / 'linkB').is_symlink())
1652 self.assertTrue((P/ 'brokenLink').is_symlink())
1653
1654 def test_is_fifo_false(self):
1655 P = self.cls(BASE)
1656 self.assertFalse((P / 'fileA').is_fifo())
1657 self.assertFalse((P / 'dirA').is_fifo())
1658 self.assertFalse((P / 'non-existing').is_fifo())
1659
1660 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1661 def test_is_fifo_true(self):
1662 P = self.cls(BASE, 'myfifo')
1663 os.mkfifo(str(P))
1664 self.assertTrue(P.is_fifo())
1665 self.assertFalse(P.is_socket())
1666 self.assertFalse(P.is_file())
1667
1668 def test_is_socket_false(self):
1669 P = self.cls(BASE)
1670 self.assertFalse((P / 'fileA').is_socket())
1671 self.assertFalse((P / 'dirA').is_socket())
1672 self.assertFalse((P / 'non-existing').is_socket())
1673
1674 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1675 def test_is_socket_true(self):
1676 P = self.cls(BASE, 'mysock')
Antoine Pitrou330ce592013-11-22 18:05:06 +01001677 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001678 self.addCleanup(sock.close)
Antoine Pitrou330ce592013-11-22 18:05:06 +01001679 try:
1680 sock.bind(str(P))
1681 except OSError as e:
1682 if "AF_UNIX path too long" in str(e):
1683 self.skipTest("cannot bind Unix socket: " + str(e))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001684 self.assertTrue(P.is_socket())
1685 self.assertFalse(P.is_fifo())
1686 self.assertFalse(P.is_file())
1687
1688 def test_is_block_device_false(self):
1689 P = self.cls(BASE)
1690 self.assertFalse((P / 'fileA').is_block_device())
1691 self.assertFalse((P / 'dirA').is_block_device())
1692 self.assertFalse((P / 'non-existing').is_block_device())
1693
1694 def test_is_char_device_false(self):
1695 P = self.cls(BASE)
1696 self.assertFalse((P / 'fileA').is_char_device())
1697 self.assertFalse((P / 'dirA').is_char_device())
1698 self.assertFalse((P / 'non-existing').is_char_device())
1699
1700 def test_is_char_device_true(self):
1701 # Under Unix, /dev/null should generally be a char device
1702 P = self.cls('/dev/null')
1703 if not P.exists():
1704 self.skipTest("/dev/null required")
1705 self.assertTrue(P.is_char_device())
1706 self.assertFalse(P.is_block_device())
1707 self.assertFalse(P.is_file())
1708
1709 def test_pickling_common(self):
1710 p = self.cls(BASE, 'fileA')
1711 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1712 dumped = pickle.dumps(p, proto)
1713 pp = pickle.loads(dumped)
1714 self.assertEqual(pp.stat(), p.stat())
1715
1716 def test_parts_interning(self):
1717 P = self.cls
1718 p = P('/usr/bin/foo')
1719 q = P('/usr/local/bin')
1720 # 'usr'
1721 self.assertIs(p.parts[1], q.parts[1])
1722 # 'bin'
1723 self.assertIs(p.parts[2], q.parts[3])
1724
Antoine Pitrouc274fd22013-12-16 19:57:41 +01001725 def _check_complex_symlinks(self, link0_target):
1726 # Test solving a non-looping chain of symlinks (issue #19887)
1727 P = self.cls(BASE)
1728 self.dirlink(os.path.join('link0', 'link0'), join('link1'))
1729 self.dirlink(os.path.join('link1', 'link1'), join('link2'))
1730 self.dirlink(os.path.join('link2', 'link2'), join('link3'))
1731 self.dirlink(link0_target, join('link0'))
1732
1733 # Resolve absolute paths
1734 p = (P / 'link0').resolve()
1735 self.assertEqual(p, P)
1736 self.assertEqual(str(p), BASE)
1737 p = (P / 'link1').resolve()
1738 self.assertEqual(p, P)
1739 self.assertEqual(str(p), BASE)
1740 p = (P / 'link2').resolve()
1741 self.assertEqual(p, P)
1742 self.assertEqual(str(p), BASE)
1743 p = (P / 'link3').resolve()
1744 self.assertEqual(p, P)
1745 self.assertEqual(str(p), BASE)
1746
1747 # Resolve relative paths
1748 old_path = os.getcwd()
1749 os.chdir(BASE)
1750 try:
1751 p = self.cls('link0').resolve()
1752 self.assertEqual(p, P)
1753 self.assertEqual(str(p), BASE)
1754 p = self.cls('link1').resolve()
1755 self.assertEqual(p, P)
1756 self.assertEqual(str(p), BASE)
1757 p = self.cls('link2').resolve()
1758 self.assertEqual(p, P)
1759 self.assertEqual(str(p), BASE)
1760 p = self.cls('link3').resolve()
1761 self.assertEqual(p, P)
1762 self.assertEqual(str(p), BASE)
1763 finally:
1764 os.chdir(old_path)
1765
1766 @with_symlinks
1767 def test_complex_symlinks_absolute(self):
1768 self._check_complex_symlinks(BASE)
1769
1770 @with_symlinks
1771 def test_complex_symlinks_relative(self):
1772 self._check_complex_symlinks('.')
1773
1774 @with_symlinks
1775 def test_complex_symlinks_relative_dot_dot(self):
1776 self._check_complex_symlinks(os.path.join('dirA', '..'))
1777
Antoine Pitrou31119e42013-11-22 17:38:12 +01001778
1779class PathTest(_BasePathTest, unittest.TestCase):
1780 cls = pathlib.Path
1781
1782 def test_concrete_class(self):
1783 p = self.cls('a')
1784 self.assertIs(type(p),
1785 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1786
1787 def test_unsupported_flavour(self):
1788 if os.name == 'nt':
1789 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1790 else:
1791 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1792
1793
1794@only_posix
1795class PosixPathTest(_BasePathTest, unittest.TestCase):
1796 cls = pathlib.PosixPath
1797
1798 def _check_symlink_loop(self, *args):
1799 path = self.cls(*args)
1800 with self.assertRaises(RuntimeError):
1801 print(path.resolve())
1802
1803 def test_open_mode(self):
1804 old_mask = os.umask(0)
1805 self.addCleanup(os.umask, old_mask)
1806 p = self.cls(BASE)
1807 with (p / 'new_file').open('wb'):
1808 pass
1809 st = os.stat(join('new_file'))
1810 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1811 os.umask(0o022)
1812 with (p / 'other_new_file').open('wb'):
1813 pass
1814 st = os.stat(join('other_new_file'))
1815 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1816
1817 def test_touch_mode(self):
1818 old_mask = os.umask(0)
1819 self.addCleanup(os.umask, old_mask)
1820 p = self.cls(BASE)
1821 (p / 'new_file').touch()
1822 st = os.stat(join('new_file'))
1823 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1824 os.umask(0o022)
1825 (p / 'other_new_file').touch()
1826 st = os.stat(join('other_new_file'))
1827 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1828 (p / 'masked_new_file').touch(mode=0o750)
1829 st = os.stat(join('masked_new_file'))
1830 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1831
1832 @with_symlinks
1833 def test_resolve_loop(self):
1834 # Loop detection for broken symlinks under POSIX
1835 P = self.cls
1836 # Loops with relative symlinks
1837 os.symlink('linkX/inside', join('linkX'))
1838 self._check_symlink_loop(BASE, 'linkX')
1839 os.symlink('linkY', join('linkY'))
1840 self._check_symlink_loop(BASE, 'linkY')
1841 os.symlink('linkZ/../linkZ', join('linkZ'))
1842 self._check_symlink_loop(BASE, 'linkZ')
1843 # Loops with absolute symlinks
1844 os.symlink(join('linkU/inside'), join('linkU'))
1845 self._check_symlink_loop(BASE, 'linkU')
1846 os.symlink(join('linkV'), join('linkV'))
1847 self._check_symlink_loop(BASE, 'linkV')
1848 os.symlink(join('linkW/../linkW'), join('linkW'))
1849 self._check_symlink_loop(BASE, 'linkW')
1850
1851 def test_glob(self):
1852 P = self.cls
1853 p = P(BASE)
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001854 given = set(p.glob("FILEa"))
1855 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1856 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001857 self.assertEqual(set(p.glob("FILEa*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001858
1859 def test_rglob(self):
1860 P = self.cls
1861 p = P(BASE, "dirC")
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001862 given = set(p.rglob("FILEd"))
1863 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1864 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001865 self.assertEqual(set(p.rglob("FILEd*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001866
1867
1868@only_nt
1869class WindowsPathTest(_BasePathTest, unittest.TestCase):
1870 cls = pathlib.WindowsPath
1871
1872 def test_glob(self):
1873 P = self.cls
1874 p = P(BASE)
1875 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1876
1877 def test_rglob(self):
1878 P = self.cls
1879 p = P(BASE, "dirC")
1880 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1881
1882
1883if __name__ == "__main__":
1884 unittest.main()