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