blob: a8d740f3ae2a9b3c8a9e1a6e4800dc224031fb0c [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
Serhiy Storchakaa9939022013-12-06 17:14:12 +0200978 def test_join(self):
979 P = self.cls
980 p = P('C:/a/b')
981 pp = p.joinpath('x/y')
982 self.assertEqual(pp, P('C:/a/b/x/y'))
983 pp = p.joinpath('/x/y')
984 self.assertEqual(pp, P('C:/x/y'))
985 # Joining with a different drive => the first path is ignored, even
986 # if the second path is relative.
987 pp = p.joinpath('D:x/y')
988 self.assertEqual(pp, P('D:x/y'))
989 pp = p.joinpath('D:/x/y')
990 self.assertEqual(pp, P('D:/x/y'))
991 pp = p.joinpath('//host/share/x/y')
992 self.assertEqual(pp, P('//host/share/x/y'))
993 # Joining with the same drive => the first path is appended to if
994 # the second path is relative.
995 pp = p.joinpath('c:x/y')
996 self.assertEqual(pp, P('C:/a/b/x/y'))
997 pp = p.joinpath('c:/x/y')
998 self.assertEqual(pp, P('C:/x/y'))
999
1000 def test_div(self):
1001 # Basically the same as joinpath()
1002 P = self.cls
1003 p = P('C:/a/b')
1004 self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
1005 self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
1006 self.assertEqual(p / '/x/y', P('C:/x/y'))
1007 self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
1008 # Joining with a different drive => the first path is ignored, even
1009 # if the second path is relative.
1010 self.assertEqual(p / 'D:x/y', P('D:x/y'))
1011 self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
1012 self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
1013 self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
1014 self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
1015 # Joining with the same drive => the first path is appended to if
1016 # the second path is relative.
Serhiy Storchaka010ff582013-12-06 17:25:51 +02001017 self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
1018 self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
Serhiy Storchakaa9939022013-12-06 17:14:12 +02001019
Antoine Pitrou31119e42013-11-22 17:38:12 +01001020 def test_is_reserved(self):
1021 P = self.cls
1022 self.assertIs(False, P('').is_reserved())
1023 self.assertIs(False, P('/').is_reserved())
1024 self.assertIs(False, P('/foo/bar').is_reserved())
1025 self.assertIs(True, P('con').is_reserved())
1026 self.assertIs(True, P('NUL').is_reserved())
1027 self.assertIs(True, P('NUL.txt').is_reserved())
1028 self.assertIs(True, P('com1').is_reserved())
1029 self.assertIs(True, P('com9.bar').is_reserved())
1030 self.assertIs(False, P('bar.com9').is_reserved())
1031 self.assertIs(True, P('lpt1').is_reserved())
1032 self.assertIs(True, P('lpt9.bar').is_reserved())
1033 self.assertIs(False, P('bar.lpt9').is_reserved())
1034 # Only the last component matters
1035 self.assertIs(False, P('c:/NUL/con/baz').is_reserved())
1036 # UNC paths are never reserved
1037 self.assertIs(False, P('//my/share/nul/con/aux').is_reserved())
1038
1039
1040class PurePathTest(_BasePurePathTest, unittest.TestCase):
1041 cls = pathlib.PurePath
1042
1043 def test_concrete_class(self):
1044 p = self.cls('a')
1045 self.assertIs(type(p),
1046 pathlib.PureWindowsPath if os.name == 'nt' else pathlib.PurePosixPath)
1047
1048 def test_different_flavours_unequal(self):
1049 p = pathlib.PurePosixPath('a')
1050 q = pathlib.PureWindowsPath('a')
1051 self.assertNotEqual(p, q)
1052
1053 def test_different_flavours_unordered(self):
1054 p = pathlib.PurePosixPath('a')
1055 q = pathlib.PureWindowsPath('a')
1056 with self.assertRaises(TypeError):
1057 p < q
1058 with self.assertRaises(TypeError):
1059 p <= q
1060 with self.assertRaises(TypeError):
1061 p > q
1062 with self.assertRaises(TypeError):
1063 p >= q
1064
1065
1066#
1067# Tests for the concrete classes
1068#
1069
1070# Make sure any symbolic links in the base test path are resolved
1071BASE = os.path.realpath(TESTFN)
1072join = lambda *x: os.path.join(BASE, *x)
1073rel_join = lambda *x: os.path.join(TESTFN, *x)
1074
1075def symlink_skip_reason():
1076 if not pathlib.supports_symlinks:
1077 return "no system support for symlinks"
1078 try:
1079 os.symlink(__file__, BASE)
1080 except OSError as e:
1081 return str(e)
1082 else:
1083 support.unlink(BASE)
1084 return None
1085
1086symlink_skip_reason = symlink_skip_reason()
1087
1088only_nt = unittest.skipIf(os.name != 'nt',
1089 'test requires a Windows-compatible system')
1090only_posix = unittest.skipIf(os.name == 'nt',
1091 'test requires a POSIX-compatible system')
1092with_symlinks = unittest.skipIf(symlink_skip_reason, symlink_skip_reason)
1093
1094
1095@only_posix
1096class PosixPathAsPureTest(PurePosixPathTest):
1097 cls = pathlib.PosixPath
1098
1099@only_nt
1100class WindowsPathAsPureTest(PureWindowsPathTest):
1101 cls = pathlib.WindowsPath
1102
1103
1104class _BasePathTest(object):
1105 """Tests for the FS-accessing functionalities of the Path classes."""
1106
1107 # (BASE)
1108 # |
1109 # |-- dirA/
1110 # |-- linkC -> "../dirB"
1111 # |-- dirB/
1112 # | |-- fileB
1113 # |-- linkD -> "../dirB"
1114 # |-- dirC/
1115 # | |-- fileC
1116 # | |-- fileD
1117 # |-- fileA
1118 # |-- linkA -> "fileA"
1119 # |-- linkB -> "dirB"
1120 #
1121
1122 def setUp(self):
1123 os.mkdir(BASE)
1124 self.addCleanup(shutil.rmtree, BASE)
1125 os.mkdir(join('dirA'))
1126 os.mkdir(join('dirB'))
1127 os.mkdir(join('dirC'))
1128 os.mkdir(join('dirC', 'dirD'))
1129 with open(join('fileA'), 'wb') as f:
1130 f.write(b"this is file A\n")
1131 with open(join('dirB', 'fileB'), 'wb') as f:
1132 f.write(b"this is file B\n")
1133 with open(join('dirC', 'fileC'), 'wb') as f:
1134 f.write(b"this is file C\n")
1135 with open(join('dirC', 'dirD', 'fileD'), 'wb') as f:
1136 f.write(b"this is file D\n")
1137 if not symlink_skip_reason:
Antoine Pitrou31119e42013-11-22 17:38:12 +01001138 # Relative symlinks
1139 os.symlink('fileA', join('linkA'))
1140 os.symlink('non-existing', join('brokenLink'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001141 self.dirlink('dirB', join('linkB'))
1142 self.dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001143 # This one goes upwards but doesn't create a loop
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001144 self.dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1145
1146 if os.name == 'nt':
1147 # Workaround for http://bugs.python.org/issue13772
1148 def dirlink(self, src, dest):
1149 os.symlink(src, dest, target_is_directory=True)
1150 else:
1151 def dirlink(self, src, dest):
1152 os.symlink(src, dest)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001153
1154 def assertSame(self, path_a, path_b):
1155 self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1156 "%r and %r don't point to the same file" %
1157 (path_a, path_b))
1158
1159 def assertFileNotFound(self, func, *args, **kwargs):
1160 with self.assertRaises(FileNotFoundError) as cm:
1161 func(*args, **kwargs)
1162 self.assertEqual(cm.exception.errno, errno.ENOENT)
1163
1164 def _test_cwd(self, p):
1165 q = self.cls(os.getcwd())
1166 self.assertEqual(p, q)
1167 self.assertEqual(str(p), str(q))
1168 self.assertIs(type(p), type(q))
1169 self.assertTrue(p.is_absolute())
1170
1171 def test_cwd(self):
1172 p = self.cls.cwd()
1173 self._test_cwd(p)
1174
1175 def test_empty_path(self):
1176 # The empty path points to '.'
1177 p = self.cls('')
1178 self.assertEqual(p.stat(), os.stat('.'))
1179
1180 def test_exists(self):
1181 P = self.cls
1182 p = P(BASE)
1183 self.assertIs(True, p.exists())
1184 self.assertIs(True, (p / 'dirA').exists())
1185 self.assertIs(True, (p / 'fileA').exists())
1186 if not symlink_skip_reason:
1187 self.assertIs(True, (p / 'linkA').exists())
1188 self.assertIs(True, (p / 'linkB').exists())
1189 self.assertIs(False, (p / 'foo').exists())
1190 self.assertIs(False, P('/xyzzy').exists())
1191
1192 def test_open_common(self):
1193 p = self.cls(BASE)
1194 with (p / 'fileA').open('r') as f:
1195 self.assertIsInstance(f, io.TextIOBase)
1196 self.assertEqual(f.read(), "this is file A\n")
1197 with (p / 'fileA').open('rb') as f:
1198 self.assertIsInstance(f, io.BufferedIOBase)
1199 self.assertEqual(f.read().strip(), b"this is file A")
1200 with (p / 'fileA').open('rb', buffering=0) as f:
1201 self.assertIsInstance(f, io.RawIOBase)
1202 self.assertEqual(f.read().strip(), b"this is file A")
1203
1204 def test_iterdir(self):
1205 P = self.cls
1206 p = P(BASE)
1207 it = p.iterdir()
1208 paths = set(it)
1209 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1210 if not symlink_skip_reason:
1211 expected += ['linkA', 'linkB', 'brokenLink']
1212 self.assertEqual(paths, { P(BASE, q) for q in expected })
1213
1214 @with_symlinks
1215 def test_iterdir_symlink(self):
1216 # __iter__ on a symlink to a directory
1217 P = self.cls
1218 p = P(BASE, 'linkB')
1219 paths = set(p.iterdir())
1220 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1221 self.assertEqual(paths, expected)
1222
1223 def test_iterdir_nodir(self):
1224 # __iter__ on something that is not a directory
1225 p = self.cls(BASE, 'fileA')
1226 with self.assertRaises(OSError) as cm:
1227 next(p.iterdir())
1228 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1229 # (see issue #12802)
1230 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1231 errno.ENOENT, errno.EINVAL))
1232
1233 def test_glob_common(self):
1234 def _check(glob, expected):
1235 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1236 P = self.cls
1237 p = P(BASE)
1238 it = p.glob("fileA")
1239 self.assertIsInstance(it, collections.Iterator)
1240 _check(it, ["fileA"])
1241 _check(p.glob("fileB"), [])
1242 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1243 if symlink_skip_reason:
1244 _check(p.glob("*A"), ['dirA', 'fileA'])
1245 else:
1246 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1247 if symlink_skip_reason:
1248 _check(p.glob("*B/*"), ['dirB/fileB'])
1249 else:
1250 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1251 'linkB/fileB', 'linkB/linkD'])
1252 if symlink_skip_reason:
1253 _check(p.glob("*/fileB"), ['dirB/fileB'])
1254 else:
1255 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1256
1257 def test_rglob_common(self):
1258 def _check(glob, expected):
1259 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1260 P = self.cls
1261 p = P(BASE)
1262 it = p.rglob("fileA")
1263 self.assertIsInstance(it, collections.Iterator)
1264 # XXX cannot test because of symlink loops in the test setup
1265 #_check(it, ["fileA"])
1266 #_check(p.rglob("fileB"), ["dirB/fileB"])
1267 #_check(p.rglob("*/fileA"), [""])
1268 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1269 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1270 # No symlink loops here
1271 p = P(BASE, "dirC")
1272 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1273 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1274
1275 def test_glob_dotdot(self):
1276 # ".." is not special in globs
1277 P = self.cls
1278 p = P(BASE)
1279 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1280 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1281 self.assertEqual(set(p.glob("../xyzzy")), set())
1282
1283 def _check_resolve_relative(self, p, expected):
1284 q = p.resolve()
1285 self.assertEqual(q, expected)
1286
1287 def _check_resolve_absolute(self, p, expected):
1288 q = p.resolve()
1289 self.assertEqual(q, expected)
1290
1291 @with_symlinks
1292 def test_resolve_common(self):
1293 P = self.cls
1294 p = P(BASE, 'foo')
1295 with self.assertRaises(OSError) as cm:
1296 p.resolve()
1297 self.assertEqual(cm.exception.errno, errno.ENOENT)
1298 # These are all relative symlinks
1299 p = P(BASE, 'dirB', 'fileB')
1300 self._check_resolve_relative(p, p)
1301 p = P(BASE, 'linkA')
1302 self._check_resolve_relative(p, P(BASE, 'fileA'))
1303 p = P(BASE, 'dirA', 'linkC', 'fileB')
1304 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1305 p = P(BASE, 'dirB', 'linkD', 'fileB')
1306 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1307 # Now create absolute symlinks
1308 d = tempfile.mkdtemp(suffix='-dirD')
1309 self.addCleanup(shutil.rmtree, d)
1310 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1311 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1312 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1313 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1314
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001315 @with_symlinks
1316 def test_resolve_dot(self):
1317 # See https://bitbucket.org/pitrou/pathlib/issue/9/pathresolve-fails-on-complex-symlinks
1318 p = self.cls(BASE)
1319 self.dirlink('.', join('0'))
Antoine Pitroucc157512013-12-03 17:13:13 +01001320 self.dirlink(os.path.join('0', '0'), join('1'))
1321 self.dirlink(os.path.join('1', '1'), join('2'))
Antoine Pitrou51af82c2013-12-03 11:01:08 +01001322 q = p / '2'
1323 self.assertEqual(q.resolve(), p)
1324
Antoine Pitrou31119e42013-11-22 17:38:12 +01001325 def test_with(self):
1326 p = self.cls(BASE)
1327 it = p.iterdir()
1328 it2 = p.iterdir()
1329 next(it2)
1330 with p:
1331 pass
1332 # I/O operation on closed path
1333 self.assertRaises(ValueError, next, it)
1334 self.assertRaises(ValueError, next, it2)
1335 self.assertRaises(ValueError, p.open)
1336 self.assertRaises(ValueError, p.resolve)
1337 self.assertRaises(ValueError, p.absolute)
1338 self.assertRaises(ValueError, p.__enter__)
1339
1340 def test_chmod(self):
1341 p = self.cls(BASE) / 'fileA'
1342 mode = p.stat().st_mode
1343 # Clear writable bit
1344 new_mode = mode & ~0o222
1345 p.chmod(new_mode)
1346 self.assertEqual(p.stat().st_mode, new_mode)
1347 # Set writable bit
1348 new_mode = mode | 0o222
1349 p.chmod(new_mode)
1350 self.assertEqual(p.stat().st_mode, new_mode)
1351
1352 # XXX also need a test for lchmod
1353
1354 def test_stat(self):
1355 p = self.cls(BASE) / 'fileA'
1356 st = p.stat()
1357 self.assertEqual(p.stat(), st)
1358 # Change file mode by flipping write bit
1359 p.chmod(st.st_mode ^ 0o222)
1360 self.addCleanup(p.chmod, st.st_mode)
1361 self.assertNotEqual(p.stat(), st)
1362
1363 @with_symlinks
1364 def test_lstat(self):
1365 p = self.cls(BASE)/ 'linkA'
1366 st = p.stat()
1367 self.assertNotEqual(st, p.lstat())
1368
1369 def test_lstat_nosymlink(self):
1370 p = self.cls(BASE) / 'fileA'
1371 st = p.stat()
1372 self.assertEqual(st, p.lstat())
1373
1374 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1375 def test_owner(self):
1376 p = self.cls(BASE) / 'fileA'
1377 uid = p.stat().st_uid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001378 try:
1379 name = pwd.getpwuid(uid).pw_name
1380 except KeyError:
1381 self.skipTest(
1382 "user %d doesn't have an entry in the system database" % uid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001383 self.assertEqual(name, p.owner())
1384
1385 @unittest.skipUnless(grp, "the grp module is needed for this test")
1386 def test_group(self):
1387 p = self.cls(BASE) / 'fileA'
1388 gid = p.stat().st_gid
Antoine Pitrou2cf4b0f2013-11-25 19:51:53 +01001389 try:
1390 name = grp.getgrgid(gid).gr_name
1391 except KeyError:
1392 self.skipTest(
1393 "group %d doesn't have an entry in the system database" % gid)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001394 self.assertEqual(name, p.group())
1395
1396 def test_unlink(self):
1397 p = self.cls(BASE) / 'fileA'
1398 p.unlink()
1399 self.assertFileNotFound(p.stat)
1400 self.assertFileNotFound(p.unlink)
1401
1402 def test_rmdir(self):
1403 p = self.cls(BASE) / 'dirA'
1404 for q in p.iterdir():
1405 q.unlink()
1406 p.rmdir()
1407 self.assertFileNotFound(p.stat)
1408 self.assertFileNotFound(p.unlink)
1409
1410 def test_rename(self):
1411 P = self.cls(BASE)
1412 p = P / 'fileA'
1413 size = p.stat().st_size
1414 # Renaming to another path
1415 q = P / 'dirA' / 'fileAA'
1416 p.rename(q)
1417 self.assertEqual(q.stat().st_size, size)
1418 self.assertFileNotFound(p.stat)
1419 # Renaming to a str of a relative path
1420 r = rel_join('fileAAA')
1421 q.rename(r)
1422 self.assertEqual(os.stat(r).st_size, size)
1423 self.assertFileNotFound(q.stat)
1424
1425 def test_replace(self):
1426 P = self.cls(BASE)
1427 p = P / 'fileA'
1428 size = p.stat().st_size
1429 # Replacing a non-existing path
1430 q = P / 'dirA' / 'fileAA'
1431 p.replace(q)
1432 self.assertEqual(q.stat().st_size, size)
1433 self.assertFileNotFound(p.stat)
1434 # Replacing another (existing) path
1435 r = rel_join('dirB', 'fileB')
1436 q.replace(r)
1437 self.assertEqual(os.stat(r).st_size, size)
1438 self.assertFileNotFound(q.stat)
1439
1440 def test_touch_common(self):
1441 P = self.cls(BASE)
1442 p = P / 'newfileA'
1443 self.assertFalse(p.exists())
1444 p.touch()
1445 self.assertTrue(p.exists())
Antoine Pitrou0f575642013-11-22 23:20:08 +01001446 st = p.stat()
1447 old_mtime = st.st_mtime
1448 old_mtime_ns = st.st_mtime_ns
Antoine Pitrou31119e42013-11-22 17:38:12 +01001449 # Rewind the mtime sufficiently far in the past to work around
1450 # filesystem-specific timestamp granularity.
1451 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001452 # The file mtime should be refreshed by calling touch() again
Antoine Pitrou31119e42013-11-22 17:38:12 +01001453 p.touch()
Antoine Pitrou0f575642013-11-22 23:20:08 +01001454 st = p.stat()
Antoine Pitrou2cf39172013-11-23 15:25:59 +01001455 self.assertGreaterEqual(st.st_mtime_ns, old_mtime_ns)
1456 self.assertGreaterEqual(st.st_mtime, old_mtime)
Antoine Pitroubb6694d2013-11-23 01:32:53 +01001457 # Now with exist_ok=False
Antoine Pitrou31119e42013-11-22 17:38:12 +01001458 p = P / 'newfileB'
1459 self.assertFalse(p.exists())
1460 p.touch(mode=0o700, exist_ok=False)
1461 self.assertTrue(p.exists())
1462 self.assertRaises(OSError, p.touch, exist_ok=False)
1463
Antoine Pitrou8b784932013-11-23 14:52:39 +01001464 def test_touch_nochange(self):
1465 P = self.cls(BASE)
1466 p = P / 'fileA'
1467 p.touch()
1468 with p.open('rb') as f:
1469 self.assertEqual(f.read().strip(), b"this is file A")
1470
Antoine Pitrou31119e42013-11-22 17:38:12 +01001471 def test_mkdir(self):
1472 P = self.cls(BASE)
1473 p = P / 'newdirA'
1474 self.assertFalse(p.exists())
1475 p.mkdir()
1476 self.assertTrue(p.exists())
1477 self.assertTrue(p.is_dir())
1478 with self.assertRaises(OSError) as cm:
1479 p.mkdir()
1480 self.assertEqual(cm.exception.errno, errno.EEXIST)
1481 # XXX test `mode` arg
1482
1483 def test_mkdir_parents(self):
1484 # Creating a chain of directories
1485 p = self.cls(BASE, 'newdirB', 'newdirC')
1486 self.assertFalse(p.exists())
1487 with self.assertRaises(OSError) as cm:
1488 p.mkdir()
1489 self.assertEqual(cm.exception.errno, errno.ENOENT)
1490 p.mkdir(parents=True)
1491 self.assertTrue(p.exists())
1492 self.assertTrue(p.is_dir())
1493 with self.assertRaises(OSError) as cm:
1494 p.mkdir(parents=True)
1495 self.assertEqual(cm.exception.errno, errno.EEXIST)
1496 # XXX test `mode` arg
1497
1498 @with_symlinks
1499 def test_symlink_to(self):
1500 P = self.cls(BASE)
1501 target = P / 'fileA'
1502 # Symlinking a path target
1503 link = P / 'dirA' / 'linkAA'
1504 link.symlink_to(target)
1505 self.assertEqual(link.stat(), target.stat())
1506 self.assertNotEqual(link.lstat(), target.stat())
1507 # Symlinking a str target
1508 link = P / 'dirA' / 'linkAAA'
1509 link.symlink_to(str(target))
1510 self.assertEqual(link.stat(), target.stat())
1511 self.assertNotEqual(link.lstat(), target.stat())
1512 self.assertFalse(link.is_dir())
1513 # Symlinking to a directory
1514 target = P / 'dirB'
1515 link = P / 'dirA' / 'linkAAAA'
1516 link.symlink_to(target, target_is_directory=True)
1517 self.assertEqual(link.stat(), target.stat())
1518 self.assertNotEqual(link.lstat(), target.stat())
1519 self.assertTrue(link.is_dir())
1520 self.assertTrue(list(link.iterdir()))
1521
1522 def test_is_dir(self):
1523 P = self.cls(BASE)
1524 self.assertTrue((P / 'dirA').is_dir())
1525 self.assertFalse((P / 'fileA').is_dir())
1526 self.assertFalse((P / 'non-existing').is_dir())
1527 if not symlink_skip_reason:
1528 self.assertFalse((P / 'linkA').is_dir())
1529 self.assertTrue((P / 'linkB').is_dir())
1530 self.assertFalse((P/ 'brokenLink').is_dir())
1531
1532 def test_is_file(self):
1533 P = self.cls(BASE)
1534 self.assertTrue((P / 'fileA').is_file())
1535 self.assertFalse((P / 'dirA').is_file())
1536 self.assertFalse((P / 'non-existing').is_file())
1537 if not symlink_skip_reason:
1538 self.assertTrue((P / 'linkA').is_file())
1539 self.assertFalse((P / 'linkB').is_file())
1540 self.assertFalse((P/ 'brokenLink').is_file())
1541
1542 def test_is_symlink(self):
1543 P = self.cls(BASE)
1544 self.assertFalse((P / 'fileA').is_symlink())
1545 self.assertFalse((P / 'dirA').is_symlink())
1546 self.assertFalse((P / 'non-existing').is_symlink())
1547 if not symlink_skip_reason:
1548 self.assertTrue((P / 'linkA').is_symlink())
1549 self.assertTrue((P / 'linkB').is_symlink())
1550 self.assertTrue((P/ 'brokenLink').is_symlink())
1551
1552 def test_is_fifo_false(self):
1553 P = self.cls(BASE)
1554 self.assertFalse((P / 'fileA').is_fifo())
1555 self.assertFalse((P / 'dirA').is_fifo())
1556 self.assertFalse((P / 'non-existing').is_fifo())
1557
1558 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1559 def test_is_fifo_true(self):
1560 P = self.cls(BASE, 'myfifo')
1561 os.mkfifo(str(P))
1562 self.assertTrue(P.is_fifo())
1563 self.assertFalse(P.is_socket())
1564 self.assertFalse(P.is_file())
1565
1566 def test_is_socket_false(self):
1567 P = self.cls(BASE)
1568 self.assertFalse((P / 'fileA').is_socket())
1569 self.assertFalse((P / 'dirA').is_socket())
1570 self.assertFalse((P / 'non-existing').is_socket())
1571
1572 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1573 def test_is_socket_true(self):
1574 P = self.cls(BASE, 'mysock')
Antoine Pitrou330ce592013-11-22 18:05:06 +01001575 sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
Antoine Pitrou31119e42013-11-22 17:38:12 +01001576 self.addCleanup(sock.close)
Antoine Pitrou330ce592013-11-22 18:05:06 +01001577 try:
1578 sock.bind(str(P))
1579 except OSError as e:
1580 if "AF_UNIX path too long" in str(e):
1581 self.skipTest("cannot bind Unix socket: " + str(e))
Antoine Pitrou31119e42013-11-22 17:38:12 +01001582 self.assertTrue(P.is_socket())
1583 self.assertFalse(P.is_fifo())
1584 self.assertFalse(P.is_file())
1585
1586 def test_is_block_device_false(self):
1587 P = self.cls(BASE)
1588 self.assertFalse((P / 'fileA').is_block_device())
1589 self.assertFalse((P / 'dirA').is_block_device())
1590 self.assertFalse((P / 'non-existing').is_block_device())
1591
1592 def test_is_char_device_false(self):
1593 P = self.cls(BASE)
1594 self.assertFalse((P / 'fileA').is_char_device())
1595 self.assertFalse((P / 'dirA').is_char_device())
1596 self.assertFalse((P / 'non-existing').is_char_device())
1597
1598 def test_is_char_device_true(self):
1599 # Under Unix, /dev/null should generally be a char device
1600 P = self.cls('/dev/null')
1601 if not P.exists():
1602 self.skipTest("/dev/null required")
1603 self.assertTrue(P.is_char_device())
1604 self.assertFalse(P.is_block_device())
1605 self.assertFalse(P.is_file())
1606
1607 def test_pickling_common(self):
1608 p = self.cls(BASE, 'fileA')
1609 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1610 dumped = pickle.dumps(p, proto)
1611 pp = pickle.loads(dumped)
1612 self.assertEqual(pp.stat(), p.stat())
1613
1614 def test_parts_interning(self):
1615 P = self.cls
1616 p = P('/usr/bin/foo')
1617 q = P('/usr/local/bin')
1618 # 'usr'
1619 self.assertIs(p.parts[1], q.parts[1])
1620 # 'bin'
1621 self.assertIs(p.parts[2], q.parts[3])
1622
1623
1624class PathTest(_BasePathTest, unittest.TestCase):
1625 cls = pathlib.Path
1626
1627 def test_concrete_class(self):
1628 p = self.cls('a')
1629 self.assertIs(type(p),
1630 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1631
1632 def test_unsupported_flavour(self):
1633 if os.name == 'nt':
1634 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1635 else:
1636 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1637
1638
1639@only_posix
1640class PosixPathTest(_BasePathTest, unittest.TestCase):
1641 cls = pathlib.PosixPath
1642
1643 def _check_symlink_loop(self, *args):
1644 path = self.cls(*args)
1645 with self.assertRaises(RuntimeError):
1646 print(path.resolve())
1647
1648 def test_open_mode(self):
1649 old_mask = os.umask(0)
1650 self.addCleanup(os.umask, old_mask)
1651 p = self.cls(BASE)
1652 with (p / 'new_file').open('wb'):
1653 pass
1654 st = os.stat(join('new_file'))
1655 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1656 os.umask(0o022)
1657 with (p / 'other_new_file').open('wb'):
1658 pass
1659 st = os.stat(join('other_new_file'))
1660 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1661
1662 def test_touch_mode(self):
1663 old_mask = os.umask(0)
1664 self.addCleanup(os.umask, old_mask)
1665 p = self.cls(BASE)
1666 (p / 'new_file').touch()
1667 st = os.stat(join('new_file'))
1668 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1669 os.umask(0o022)
1670 (p / 'other_new_file').touch()
1671 st = os.stat(join('other_new_file'))
1672 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1673 (p / 'masked_new_file').touch(mode=0o750)
1674 st = os.stat(join('masked_new_file'))
1675 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1676
1677 @with_symlinks
1678 def test_resolve_loop(self):
1679 # Loop detection for broken symlinks under POSIX
1680 P = self.cls
1681 # Loops with relative symlinks
1682 os.symlink('linkX/inside', join('linkX'))
1683 self._check_symlink_loop(BASE, 'linkX')
1684 os.symlink('linkY', join('linkY'))
1685 self._check_symlink_loop(BASE, 'linkY')
1686 os.symlink('linkZ/../linkZ', join('linkZ'))
1687 self._check_symlink_loop(BASE, 'linkZ')
1688 # Loops with absolute symlinks
1689 os.symlink(join('linkU/inside'), join('linkU'))
1690 self._check_symlink_loop(BASE, 'linkU')
1691 os.symlink(join('linkV'), join('linkV'))
1692 self._check_symlink_loop(BASE, 'linkV')
1693 os.symlink(join('linkW/../linkW'), join('linkW'))
1694 self._check_symlink_loop(BASE, 'linkW')
1695
1696 def test_glob(self):
1697 P = self.cls
1698 p = P(BASE)
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001699 given = set(p.glob("FILEa"))
1700 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1701 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001702 self.assertEqual(set(p.glob("FILEa*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001703
1704 def test_rglob(self):
1705 P = self.cls
1706 p = P(BASE, "dirC")
Brett Cannonfe77f4e2013-11-22 16:14:10 -05001707 given = set(p.rglob("FILEd"))
1708 expect = set() if not support.fs_is_case_insensitive(BASE) else given
1709 self.assertEqual(given, expect)
Antoine Pitrou2dd38fb2013-11-22 22:26:01 +01001710 self.assertEqual(set(p.rglob("FILEd*")), set())
Antoine Pitrou31119e42013-11-22 17:38:12 +01001711
1712
1713@only_nt
1714class WindowsPathTest(_BasePathTest, unittest.TestCase):
1715 cls = pathlib.WindowsPath
1716
1717 def test_glob(self):
1718 P = self.cls
1719 p = P(BASE)
1720 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1721
1722 def test_rglob(self):
1723 P = self.cls
1724 p = P(BASE, "dirC")
1725 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1726
1727
1728if __name__ == "__main__":
1729 unittest.main()