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