blob: 2268f94a725cfb7d4cc98c49c166e18395bdc07e [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
200 def test_join_common(self):
201 P = self.cls
202 p = P('a/b')
203 pp = p.joinpath('c')
204 self.assertEqual(pp, P('a/b/c'))
205 self.assertIs(type(pp), type(p))
206 pp = p.joinpath('c', 'd')
207 self.assertEqual(pp, P('a/b/c/d'))
208 pp = p.joinpath(P('c'))
209 self.assertEqual(pp, P('a/b/c'))
210 pp = p.joinpath('/c')
211 self.assertEqual(pp, P('/c'))
212
213 def test_div_common(self):
214 # Basically the same as joinpath()
215 P = self.cls
216 p = P('a/b')
217 pp = p / 'c'
218 self.assertEqual(pp, P('a/b/c'))
219 self.assertIs(type(pp), type(p))
220 pp = p / 'c/d'
221 self.assertEqual(pp, P('a/b/c/d'))
222 pp = p / 'c' / 'd'
223 self.assertEqual(pp, P('a/b/c/d'))
224 pp = 'c' / p / 'd'
225 self.assertEqual(pp, P('c/a/b/d'))
226 pp = p / P('c')
227 self.assertEqual(pp, P('a/b/c'))
228 pp = p/ '/c'
229 self.assertEqual(pp, P('/c'))
230
231 def _check_str(self, expected, args):
232 p = self.cls(*args)
233 self.assertEqual(str(p), expected.replace('/', self.sep))
234
235 def test_str_common(self):
236 # Canonicalized paths roundtrip
237 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
238 self._check_str(pathstr, (pathstr,))
239 # Special case for the empty path
240 self._check_str('.', ('',))
241 # Other tests for str() are in test_equivalences()
242
243 def test_as_posix_common(self):
244 P = self.cls
245 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
246 self.assertEqual(P(pathstr).as_posix(), pathstr)
247 # Other tests for as_posix() are in test_equivalences()
248
249 def test_as_bytes_common(self):
250 sep = os.fsencode(self.sep)
251 P = self.cls
252 self.assertEqual(bytes(P('a/b')), b'a' + sep + b'b')
253
254 def test_as_uri_common(self):
255 P = self.cls
256 with self.assertRaises(ValueError):
257 P('a').as_uri()
258 with self.assertRaises(ValueError):
259 P().as_uri()
260
261 def test_repr_common(self):
262 for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
263 p = self.cls(pathstr)
264 clsname = p.__class__.__name__
265 r = repr(p)
266 # The repr() is in the form ClassName("forward-slashes path")
267 self.assertTrue(r.startswith(clsname + '('), r)
268 self.assertTrue(r.endswith(')'), r)
269 inner = r[len(clsname) + 1 : -1]
270 self.assertEqual(eval(inner), p.as_posix())
271 # The repr() roundtrips
272 q = eval(r, pathlib.__dict__)
273 self.assertIs(q.__class__, p.__class__)
274 self.assertEqual(q, p)
275 self.assertEqual(repr(q), r)
276
277 def test_eq_common(self):
278 P = self.cls
279 self.assertEqual(P('a/b'), P('a/b'))
280 self.assertEqual(P('a/b'), P('a', 'b'))
281 self.assertNotEqual(P('a/b'), P('a'))
282 self.assertNotEqual(P('a/b'), P('/a/b'))
283 self.assertNotEqual(P('a/b'), P())
284 self.assertNotEqual(P('/a/b'), P('/'))
285 self.assertNotEqual(P(), P('/'))
286 self.assertNotEqual(P(), "")
287 self.assertNotEqual(P(), {})
288 self.assertNotEqual(P(), int)
289
290 def test_match_common(self):
291 P = self.cls
292 self.assertRaises(ValueError, P('a').match, '')
293 self.assertRaises(ValueError, P('a').match, '.')
294 # Simple relative pattern
295 self.assertTrue(P('b.py').match('b.py'))
296 self.assertTrue(P('a/b.py').match('b.py'))
297 self.assertTrue(P('/a/b.py').match('b.py'))
298 self.assertFalse(P('a.py').match('b.py'))
299 self.assertFalse(P('b/py').match('b.py'))
300 self.assertFalse(P('/a.py').match('b.py'))
301 self.assertFalse(P('b.py/c').match('b.py'))
302 # Wilcard relative pattern
303 self.assertTrue(P('b.py').match('*.py'))
304 self.assertTrue(P('a/b.py').match('*.py'))
305 self.assertTrue(P('/a/b.py').match('*.py'))
306 self.assertFalse(P('b.pyc').match('*.py'))
307 self.assertFalse(P('b./py').match('*.py'))
308 self.assertFalse(P('b.py/c').match('*.py'))
309 # Multi-part relative pattern
310 self.assertTrue(P('ab/c.py').match('a*/*.py'))
311 self.assertTrue(P('/d/ab/c.py').match('a*/*.py'))
312 self.assertFalse(P('a.py').match('a*/*.py'))
313 self.assertFalse(P('/dab/c.py').match('a*/*.py'))
314 self.assertFalse(P('ab/c.py/d').match('a*/*.py'))
315 # Absolute pattern
316 self.assertTrue(P('/b.py').match('/*.py'))
317 self.assertFalse(P('b.py').match('/*.py'))
318 self.assertFalse(P('a/b.py').match('/*.py'))
319 self.assertFalse(P('/a/b.py').match('/*.py'))
320 # Multi-part absolute pattern
321 self.assertTrue(P('/a/b.py').match('/a/*.py'))
322 self.assertFalse(P('/ab.py').match('/a/*.py'))
323 self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
324
325 def test_ordering_common(self):
326 # Ordering is tuple-alike
327 def assertLess(a, b):
328 self.assertLess(a, b)
329 self.assertGreater(b, a)
330 P = self.cls
331 a = P('a')
332 b = P('a/b')
333 c = P('abc')
334 d = P('b')
335 assertLess(a, b)
336 assertLess(a, c)
337 assertLess(a, d)
338 assertLess(b, c)
339 assertLess(c, d)
340 P = self.cls
341 a = P('/a')
342 b = P('/a/b')
343 c = P('/abc')
344 d = P('/b')
345 assertLess(a, b)
346 assertLess(a, c)
347 assertLess(a, d)
348 assertLess(b, c)
349 assertLess(c, d)
350 with self.assertRaises(TypeError):
351 P() < {}
352
353 def test_parts_common(self):
354 # `parts` returns a tuple
355 sep = self.sep
356 P = self.cls
357 p = P('a/b')
358 parts = p.parts
359 self.assertEqual(parts, ('a', 'b'))
360 # The object gets reused
361 self.assertIs(parts, p.parts)
362 # When the path is absolute, the anchor is a separate part
363 p = P('/a/b')
364 parts = p.parts
365 self.assertEqual(parts, (sep, 'a', 'b'))
366
367 def test_equivalences(self):
368 for k, tuples in self.equivalences.items():
369 canon = k.replace('/', self.sep)
370 posix = k.replace(self.sep, '/')
371 if canon != posix:
372 tuples = tuples + [
373 tuple(part.replace('/', self.sep) for part in t)
374 for t in tuples
375 ]
376 tuples.append((posix, ))
377 pcanon = self.cls(canon)
378 for t in tuples:
379 p = self.cls(*t)
380 self.assertEqual(p, pcanon, "failed with args {}".format(t))
381 self.assertEqual(hash(p), hash(pcanon))
382 self.assertEqual(str(p), canon)
383 self.assertEqual(p.as_posix(), posix)
384
385 def test_parent_common(self):
386 # Relative
387 P = self.cls
388 p = P('a/b/c')
389 self.assertEqual(p.parent, P('a/b'))
390 self.assertEqual(p.parent.parent, P('a'))
391 self.assertEqual(p.parent.parent.parent, P())
392 self.assertEqual(p.parent.parent.parent.parent, P())
393 # Anchored
394 p = P('/a/b/c')
395 self.assertEqual(p.parent, P('/a/b'))
396 self.assertEqual(p.parent.parent, P('/a'))
397 self.assertEqual(p.parent.parent.parent, P('/'))
398 self.assertEqual(p.parent.parent.parent.parent, P('/'))
399
400 def test_parents_common(self):
401 # Relative
402 P = self.cls
403 p = P('a/b/c')
404 par = p.parents
405 self.assertEqual(len(par), 3)
406 self.assertEqual(par[0], P('a/b'))
407 self.assertEqual(par[1], P('a'))
408 self.assertEqual(par[2], P('.'))
409 self.assertEqual(list(par), [P('a/b'), P('a'), P('.')])
410 with self.assertRaises(IndexError):
411 par[-1]
412 with self.assertRaises(IndexError):
413 par[3]
414 with self.assertRaises(TypeError):
415 par[0] = p
416 # Anchored
417 p = P('/a/b/c')
418 par = p.parents
419 self.assertEqual(len(par), 3)
420 self.assertEqual(par[0], P('/a/b'))
421 self.assertEqual(par[1], P('/a'))
422 self.assertEqual(par[2], P('/'))
423 self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')])
424 with self.assertRaises(IndexError):
425 par[3]
426
427 def test_drive_common(self):
428 P = self.cls
429 self.assertEqual(P('a/b').drive, '')
430 self.assertEqual(P('/a/b').drive, '')
431 self.assertEqual(P('').drive, '')
432
433 def test_root_common(self):
434 P = self.cls
435 sep = self.sep
436 self.assertEqual(P('').root, '')
437 self.assertEqual(P('a/b').root, '')
438 self.assertEqual(P('/').root, sep)
439 self.assertEqual(P('/a/b').root, sep)
440
441 def test_anchor_common(self):
442 P = self.cls
443 sep = self.sep
444 self.assertEqual(P('').anchor, '')
445 self.assertEqual(P('a/b').anchor, '')
446 self.assertEqual(P('/').anchor, sep)
447 self.assertEqual(P('/a/b').anchor, sep)
448
449 def test_name_common(self):
450 P = self.cls
451 self.assertEqual(P('').name, '')
452 self.assertEqual(P('.').name, '')
453 self.assertEqual(P('/').name, '')
454 self.assertEqual(P('a/b').name, 'b')
455 self.assertEqual(P('/a/b').name, 'b')
456 self.assertEqual(P('/a/b/.').name, 'b')
457 self.assertEqual(P('a/b.py').name, 'b.py')
458 self.assertEqual(P('/a/b.py').name, 'b.py')
459
460 def test_suffix_common(self):
461 P = self.cls
462 self.assertEqual(P('').suffix, '')
463 self.assertEqual(P('.').suffix, '')
464 self.assertEqual(P('..').suffix, '')
465 self.assertEqual(P('/').suffix, '')
466 self.assertEqual(P('a/b').suffix, '')
467 self.assertEqual(P('/a/b').suffix, '')
468 self.assertEqual(P('/a/b/.').suffix, '')
469 self.assertEqual(P('a/b.py').suffix, '.py')
470 self.assertEqual(P('/a/b.py').suffix, '.py')
471 self.assertEqual(P('a/.hgrc').suffix, '')
472 self.assertEqual(P('/a/.hgrc').suffix, '')
473 self.assertEqual(P('a/.hg.rc').suffix, '.rc')
474 self.assertEqual(P('/a/.hg.rc').suffix, '.rc')
475 self.assertEqual(P('a/b.tar.gz').suffix, '.gz')
476 self.assertEqual(P('/a/b.tar.gz').suffix, '.gz')
477 self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '')
478 self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '')
479
480 def test_suffixes_common(self):
481 P = self.cls
482 self.assertEqual(P('').suffixes, [])
483 self.assertEqual(P('.').suffixes, [])
484 self.assertEqual(P('/').suffixes, [])
485 self.assertEqual(P('a/b').suffixes, [])
486 self.assertEqual(P('/a/b').suffixes, [])
487 self.assertEqual(P('/a/b/.').suffixes, [])
488 self.assertEqual(P('a/b.py').suffixes, ['.py'])
489 self.assertEqual(P('/a/b.py').suffixes, ['.py'])
490 self.assertEqual(P('a/.hgrc').suffixes, [])
491 self.assertEqual(P('/a/.hgrc').suffixes, [])
492 self.assertEqual(P('a/.hg.rc').suffixes, ['.rc'])
493 self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc'])
494 self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz'])
495 self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz'])
496 self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, [])
497 self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, [])
498
499 def test_stem_common(self):
500 P = self.cls
501 self.assertEqual(P('').stem, '')
502 self.assertEqual(P('.').stem, '')
503 self.assertEqual(P('..').stem, '..')
504 self.assertEqual(P('/').stem, '')
505 self.assertEqual(P('a/b').stem, 'b')
506 self.assertEqual(P('a/b.py').stem, 'b')
507 self.assertEqual(P('a/.hgrc').stem, '.hgrc')
508 self.assertEqual(P('a/.hg.rc').stem, '.hg')
509 self.assertEqual(P('a/b.tar.gz').stem, 'b.tar')
510 self.assertEqual(P('a/Some name. Ending with a dot.').stem,
511 'Some name. Ending with a dot.')
512
513 def test_with_name_common(self):
514 P = self.cls
515 self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml'))
516 self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml'))
517 self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml'))
518 self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml'))
519 self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml'))
520 self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml'))
521 self.assertRaises(ValueError, P('').with_name, 'd.xml')
522 self.assertRaises(ValueError, P('.').with_name, 'd.xml')
523 self.assertRaises(ValueError, P('/').with_name, 'd.xml')
524
525 def test_with_suffix_common(self):
526 P = self.cls
527 self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
528 self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
529 self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
530 self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100531 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100532 self.assertRaises(ValueError, P('').with_suffix, '.gz')
533 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
534 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100535 # Invalid suffix
536 self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
537 self.assertRaises(ValueError, P('a/b').with_suffix, '/')
538 self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
539 self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
540 self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100541
542 def test_relative_to_common(self):
543 P = self.cls
544 p = P('a/b')
545 self.assertRaises(TypeError, p.relative_to)
Antoine Pitrou156b3612013-12-28 19:49:04 +0100546 self.assertRaises(TypeError, p.relative_to, b'a')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100547 self.assertEqual(p.relative_to(P()), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100548 self.assertEqual(p.relative_to(''), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100549 self.assertEqual(p.relative_to(P('a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100550 self.assertEqual(p.relative_to('a'), P('b'))
551 self.assertEqual(p.relative_to('a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100552 self.assertEqual(p.relative_to(P('a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100553 self.assertEqual(p.relative_to('a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100554 # With several args
555 self.assertEqual(p.relative_to('a', 'b'), P())
556 # Unrelated paths
557 self.assertRaises(ValueError, p.relative_to, P('c'))
558 self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
559 self.assertRaises(ValueError, p.relative_to, P('a/c'))
560 self.assertRaises(ValueError, p.relative_to, P('/a'))
561 p = P('/a/b')
562 self.assertEqual(p.relative_to(P('/')), P('a/b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100563 self.assertEqual(p.relative_to('/'), P('a/b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100564 self.assertEqual(p.relative_to(P('/a')), P('b'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100565 self.assertEqual(p.relative_to('/a'), P('b'))
566 self.assertEqual(p.relative_to('/a/'), P('b'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100567 self.assertEqual(p.relative_to(P('/a/b')), P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100568 self.assertEqual(p.relative_to('/a/b'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100569 # Unrelated paths
570 self.assertRaises(ValueError, p.relative_to, P('/c'))
571 self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
572 self.assertRaises(ValueError, p.relative_to, P('/a/c'))
573 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100574 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100575 self.assertRaises(ValueError, p.relative_to, P('a'))
576
577 def test_pickling_common(self):
578 P = self.cls
579 p = P('/a/b')
580 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
581 dumped = pickle.dumps(p, proto)
582 pp = pickle.loads(dumped)
583 self.assertIs(pp.__class__, p.__class__)
584 self.assertEqual(pp, p)
585 self.assertEqual(hash(pp), hash(p))
586 self.assertEqual(str(pp), str(p))
587
588
589class PurePosixPathTest(_BasePurePathTest, unittest.TestCase):
590 cls = pathlib.PurePosixPath
591
592 def test_root(self):
593 P = self.cls
594 self.assertEqual(P('/a/b').root, '/')
595 self.assertEqual(P('///a/b').root, '/')
596 # POSIX special case for two leading slashes
597 self.assertEqual(P('//a/b').root, '//')
598
599 def test_eq(self):
600 P = self.cls
601 self.assertNotEqual(P('a/b'), P('A/b'))
602 self.assertEqual(P('/a'), P('///a'))
603 self.assertNotEqual(P('/a'), P('//a'))
604
605 def test_as_uri(self):
Antoine Pitrou31119e42013-11-22 17:38:12 +0100606 P = self.cls
607 self.assertEqual(P('/').as_uri(), 'file:///')
608 self.assertEqual(P('/a/b.c').as_uri(), 'file:///a/b.c')
609 self.assertEqual(P('/a/b%#c').as_uri(), 'file:///a/b%25%23c')
Antoine Pitrou29eac422013-11-22 17:57:03 +0100610
611 def test_as_uri_non_ascii(self):
612 from urllib.parse import quote_from_bytes
613 P = self.cls
614 try:
615 os.fsencode('\xe9')
616 except UnicodeEncodeError:
617 self.skipTest("\\xe9 cannot be encoded to the filesystem encoding")
Antoine Pitrou31119e42013-11-22 17:38:12 +0100618 self.assertEqual(P('/a/b\xe9').as_uri(),
619 'file:///a/b' + quote_from_bytes(os.fsencode('\xe9')))
620
621 def test_match(self):
622 P = self.cls
623 self.assertFalse(P('A.py').match('a.PY'))
624
625 def test_is_absolute(self):
626 P = self.cls
627 self.assertFalse(P().is_absolute())
628 self.assertFalse(P('a').is_absolute())
629 self.assertFalse(P('a/b/').is_absolute())
630 self.assertTrue(P('/').is_absolute())
631 self.assertTrue(P('/a').is_absolute())
632 self.assertTrue(P('/a/b/').is_absolute())
633 self.assertTrue(P('//a').is_absolute())
634 self.assertTrue(P('//a/b').is_absolute())
635
636 def test_is_reserved(self):
637 P = self.cls
638 self.assertIs(False, P('').is_reserved())
639 self.assertIs(False, P('/').is_reserved())
640 self.assertIs(False, P('/foo/bar').is_reserved())
641 self.assertIs(False, P('/dev/con/PRN/NUL').is_reserved())
642
643 def test_join(self):
644 P = self.cls
645 p = P('//a')
646 pp = p.joinpath('b')
647 self.assertEqual(pp, P('//a/b'))
648 pp = P('/a').joinpath('//c')
649 self.assertEqual(pp, P('//c'))
650 pp = P('//a').joinpath('/c')
651 self.assertEqual(pp, P('/c'))
652
653 def test_div(self):
654 # Basically the same as joinpath()
655 P = self.cls
656 p = P('//a')
657 pp = p / 'b'
658 self.assertEqual(pp, P('//a/b'))
659 pp = P('/a') / '//c'
660 self.assertEqual(pp, P('//c'))
661 pp = P('//a') / '/c'
662 self.assertEqual(pp, P('/c'))
663
664
665class PureWindowsPathTest(_BasePurePathTest, unittest.TestCase):
666 cls = pathlib.PureWindowsPath
667
668 equivalences = _BasePurePathTest.equivalences.copy()
669 equivalences.update({
670 'c:a': [ ('c:', 'a'), ('c:', 'a/'), ('/', 'c:', 'a') ],
671 'c:/a': [
672 ('c:/', 'a'), ('c:', '/', 'a'), ('c:', '/a'),
673 ('/z', 'c:/', 'a'), ('//x/y', 'c:/', 'a'),
674 ],
675 '//a/b/': [ ('//a/b',) ],
676 '//a/b/c': [
677 ('//a/b', 'c'), ('//a/b/', 'c'),
678 ],
679 })
680
681 def test_str(self):
682 p = self.cls('a/b/c')
683 self.assertEqual(str(p), 'a\\b\\c')
684 p = self.cls('c:/a/b/c')
685 self.assertEqual(str(p), 'c:\\a\\b\\c')
686 p = self.cls('//a/b')
687 self.assertEqual(str(p), '\\\\a\\b\\')
688 p = self.cls('//a/b/c')
689 self.assertEqual(str(p), '\\\\a\\b\\c')
690 p = self.cls('//a/b/c/d')
691 self.assertEqual(str(p), '\\\\a\\b\\c\\d')
692
693 def test_eq(self):
694 P = self.cls
695 self.assertEqual(P('c:a/b'), P('c:a/b'))
696 self.assertEqual(P('c:a/b'), P('c:', 'a', 'b'))
697 self.assertNotEqual(P('c:a/b'), P('d:a/b'))
698 self.assertNotEqual(P('c:a/b'), P('c:/a/b'))
699 self.assertNotEqual(P('/a/b'), P('c:/a/b'))
700 # Case-insensitivity
701 self.assertEqual(P('a/B'), P('A/b'))
702 self.assertEqual(P('C:a/B'), P('c:A/b'))
703 self.assertEqual(P('//Some/SHARE/a/B'), P('//somE/share/A/b'))
704
705 def test_as_uri(self):
706 from urllib.parse import quote_from_bytes
707 P = self.cls
708 with self.assertRaises(ValueError):
709 P('/a/b').as_uri()
710 with self.assertRaises(ValueError):
711 P('c:a/b').as_uri()
712 self.assertEqual(P('c:/').as_uri(), 'file:///c:/')
713 self.assertEqual(P('c:/a/b.c').as_uri(), 'file:///c:/a/b.c')
714 self.assertEqual(P('c:/a/b%#c').as_uri(), 'file:///c:/a/b%25%23c')
715 self.assertEqual(P('c:/a/b\xe9').as_uri(), 'file:///c:/a/b%C3%A9')
716 self.assertEqual(P('//some/share/').as_uri(), 'file://some/share/')
717 self.assertEqual(P('//some/share/a/b.c').as_uri(),
718 'file://some/share/a/b.c')
719 self.assertEqual(P('//some/share/a/b%#c\xe9').as_uri(),
720 'file://some/share/a/b%25%23c%C3%A9')
721
722 def test_match_common(self):
723 P = self.cls
724 # Absolute patterns
725 self.assertTrue(P('c:/b.py').match('/*.py'))
726 self.assertTrue(P('c:/b.py').match('c:*.py'))
727 self.assertTrue(P('c:/b.py').match('c:/*.py'))
728 self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive
729 self.assertFalse(P('b.py').match('/*.py'))
730 self.assertFalse(P('b.py').match('c:*.py'))
731 self.assertFalse(P('b.py').match('c:/*.py'))
732 self.assertFalse(P('c:b.py').match('/*.py'))
733 self.assertFalse(P('c:b.py').match('c:/*.py'))
734 self.assertFalse(P('/b.py').match('c:*.py'))
735 self.assertFalse(P('/b.py').match('c:/*.py'))
736 # UNC patterns
737 self.assertTrue(P('//some/share/a.py').match('/*.py'))
738 self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
739 self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
740 self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
741 # Case-insensitivity
742 self.assertTrue(P('B.py').match('b.PY'))
743 self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
744 self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
745
746 def test_ordering_common(self):
747 # Case-insensitivity
748 def assertOrderedEqual(a, b):
749 self.assertLessEqual(a, b)
750 self.assertGreaterEqual(b, a)
751 P = self.cls
752 p = P('c:A/b')
753 q = P('C:a/B')
754 assertOrderedEqual(p, q)
755 self.assertFalse(p < q)
756 self.assertFalse(p > q)
757 p = P('//some/Share/A/b')
758 q = P('//Some/SHARE/a/B')
759 assertOrderedEqual(p, q)
760 self.assertFalse(p < q)
761 self.assertFalse(p > q)
762
763 def test_parts(self):
764 P = self.cls
765 p = P('c:a/b')
766 parts = p.parts
767 self.assertEqual(parts, ('c:', 'a', 'b'))
768 p = P('c:/a/b')
769 parts = p.parts
770 self.assertEqual(parts, ('c:\\', 'a', 'b'))
771 p = P('//a/b/c/d')
772 parts = p.parts
773 self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
774
775 def test_parent(self):
776 # Anchored
777 P = self.cls
778 p = P('z:a/b/c')
779 self.assertEqual(p.parent, P('z:a/b'))
780 self.assertEqual(p.parent.parent, P('z:a'))
781 self.assertEqual(p.parent.parent.parent, P('z:'))
782 self.assertEqual(p.parent.parent.parent.parent, P('z:'))
783 p = P('z:/a/b/c')
784 self.assertEqual(p.parent, P('z:/a/b'))
785 self.assertEqual(p.parent.parent, P('z:/a'))
786 self.assertEqual(p.parent.parent.parent, P('z:/'))
787 self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
788 p = P('//a/b/c/d')
789 self.assertEqual(p.parent, P('//a/b/c'))
790 self.assertEqual(p.parent.parent, P('//a/b'))
791 self.assertEqual(p.parent.parent.parent, P('//a/b'))
792
793 def test_parents(self):
794 # Anchored
795 P = self.cls
796 p = P('z:a/b/')
797 par = p.parents
798 self.assertEqual(len(par), 2)
799 self.assertEqual(par[0], P('z:a'))
800 self.assertEqual(par[1], P('z:'))
801 self.assertEqual(list(par), [P('z:a'), P('z:')])
802 with self.assertRaises(IndexError):
803 par[2]
804 p = P('z:/a/b/')
805 par = p.parents
806 self.assertEqual(len(par), 2)
807 self.assertEqual(par[0], P('z:/a'))
808 self.assertEqual(par[1], P('z:/'))
809 self.assertEqual(list(par), [P('z:/a'), P('z:/')])
810 with self.assertRaises(IndexError):
811 par[2]
812 p = P('//a/b/c/d')
813 par = p.parents
814 self.assertEqual(len(par), 2)
815 self.assertEqual(par[0], P('//a/b/c'))
816 self.assertEqual(par[1], P('//a/b'))
817 self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
818 with self.assertRaises(IndexError):
819 par[2]
820
821 def test_drive(self):
822 P = self.cls
823 self.assertEqual(P('c:').drive, 'c:')
824 self.assertEqual(P('c:a/b').drive, 'c:')
825 self.assertEqual(P('c:/').drive, 'c:')
826 self.assertEqual(P('c:/a/b/').drive, 'c:')
827 self.assertEqual(P('//a/b').drive, '\\\\a\\b')
828 self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
829 self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
830
831 def test_root(self):
832 P = self.cls
833 self.assertEqual(P('c:').root, '')
834 self.assertEqual(P('c:a/b').root, '')
835 self.assertEqual(P('c:/').root, '\\')
836 self.assertEqual(P('c:/a/b/').root, '\\')
837 self.assertEqual(P('//a/b').root, '\\')
838 self.assertEqual(P('//a/b/').root, '\\')
839 self.assertEqual(P('//a/b/c/d').root, '\\')
840
841 def test_anchor(self):
842 P = self.cls
843 self.assertEqual(P('c:').anchor, 'c:')
844 self.assertEqual(P('c:a/b').anchor, 'c:')
845 self.assertEqual(P('c:/').anchor, 'c:\\')
846 self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
847 self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
848 self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
849 self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
850
851 def test_name(self):
852 P = self.cls
853 self.assertEqual(P('c:').name, '')
854 self.assertEqual(P('c:/').name, '')
855 self.assertEqual(P('c:a/b').name, 'b')
856 self.assertEqual(P('c:/a/b').name, 'b')
857 self.assertEqual(P('c:a/b.py').name, 'b.py')
858 self.assertEqual(P('c:/a/b.py').name, 'b.py')
859 self.assertEqual(P('//My.py/Share.php').name, '')
860 self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
861
862 def test_suffix(self):
863 P = self.cls
864 self.assertEqual(P('c:').suffix, '')
865 self.assertEqual(P('c:/').suffix, '')
866 self.assertEqual(P('c:a/b').suffix, '')
867 self.assertEqual(P('c:/a/b').suffix, '')
868 self.assertEqual(P('c:a/b.py').suffix, '.py')
869 self.assertEqual(P('c:/a/b.py').suffix, '.py')
870 self.assertEqual(P('c:a/.hgrc').suffix, '')
871 self.assertEqual(P('c:/a/.hgrc').suffix, '')
872 self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
873 self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
874 self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
875 self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
876 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
877 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
878 self.assertEqual(P('//My.py/Share.php').suffix, '')
879 self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
880
881 def test_suffixes(self):
882 P = self.cls
883 self.assertEqual(P('c:').suffixes, [])
884 self.assertEqual(P('c:/').suffixes, [])
885 self.assertEqual(P('c:a/b').suffixes, [])
886 self.assertEqual(P('c:/a/b').suffixes, [])
887 self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
888 self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
889 self.assertEqual(P('c:a/.hgrc').suffixes, [])
890 self.assertEqual(P('c:/a/.hgrc').suffixes, [])
891 self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
892 self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
893 self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
894 self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
895 self.assertEqual(P('//My.py/Share.php').suffixes, [])
896 self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
897 self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
898 self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
899
900 def test_stem(self):
901 P = self.cls
902 self.assertEqual(P('c:').stem, '')
903 self.assertEqual(P('c:.').stem, '')
904 self.assertEqual(P('c:..').stem, '..')
905 self.assertEqual(P('c:/').stem, '')
906 self.assertEqual(P('c:a/b').stem, 'b')
907 self.assertEqual(P('c:a/b.py').stem, 'b')
908 self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
909 self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
910 self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
911 self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
912 'Some name. Ending with a dot.')
913
914 def test_with_name(self):
915 P = self.cls
916 self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
917 self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
918 self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
919 self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
920 self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
921 self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
922 self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
923
924 def test_with_suffix(self):
925 P = self.cls
926 self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
927 self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
928 self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
929 self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100930 # Path doesn't have a "filename" component
Antoine Pitrou31119e42013-11-22 17:38:12 +0100931 self.assertRaises(ValueError, P('').with_suffix, '.gz')
932 self.assertRaises(ValueError, P('.').with_suffix, '.gz')
933 self.assertRaises(ValueError, P('/').with_suffix, '.gz')
934 self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
Antoine Pitrou1b02da92014-01-03 00:07:17 +0100935 # Invalid suffix
936 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
937 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
938 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
939 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
940 self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
941 self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
942 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
943 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
944 self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
945 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
946 self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100947
948 def test_relative_to(self):
949 P = self.cls
Antoine Pitrou156b3612013-12-28 19:49:04 +0100950 p = P('C:Foo/Bar')
951 self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
952 self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
953 self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
954 self.assertEqual(p.relative_to('c:foO'), P('Bar'))
955 self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
956 self.assertEqual(p.relative_to(P('c:foO/baR')), P())
957 self.assertEqual(p.relative_to('c:foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100958 # Unrelated paths
959 self.assertRaises(ValueError, p.relative_to, P())
Antoine Pitrou156b3612013-12-28 19:49:04 +0100960 self.assertRaises(ValueError, p.relative_to, '')
Antoine Pitrou31119e42013-11-22 17:38:12 +0100961 self.assertRaises(ValueError, p.relative_to, P('d:'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100962 self.assertRaises(ValueError, p.relative_to, P('/'))
963 self.assertRaises(ValueError, p.relative_to, P('Foo'))
964 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
965 self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
966 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
967 self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
968 p = P('C:/Foo/Bar')
969 self.assertEqual(p.relative_to(P('c:')), P('/Foo/Bar'))
970 self.assertEqual(p.relative_to('c:'), P('/Foo/Bar'))
971 self.assertEqual(str(p.relative_to(P('c:'))), '\\Foo\\Bar')
972 self.assertEqual(str(p.relative_to('c:')), '\\Foo\\Bar')
973 self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
974 self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
975 self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
976 self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
977 self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
978 self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
979 self.assertEqual(p.relative_to('c:/foO/baR'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +0100980 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +0100981 self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
982 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
983 self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
984 self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100985 self.assertRaises(ValueError, p.relative_to, P('d:'))
986 self.assertRaises(ValueError, p.relative_to, P('d:/'))
Antoine Pitrou156b3612013-12-28 19:49:04 +0100987 self.assertRaises(ValueError, p.relative_to, P('/'))
988 self.assertRaises(ValueError, p.relative_to, P('/Foo'))
989 self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +0100990 # UNC paths
Antoine Pitrou156b3612013-12-28 19:49:04 +0100991 p = P('//Server/Share/Foo/Bar')
992 self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
993 self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
994 self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
995 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
996 self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
997 self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
998 self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
999 self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001000 # Unrelated paths
Antoine Pitrou156b3612013-12-28 19:49:04 +01001001 self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
1002 self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
1003 self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
1004 self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001005
1006 def test_is_absolute(self):
1007 P = self.cls
1008 # Under NT, only paths with both a drive and a root are absolute
1009 self.assertFalse(P().is_absolute())
1010 self.assertFalse(P('a').is_absolute())
1011 self.assertFalse(P('a/b/').is_absolute())
1012 self.assertFalse(P('/').is_absolute())
1013 self.assertFalse(P('/a').is_absolute())
1014 self.assertFalse(P('/a/b/').is_absolute())
1015 self.assertFalse(P('c:').is_absolute())
1016 self.assertFalse(P('c:a').is_absolute())
1017 self.assertFalse(P('c:a/b/').is_absolute())
1018 self.assertTrue(P('c:/').is_absolute())
1019 self.assertTrue(P('c:/a').is_absolute())
1020 self.assertTrue(P('c:/a/b/').is_absolute())
1021 # UNC paths are absolute by definition
1022 self.assertTrue(P('//a/b').is_absolute())
1023 self.assertTrue(P('//a/b/').is_absolute())
1024 self.assertTrue(P('//a/b/c').is_absolute())
1025 self.assertTrue(P('//a/b/c/d').is_absolute())
1026
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001027 def test_join(self):
1028 P = self.cls
1029 p = P('C:/a/b')
1030 pp = p.joinpath('x/y')
1031 self.assertEqual(pp, P('C:/a/b/x/y'))
1032 pp = p.joinpath('/x/y')
1033 self.assertEqual(pp, P('C:/x/y'))
1034 # Joining with a different drive => the first path is ignored, even
1035 # if the second path is relative.
1036 pp = p.joinpath('D:x/y')
1037 self.assertEqual(pp, P('D:x/y'))
1038 pp = p.joinpath('D:/x/y')
1039 self.assertEqual(pp, P('D:/x/y'))
1040 pp = p.joinpath('//host/share/x/y')
1041 self.assertEqual(pp, P('//host/share/x/y'))
1042 # Joining with the same drive => the first path is appended to if
1043 # the second path is relative.
1044 pp = p.joinpath('c:x/y')
1045 self.assertEqual(pp, P('C:/a/b/x/y'))
1046 pp = p.joinpath('c:/x/y')
1047 self.assertEqual(pp, P('C:/x/y'))
1048
1049 def test_div(self):
1050 # Basically the same as joinpath()
1051 P = self.cls
1052 p = P('C:/a/b')
1053 self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
1054 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
1055 self.assertEqual(p / '/x/y', P('C:/x/y'))
1056 self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
1057 # Joining with a different drive => the first path is ignored, even
1058 # if the second path is relative.
1059 self.assertEqual(p / 'D:x/y', P('D:x/y'))
1060 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
1061 self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
1062 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
1063 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
1064 # Joining with the same drive => the first path is appended to if
1065 # the second path is relative.
Serhiy Storchaka010ff582013-12-06 17:25:51 +02001066 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
1067 self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001068
Antoine Pitrou31119e42013-11-22 17:38:12 +01001069 def test_is_reserved(self):
1070 P = self.cls
1071 self.assertIs(False, P('').is_reserved())
1072 self.assertIs(False, P('/').is_reserved())
1073 self.assertIs(False, P('/foo/bar').is_reserved())
1074 self.assertIs(True, P('con').is_reserved())
1075 self.assertIs(True, P('NUL').is_reserved())
1076 self.assertIs(True, P('NUL.txt').is_reserved())
1077 self.assertIs(True, P('com1').is_reserved())
1078 self.assertIs(True, P('com9.bar').is_reserved())
1079 self.assertIs(False, P('bar.com9').is_reserved())
1080 self.assertIs(True, P('lpt1').is_reserved())
1081 self.assertIs(True, P('lpt9.bar').is_reserved())
1082 self.assertIs(False, P('bar.lpt9').is_reserved())
1083 # Only the last component matters
1084 self.assertIs(False, P('c:/NUL/con/baz').is_reserved())
1085 # UNC paths are never reserved
1086 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved())
1087
1088
1089class PurePathTest(_BasePurePathTest, unittest.TestCase):
1090 cls = pathlib.PurePath
1091
1092 def test_concrete_class(self):
1093 p = self.cls('a')
1094 self.assertIs(type(p),
1095 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath)
1096
1097 def test_different_flavours_unequal(self):
1098 p = pathlib.PurePosixPath('a')
1099 q = pathlib.PureWindowsPath('a')
1100 self.assertNotEqual(p, q)
1101
1102 def test_different_flavours_unordered(self):
1103 p = pathlib.PurePosixPath('a')
1104 q = pathlib.PureWindowsPath('a')
1105 with self.assertRaises(TypeError):
1106 p < q
1107 with self.assertRaises(TypeError):
1108 p <= q
1109 with self.assertRaises(TypeError):
1110 p > q
1111 with self.assertRaises(TypeError):
1112 p >= q
1113
1114
1115#
1116# Tests for the concrete classes
1117#
1118
1119# Make sure any symbolic links in the base test path are resolved
1120BASE = os.path.realpath(TESTFN)
1121join = lambda *x: os.path.join(BASE, *x)
1122rel_join = lambda *x: os.path.join(TESTFN, *x)
1123
1124def symlink_skip_reason():
1125 if not pathlib.supports_symlinks:
1126 return "no system support for symlinks"
1127 try:
1128 os.symlink(__file__, BASE)
1129 except OSError as e:
1130 return str(e)
1131 else:
1132 support.unlink(BASE)
1133 return None
1134
1135symlink_skip_reason = symlink_skip_reason()
1136
1137only_nt = unittest.skipIf(os.name != 'nt',
1138 'test requires a Windows-compatible system')
1139only_posix = unittest.skipIf(os.name == 'nt',
1140 'test requires a POSIX-compatible system')
1141with_symlinks = unittest.skipIf(symlink_skip_reason, symlink_skip_reason)
1142
1143
1144@only_posix
1145class PosixPathAsPureTest(PurePosixPathTest):
1146 cls = pathlib.PosixPath
1147
1148@only_nt
1149class WindowsPathAsPureTest(PureWindowsPathTest):
1150 cls = pathlib.WindowsPath
1151
1152
1153class _BasePathTest(object):
1154 """Tests for the FS-accessing functionalities of the Path classes."""
1155
1156 # (BASE)
1157 # |
1158 # |-- dirA/
1159 # |-- linkC -> "../dirB"
1160 # |-- dirB/
1161 # | |-- fileB
1162 # |-- linkD -> "../dirB"
1163 # |-- dirC/
1164 # | |-- fileC
1165 # | |-- fileD
1166 # |-- fileA
1167 # |-- linkA -> "fileA"
1168 # |-- linkB -> "dirB"
1169 #
1170
1171 def setUp(self):
1172 os.mkdir(BASE)
1173 self.addCleanup(shutil.rmtree, BASE)
1174 os.mkdir(join('dirA'))
1175 os.mkdir(join('dirB'))
1176 os.mkdir(join('dirC'))
1177 os.mkdir(join('dirC', 'dirD'))
1178 with open(join('fileA'), 'wb') as f:
1179 f.write(b"this is file A\n")
1180 with open(join('dirB', 'fileB'), 'wb') as f:
1181 f.write(b"this is file B\n")
1182 with open(join('dirC', 'fileC'), 'wb') as f:
1183 f.write(b"this is file C\n")
1184 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
1185 f.write(b"this is file D\n")
1186 if not symlink_skip_reason:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001187 # Relative symlinks
1188 os.symlink('fileA', join('linkA'))
1189 os.symlink('non-existing', join('brokenLink'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001190 self.dirlink('dirB', join('linkB'))
1191 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001192 # This one goes upwards but doesn't create a loop
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001193 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1194
1195 if os.name == 'nt':
1196 # Workaround for http://bugs.python.org/issue13772
1197 def dirlink(self, src, dest):
1198 os.symlink(src, dest, target_is_directory=True)
1199 else:
1200 def dirlink(self, src, dest):
1201 os.symlink(src, dest)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001202
1203 def assertSame(self, path_a, path_b):
1204 self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1205 "%r and %r don't point to the same file" %
1206 (path_a, path_b))
1207
1208 def assertFileNotFound(self, func, *args, **kwargs):
1209 with self.assertRaises(FileNotFoundError) as cm:
1210 func(*args, **kwargs)
1211 self.assertEqual(cm.exception.errno, errno.ENOENT)
1212
1213 def _test_cwd(self, p):
1214 q = self.cls(os.getcwd())
1215 self.assertEqual(p, q)
1216 self.assertEqual(str(p), str(q))
1217 self.assertIs(type(p), type(q))
1218 self.assertTrue(p.is_absolute())
1219
1220 def test_cwd(self):
1221 p = self.cls.cwd()
1222 self._test_cwd(p)
1223
1224 def test_empty_path(self):
1225 # The empty path points to '.'
1226 p = self.cls('')
1227 self.assertEqual(p.stat(), os.stat('.'))
1228
1229 def test_exists(self):
1230 P = self.cls
1231 p = P(BASE)
1232 self.assertIs(True, p.exists())
1233 self.assertIs(True, (p / 'dirA').exists())
1234 self.assertIs(True, (p / 'fileA').exists())
1235 if not symlink_skip_reason:
1236 self.assertIs(True, (p / 'linkA').exists())
1237 self.assertIs(True, (p / 'linkB').exists())
1238 self.assertIs(False, (p / 'foo').exists())
1239 self.assertIs(False, P('/xyzzy').exists())
1240
1241 def test_open_common(self):
1242 p = self.cls(BASE)
1243 with (p / 'fileA').open('r') as f:
1244 self.assertIsInstance(f, io.TextIOBase)
1245 self.assertEqual(f.read(), "this is file A\n")
1246 with (p / 'fileA').open('rb') as f:
1247 self.assertIsInstance(f, io.BufferedIOBase)
1248 self.assertEqual(f.read().strip(), b"this is file A")
1249 with (p / 'fileA').open('rb', buffering=0) as f:
1250 self.assertIsInstance(f, io.RawIOBase)
1251 self.assertEqual(f.read().strip(), b"this is file A")
1252
1253 def test_iterdir(self):
1254 P = self.cls
1255 p = P(BASE)
1256 it = p.iterdir()
1257 paths = set(it)
1258 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1259 if not symlink_skip_reason:
1260 expected += ['linkA', 'linkB', 'brokenLink']
1261 self.assertEqual(paths, { P(BASE, q) for q in expected })
1262
1263 @with_symlinks
1264 def test_iterdir_symlink(self):
1265 # __iter__ on a symlink to a directory
1266 P = self.cls
1267 p = P(BASE, 'linkB')
1268 paths = set(p.iterdir())
1269 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1270 self.assertEqual(paths, expected)
1271
1272 def test_iterdir_nodir(self):
1273 # __iter__ on something that is not a directory
1274 p = self.cls(BASE, 'fileA')
1275 with self.assertRaises(OSError) as cm:
1276 next(p.iterdir())
1277 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1278 # (see issue #12802)
1279 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1280 errno.ENOENT, errno.EINVAL))
1281
1282 def test_glob_common(self):
1283 def _check(glob, expected):
1284 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1285 P = self.cls
1286 p = P(BASE)
1287 it = p.glob("fileA")
1288 self.assertIsInstance(it, collections.Iterator)
1289 _check(it, ["fileA"])
1290 _check(p.glob("fileB"), [])
1291 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1292 if symlink_skip_reason:
1293 _check(p.glob("*A"), ['dirA', 'fileA'])
1294 else:
1295 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1296 if symlink_skip_reason:
1297 _check(p.glob("*B/*"), ['dirB/fileB'])
1298 else:
1299 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1300 'linkB/fileB', 'linkB/linkD'])
1301 if symlink_skip_reason:
1302 _check(p.glob("*/fileB"), ['dirB/fileB'])
1303 else:
1304 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1305
1306 def test_rglob_common(self):
1307 def _check(glob, expected):
1308 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1309 P = self.cls
1310 p = P(BASE)
1311 it = p.rglob("fileA")
1312 self.assertIsInstance(it, collections.Iterator)
1313 # XXX cannot test because of symlink loops in the test setup
1314 #_check(it, ["fileA"])
1315 #_check(p.rglob("fileB"), ["dirB/fileB"])
1316 #_check(p.rglob("*/fileA"), [""])
1317 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1318 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1319 # No symlink loops here
1320 p = P(BASE, "dirC")
1321 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1322 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1323
1324 def test_glob_dotdot(self):
1325 # ".." is not special in globs
1326 P = self.cls
1327 p = P(BASE)
1328 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1329 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1330 self.assertEqual(set(p.glob("../xyzzy")), set())
1331
1332 def _check_resolve_relative(self, p, expected):
1333 q = p.resolve()
1334 self.assertEqual(q, expected)
1335
1336 def _check_resolve_absolute(self, p, expected):
1337 q = p.resolve()
1338 self.assertEqual(q, expected)
1339
1340 @with_symlinks
1341 def test_resolve_common(self):
1342 P = self.cls
1343 p = P(BASE, 'foo')
1344 with self.assertRaises(OSError) as cm:
1345 p.resolve()
1346 self.assertEqual(cm.exception.errno, errno.ENOENT)
1347 # These are all relative symlinks
1348 p = P(BASE, 'dirB', 'fileB')
1349 self._check_resolve_relative(p, p)
1350 p = P(BASE, 'linkA')
1351 self._check_resolve_relative(p, P(BASE, 'fileA'))
1352 p = P(BASE, 'dirA', 'linkC', 'fileB')
1353 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1354 p = P(BASE, 'dirB', 'linkD', 'fileB')
1355 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1356 # Now create absolute symlinks
1357 d = tempfile.mkdtemp(suffix='-dirD')
1358 self.addCleanup(shutil.rmtree, d)
1359 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1360 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1361 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1362 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1363
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001364 @with_symlinks
1365 def test_resolve_dot(self):
1366 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1367 p = self.cls(BASE)
1368 self.dirlink('.', join('0'))
Antoine Pitroucc157512013-12-03 17:13:13 +01001369 self.dirlink(os.path.join('0', '0'), join('1'))
1370 self.dirlink(os.path.join('1', '1'), join('2'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001371 q = p / '2'
1372 self.assertEqual(q.resolve(), p)
1373
Antoine Pitrou31119e42013-11-22 17:38:12 +01001374 def test_with(self):
1375 p = self.cls(BASE)
1376 it = p.iterdir()
1377 it2 = p.iterdir()
1378 next(it2)
1379 with p:
1380 pass
1381 # I/O operation on closed path
1382 self.assertRaises(ValueError, next, it)
1383 self.assertRaises(ValueError, next, it2)
1384 self.assertRaises(ValueError, p.open)
1385 self.assertRaises(ValueError, p.resolve)
1386 self.assertRaises(ValueError, p.absolute)
1387 self.assertRaises(ValueError, p.__enter__)
1388
1389 def test_chmod(self):
1390 p = self.cls(BASE) / 'fileA'
1391 mode = p.stat().st_mode
1392 # Clear writable bit
1393 new_mode = mode & ~0o222
1394 p.chmod(new_mode)
1395 self.assertEqual(p.stat().st_mode, new_mode)
1396 # Set writable bit
1397 new_mode = mode | 0o222
1398 p.chmod(new_mode)
1399 self.assertEqual(p.stat().st_mode, new_mode)
1400
1401 # XXX also need a test for lchmod
1402
1403 def test_stat(self):
1404 p = self.cls(BASE) / 'fileA'
1405 st = p.stat()
1406 self.assertEqual(p.stat(), st)
1407 # Change file mode by flipping write bit
1408 p.chmod(st.st_mode ^ 0o222)
1409 self.addCleanup(p.chmod, st.st_mode)
1410 self.assertNotEqual(p.stat(), st)
1411
1412 @with_symlinks
1413 def test_lstat(self):
1414 p = self.cls(BASE)/ 'linkA'
1415 st = p.stat()
1416 self.assertNotEqual(st, p.lstat())
1417
1418 def test_lstat_nosymlink(self):
1419 p = self.cls(BASE) / 'fileA'
1420 st = p.stat()
1421 self.assertEqual(st, p.lstat())
1422
1423 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1424 def test_owner(self):
1425 p = self.cls(BASE) / 'fileA'
1426 uid = p.stat().st_uid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001427 try:
1428 name = pwd.getpwuid(uid).pw_name
1429 except KeyError:
1430 self.skipTest(
1431 "user %d doesn't have an entry in the system database" % uid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001432 self.assertEqual(name, p.owner())
1433
1434 @unittest.skipUnless(grp, "the grp module is needed for this test")
1435 def test_group(self):
1436 p = self.cls(BASE) / 'fileA'
1437 gid = p.stat().st_gid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001438 try:
1439 name = grp.getgrgid(gid).gr_name
1440 except KeyError:
1441 self.skipTest(
1442 "group %d doesn't have an entry in the system database" % gid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001443 self.assertEqual(name, p.group())
1444
1445 def test_unlink(self):
1446 p = self.cls(BASE) / 'fileA'
1447 p.unlink()
1448 self.assertFileNotFound(p.stat)
1449 self.assertFileNotFound(p.unlink)
1450
1451 def test_rmdir(self):
1452 p = self.cls(BASE) / 'dirA'
1453 for q in p.iterdir():
1454 q.unlink()
1455 p.rmdir()
1456 self.assertFileNotFound(p.stat)
1457 self.assertFileNotFound(p.unlink)
1458
1459 def test_rename(self):
1460 P = self.cls(BASE)
1461 p = P / 'fileA'
1462 size = p.stat().st_size
1463 # Renaming to another path
1464 q = P / 'dirA' / 'fileAA'
1465 p.rename(q)
1466 self.assertEqual(q.stat().st_size, size)
1467 self.assertFileNotFound(p.stat)
1468 # Renaming to a str of a relative path
1469 r = rel_join('fileAAA')
1470 q.rename(r)
1471 self.assertEqual(os.stat(r).st_size, size)
1472 self.assertFileNotFound(q.stat)
1473
1474 def test_replace(self):
1475 P = self.cls(BASE)
1476 p = P / 'fileA'
1477 size = p.stat().st_size
1478 # Replacing a non-existing path
1479 q = P / 'dirA' / 'fileAA'
1480 p.replace(q)
1481 self.assertEqual(q.stat().st_size, size)
1482 self.assertFileNotFound(p.stat)
1483 # Replacing another (existing) path
1484 r = rel_join('dirB', 'fileB')
1485 q.replace(r)
1486 self.assertEqual(os.stat(r).st_size, size)
1487 self.assertFileNotFound(q.stat)
1488
1489 def test_touch_common(self):
1490 P = self.cls(BASE)
1491 p = P / 'newfileA'
1492 self.assertFalse(p.exists())
1493 p.touch()
1494 self.assertTrue(p.exists())
Antoine Pitrou0f575642013-11-22 23:20:08 +01001495 st = p.stat()
1496 old_mtime = st.st_mtime
1497 old_mtime_ns = st.st_mtime_ns
Antoine Pitrou31119e42013-11-22 17:38:12 +01001498 # Rewind the mtime sufficiently far in the past to work around
1499 # filesystem-specific timestamp granularity.
1500 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001501 # The file mtime should be refreshed by calling touch() again
Antoine Pitrou31119e42013-11-22 17:38:12 +01001502 p.touch()
Antoine Pitrou0f575642013-11-22 23:20:08 +01001503 st = p.stat()
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001504 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
1505 self.assertGreaterEqual(st.st_mtime, old_mtime)
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001506 # Now with exist_ok=False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001507 p = P / 'newfileB'
1508 self.assertFalse(p.exists())
1509 p.touch(mode=0o700, exist_ok=False)
1510 self.assertTrue(p.exists())
1511 self.assertRaises(OSError, p.touch, exist_ok=False)
1512
Antoine Pitrou8b784932013-11-23 14:52:39 +01001513 def test_touch_nochange(self):
1514 P = self.cls(BASE)
1515 p = P / 'fileA'
1516 p.touch()
1517 with p.open('rb') as f:
1518 self.assertEqual(f.read().strip(), b"this is file A")
1519
Antoine Pitrou31119e42013-11-22 17:38:12 +01001520 def test_mkdir(self):
1521 P = self.cls(BASE)
1522 p = P / 'newdirA'
1523 self.assertFalse(p.exists())
1524 p.mkdir()
1525 self.assertTrue(p.exists())
1526 self.assertTrue(p.is_dir())
1527 with self.assertRaises(OSError) as cm:
1528 p.mkdir()
1529 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001530
1531 def test_mkdir_parents(self):
1532 # Creating a chain of directories
1533 p = self.cls(BASE, 'newdirB', 'newdirC')
1534 self.assertFalse(p.exists())
1535 with self.assertRaises(OSError) as cm:
1536 p.mkdir()
1537 self.assertEqual(cm.exception.errno, errno.ENOENT)
1538 p.mkdir(parents=True)
1539 self.assertTrue(p.exists())
1540 self.assertTrue(p.is_dir())
1541 with self.assertRaises(OSError) as cm:
1542 p.mkdir(parents=True)
1543 self.assertEqual(cm.exception.errno, errno.EEXIST)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001544 # test `mode` arg
1545 mode = stat.S_IMODE(p.stat().st_mode) # default mode
1546 p = self.cls(BASE, 'newdirD', 'newdirE')
1547 p.mkdir(0o555, parents=True)
1548 self.assertTrue(p.exists())
1549 self.assertTrue(p.is_dir())
1550 if os.name != 'nt':
1551 # the directory's permissions follow the mode argument
Gregory P. Smithb599c612014-01-20 01:10:33 -08001552 self.assertEqual(stat.S_IMODE(p.stat().st_mode), 0o7555 & mode)
Antoine Pitrou0048c982013-12-16 20:22:37 +01001553 # the parent's permissions follow the default process settings
1554 self.assertEqual(stat.S_IMODE(p.parent.stat().st_mode), mode)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001555
1556 @with_symlinks
1557 def test_symlink_to(self):
1558 P = self.cls(BASE)
1559 target = P / 'fileA'
1560 # Symlinking a path target
1561 link = P / 'dirA' / 'linkAA'
1562 link.symlink_to(target)
1563 self.assertEqual(link.stat(), target.stat())
1564 self.assertNotEqual(link.lstat(), target.stat())
1565 # Symlinking a str target
1566 link = P / 'dirA' / 'linkAAA'
1567 link.symlink_to(str(target))
1568 self.assertEqual(link.stat(), target.stat())
1569 self.assertNotEqual(link.lstat(), target.stat())
1570 self.assertFalse(link.is_dir())
1571 # Symlinking to a directory
1572 target = P / 'dirB'
1573 link = P / 'dirA' / 'linkAAAA'
1574 link.symlink_to(target, target_is_directory=True)
1575 self.assertEqual(link.stat(), target.stat())
1576 self.assertNotEqual(link.lstat(), target.stat())
1577 self.assertTrue(link.is_dir())
1578 self.assertTrue(list(link.iterdir()))
1579
1580 def test_is_dir(self):
1581 P = self.cls(BASE)
1582 self.assertTrue((P / 'dirA').is_dir())
1583 self.assertFalse((P / 'fileA').is_dir())
1584 self.assertFalse((P / 'non-existing').is_dir())
1585 if not symlink_skip_reason:
1586 self.assertFalse((P / 'linkA').is_dir())
1587 self.assertTrue((P / 'linkB').is_dir())
1588 self.assertFalse((P/ 'brokenLink').is_dir())
1589
1590 def test_is_file(self):
1591 P = self.cls(BASE)
1592 self.assertTrue((P / 'fileA').is_file())
1593 self.assertFalse((P / 'dirA').is_file())
1594 self.assertFalse((P / 'non-existing').is_file())
1595 if not symlink_skip_reason:
1596 self.assertTrue((P / 'linkA').is_file())
1597 self.assertFalse((P / 'linkB').is_file())
1598 self.assertFalse((P/ 'brokenLink').is_file())
1599
1600 def test_is_symlink(self):
1601 P = self.cls(BASE)
1602 self.assertFalse((P / 'fileA').is_symlink())
1603 self.assertFalse((P / 'dirA').is_symlink())
1604 self.assertFalse((P / 'non-existing').is_symlink())
1605 if not symlink_skip_reason:
1606 self.assertTrue((P / 'linkA').is_symlink())
1607 self.assertTrue((P / 'linkB').is_symlink())
1608 self.assertTrue((P/ 'brokenLink').is_symlink())
1609
1610 def test_is_fifo_false(self):
1611 P = self.cls(BASE)
1612 self.assertFalse((P / 'fileA').is_fifo())
1613 self.assertFalse((P / 'dirA').is_fifo())
1614 self.assertFalse((P / 'non-existing').is_fifo())
1615
1616 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1617 def test_is_fifo_true(self):
1618 P = self.cls(BASE, 'myfifo')
1619 os.mkfifo(str(P))
1620 self.assertTrue(P.is_fifo())
1621 self.assertFalse(P.is_socket())
1622 self.assertFalse(P.is_file())
1623
1624 def test_is_socket_false(self):
1625 P = self.cls(BASE)
1626 self.assertFalse((P / 'fileA').is_socket())
1627 self.assertFalse((P / 'dirA').is_socket())
1628 self.assertFalse((P / 'non-existing').is_socket())
1629
1630 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1631 def test_is_socket_true(self):
1632 P = self.cls(BASE, 'mysock')
Antoine Pitrou330ce592013-11-22 18:05:06 +01001633 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001634 self.addCleanup(sock.close)
Antoine Pitrou330ce592013-11-22 18:05:06 +01001635 try:
1636 sock.bind(str(P))
1637 except OSError as e:
1638 if "AF_UNIX path too long" in str(e):
1639 self.skipTest("cannot bind Unix socket: " + str(e))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001640 self.assertTrue(P.is_socket())
1641 self.assertFalse(P.is_fifo())
1642 self.assertFalse(P.is_file())
1643
1644 def test_is_block_device_false(self):
1645 P = self.cls(BASE)
1646 self.assertFalse((P / 'fileA').is_block_device())
1647 self.assertFalse((P / 'dirA').is_block_device())
1648 self.assertFalse((P / 'non-existing').is_block_device())
1649
1650 def test_is_char_device_false(self):
1651 P = self.cls(BASE)
1652 self.assertFalse((P / 'fileA').is_char_device())
1653 self.assertFalse((P / 'dirA').is_char_device())
1654 self.assertFalse((P / 'non-existing').is_char_device())
1655
1656 def test_is_char_device_true(self):
1657 # Under Unix, /dev/null should generally be a char device
1658 P = self.cls('/dev/null')
1659 if not P.exists():
1660 self.skipTest("/dev/null required")
1661 self.assertTrue(P.is_char_device())
1662 self.assertFalse(P.is_block_device())
1663 self.assertFalse(P.is_file())
1664
1665 def test_pickling_common(self):
1666 p = self.cls(BASE, 'fileA')
1667 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1668 dumped = pickle.dumps(p, proto)
1669 pp = pickle.loads(dumped)
1670 self.assertEqual(pp.stat(), p.stat())
1671
1672 def test_parts_interning(self):
1673 P = self.cls
1674 p = P('/usr/bin/foo')
1675 q = P('/usr/local/bin')
1676 # 'usr'
1677 self.assertIs(p.parts[1], q.parts[1])
1678 # 'bin'
1679 self.assertIs(p.parts[2], q.parts[3])
1680
Antoine Pitrouc274fd22013-12-16 19:57:41 +01001681 def _check_complex_symlinks(self, link0_target):
1682 # Test solving a non-looping chain of symlinks (issue #19887)
1683 P = self.cls(BASE)
1684 self.dirlink(os.path.join('link0', 'link0'), join('link1'))
1685 self.dirlink(os.path.join('link1', 'link1'), join('link2'))
1686 self.dirlink(os.path.join('link2', 'link2'), join('link3'))
1687 self.dirlink(link0_target, join('link0'))
1688
1689 # Resolve absolute paths
1690 p = (P / 'link0').resolve()
1691 self.assertEqual(p, P)
1692 self.assertEqual(str(p), BASE)
1693 p = (P / 'link1').resolve()
1694 self.assertEqual(p, P)
1695 self.assertEqual(str(p), BASE)
1696 p = (P / 'link2').resolve()
1697 self.assertEqual(p, P)
1698 self.assertEqual(str(p), BASE)
1699 p = (P / 'link3').resolve()
1700 self.assertEqual(p, P)
1701 self.assertEqual(str(p), BASE)
1702
1703 # Resolve relative paths
1704 old_path = os.getcwd()
1705 os.chdir(BASE)
1706 try:
1707 p = self.cls('link0').resolve()
1708 self.assertEqual(p, P)
1709 self.assertEqual(str(p), BASE)
1710 p = self.cls('link1').resolve()
1711 self.assertEqual(p, P)
1712 self.assertEqual(str(p), BASE)
1713 p = self.cls('link2').resolve()
1714 self.assertEqual(p, P)
1715 self.assertEqual(str(p), BASE)
1716 p = self.cls('link3').resolve()
1717 self.assertEqual(p, P)
1718 self.assertEqual(str(p), BASE)
1719 finally:
1720 os.chdir(old_path)
1721
1722 @with_symlinks
1723 def test_complex_symlinks_absolute(self):
1724 self._check_complex_symlinks(BASE)
1725
1726 @with_symlinks
1727 def test_complex_symlinks_relative(self):
1728 self._check_complex_symlinks('.')
1729
1730 @with_symlinks
1731 def test_complex_symlinks_relative_dot_dot(self):
1732 self._check_complex_symlinks(os.path.join('dirA', '..'))
1733
Antoine Pitrou31119e42013-11-22 17:38:12 +01001734
1735class PathTest(_BasePathTest, unittest.TestCase):
1736 cls = pathlib.Path
1737
1738 def test_concrete_class(self):
1739 p = self.cls('a')
1740 self.assertIs(type(p),
1741 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1742
1743 def test_unsupported_flavour(self):
1744 if os.name == 'nt':
1745 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1746 else:
1747 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1748
1749
1750@only_posix
1751class PosixPathTest(_BasePathTest, unittest.TestCase):
1752 cls = pathlib.PosixPath
1753
1754 def _check_symlink_loop(self, *args):
1755 path = self.cls(*args)
1756 with self.assertRaises(RuntimeError):
1757 print(path.resolve())
1758
1759 def test_open_mode(self):
1760 old_mask = os.umask(0)
1761 self.addCleanup(os.umask, old_mask)
1762 p = self.cls(BASE)
1763 with (p / 'new_file').open('wb'):
1764 pass
1765 st = os.stat(join('new_file'))
1766 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1767 os.umask(0o022)
1768 with (p / 'other_new_file').open('wb'):
1769 pass
1770 st = os.stat(join('other_new_file'))
1771 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1772
1773 def test_touch_mode(self):
1774 old_mask = os.umask(0)
1775 self.addCleanup(os.umask, old_mask)
1776 p = self.cls(BASE)
1777 (p / 'new_file').touch()
1778 st = os.stat(join('new_file'))
1779 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1780 os.umask(0o022)
1781 (p / 'other_new_file').touch()
1782 st = os.stat(join('other_new_file'))
1783 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1784 (p / 'masked_new_file').touch(mode=0o750)
1785 st = os.stat(join('masked_new_file'))
1786 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1787
1788 @with_symlinks
1789 def test_resolve_loop(self):
1790 # Loop detection for broken symlinks under POSIX
1791 P = self.cls
1792 # Loops with relative symlinks
1793 os.symlink('linkX/inside', join('linkX'))
1794 self._check_symlink_loop(BASE, 'linkX')
1795 os.symlink('linkY', join('linkY'))
1796 self._check_symlink_loop(BASE, 'linkY')
1797 os.symlink('linkZ/../linkZ', join('linkZ'))
1798 self._check_symlink_loop(BASE, 'linkZ')
1799 # Loops with absolute symlinks
1800 os.symlink(join('linkU/inside'), join('linkU'))
1801 self._check_symlink_loop(BASE, 'linkU')
1802 os.symlink(join('linkV'), join('linkV'))
1803 self._check_symlink_loop(BASE, 'linkV')
1804 os.symlink(join('linkW/../linkW'), join('linkW'))
1805 self._check_symlink_loop(BASE, 'linkW')
1806
1807 def test_glob(self):
1808 P = self.cls
1809 p = P(BASE)
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001810 given = set(p.glob("FILEa"))
1811 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1812 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001813 self.assertEqual(set(p.glob("FILEa*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001814
1815 def test_rglob(self):
1816 P = self.cls
1817 p = P(BASE, "dirC")
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001818 given = set(p.rglob("FILEd"))
1819 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1820 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001821 self.assertEqual(set(p.rglob("FILEd*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001822
1823
1824@only_nt
1825class WindowsPathTest(_BasePathTest, unittest.TestCase):
1826 cls = pathlib.WindowsPath
1827
1828 def test_glob(self):
1829 P = self.cls
1830 p = P(BASE)
1831 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1832
1833 def test_rglob(self):
1834 P = self.cls
1835 p = P(BASE, "dirC")
1836 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1837
1838
1839if __name__ == "__main__":
1840 unittest.main()