blob: 7ece1f596924a0009fbbb1e54a7dd6e0206329e5 [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:
1096 if os.name == 'nt':
1097 # Workaround for http://bugs.python.org/issue13772
1098 def dirlink(src, dest):
1099 os.symlink(src, dest, target_is_directory=True)
1100 else:
1101 def dirlink(src, dest):
1102 os.symlink(src, dest)
1103 # Relative symlinks
1104 os.symlink('fileA', join('linkA'))
1105 os.symlink('non-existing', join('brokenLink'))
1106 dirlink('dirB', join('linkB'))
1107 dirlink(os.path.join('..', 'dirB'), join('dirA', 'linkC'))
1108 # This one goes upwards but doesn't create a loop
1109 dirlink(os.path.join('..', 'dirB'), join('dirB', 'linkD'))
1110
1111 def assertSame(self, path_a, path_b):
1112 self.assertTrue(os.path.samefile(str(path_a), str(path_b)),
1113 "%r and %r don't point to the same file" %
1114 (path_a, path_b))
1115
1116 def assertFileNotFound(self, func, *args, **kwargs):
1117 with self.assertRaises(FileNotFoundError) as cm:
1118 func(*args, **kwargs)
1119 self.assertEqual(cm.exception.errno, errno.ENOENT)
1120
1121 def _test_cwd(self, p):
1122 q = self.cls(os.getcwd())
1123 self.assertEqual(p, q)
1124 self.assertEqual(str(p), str(q))
1125 self.assertIs(type(p), type(q))
1126 self.assertTrue(p.is_absolute())
1127
1128 def test_cwd(self):
1129 p = self.cls.cwd()
1130 self._test_cwd(p)
1131
1132 def test_empty_path(self):
1133 # The empty path points to '.'
1134 p = self.cls('')
1135 self.assertEqual(p.stat(), os.stat('.'))
1136
1137 def test_exists(self):
1138 P = self.cls
1139 p = P(BASE)
1140 self.assertIs(True, p.exists())
1141 self.assertIs(True, (p / 'dirA').exists())
1142 self.assertIs(True, (p / 'fileA').exists())
1143 if not symlink_skip_reason:
1144 self.assertIs(True, (p / 'linkA').exists())
1145 self.assertIs(True, (p / 'linkB').exists())
1146 self.assertIs(False, (p / 'foo').exists())
1147 self.assertIs(False, P('/xyzzy').exists())
1148
1149 def test_open_common(self):
1150 p = self.cls(BASE)
1151 with (p / 'fileA').open('r') as f:
1152 self.assertIsInstance(f, io.TextIOBase)
1153 self.assertEqual(f.read(), "this is file A\n")
1154 with (p / 'fileA').open('rb') as f:
1155 self.assertIsInstance(f, io.BufferedIOBase)
1156 self.assertEqual(f.read().strip(), b"this is file A")
1157 with (p / 'fileA').open('rb', buffering=0) as f:
1158 self.assertIsInstance(f, io.RawIOBase)
1159 self.assertEqual(f.read().strip(), b"this is file A")
1160
1161 def test_iterdir(self):
1162 P = self.cls
1163 p = P(BASE)
1164 it = p.iterdir()
1165 paths = set(it)
1166 expected = ['dirA', 'dirB', 'dirC', 'fileA']
1167 if not symlink_skip_reason:
1168 expected += ['linkA', 'linkB', 'brokenLink']
1169 self.assertEqual(paths, { P(BASE, q) for q in expected })
1170
1171 @with_symlinks
1172 def test_iterdir_symlink(self):
1173 # __iter__ on a symlink to a directory
1174 P = self.cls
1175 p = P(BASE, 'linkB')
1176 paths = set(p.iterdir())
1177 expected = { P(BASE, 'linkB', q) for q in ['fileB', 'linkD'] }
1178 self.assertEqual(paths, expected)
1179
1180 def test_iterdir_nodir(self):
1181 # __iter__ on something that is not a directory
1182 p = self.cls(BASE, 'fileA')
1183 with self.assertRaises(OSError) as cm:
1184 next(p.iterdir())
1185 # ENOENT or EINVAL under Windows, ENOTDIR otherwise
1186 # (see issue #12802)
1187 self.assertIn(cm.exception.errno, (errno.ENOTDIR,
1188 errno.ENOENT, errno.EINVAL))
1189
1190 def test_glob_common(self):
1191 def _check(glob, expected):
1192 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1193 P = self.cls
1194 p = P(BASE)
1195 it = p.glob("fileA")
1196 self.assertIsInstance(it, collections.Iterator)
1197 _check(it, ["fileA"])
1198 _check(p.glob("fileB"), [])
1199 _check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
1200 if symlink_skip_reason:
1201 _check(p.glob("*A"), ['dirA', 'fileA'])
1202 else:
1203 _check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
1204 if symlink_skip_reason:
1205 _check(p.glob("*B/*"), ['dirB/fileB'])
1206 else:
1207 _check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
1208 'linkB/fileB', 'linkB/linkD'])
1209 if symlink_skip_reason:
1210 _check(p.glob("*/fileB"), ['dirB/fileB'])
1211 else:
1212 _check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
1213
1214 def test_rglob_common(self):
1215 def _check(glob, expected):
1216 self.assertEqual(set(glob), { P(BASE, q) for q in expected })
1217 P = self.cls
1218 p = P(BASE)
1219 it = p.rglob("fileA")
1220 self.assertIsInstance(it, collections.Iterator)
1221 # XXX cannot test because of symlink loops in the test setup
1222 #_check(it, ["fileA"])
1223 #_check(p.rglob("fileB"), ["dirB/fileB"])
1224 #_check(p.rglob("*/fileA"), [""])
1225 #_check(p.rglob("*/fileB"), ["dirB/fileB"])
1226 #_check(p.rglob("file*"), ["fileA", "dirB/fileB"])
1227 # No symlink loops here
1228 p = P(BASE, "dirC")
1229 _check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
1230 _check(p.rglob("*/*"), ["dirC/dirD/fileD"])
1231
1232 def test_glob_dotdot(self):
1233 # ".." is not special in globs
1234 P = self.cls
1235 p = P(BASE)
1236 self.assertEqual(set(p.glob("..")), { P(BASE, "..") })
1237 self.assertEqual(set(p.glob("dirA/../file*")), { P(BASE, "dirA/../fileA") })
1238 self.assertEqual(set(p.glob("../xyzzy")), set())
1239
1240 def _check_resolve_relative(self, p, expected):
1241 q = p.resolve()
1242 self.assertEqual(q, expected)
1243
1244 def _check_resolve_absolute(self, p, expected):
1245 q = p.resolve()
1246 self.assertEqual(q, expected)
1247
1248 @with_symlinks
1249 def test_resolve_common(self):
1250 P = self.cls
1251 p = P(BASE, 'foo')
1252 with self.assertRaises(OSError) as cm:
1253 p.resolve()
1254 self.assertEqual(cm.exception.errno, errno.ENOENT)
1255 # These are all relative symlinks
1256 p = P(BASE, 'dirB', 'fileB')
1257 self._check_resolve_relative(p, p)
1258 p = P(BASE, 'linkA')
1259 self._check_resolve_relative(p, P(BASE, 'fileA'))
1260 p = P(BASE, 'dirA', 'linkC', 'fileB')
1261 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1262 p = P(BASE, 'dirB', 'linkD', 'fileB')
1263 self._check_resolve_relative(p, P(BASE, 'dirB', 'fileB'))
1264 # Now create absolute symlinks
1265 d = tempfile.mkdtemp(suffix='-dirD')
1266 self.addCleanup(shutil.rmtree, d)
1267 os.symlink(os.path.join(d), join('dirA', 'linkX'))
1268 os.symlink(join('dirB'), os.path.join(d, 'linkY'))
1269 p = P(BASE, 'dirA', 'linkX', 'linkY', 'fileB')
1270 self._check_resolve_absolute(p, P(BASE, 'dirB', 'fileB'))
1271
1272 def test_with(self):
1273 p = self.cls(BASE)
1274 it = p.iterdir()
1275 it2 = p.iterdir()
1276 next(it2)
1277 with p:
1278 pass
1279 # I/O operation on closed path
1280 self.assertRaises(ValueError, next, it)
1281 self.assertRaises(ValueError, next, it2)
1282 self.assertRaises(ValueError, p.open)
1283 self.assertRaises(ValueError, p.resolve)
1284 self.assertRaises(ValueError, p.absolute)
1285 self.assertRaises(ValueError, p.__enter__)
1286
1287 def test_chmod(self):
1288 p = self.cls(BASE) / 'fileA'
1289 mode = p.stat().st_mode
1290 # Clear writable bit
1291 new_mode = mode & ~0o222
1292 p.chmod(new_mode)
1293 self.assertEqual(p.stat().st_mode, new_mode)
1294 # Set writable bit
1295 new_mode = mode | 0o222
1296 p.chmod(new_mode)
1297 self.assertEqual(p.stat().st_mode, new_mode)
1298
1299 # XXX also need a test for lchmod
1300
1301 def test_stat(self):
1302 p = self.cls(BASE) / 'fileA'
1303 st = p.stat()
1304 self.assertEqual(p.stat(), st)
1305 # Change file mode by flipping write bit
1306 p.chmod(st.st_mode ^ 0o222)
1307 self.addCleanup(p.chmod, st.st_mode)
1308 self.assertNotEqual(p.stat(), st)
1309
1310 @with_symlinks
1311 def test_lstat(self):
1312 p = self.cls(BASE)/ 'linkA'
1313 st = p.stat()
1314 self.assertNotEqual(st, p.lstat())
1315
1316 def test_lstat_nosymlink(self):
1317 p = self.cls(BASE) / 'fileA'
1318 st = p.stat()
1319 self.assertEqual(st, p.lstat())
1320
1321 @unittest.skipUnless(pwd, "the pwd module is needed for this test")
1322 def test_owner(self):
1323 p = self.cls(BASE) / 'fileA'
1324 uid = p.stat().st_uid
1325 name = pwd.getpwuid(uid).pw_name
1326 self.assertEqual(name, p.owner())
1327
1328 @unittest.skipUnless(grp, "the grp module is needed for this test")
1329 def test_group(self):
1330 p = self.cls(BASE) / 'fileA'
1331 gid = p.stat().st_gid
1332 name = grp.getgrgid(gid).gr_name
1333 self.assertEqual(name, p.group())
1334
1335 def test_unlink(self):
1336 p = self.cls(BASE) / 'fileA'
1337 p.unlink()
1338 self.assertFileNotFound(p.stat)
1339 self.assertFileNotFound(p.unlink)
1340
1341 def test_rmdir(self):
1342 p = self.cls(BASE) / 'dirA'
1343 for q in p.iterdir():
1344 q.unlink()
1345 p.rmdir()
1346 self.assertFileNotFound(p.stat)
1347 self.assertFileNotFound(p.unlink)
1348
1349 def test_rename(self):
1350 P = self.cls(BASE)
1351 p = P / 'fileA'
1352 size = p.stat().st_size
1353 # Renaming to another path
1354 q = P / 'dirA' / 'fileAA'
1355 p.rename(q)
1356 self.assertEqual(q.stat().st_size, size)
1357 self.assertFileNotFound(p.stat)
1358 # Renaming to a str of a relative path
1359 r = rel_join('fileAAA')
1360 q.rename(r)
1361 self.assertEqual(os.stat(r).st_size, size)
1362 self.assertFileNotFound(q.stat)
1363
1364 def test_replace(self):
1365 P = self.cls(BASE)
1366 p = P / 'fileA'
1367 size = p.stat().st_size
1368 # Replacing a non-existing path
1369 q = P / 'dirA' / 'fileAA'
1370 p.replace(q)
1371 self.assertEqual(q.stat().st_size, size)
1372 self.assertFileNotFound(p.stat)
1373 # Replacing another (existing) path
1374 r = rel_join('dirB', 'fileB')
1375 q.replace(r)
1376 self.assertEqual(os.stat(r).st_size, size)
1377 self.assertFileNotFound(q.stat)
1378
1379 def test_touch_common(self):
1380 P = self.cls(BASE)
1381 p = P / 'newfileA'
1382 self.assertFalse(p.exists())
1383 p.touch()
1384 self.assertTrue(p.exists())
1385 old_mtime = p.stat().st_mtime
1386 # Rewind the mtime sufficiently far in the past to work around
1387 # filesystem-specific timestamp granularity.
1388 os.utime(str(p), (old_mtime - 10, old_mtime - 10))
1389 # The file mtime is refreshed by calling touch() again
1390 p.touch()
1391 self.assertGreaterEqual(p.stat().st_mtime, old_mtime)
1392 p = P / 'newfileB'
1393 self.assertFalse(p.exists())
1394 p.touch(mode=0o700, exist_ok=False)
1395 self.assertTrue(p.exists())
1396 self.assertRaises(OSError, p.touch, exist_ok=False)
1397
1398 def test_mkdir(self):
1399 P = self.cls(BASE)
1400 p = P / 'newdirA'
1401 self.assertFalse(p.exists())
1402 p.mkdir()
1403 self.assertTrue(p.exists())
1404 self.assertTrue(p.is_dir())
1405 with self.assertRaises(OSError) as cm:
1406 p.mkdir()
1407 self.assertEqual(cm.exception.errno, errno.EEXIST)
1408 # XXX test `mode` arg
1409
1410 def test_mkdir_parents(self):
1411 # Creating a chain of directories
1412 p = self.cls(BASE, 'newdirB', 'newdirC')
1413 self.assertFalse(p.exists())
1414 with self.assertRaises(OSError) as cm:
1415 p.mkdir()
1416 self.assertEqual(cm.exception.errno, errno.ENOENT)
1417 p.mkdir(parents=True)
1418 self.assertTrue(p.exists())
1419 self.assertTrue(p.is_dir())
1420 with self.assertRaises(OSError) as cm:
1421 p.mkdir(parents=True)
1422 self.assertEqual(cm.exception.errno, errno.EEXIST)
1423 # XXX test `mode` arg
1424
1425 @with_symlinks
1426 def test_symlink_to(self):
1427 P = self.cls(BASE)
1428 target = P / 'fileA'
1429 # Symlinking a path target
1430 link = P / 'dirA' / 'linkAA'
1431 link.symlink_to(target)
1432 self.assertEqual(link.stat(), target.stat())
1433 self.assertNotEqual(link.lstat(), target.stat())
1434 # Symlinking a str target
1435 link = P / 'dirA' / 'linkAAA'
1436 link.symlink_to(str(target))
1437 self.assertEqual(link.stat(), target.stat())
1438 self.assertNotEqual(link.lstat(), target.stat())
1439 self.assertFalse(link.is_dir())
1440 # Symlinking to a directory
1441 target = P / 'dirB'
1442 link = P / 'dirA' / 'linkAAAA'
1443 link.symlink_to(target, target_is_directory=True)
1444 self.assertEqual(link.stat(), target.stat())
1445 self.assertNotEqual(link.lstat(), target.stat())
1446 self.assertTrue(link.is_dir())
1447 self.assertTrue(list(link.iterdir()))
1448
1449 def test_is_dir(self):
1450 P = self.cls(BASE)
1451 self.assertTrue((P / 'dirA').is_dir())
1452 self.assertFalse((P / 'fileA').is_dir())
1453 self.assertFalse((P / 'non-existing').is_dir())
1454 if not symlink_skip_reason:
1455 self.assertFalse((P / 'linkA').is_dir())
1456 self.assertTrue((P / 'linkB').is_dir())
1457 self.assertFalse((P/ 'brokenLink').is_dir())
1458
1459 def test_is_file(self):
1460 P = self.cls(BASE)
1461 self.assertTrue((P / 'fileA').is_file())
1462 self.assertFalse((P / 'dirA').is_file())
1463 self.assertFalse((P / 'non-existing').is_file())
1464 if not symlink_skip_reason:
1465 self.assertTrue((P / 'linkA').is_file())
1466 self.assertFalse((P / 'linkB').is_file())
1467 self.assertFalse((P/ 'brokenLink').is_file())
1468
1469 def test_is_symlink(self):
1470 P = self.cls(BASE)
1471 self.assertFalse((P / 'fileA').is_symlink())
1472 self.assertFalse((P / 'dirA').is_symlink())
1473 self.assertFalse((P / 'non-existing').is_symlink())
1474 if not symlink_skip_reason:
1475 self.assertTrue((P / 'linkA').is_symlink())
1476 self.assertTrue((P / 'linkB').is_symlink())
1477 self.assertTrue((P/ 'brokenLink').is_symlink())
1478
1479 def test_is_fifo_false(self):
1480 P = self.cls(BASE)
1481 self.assertFalse((P / 'fileA').is_fifo())
1482 self.assertFalse((P / 'dirA').is_fifo())
1483 self.assertFalse((P / 'non-existing').is_fifo())
1484
1485 @unittest.skipUnless(hasattr(os, "mkfifo"), "os.mkfifo() required")
1486 def test_is_fifo_true(self):
1487 P = self.cls(BASE, 'myfifo')
1488 os.mkfifo(str(P))
1489 self.assertTrue(P.is_fifo())
1490 self.assertFalse(P.is_socket())
1491 self.assertFalse(P.is_file())
1492
1493 def test_is_socket_false(self):
1494 P = self.cls(BASE)
1495 self.assertFalse((P / 'fileA').is_socket())
1496 self.assertFalse((P / 'dirA').is_socket())
1497 self.assertFalse((P / 'non-existing').is_socket())
1498
1499 @unittest.skipUnless(hasattr(socket, "AF_UNIX"), "Unix sockets required")
1500 def test_is_socket_true(self):
1501 P = self.cls(BASE, 'mysock')
1502 sock = socket.socket(socket.SOCK_STREAM, socket.AF_UNIX)
1503 self.addCleanup(sock.close)
1504 sock.bind(str(P))
1505 self.assertTrue(P.is_socket())
1506 self.assertFalse(P.is_fifo())
1507 self.assertFalse(P.is_file())
1508
1509 def test_is_block_device_false(self):
1510 P = self.cls(BASE)
1511 self.assertFalse((P / 'fileA').is_block_device())
1512 self.assertFalse((P / 'dirA').is_block_device())
1513 self.assertFalse((P / 'non-existing').is_block_device())
1514
1515 def test_is_char_device_false(self):
1516 P = self.cls(BASE)
1517 self.assertFalse((P / 'fileA').is_char_device())
1518 self.assertFalse((P / 'dirA').is_char_device())
1519 self.assertFalse((P / 'non-existing').is_char_device())
1520
1521 def test_is_char_device_true(self):
1522 # Under Unix, /dev/null should generally be a char device
1523 P = self.cls('/dev/null')
1524 if not P.exists():
1525 self.skipTest("/dev/null required")
1526 self.assertTrue(P.is_char_device())
1527 self.assertFalse(P.is_block_device())
1528 self.assertFalse(P.is_file())
1529
1530 def test_pickling_common(self):
1531 p = self.cls(BASE, 'fileA')
1532 for proto in range(0, pickle.HIGHEST_PROTOCOL + 1):
1533 dumped = pickle.dumps(p, proto)
1534 pp = pickle.loads(dumped)
1535 self.assertEqual(pp.stat(), p.stat())
1536
1537 def test_parts_interning(self):
1538 P = self.cls
1539 p = P('/usr/bin/foo')
1540 q = P('/usr/local/bin')
1541 # 'usr'
1542 self.assertIs(p.parts[1], q.parts[1])
1543 # 'bin'
1544 self.assertIs(p.parts[2], q.parts[3])
1545
1546
1547class PathTest(_BasePathTest, unittest.TestCase):
1548 cls = pathlib.Path
1549
1550 def test_concrete_class(self):
1551 p = self.cls('a')
1552 self.assertIs(type(p),
1553 pathlib.WindowsPath if os.name == 'nt' else pathlib.PosixPath)
1554
1555 def test_unsupported_flavour(self):
1556 if os.name == 'nt':
1557 self.assertRaises(NotImplementedError, pathlib.PosixPath)
1558 else:
1559 self.assertRaises(NotImplementedError, pathlib.WindowsPath)
1560
1561
1562@only_posix
1563class PosixPathTest(_BasePathTest, unittest.TestCase):
1564 cls = pathlib.PosixPath
1565
1566 def _check_symlink_loop(self, *args):
1567 path = self.cls(*args)
1568 with self.assertRaises(RuntimeError):
1569 print(path.resolve())
1570
1571 def test_open_mode(self):
1572 old_mask = os.umask(0)
1573 self.addCleanup(os.umask, old_mask)
1574 p = self.cls(BASE)
1575 with (p / 'new_file').open('wb'):
1576 pass
1577 st = os.stat(join('new_file'))
1578 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1579 os.umask(0o022)
1580 with (p / 'other_new_file').open('wb'):
1581 pass
1582 st = os.stat(join('other_new_file'))
1583 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1584
1585 def test_touch_mode(self):
1586 old_mask = os.umask(0)
1587 self.addCleanup(os.umask, old_mask)
1588 p = self.cls(BASE)
1589 (p / 'new_file').touch()
1590 st = os.stat(join('new_file'))
1591 self.assertEqual(stat.S_IMODE(st.st_mode), 0o666)
1592 os.umask(0o022)
1593 (p / 'other_new_file').touch()
1594 st = os.stat(join('other_new_file'))
1595 self.assertEqual(stat.S_IMODE(st.st_mode), 0o644)
1596 (p / 'masked_new_file').touch(mode=0o750)
1597 st = os.stat(join('masked_new_file'))
1598 self.assertEqual(stat.S_IMODE(st.st_mode), 0o750)
1599
1600 @with_symlinks
1601 def test_resolve_loop(self):
1602 # Loop detection for broken symlinks under POSIX
1603 P = self.cls
1604 # Loops with relative symlinks
1605 os.symlink('linkX/inside', join('linkX'))
1606 self._check_symlink_loop(BASE, 'linkX')
1607 os.symlink('linkY', join('linkY'))
1608 self._check_symlink_loop(BASE, 'linkY')
1609 os.symlink('linkZ/../linkZ', join('linkZ'))
1610 self._check_symlink_loop(BASE, 'linkZ')
1611 # Loops with absolute symlinks
1612 os.symlink(join('linkU/inside'), join('linkU'))
1613 self._check_symlink_loop(BASE, 'linkU')
1614 os.symlink(join('linkV'), join('linkV'))
1615 self._check_symlink_loop(BASE, 'linkV')
1616 os.symlink(join('linkW/../linkW'), join('linkW'))
1617 self._check_symlink_loop(BASE, 'linkW')
1618
1619 def test_glob(self):
1620 P = self.cls
1621 p = P(BASE)
1622 self.assertEqual(set(p.glob("FILEa")), set())
1623
1624 def test_rglob(self):
1625 P = self.cls
1626 p = P(BASE, "dirC")
1627 self.assertEqual(set(p.rglob("FILEd")), set())
1628
1629
1630@only_nt
1631class WindowsPathTest(_BasePathTest, unittest.TestCase):
1632 cls = pathlib.WindowsPath
1633
1634 def test_glob(self):
1635 P = self.cls
1636 p = P(BASE)
1637 self.assertEqual(set(p.glob("FILEa")), { P(BASE, "fileA") })
1638
1639 def test_rglob(self):
1640 P = self.cls
1641 p = P(BASE, "dirC")
1642 self.assertEqual(set(p.rglob("FILEd")), { P(BASE, "dirC/dirD/fileD") })
1643
1644
1645if __name__ == "__main__":
1646 unittest.main()