blob: ab88c340bf0de8711e44d84c5bf24ae88daaeb6c [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())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001279 self.assertIs(False, (p / 'fileA' / 'bah').exists())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001280 if not symlink_skip_reason:
1281 self.assertIs(True, (p / 'linkA').exists())
1282 self.assertIs(True, (p / 'linkB').exists())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001283 self.assertIs(True, (p / 'linkB' / 'fileB').exists())
1284 self.assertIs(False, (p / 'linkA' / 'bah').exists())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001285 self.assertIs(False, (p / 'foo').exists())
1286 self.assertIs(False, P('/xyzzy').exists())
1287
1288 def test_open_common(self):
1289 p = self.cls(BASE)
1290 with (p / 'fileA').open('r') as f:
1291 self.assertIsInstance(f, io.TextIOBase)
1292 self.assertEqual(f.read(), "this is file A\n")
1293 with (p / 'fileA').open('rb') as f:
1294 self.assertIsInstance(f, io.BufferedIOBase)
1295 self.assertEqual(f.read().strip(), b"this is file A")
1296 with (p / 'fileA').open('rb', buffering=0) as f:
1297 self.assertIsInstance(f, io.RawIOBase)
1298 self.assertEqual(f.read().strip(), b"this is file A")
1299
1300 def test_iterdir(self):
1301 P = self.cls
1302 p = P(BASE)
1303 it = p.iterdir()
1304 paths = set(it)
1305 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1306 if not symlink_skip_reason:
1307 expected += ['linkA', 'linkB', 'brokenLink']
1308 self.assertEqual(paths, { P(BASE, q) for q in expected })
1309
1310 @with_symlinks
1311 def test_iterdir_symlink(self):
1312 # __iter__ on a symlink to a directory
1313 P = self.cls
1314 p = P(BASE, 'linkB')
1315 paths = set(p.iterdir())
1316 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1317 self.assertEqual(paths, expected)
1318
1319 def test_iterdir_nodir(self):
1320 # __iter__ on something that is not a directory
1321 p = self.cls(BASE, 'fileA')
1322 with self.assertRaises(OSError) as cm:
1323 next(p.iterdir())
1324 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1325 # (see issue #12802)
1326 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1327 errno.ENOENT, errno.EINVAL))
1328
1329 def test_glob_common(self):
1330 def _check(glob, expected):
1331 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1332 P = self.cls
1333 p = P(BASE)
1334 it = p.glob("fileA")
1335 self.assertIsInstance(it, collections.Iterator)
1336 _check(it, ["fileA"])
1337 _check(p.glob("fileB"), [])
1338 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1339 if symlink_skip_reason:
1340 _check(p.glob("*A"), ['dirA', 'fileA'])
1341 else:
1342 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1343 if symlink_skip_reason:
1344 _check(p.glob("*B/*"), ['dirB/fileB'])
1345 else:
1346 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1347 'linkB/fileB', 'linkB/linkD'])
1348 if symlink_skip_reason:
1349 _check(p.glob("*/fileB"), ['dirB/fileB'])
1350 else:
1351 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1352
1353 def test_rglob_common(self):
1354 def _check(glob, expected):
1355 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1356 P = self.cls
1357 p = P(BASE)
1358 it = p.rglob("fileA")
1359 self.assertIsInstance(it, collections.Iterator)
1360 # XXX cannot test because of symlink loops in the test setup
1361 #_check(it, ["fileA"])
1362 #_check(p.rglob("fileB"), ["dirB/fileB"])
1363 #_check(p.rglob("*/fileA"), [""])
1364 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1365 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1366 # No symlink loops here
1367 p = P(BASE, "dirC")
1368 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1369 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1370
1371 def test_glob_dotdot(self):
1372 # ".." is not special in globs
1373 P = self.cls
1374 p = P(BASE)
1375 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1376 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1377 self.assertEqual(set(p.glob("../xyzzy")), set())
1378
1379 def _check_resolve_relative(self, p, expected):
1380 q = p.resolve()
1381 self.assertEqual(q, expected)
1382
1383 def _check_resolve_absolute(self, p, expected):
1384 q = p.resolve()
1385 self.assertEqual(q, expected)
1386
1387 @with_symlinks
1388 def test_resolve_common(self):
1389 P = self.cls
1390 p = P(BASE, 'foo')
1391 with self.assertRaises(OSError) as cm:
1392 p.resolve()
1393 self.assertEqual(cm.exception.errno, errno.ENOENT)
1394 # These are all relative symlinks
1395 p = P(BASE, 'dirB', 'fileB')
1396 self._check_resolve_relative(p, p)
1397 p = P(BASE, 'linkA')
1398 self._check_resolve_relative(p, P(BASE, 'fileA'))
1399 p = P(BASE, 'dirA', 'linkC', 'fileB')
1400 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1401 p = P(BASE, 'dirB', 'linkD', 'fileB')
1402 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1403 # Now create absolute symlinks
1404 d = tempfile.mkdtemp(suffix='-dirD')
Victor Stinnerec864692014-07-21 19:19:05 +02001405 self.addCleanup(support.rmtree, d)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001406 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1407 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1408 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1409 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1410
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001411 @with_symlinks
1412 def test_resolve_dot(self):
1413 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1414 p = self.cls(BASE)
1415 self.dirlink('.', join('0'))
Antoine Pitroucc157512013-12-03 17:13:13 +01001416 self.dirlink(os.path.join('0', '0'), join('1'))
1417 self.dirlink(os.path.join('1', '1'), join('2'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001418 q = p / '2'
1419 self.assertEqual(q.resolve(), p)
1420
Antoine Pitrou31119e42013-11-22 17:38:12 +01001421 def test_with(self):
1422 p = self.cls(BASE)
1423 it = p.iterdir()
1424 it2 = p.iterdir()
1425 next(it2)
1426 with p:
1427 pass
1428 # I/O operation on closed path
1429 self.assertRaises(ValueError, next, it)
1430 self.assertRaises(ValueError, next, it2)
1431 self.assertRaises(ValueError, p.open)
1432 self.assertRaises(ValueError, p.resolve)
1433 self.assertRaises(ValueError, p.absolute)
1434 self.assertRaises(ValueError, p.__enter__)
1435
1436 def test_chmod(self):
1437 p = self.cls(BASE) / 'fileA'
1438 mode = p.stat().st_mode
1439 # Clear writable bit
1440 new_mode = mode & ~0o222
1441 p.chmod(new_mode)
1442 self.assertEqual(p.stat().st_mode, new_mode)
1443 # Set writable bit
1444 new_mode = mode | 0o222
1445 p.chmod(new_mode)
1446 self.assertEqual(p.stat().st_mode, new_mode)
1447
1448 # XXX also need a test for lchmod
1449
1450 def test_stat(self):
1451 p = self.cls(BASE) / 'fileA'
1452 st = p.stat()
1453 self.assertEqual(p.stat(), st)
1454 # Change file mode by flipping write bit
1455 p.chmod(st.st_mode ^ 0o222)
1456 self.addCleanup(p.chmod, st.st_mode)
1457 self.assertNotEqual(p.stat(), st)
1458
1459 @with_symlinks
1460 def test_lstat(self):
1461 p = self.cls(BASE)/ 'linkA'
1462 st = p.stat()
1463 self.assertNotEqual(st, p.lstat())
1464
1465 def test_lstat_nosymlink(self):
1466 p = self.cls(BASE) / 'fileA'
1467 st = p.stat()
1468 self.assertEqual(st, p.lstat())
1469
1470 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1471 def test_owner(self):
1472 p = self.cls(BASE) / 'fileA'
1473 uid = p.stat().st_uid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001474 try:
1475 name = pwd.getpwuid(uid).pw_name
1476 except KeyError:
1477 self.skipTest(
1478 "user %d doesn't have an entry in the system database" % uid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001479 self.assertEqual(name, p.owner())
1480
1481 @unittest.skipUnless(grp, "the grp module is needed for this test")
1482 def test_group(self):
1483 p = self.cls(BASE) / 'fileA'
1484 gid = p.stat().st_gid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001485 try:
1486 name = grp.getgrgid(gid).gr_name
1487 except KeyError:
1488 self.skipTest(
1489 "group %d doesn't have an entry in the system database" % gid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001490 self.assertEqual(name, p.group())
1491
1492 def test_unlink(self):
1493 p = self.cls(BASE) / 'fileA'
1494 p.unlink()
1495 self.assertFileNotFound(p.stat)
1496 self.assertFileNotFound(p.unlink)
1497
1498 def test_rmdir(self):
1499 p = self.cls(BASE) / 'dirA'
1500 for q in p.iterdir():
1501 q.unlink()
1502 p.rmdir()
1503 self.assertFileNotFound(p.stat)
1504 self.assertFileNotFound(p.unlink)
1505
1506 def test_rename(self):
1507 P = self.cls(BASE)
1508 p = P / 'fileA'
1509 size = p.stat().st_size
1510 # Renaming to another path
1511 q = P / 'dirA' / 'fileAA'
1512 p.rename(q)
1513 self.assertEqual(q.stat().st_size, size)
1514 self.assertFileNotFound(p.stat)
1515 # Renaming to a str of a relative path
1516 r = rel_join('fileAAA')
1517 q.rename(r)
1518 self.assertEqual(os.stat(r).st_size, size)
1519 self.assertFileNotFound(q.stat)
1520
1521 def test_replace(self):
1522 P = self.cls(BASE)
1523 p = P / 'fileA'
1524 size = p.stat().st_size
1525 # Replacing a non-existing path
1526 q = P / 'dirA' / 'fileAA'
1527 p.replace(q)
1528 self.assertEqual(q.stat().st_size, size)
1529 self.assertFileNotFound(p.stat)
1530 # Replacing another (existing) path
1531 r = rel_join('dirB', 'fileB')
1532 q.replace(r)
1533 self.assertEqual(os.stat(r).st_size, size)
1534 self.assertFileNotFound(q.stat)
1535
1536 def test_touch_common(self):
1537 P = self.cls(BASE)
1538 p = P / 'newfileA'
1539 self.assertFalse(p.exists())
1540 p.touch()
1541 self.assertTrue(p.exists())
Antoine Pitrou0f575642013-11-22 23:20:08 +01001542 st = p.stat()
1543 old_mtime = st.st_mtime
1544 old_mtime_ns = st.st_mtime_ns
Antoine Pitrou31119e42013-11-22 17:38:12 +01001545 # Rewind the mtime sufficiently far in the past to work around
1546 # filesystem-specific timestamp granularity.
1547 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001548 # The file mtime should be refreshed by calling touch() again
Antoine Pitrou31119e42013-11-22 17:38:12 +01001549 p.touch()
Antoine Pitrou0f575642013-11-22 23:20:08 +01001550 st = p.stat()
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001551 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
1552 self.assertGreaterEqual(st.st_mtime, old_mtime)
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001553 # Now with exist_ok=False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001554 p = P / 'newfileB'
1555 self.assertFalse(p.exists())
1556 p.touch(mode=0o700, exist_ok=False)
1557 self.assertTrue(p.exists())
1558 self.assertRaises(OSError, p.touch, exist_ok=False)
1559
Antoine Pitrou8b784932013-11-23 14:52:39 +01001560 def test_touch_nochange(self):
1561 P = self.cls(BASE)
1562 p = P / 'fileA'
1563 p.touch()
1564 with p.open('rb') as f:
1565 self.assertEqual(f.read().strip(), b"this is file A")
1566
Antoine Pitrou31119e42013-11-22 17:38:12 +01001567 def test_mkdir(self):
1568 P = self.cls(BASE)
1569 p = P / 'newdirA'
1570 self.assertFalse(p.exists())
1571 p.mkdir()
1572 self.assertTrue(p.exists())
1573 self.assertTrue(p.is_dir())
1574 with self.assertRaises(OSError) as cm:
1575 p.mkdir()
1576 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001577
1578 def test_mkdir_parents(self):
1579 # Creating a chain of directories
1580 p = self.cls(BASE, 'newdirB', 'newdirC')
1581 self.assertFalse(p.exists())
1582 with self.assertRaises(OSError) as cm:
1583 p.mkdir()
1584 self.assertEqual(cm.exception.errno, errno.ENOENT)
1585 p.mkdir(parents=True)
1586 self.assertTrue(p.exists())
1587 self.assertTrue(p.is_dir())
1588 with self.assertRaises(OSError) as cm:
1589 p.mkdir(parents=True)
1590 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001591 # test `mode` arg
1592 mode = stat.S_IMODE(p.stat().st_mode) # default mode
1593 p = self.cls(BASE, 'newdirD', 'newdirE')
1594 p.mkdir(0o555, parents=True)
1595 self.assertTrue(p.exists())
1596 self.assertTrue(p.is_dir())
1597 if os.name != 'nt':
1598 # the directory's permissions follow the mode argument
Gregory P. Smithb599c612014-01-20 01:10:33 -08001599 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001600 # the parent's permissions follow the default process settings
1601 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001602
1603 @with_symlinks
1604 def test_symlink_to(self):
1605 P = self.cls(BASE)
1606 target = P / 'fileA'
1607 # Symlinking a path target
1608 link = P / 'dirA' / 'linkAA'
1609 link.symlink_to(target)
1610 self.assertEqual(link.stat(), target.stat())
1611 self.assertNotEqual(link.lstat(), target.stat())
1612 # Symlinking a str target
1613 link = P / 'dirA' / 'linkAAA'
1614 link.symlink_to(str(target))
1615 self.assertEqual(link.stat(), target.stat())
1616 self.assertNotEqual(link.lstat(), target.stat())
1617 self.assertFalse(link.is_dir())
1618 # Symlinking to a directory
1619 target = P / 'dirB'
1620 link = P / 'dirA' / 'linkAAAA'
1621 link.symlink_to(target, target_is_directory=True)
1622 self.assertEqual(link.stat(), target.stat())
1623 self.assertNotEqual(link.lstat(), target.stat())
1624 self.assertTrue(link.is_dir())
1625 self.assertTrue(list(link.iterdir()))
1626
1627 def test_is_dir(self):
1628 P = self.cls(BASE)
1629 self.assertTrue((P / 'dirA').is_dir())
1630 self.assertFalse((P / 'fileA').is_dir())
1631 self.assertFalse((P / 'non-existing').is_dir())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001632 self.assertFalse((P / 'fileA' / 'bah').is_dir())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001633 if not symlink_skip_reason:
1634 self.assertFalse((P / 'linkA').is_dir())
1635 self.assertTrue((P / 'linkB').is_dir())
1636 self.assertFalse((P/ 'brokenLink').is_dir())
1637
1638 def test_is_file(self):
1639 P = self.cls(BASE)
1640 self.assertTrue((P / 'fileA').is_file())
1641 self.assertFalse((P / 'dirA').is_file())
1642 self.assertFalse((P / 'non-existing').is_file())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001643 self.assertFalse((P / 'fileA' / 'bah').is_file())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001644 if not symlink_skip_reason:
1645 self.assertTrue((P / 'linkA').is_file())
1646 self.assertFalse((P / 'linkB').is_file())
1647 self.assertFalse((P/ 'brokenLink').is_file())
1648
1649 def test_is_symlink(self):
1650 P = self.cls(BASE)
1651 self.assertFalse((P / 'fileA').is_symlink())
1652 self.assertFalse((P / 'dirA').is_symlink())
1653 self.assertFalse((P / 'non-existing').is_symlink())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001654 self.assertFalse((P / 'fileA' / 'bah').is_symlink())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001655 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())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001665 self.assertFalse((P / 'fileA' / 'bah').is_fifo())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001666
1667 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1668 def test_is_fifo_true(self):
1669 P = self.cls(BASE, 'myfifo')
1670 os.mkfifo(str(P))
1671 self.assertTrue(P.is_fifo())
1672 self.assertFalse(P.is_socket())
1673 self.assertFalse(P.is_file())
1674
1675 def test_is_socket_false(self):
1676 P = self.cls(BASE)
1677 self.assertFalse((P / 'fileA').is_socket())
1678 self.assertFalse((P / 'dirA').is_socket())
1679 self.assertFalse((P / 'non-existing').is_socket())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001680 self.assertFalse((P / 'fileA' / 'bah').is_socket())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001681
1682 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1683 def test_is_socket_true(self):
1684 P = self.cls(BASE, 'mysock')
Antoine Pitrou330ce592013-11-22 18:05:06 +01001685 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001686 self.addCleanup(sock.close)
Antoine Pitrou330ce592013-11-22 18:05:06 +01001687 try:
1688 sock.bind(str(P))
1689 except OSError as e:
1690 if "AF_UNIX path too long" in str(e):
1691 self.skipTest("cannot bind Unix socket: " + str(e))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001692 self.assertTrue(P.is_socket())
1693 self.assertFalse(P.is_fifo())
1694 self.assertFalse(P.is_file())
1695
1696 def test_is_block_device_false(self):
1697 P = self.cls(BASE)
1698 self.assertFalse((P / 'fileA').is_block_device())
1699 self.assertFalse((P / 'dirA').is_block_device())
1700 self.assertFalse((P / 'non-existing').is_block_device())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001701 self.assertFalse((P / 'fileA' / 'bah').is_block_device())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001702
1703 def test_is_char_device_false(self):
1704 P = self.cls(BASE)
1705 self.assertFalse((P / 'fileA').is_char_device())
1706 self.assertFalse((P / 'dirA').is_char_device())
1707 self.assertFalse((P / 'non-existing').is_char_device())
Antoine Pitrou2b2852b2014-10-30 23:14:03 +01001708 self.assertFalse((P / 'fileA' / 'bah').is_char_device())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001709
1710 def test_is_char_device_true(self):
1711 # Under Unix, /dev/null should generally be a char device
1712 P = self.cls('/dev/null')
1713 if not P.exists():
1714 self.skipTest("/dev/null required")
1715 self.assertTrue(P.is_char_device())
1716 self.assertFalse(P.is_block_device())
1717 self.assertFalse(P.is_file())
1718
1719 def test_pickling_common(self):
1720 p = self.cls(BASE, 'fileA')
1721 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1722 dumped = pickle.dumps(p, proto)
1723 pp = pickle.loads(dumped)
1724 self.assertEqual(pp.stat(), p.stat())
1725
1726 def test_parts_interning(self):
1727 P = self.cls
1728 p = P('/usr/bin/foo')
1729 q = P('/usr/local/bin')
1730 # 'usr'
1731 self.assertIs(p.parts[1], q.parts[1])
1732 # 'bin'
1733 self.assertIs(p.parts[2], q.parts[3])
1734
Antoine Pitrouc274fd22013-12-16 19:57:41 +01001735 def _check_complex_symlinks(self, link0_target):
1736 # Test solving a non-looping chain of symlinks (issue #19887)
1737 P = self.cls(BASE)
1738 self.dirlink(os.path.join('link0', 'link0'), join('link1'))
1739 self.dirlink(os.path.join('link1', 'link1'), join('link2'))
1740 self.dirlink(os.path.join('link2', 'link2'), join('link3'))
1741 self.dirlink(link0_target, join('link0'))
1742
1743 # Resolve absolute paths
1744 p = (P / 'link0').resolve()
1745 self.assertEqual(p, P)
1746 self.assertEqual(str(p), BASE)
1747 p = (P / 'link1').resolve()
1748 self.assertEqual(p, P)
1749 self.assertEqual(str(p), BASE)
1750 p = (P / 'link2').resolve()
1751 self.assertEqual(p, P)
1752 self.assertEqual(str(p), BASE)
1753 p = (P / 'link3').resolve()
1754 self.assertEqual(p, P)
1755 self.assertEqual(str(p), BASE)
1756
1757 # Resolve relative paths
1758 old_path = os.getcwd()
1759 os.chdir(BASE)
1760 try:
1761 p = self.cls('link0').resolve()
1762 self.assertEqual(p, P)
1763 self.assertEqual(str(p), BASE)
1764 p = self.cls('link1').resolve()
1765 self.assertEqual(p, P)
1766 self.assertEqual(str(p), BASE)
1767 p = self.cls('link2').resolve()
1768 self.assertEqual(p, P)
1769 self.assertEqual(str(p), BASE)
1770 p = self.cls('link3').resolve()
1771 self.assertEqual(p, P)
1772 self.assertEqual(str(p), BASE)
1773 finally:
1774 os.chdir(old_path)
1775
1776 @with_symlinks
1777 def test_complex_symlinks_absolute(self):
1778 self._check_complex_symlinks(BASE)
1779
1780 @with_symlinks
1781 def test_complex_symlinks_relative(self):
1782 self._check_complex_symlinks('.')
1783
1784 @with_symlinks
1785 def test_complex_symlinks_relative_dot_dot(self):
1786 self._check_complex_symlinks(os.path.join('dirA', '..'))
1787
Antoine Pitrou31119e42013-11-22 17:38:12 +01001788
1789class PathTest(_BasePathTest, unittest.TestCase):
1790 cls = pathlib.Path
1791
1792 def test_concrete_class(self):
1793 p = self.cls('a')
1794 self.assertIs(type(p),
1795 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1796
1797 def test_unsupported_flavour(self):
1798 if os.name == 'nt':
1799 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1800 else:
1801 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1802
1803
1804@only_posix
1805class PosixPathTest(_BasePathTest, unittest.TestCase):
1806 cls = pathlib.PosixPath
1807
1808 def _check_symlink_loop(self, *args):
1809 path = self.cls(*args)
1810 with self.assertRaises(RuntimeError):
1811 print(path.resolve())
1812
1813 def test_open_mode(self):
1814 old_mask = os.umask(0)
1815 self.addCleanup(os.umask, old_mask)
1816 p = self.cls(BASE)
1817 with (p / 'new_file').open('wb'):
1818 pass
1819 st = os.stat(join('new_file'))
1820 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1821 os.umask(0o022)
1822 with (p / 'other_new_file').open('wb'):
1823 pass
1824 st = os.stat(join('other_new_file'))
1825 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1826
1827 def test_touch_mode(self):
1828 old_mask = os.umask(0)
1829 self.addCleanup(os.umask, old_mask)
1830 p = self.cls(BASE)
1831 (p / 'new_file').touch()
1832 st = os.stat(join('new_file'))
1833 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1834 os.umask(0o022)
1835 (p / 'other_new_file').touch()
1836 st = os.stat(join('other_new_file'))
1837 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1838 (p / 'masked_new_file').touch(mode=0o750)
1839 st = os.stat(join('masked_new_file'))
1840 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1841
1842 @with_symlinks
1843 def test_resolve_loop(self):
1844 # Loop detection for broken symlinks under POSIX
1845 P = self.cls
1846 # Loops with relative symlinks
1847 os.symlink('linkX/inside', join('linkX'))
1848 self._check_symlink_loop(BASE, 'linkX')
1849 os.symlink('linkY', join('linkY'))
1850 self._check_symlink_loop(BASE, 'linkY')
1851 os.symlink('linkZ/../linkZ', join('linkZ'))
1852 self._check_symlink_loop(BASE, 'linkZ')
1853 # Loops with absolute symlinks
1854 os.symlink(join('linkU/inside'), join('linkU'))
1855 self._check_symlink_loop(BASE, 'linkU')
1856 os.symlink(join('linkV'), join('linkV'))
1857 self._check_symlink_loop(BASE, 'linkV')
1858 os.symlink(join('linkW/../linkW'), join('linkW'))
1859 self._check_symlink_loop(BASE, 'linkW')
1860
1861 def test_glob(self):
1862 P = self.cls
1863 p = P(BASE)
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001864 given = set(p.glob("FILEa"))
1865 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1866 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001867 self.assertEqual(set(p.glob("FILEa*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001868
1869 def test_rglob(self):
1870 P = self.cls
1871 p = P(BASE, "dirC")
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001872 given = set(p.rglob("FILEd"))
1873 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1874 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001875 self.assertEqual(set(p.rglob("FILEd*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001876
1877
1878@only_nt
1879class WindowsPathTest(_BasePathTest, unittest.TestCase):
1880 cls = pathlib.WindowsPath
1881
1882 def test_glob(self):
1883 P = self.cls
1884 p = P(BASE)
1885 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1886
1887 def test_rglob(self):
1888 P = self.cls
1889 p = P(BASE, "dirC")
1890 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1891
1892
1893if __name__ == "__main__":
1894 unittest.main()