cpython/Lib/test/test_pathlib/test_pathlib_abc.py
Barney Gale 823a38a960
GH-79634: Speed up pathlib globbing by removing joinpath() call. (#114623)
Remove `self.joinpath('')` call that should have been removed in 6313cdde.

This makes `PathBase.glob('')` yield itself *without* adding a trailing slash. It's hard to say whether this is more or less correct, but at least everything else is faster, and there's no behaviour change in the public classes where empty glob patterns are disallowed.
2024-01-27 19:59:51 +00:00

2504 lines
108 KiB
Python

import collections
import io
import os
import errno
import stat
import unittest
from pathlib._abc import UnsupportedOperation, PathModuleBase, PurePathBase, PathBase
import posixpath
from test.support.os_helper import TESTFN
_tests_needing_posix = set()
_tests_needing_windows = set()
_tests_needing_symlinks = set()
def needs_posix(fn):
"""Decorator that marks a test as requiring a POSIX-flavoured path class."""
_tests_needing_posix.add(fn.__name__)
return fn
def needs_windows(fn):
"""Decorator that marks a test as requiring a Windows-flavoured path class."""
_tests_needing_windows.add(fn.__name__)
return fn
def needs_symlinks(fn):
"""Decorator that marks a test as requiring a path class that supports symlinks."""
_tests_needing_symlinks.add(fn.__name__)
return fn
class UnsupportedOperationTest(unittest.TestCase):
def test_is_notimplemented(self):
self.assertTrue(issubclass(UnsupportedOperation, NotImplementedError))
self.assertTrue(isinstance(UnsupportedOperation(), NotImplementedError))
class PathModuleBaseTest(unittest.TestCase):
cls = PathModuleBase
def test_unsupported_operation(self):
m = self.cls()
e = UnsupportedOperation
with self.assertRaises(e):
m.sep
self.assertRaises(e, m.join, 'foo')
self.assertRaises(e, m.split, 'foo')
self.assertRaises(e, m.splitdrive, 'foo')
self.assertRaises(e, m.normcase, 'foo')
self.assertRaises(e, m.isabs, 'foo')
#
# Tests for the pure classes.
#
class PurePathBaseTest(unittest.TestCase):
cls = PurePathBase
def test_unsupported_operation_pure(self):
p = self.cls('foo')
e = UnsupportedOperation
with self.assertRaises(e):
p.drive
with self.assertRaises(e):
p.root
with self.assertRaises(e):
p.anchor
with self.assertRaises(e):
p.parts
with self.assertRaises(e):
p.parent
with self.assertRaises(e):
p.parents
with self.assertRaises(e):
p.name
with self.assertRaises(e):
p.stem
with self.assertRaises(e):
p.suffix
with self.assertRaises(e):
p.suffixes
with self.assertRaises(e):
p / 'bar'
with self.assertRaises(e):
'bar' / p
self.assertRaises(e, p.joinpath, 'bar')
self.assertRaises(e, p.with_name, 'bar')
self.assertRaises(e, p.with_stem, 'bar')
self.assertRaises(e, p.with_suffix, '.txt')
self.assertRaises(e, p.relative_to, '')
self.assertRaises(e, p.is_relative_to, '')
self.assertRaises(e, p.is_absolute)
self.assertRaises(e, p.match, '*')
def test_magic_methods(self):
P = self.cls
self.assertFalse(hasattr(P, '__fspath__'))
self.assertFalse(hasattr(P, '__bytes__'))
self.assertIs(P.__reduce__, object.__reduce__)
self.assertIs(P.__repr__, object.__repr__)
self.assertIs(P.__hash__, object.__hash__)
self.assertIs(P.__eq__, object.__eq__)
self.assertIs(P.__lt__, object.__lt__)
self.assertIs(P.__le__, object.__le__)
self.assertIs(P.__gt__, object.__gt__)
self.assertIs(P.__ge__, object.__ge__)
def test_pathmod(self):
self.assertIsInstance(self.cls.pathmod, PathModuleBase)
class DummyPurePath(PurePathBase):
__slots__ = ()
pathmod = posixpath
def __eq__(self, other):
if not isinstance(other, DummyPurePath):
return NotImplemented
return str(self) == str(other)
def __hash__(self):
return hash(str(self))
def __repr__(self):
return "{}({!r})".format(self.__class__.__name__, self.as_posix())
class DummyPurePathTest(unittest.TestCase):
cls = DummyPurePath
# Use a base path that's unrelated to any real filesystem path.
base = f'/this/path/kills/fascists/{TESTFN}'
def setUp(self):
name = self.id().split('.')[-1]
if name in _tests_needing_posix and self.cls.pathmod is not posixpath:
self.skipTest('requires POSIX-flavoured path class')
if name in _tests_needing_windows and self.cls.pathmod is posixpath:
self.skipTest('requires Windows-flavoured path class')
p = self.cls('a')
self.pathmod = p.pathmod
self.sep = self.pathmod.sep
self.altsep = self.pathmod.altsep
def test_constructor_common(self):
P = self.cls
p = P('a')
self.assertIsInstance(p, P)
P('a', 'b', 'c')
P('/a', 'b', 'c')
P('a/b/c')
P('/a/b/c')
def _check_str_subclass(self, *args):
# Issue #21127: it should be possible to construct a PurePath object
# from a str subclass instance, and it then gets converted to
# a pure str object.
class StrSubclass(str):
pass
P = self.cls
p = P(*(StrSubclass(x) for x in args))
self.assertEqual(p, P(*args))
for part in p.parts:
self.assertIs(type(part), str)
def test_str_subclass_common(self):
self._check_str_subclass('')
self._check_str_subclass('.')
self._check_str_subclass('a')
self._check_str_subclass('a/b.txt')
self._check_str_subclass('/a/b.txt')
@needs_windows
def test_str_subclass_windows(self):
self._check_str_subclass('.\\a:b')
self._check_str_subclass('c:')
self._check_str_subclass('c:a')
self._check_str_subclass('c:a\\b.txt')
self._check_str_subclass('c:\\')
self._check_str_subclass('c:\\a')
self._check_str_subclass('c:\\a\\b.txt')
self._check_str_subclass('\\\\some\\share')
self._check_str_subclass('\\\\some\\share\\a')
self._check_str_subclass('\\\\some\\share\\a\\b.txt')
def test_with_segments_common(self):
class P(self.cls):
def __init__(self, *pathsegments, session_id):
super().__init__(*pathsegments)
self.session_id = session_id
def with_segments(self, *pathsegments):
return type(self)(*pathsegments, session_id=self.session_id)
p = P('foo', 'bar', session_id=42)
self.assertEqual(42, (p / 'foo').session_id)
self.assertEqual(42, ('foo' / p).session_id)
self.assertEqual(42, p.joinpath('foo').session_id)
self.assertEqual(42, p.with_name('foo').session_id)
self.assertEqual(42, p.with_stem('foo').session_id)
self.assertEqual(42, p.with_suffix('.foo').session_id)
self.assertEqual(42, p.with_segments('foo').session_id)
self.assertEqual(42, p.relative_to('foo').session_id)
self.assertEqual(42, p.parent.session_id)
for parent in p.parents:
self.assertEqual(42, parent.session_id)
def test_join_common(self):
P = self.cls
p = P('a/b')
pp = p.joinpath('c')
self.assertEqual(pp, P('a/b/c'))
self.assertIs(type(pp), type(p))
pp = p.joinpath('c', 'd')
self.assertEqual(pp, P('a/b/c/d'))
pp = p.joinpath('/c')
self.assertEqual(pp, P('/c'))
@needs_posix
def test_join_posix(self):
P = self.cls
p = P('//a')
pp = p.joinpath('b')
self.assertEqual(pp, P('//a/b'))
pp = P('/a').joinpath('//c')
self.assertEqual(pp, P('//c'))
pp = P('//a').joinpath('/c')
self.assertEqual(pp, P('/c'))
@needs_windows
def test_join_windows(self):
P = self.cls
p = P('C:/a/b')
pp = p.joinpath('x/y')
self.assertEqual(pp, P('C:/a/b/x/y'))
pp = p.joinpath('/x/y')
self.assertEqual(pp, P('C:/x/y'))
# Joining with a different drive => the first path is ignored, even
# if the second path is relative.
pp = p.joinpath('D:x/y')
self.assertEqual(pp, P('D:x/y'))
pp = p.joinpath('D:/x/y')
self.assertEqual(pp, P('D:/x/y'))
pp = p.joinpath('//host/share/x/y')
self.assertEqual(pp, P('//host/share/x/y'))
# Joining with the same drive => the first path is appended to if
# the second path is relative.
pp = p.joinpath('c:x/y')
self.assertEqual(pp, P('C:/a/b/x/y'))
pp = p.joinpath('c:/x/y')
self.assertEqual(pp, P('C:/x/y'))
# Joining with files with NTFS data streams => the filename should
# not be parsed as a drive letter
pp = p.joinpath(P('./d:s'))
self.assertEqual(pp, P('C:/a/b/d:s'))
pp = p.joinpath(P('./dd:s'))
self.assertEqual(pp, P('C:/a/b/dd:s'))
pp = p.joinpath(P('E:d:s'))
self.assertEqual(pp, P('E:d:s'))
# Joining onto a UNC path with no root
pp = P('//').joinpath('server')
self.assertEqual(pp, P('//server'))
pp = P('//server').joinpath('share')
self.assertEqual(pp, P('//server/share'))
pp = P('//./BootPartition').joinpath('Windows')
self.assertEqual(pp, P('//./BootPartition/Windows'))
def test_div_common(self):
# Basically the same as joinpath().
P = self.cls
p = P('a/b')
pp = p / 'c'
self.assertEqual(pp, P('a/b/c'))
self.assertIs(type(pp), type(p))
pp = p / 'c/d'
self.assertEqual(pp, P('a/b/c/d'))
pp = p / 'c' / 'd'
self.assertEqual(pp, P('a/b/c/d'))
pp = 'c' / p / 'd'
self.assertEqual(pp, P('c/a/b/d'))
pp = p/ '/c'
self.assertEqual(pp, P('/c'))
@needs_posix
def test_div_posix(self):
# Basically the same as joinpath().
P = self.cls
p = P('//a')
pp = p / 'b'
self.assertEqual(pp, P('//a/b'))
pp = P('/a') / '//c'
self.assertEqual(pp, P('//c'))
pp = P('//a') / '/c'
self.assertEqual(pp, P('/c'))
@needs_windows
def test_div_windows(self):
# Basically the same as joinpath().
P = self.cls
p = P('C:/a/b')
self.assertEqual(p / 'x/y', P('C:/a/b/x/y'))
self.assertEqual(p / 'x' / 'y', P('C:/a/b/x/y'))
self.assertEqual(p / '/x/y', P('C:/x/y'))
self.assertEqual(p / '/x' / 'y', P('C:/x/y'))
# Joining with a different drive => the first path is ignored, even
# if the second path is relative.
self.assertEqual(p / 'D:x/y', P('D:x/y'))
self.assertEqual(p / 'D:' / 'x/y', P('D:x/y'))
self.assertEqual(p / 'D:/x/y', P('D:/x/y'))
self.assertEqual(p / 'D:' / '/x/y', P('D:/x/y'))
self.assertEqual(p / '//host/share/x/y', P('//host/share/x/y'))
# Joining with the same drive => the first path is appended to if
# the second path is relative.
self.assertEqual(p / 'c:x/y', P('C:/a/b/x/y'))
self.assertEqual(p / 'c:/x/y', P('C:/x/y'))
# Joining with files with NTFS data streams => the filename should
# not be parsed as a drive letter
self.assertEqual(p / P('./d:s'), P('C:/a/b/d:s'))
self.assertEqual(p / P('./dd:s'), P('C:/a/b/dd:s'))
self.assertEqual(p / P('E:d:s'), P('E:d:s'))
def _check_str(self, expected, args):
p = self.cls(*args)
self.assertEqual(str(p), expected.replace('/', self.sep))
def test_str_common(self):
# Canonicalized paths roundtrip.
for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
self._check_str(pathstr, (pathstr,))
# Other tests for str() are in test_equivalences().
@needs_windows
def test_str_windows(self):
p = self.cls('a/b/c')
self.assertEqual(str(p), 'a\\b\\c')
p = self.cls('c:/a/b/c')
self.assertEqual(str(p), 'c:\\a\\b\\c')
p = self.cls('//a/b')
self.assertEqual(str(p), '\\\\a\\b\\')
p = self.cls('//a/b/c')
self.assertEqual(str(p), '\\\\a\\b\\c')
p = self.cls('//a/b/c/d')
self.assertEqual(str(p), '\\\\a\\b\\c\\d')
def test_as_posix_common(self):
P = self.cls
for pathstr in ('a', 'a/b', 'a/b/c', '/', '/a/b', '/a/b/c'):
self.assertEqual(P(pathstr).as_posix(), pathstr)
# Other tests for as_posix() are in test_equivalences().
def test_match_empty(self):
P = self.cls
self.assertRaises(ValueError, P('a').match, '')
def test_match_common(self):
P = self.cls
# Simple relative pattern.
self.assertTrue(P('b.py').match('b.py'))
self.assertTrue(P('a/b.py').match('b.py'))
self.assertTrue(P('/a/b.py').match('b.py'))
self.assertFalse(P('a.py').match('b.py'))
self.assertFalse(P('b/py').match('b.py'))
self.assertFalse(P('/a.py').match('b.py'))
self.assertFalse(P('b.py/c').match('b.py'))
# Wildcard relative pattern.
self.assertTrue(P('b.py').match('*.py'))
self.assertTrue(P('a/b.py').match('*.py'))
self.assertTrue(P('/a/b.py').match('*.py'))
self.assertFalse(P('b.pyc').match('*.py'))
self.assertFalse(P('b./py').match('*.py'))
self.assertFalse(P('b.py/c').match('*.py'))
# Multi-part relative pattern.
self.assertTrue(P('ab/c.py').match('a*/*.py'))
self.assertTrue(P('/d/ab/c.py').match('a*/*.py'))
self.assertFalse(P('a.py').match('a*/*.py'))
self.assertFalse(P('/dab/c.py').match('a*/*.py'))
self.assertFalse(P('ab/c.py/d').match('a*/*.py'))
# Absolute pattern.
self.assertTrue(P('/b.py').match('/*.py'))
self.assertFalse(P('b.py').match('/*.py'))
self.assertFalse(P('a/b.py').match('/*.py'))
self.assertFalse(P('/a/b.py').match('/*.py'))
# Multi-part absolute pattern.
self.assertTrue(P('/a/b.py').match('/a/*.py'))
self.assertFalse(P('/ab.py').match('/a/*.py'))
self.assertFalse(P('/a/b/c.py').match('/a/*.py'))
# Multi-part glob-style pattern.
self.assertFalse(P('/a/b/c.py').match('/**/*.py'))
self.assertTrue(P('/a/b/c.py').match('/a/**/*.py'))
# Case-sensitive flag
self.assertFalse(P('A.py').match('a.PY', case_sensitive=True))
self.assertTrue(P('A.py').match('a.PY', case_sensitive=False))
self.assertFalse(P('c:/a/B.Py').match('C:/A/*.pY', case_sensitive=True))
self.assertTrue(P('/a/b/c.py').match('/A/*/*.Py', case_sensitive=False))
# Matching against empty path
self.assertFalse(P('').match('*'))
self.assertFalse(P('').match('**'))
self.assertFalse(P('').match('**/*'))
@needs_posix
def test_match_posix(self):
P = self.cls
self.assertFalse(P('A.py').match('a.PY'))
@needs_windows
def test_match_windows(self):
P = self.cls
# Absolute patterns.
self.assertTrue(P('c:/b.py').match('*:/*.py'))
self.assertTrue(P('c:/b.py').match('c:/*.py'))
self.assertFalse(P('d:/b.py').match('c:/*.py')) # wrong drive
self.assertFalse(P('b.py').match('/*.py'))
self.assertFalse(P('b.py').match('c:*.py'))
self.assertFalse(P('b.py').match('c:/*.py'))
self.assertFalse(P('c:b.py').match('/*.py'))
self.assertFalse(P('c:b.py').match('c:/*.py'))
self.assertFalse(P('/b.py').match('c:*.py'))
self.assertFalse(P('/b.py').match('c:/*.py'))
# UNC patterns.
self.assertTrue(P('//some/share/a.py').match('//*/*/*.py'))
self.assertTrue(P('//some/share/a.py').match('//some/share/*.py'))
self.assertFalse(P('//other/share/a.py').match('//some/share/*.py'))
self.assertFalse(P('//some/share/a/b.py').match('//some/share/*.py'))
# Case-insensitivity.
self.assertTrue(P('B.py').match('b.PY'))
self.assertTrue(P('c:/a/B.Py').match('C:/A/*.pY'))
self.assertTrue(P('//Some/Share/B.Py').match('//somE/sharE/*.pY'))
# Path anchor doesn't match pattern anchor
self.assertFalse(P('c:/b.py').match('/*.py')) # 'c:/' vs '/'
self.assertFalse(P('c:/b.py').match('c:*.py')) # 'c:/' vs 'c:'
self.assertFalse(P('//some/share/a.py').match('/*.py')) # '//some/share/' vs '/'
def test_full_match_common(self):
P = self.cls
# Simple relative pattern.
self.assertTrue(P('b.py').full_match('b.py'))
self.assertFalse(P('a/b.py').full_match('b.py'))
self.assertFalse(P('/a/b.py').full_match('b.py'))
self.assertFalse(P('a.py').full_match('b.py'))
self.assertFalse(P('b/py').full_match('b.py'))
self.assertFalse(P('/a.py').full_match('b.py'))
self.assertFalse(P('b.py/c').full_match('b.py'))
# Wildcard relative pattern.
self.assertTrue(P('b.py').full_match('*.py'))
self.assertFalse(P('a/b.py').full_match('*.py'))
self.assertFalse(P('/a/b.py').full_match('*.py'))
self.assertFalse(P('b.pyc').full_match('*.py'))
self.assertFalse(P('b./py').full_match('*.py'))
self.assertFalse(P('b.py/c').full_match('*.py'))
# Multi-part relative pattern.
self.assertTrue(P('ab/c.py').full_match('a*/*.py'))
self.assertFalse(P('/d/ab/c.py').full_match('a*/*.py'))
self.assertFalse(P('a.py').full_match('a*/*.py'))
self.assertFalse(P('/dab/c.py').full_match('a*/*.py'))
self.assertFalse(P('ab/c.py/d').full_match('a*/*.py'))
# Absolute pattern.
self.assertTrue(P('/b.py').full_match('/*.py'))
self.assertFalse(P('b.py').full_match('/*.py'))
self.assertFalse(P('a/b.py').full_match('/*.py'))
self.assertFalse(P('/a/b.py').full_match('/*.py'))
# Multi-part absolute pattern.
self.assertTrue(P('/a/b.py').full_match('/a/*.py'))
self.assertFalse(P('/ab.py').full_match('/a/*.py'))
self.assertFalse(P('/a/b/c.py').full_match('/a/*.py'))
# Multi-part glob-style pattern.
self.assertTrue(P('a').full_match('**'))
self.assertTrue(P('c.py').full_match('**'))
self.assertTrue(P('a/b/c.py').full_match('**'))
self.assertTrue(P('/a/b/c.py').full_match('**'))
self.assertTrue(P('/a/b/c.py').full_match('/**'))
self.assertTrue(P('/a/b/c.py').full_match('/a/**'))
self.assertTrue(P('/a/b/c.py').full_match('**/*.py'))
self.assertTrue(P('/a/b/c.py').full_match('/**/*.py'))
self.assertTrue(P('/a/b/c.py').full_match('/a/**/*.py'))
self.assertTrue(P('/a/b/c.py').full_match('/a/b/**/*.py'))
self.assertTrue(P('/a/b/c.py').full_match('/**/**/**/**/*.py'))
self.assertFalse(P('c.py').full_match('**/a.py'))
self.assertFalse(P('c.py').full_match('c/**'))
self.assertFalse(P('a/b/c.py').full_match('**/a'))
self.assertFalse(P('a/b/c.py').full_match('**/a/b'))
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c'))
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c.'))
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
self.assertFalse(P('a/b/c.py').full_match('**/a/b/c./**'))
self.assertFalse(P('a/b/c.py').full_match('/a/b/c.py/**'))
self.assertFalse(P('a/b/c.py').full_match('/**/a/b/c.py'))
self.assertRaises(ValueError, P('a').full_match, '**a/b/c')
self.assertRaises(ValueError, P('a').full_match, 'a/b/c**')
# Case-sensitive flag
self.assertFalse(P('A.py').full_match('a.PY', case_sensitive=True))
self.assertTrue(P('A.py').full_match('a.PY', case_sensitive=False))
self.assertFalse(P('c:/a/B.Py').full_match('C:/A/*.pY', case_sensitive=True))
self.assertTrue(P('/a/b/c.py').full_match('/A/*/*.Py', case_sensitive=False))
# Matching against empty path
self.assertFalse(P('').full_match('*'))
self.assertTrue(P('').full_match('**'))
self.assertFalse(P('').full_match('**/*'))
# Matching with empty pattern
self.assertTrue(P('').full_match(''))
self.assertTrue(P('.').full_match('.'))
self.assertFalse(P('/').full_match(''))
self.assertFalse(P('/').full_match('.'))
self.assertFalse(P('foo').full_match(''))
self.assertFalse(P('foo').full_match('.'))
def test_parts_common(self):
# `parts` returns a tuple.
sep = self.sep
P = self.cls
p = P('a/b')
parts = p.parts
self.assertEqual(parts, ('a', 'b'))
# When the path is absolute, the anchor is a separate part.
p = P('/a/b')
parts = p.parts
self.assertEqual(parts, (sep, 'a', 'b'))
@needs_windows
def test_parts_windows(self):
P = self.cls
p = P('c:a/b')
parts = p.parts
self.assertEqual(parts, ('c:', 'a', 'b'))
p = P('c:/a/b')
parts = p.parts
self.assertEqual(parts, ('c:\\', 'a', 'b'))
p = P('//a/b/c/d')
parts = p.parts
self.assertEqual(parts, ('\\\\a\\b\\', 'c', 'd'))
def test_parent_common(self):
# Relative
P = self.cls
p = P('a/b/c')
self.assertEqual(p.parent, P('a/b'))
self.assertEqual(p.parent.parent, P('a'))
self.assertEqual(p.parent.parent.parent, P(''))
self.assertEqual(p.parent.parent.parent.parent, P(''))
# Anchored
p = P('/a/b/c')
self.assertEqual(p.parent, P('/a/b'))
self.assertEqual(p.parent.parent, P('/a'))
self.assertEqual(p.parent.parent.parent, P('/'))
self.assertEqual(p.parent.parent.parent.parent, P('/'))
@needs_windows
def test_parent_windows(self):
# Anchored
P = self.cls
p = P('z:a/b/c')
self.assertEqual(p.parent, P('z:a/b'))
self.assertEqual(p.parent.parent, P('z:a'))
self.assertEqual(p.parent.parent.parent, P('z:'))
self.assertEqual(p.parent.parent.parent.parent, P('z:'))
p = P('z:/a/b/c')
self.assertEqual(p.parent, P('z:/a/b'))
self.assertEqual(p.parent.parent, P('z:/a'))
self.assertEqual(p.parent.parent.parent, P('z:/'))
self.assertEqual(p.parent.parent.parent.parent, P('z:/'))
p = P('//a/b/c/d')
self.assertEqual(p.parent, P('//a/b/c'))
self.assertEqual(p.parent.parent, P('//a/b'))
self.assertEqual(p.parent.parent.parent, P('//a/b'))
def test_parents_common(self):
# Relative
P = self.cls
p = P('a/b/c')
par = p.parents
self.assertEqual(len(par), 3)
self.assertEqual(par[0], P('a/b'))
self.assertEqual(par[1], P('a'))
self.assertEqual(par[2], P(''))
self.assertEqual(par[-1], P(''))
self.assertEqual(par[-2], P('a'))
self.assertEqual(par[-3], P('a/b'))
self.assertEqual(par[0:1], (P('a/b'),))
self.assertEqual(par[:2], (P('a/b'), P('a')))
self.assertEqual(par[:-1], (P('a/b'), P('a')))
self.assertEqual(par[1:], (P('a'), P('')))
self.assertEqual(par[::2], (P('a/b'), P('')))
self.assertEqual(par[::-1], (P(''), P('a'), P('a/b')))
self.assertEqual(list(par), [P('a/b'), P('a'), P('')])
with self.assertRaises(IndexError):
par[-4]
with self.assertRaises(IndexError):
par[3]
with self.assertRaises(TypeError):
par[0] = p
# Anchored
p = P('/a/b/c')
par = p.parents
self.assertEqual(len(par), 3)
self.assertEqual(par[0], P('/a/b'))
self.assertEqual(par[1], P('/a'))
self.assertEqual(par[2], P('/'))
self.assertEqual(par[-1], P('/'))
self.assertEqual(par[-2], P('/a'))
self.assertEqual(par[-3], P('/a/b'))
self.assertEqual(par[0:1], (P('/a/b'),))
self.assertEqual(par[:2], (P('/a/b'), P('/a')))
self.assertEqual(par[:-1], (P('/a/b'), P('/a')))
self.assertEqual(par[1:], (P('/a'), P('/')))
self.assertEqual(par[::2], (P('/a/b'), P('/')))
self.assertEqual(par[::-1], (P('/'), P('/a'), P('/a/b')))
self.assertEqual(list(par), [P('/a/b'), P('/a'), P('/')])
with self.assertRaises(IndexError):
par[-4]
with self.assertRaises(IndexError):
par[3]
@needs_windows
def test_parents_windows(self):
# Anchored
P = self.cls
p = P('z:a/b/')
par = p.parents
self.assertEqual(len(par), 2)
self.assertEqual(par[0], P('z:a'))
self.assertEqual(par[1], P('z:'))
self.assertEqual(par[0:1], (P('z:a'),))
self.assertEqual(par[:-1], (P('z:a'),))
self.assertEqual(par[:2], (P('z:a'), P('z:')))
self.assertEqual(par[1:], (P('z:'),))
self.assertEqual(par[::2], (P('z:a'),))
self.assertEqual(par[::-1], (P('z:'), P('z:a')))
self.assertEqual(list(par), [P('z:a'), P('z:')])
with self.assertRaises(IndexError):
par[2]
p = P('z:/a/b/')
par = p.parents
self.assertEqual(len(par), 2)
self.assertEqual(par[0], P('z:/a'))
self.assertEqual(par[1], P('z:/'))
self.assertEqual(par[0:1], (P('z:/a'),))
self.assertEqual(par[0:-1], (P('z:/a'),))
self.assertEqual(par[:2], (P('z:/a'), P('z:/')))
self.assertEqual(par[1:], (P('z:/'),))
self.assertEqual(par[::2], (P('z:/a'),))
self.assertEqual(par[::-1], (P('z:/'), P('z:/a'),))
self.assertEqual(list(par), [P('z:/a'), P('z:/')])
with self.assertRaises(IndexError):
par[2]
p = P('//a/b/c/d')
par = p.parents
self.assertEqual(len(par), 2)
self.assertEqual(par[0], P('//a/b/c'))
self.assertEqual(par[1], P('//a/b'))
self.assertEqual(par[0:1], (P('//a/b/c'),))
self.assertEqual(par[0:-1], (P('//a/b/c'),))
self.assertEqual(par[:2], (P('//a/b/c'), P('//a/b')))
self.assertEqual(par[1:], (P('//a/b'),))
self.assertEqual(par[::2], (P('//a/b/c'),))
self.assertEqual(par[::-1], (P('//a/b'), P('//a/b/c')))
self.assertEqual(list(par), [P('//a/b/c'), P('//a/b')])
with self.assertRaises(IndexError):
par[2]
def test_drive_common(self):
P = self.cls
self.assertEqual(P('a/b').drive, '')
self.assertEqual(P('/a/b').drive, '')
self.assertEqual(P('').drive, '')
@needs_windows
def test_drive_windows(self):
P = self.cls
self.assertEqual(P('c:').drive, 'c:')
self.assertEqual(P('c:a/b').drive, 'c:')
self.assertEqual(P('c:/').drive, 'c:')
self.assertEqual(P('c:/a/b/').drive, 'c:')
self.assertEqual(P('//a/b').drive, '\\\\a\\b')
self.assertEqual(P('//a/b/').drive, '\\\\a\\b')
self.assertEqual(P('//a/b/c/d').drive, '\\\\a\\b')
self.assertEqual(P('./c:a').drive, '')
def test_root_common(self):
P = self.cls
sep = self.sep
self.assertEqual(P('').root, '')
self.assertEqual(P('a/b').root, '')
self.assertEqual(P('/').root, sep)
self.assertEqual(P('/a/b').root, sep)
@needs_posix
def test_root_posix(self):
P = self.cls
self.assertEqual(P('/a/b').root, '/')
# POSIX special case for two leading slashes.
self.assertEqual(P('//a/b').root, '//')
@needs_windows
def test_root_windows(self):
P = self.cls
self.assertEqual(P('c:').root, '')
self.assertEqual(P('c:a/b').root, '')
self.assertEqual(P('c:/').root, '\\')
self.assertEqual(P('c:/a/b/').root, '\\')
self.assertEqual(P('//a/b').root, '\\')
self.assertEqual(P('//a/b/').root, '\\')
self.assertEqual(P('//a/b/c/d').root, '\\')
def test_anchor_common(self):
P = self.cls
sep = self.sep
self.assertEqual(P('').anchor, '')
self.assertEqual(P('a/b').anchor, '')
self.assertEqual(P('/').anchor, sep)
self.assertEqual(P('/a/b').anchor, sep)
@needs_windows
def test_anchor_windows(self):
P = self.cls
self.assertEqual(P('c:').anchor, 'c:')
self.assertEqual(P('c:a/b').anchor, 'c:')
self.assertEqual(P('c:/').anchor, 'c:\\')
self.assertEqual(P('c:/a/b/').anchor, 'c:\\')
self.assertEqual(P('//a/b').anchor, '\\\\a\\b\\')
self.assertEqual(P('//a/b/').anchor, '\\\\a\\b\\')
self.assertEqual(P('//a/b/c/d').anchor, '\\\\a\\b\\')
def test_name_empty(self):
P = self.cls
self.assertEqual(P('').name, '')
self.assertEqual(P('.').name, '.')
self.assertEqual(P('/a/b/.').name, '.')
def test_name_common(self):
P = self.cls
self.assertEqual(P('/').name, '')
self.assertEqual(P('a/b').name, 'b')
self.assertEqual(P('/a/b').name, 'b')
self.assertEqual(P('a/b.py').name, 'b.py')
self.assertEqual(P('/a/b.py').name, 'b.py')
@needs_windows
def test_name_windows(self):
P = self.cls
self.assertEqual(P('c:').name, '')
self.assertEqual(P('c:/').name, '')
self.assertEqual(P('c:a/b').name, 'b')
self.assertEqual(P('c:/a/b').name, 'b')
self.assertEqual(P('c:a/b.py').name, 'b.py')
self.assertEqual(P('c:/a/b.py').name, 'b.py')
self.assertEqual(P('//My.py/Share.php').name, '')
self.assertEqual(P('//My.py/Share.php/a/b').name, 'b')
def test_suffix_common(self):
P = self.cls
self.assertEqual(P('').suffix, '')
self.assertEqual(P('.').suffix, '')
self.assertEqual(P('..').suffix, '')
self.assertEqual(P('/').suffix, '')
self.assertEqual(P('a/b').suffix, '')
self.assertEqual(P('/a/b').suffix, '')
self.assertEqual(P('/a/b/.').suffix, '')
self.assertEqual(P('a/b.py').suffix, '.py')
self.assertEqual(P('/a/b.py').suffix, '.py')
self.assertEqual(P('a/.hgrc').suffix, '')
self.assertEqual(P('/a/.hgrc').suffix, '')
self.assertEqual(P('a/.hg.rc').suffix, '.rc')
self.assertEqual(P('/a/.hg.rc').suffix, '.rc')
self.assertEqual(P('a/b.tar.gz').suffix, '.gz')
self.assertEqual(P('/a/b.tar.gz').suffix, '.gz')
self.assertEqual(P('a/Some name. Ending with a dot.').suffix, '')
self.assertEqual(P('/a/Some name. Ending with a dot.').suffix, '')
@needs_windows
def test_suffix_windows(self):
P = self.cls
self.assertEqual(P('c:').suffix, '')
self.assertEqual(P('c:/').suffix, '')
self.assertEqual(P('c:a/b').suffix, '')
self.assertEqual(P('c:/a/b').suffix, '')
self.assertEqual(P('c:a/b.py').suffix, '.py')
self.assertEqual(P('c:/a/b.py').suffix, '.py')
self.assertEqual(P('c:a/.hgrc').suffix, '')
self.assertEqual(P('c:/a/.hgrc').suffix, '')
self.assertEqual(P('c:a/.hg.rc').suffix, '.rc')
self.assertEqual(P('c:/a/.hg.rc').suffix, '.rc')
self.assertEqual(P('c:a/b.tar.gz').suffix, '.gz')
self.assertEqual(P('c:/a/b.tar.gz').suffix, '.gz')
self.assertEqual(P('c:a/Some name. Ending with a dot.').suffix, '')
self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffix, '')
self.assertEqual(P('//My.py/Share.php').suffix, '')
self.assertEqual(P('//My.py/Share.php/a/b').suffix, '')
def test_suffixes_common(self):
P = self.cls
self.assertEqual(P('').suffixes, [])
self.assertEqual(P('.').suffixes, [])
self.assertEqual(P('/').suffixes, [])
self.assertEqual(P('a/b').suffixes, [])
self.assertEqual(P('/a/b').suffixes, [])
self.assertEqual(P('/a/b/.').suffixes, [])
self.assertEqual(P('a/b.py').suffixes, ['.py'])
self.assertEqual(P('/a/b.py').suffixes, ['.py'])
self.assertEqual(P('a/.hgrc').suffixes, [])
self.assertEqual(P('/a/.hgrc').suffixes, [])
self.assertEqual(P('a/.hg.rc').suffixes, ['.rc'])
self.assertEqual(P('/a/.hg.rc').suffixes, ['.rc'])
self.assertEqual(P('a/b.tar.gz').suffixes, ['.tar', '.gz'])
self.assertEqual(P('/a/b.tar.gz').suffixes, ['.tar', '.gz'])
self.assertEqual(P('a/Some name. Ending with a dot.').suffixes, [])
self.assertEqual(P('/a/Some name. Ending with a dot.').suffixes, [])
@needs_windows
def test_suffixes_windows(self):
P = self.cls
self.assertEqual(P('c:').suffixes, [])
self.assertEqual(P('c:/').suffixes, [])
self.assertEqual(P('c:a/b').suffixes, [])
self.assertEqual(P('c:/a/b').suffixes, [])
self.assertEqual(P('c:a/b.py').suffixes, ['.py'])
self.assertEqual(P('c:/a/b.py').suffixes, ['.py'])
self.assertEqual(P('c:a/.hgrc').suffixes, [])
self.assertEqual(P('c:/a/.hgrc').suffixes, [])
self.assertEqual(P('c:a/.hg.rc').suffixes, ['.rc'])
self.assertEqual(P('c:/a/.hg.rc').suffixes, ['.rc'])
self.assertEqual(P('c:a/b.tar.gz').suffixes, ['.tar', '.gz'])
self.assertEqual(P('c:/a/b.tar.gz').suffixes, ['.tar', '.gz'])
self.assertEqual(P('//My.py/Share.php').suffixes, [])
self.assertEqual(P('//My.py/Share.php/a/b').suffixes, [])
self.assertEqual(P('c:a/Some name. Ending with a dot.').suffixes, [])
self.assertEqual(P('c:/a/Some name. Ending with a dot.').suffixes, [])
def test_stem_empty(self):
P = self.cls
self.assertEqual(P('').stem, '')
self.assertEqual(P('.').stem, '.')
def test_stem_common(self):
P = self.cls
self.assertEqual(P('..').stem, '..')
self.assertEqual(P('/').stem, '')
self.assertEqual(P('a/b').stem, 'b')
self.assertEqual(P('a/b.py').stem, 'b')
self.assertEqual(P('a/.hgrc').stem, '.hgrc')
self.assertEqual(P('a/.hg.rc').stem, '.hg')
self.assertEqual(P('a/b.tar.gz').stem, 'b.tar')
self.assertEqual(P('a/Some name. Ending with a dot.').stem,
'Some name. Ending with a dot.')
@needs_windows
def test_stem_windows(self):
P = self.cls
self.assertEqual(P('c:').stem, '')
self.assertEqual(P('c:.').stem, '')
self.assertEqual(P('c:..').stem, '..')
self.assertEqual(P('c:/').stem, '')
self.assertEqual(P('c:a/b').stem, 'b')
self.assertEqual(P('c:a/b.py').stem, 'b')
self.assertEqual(P('c:a/.hgrc').stem, '.hgrc')
self.assertEqual(P('c:a/.hg.rc').stem, '.hg')
self.assertEqual(P('c:a/b.tar.gz').stem, 'b.tar')
self.assertEqual(P('c:a/Some name. Ending with a dot.').stem,
'Some name. Ending with a dot.')
def test_with_name_common(self):
P = self.cls
self.assertEqual(P('a/b').with_name('d.xml'), P('a/d.xml'))
self.assertEqual(P('/a/b').with_name('d.xml'), P('/a/d.xml'))
self.assertEqual(P('a/b.py').with_name('d.xml'), P('a/d.xml'))
self.assertEqual(P('/a/b.py').with_name('d.xml'), P('/a/d.xml'))
self.assertEqual(P('a/Dot ending.').with_name('d.xml'), P('a/d.xml'))
self.assertEqual(P('/a/Dot ending.').with_name('d.xml'), P('/a/d.xml'))
@needs_windows
def test_with_name_windows(self):
P = self.cls
self.assertEqual(P('c:a/b').with_name('d.xml'), P('c:a/d.xml'))
self.assertEqual(P('c:/a/b').with_name('d.xml'), P('c:/a/d.xml'))
self.assertEqual(P('c:a/Dot ending.').with_name('d.xml'), P('c:a/d.xml'))
self.assertEqual(P('c:/a/Dot ending.').with_name('d.xml'), P('c:/a/d.xml'))
self.assertRaises(ValueError, P('c:').with_name, 'd.xml')
self.assertRaises(ValueError, P('c:/').with_name, 'd.xml')
self.assertRaises(ValueError, P('//My/Share').with_name, 'd.xml')
self.assertEqual(str(P('a').with_name('d:')), '.\\d:')
self.assertEqual(str(P('a').with_name('d:e')), '.\\d:e')
self.assertEqual(P('c:a/b').with_name('d:'), P('c:a/d:'))
self.assertEqual(P('c:a/b').with_name('d:e'), P('c:a/d:e'))
self.assertRaises(ValueError, P('c:a/b').with_name, 'd:/e')
self.assertRaises(ValueError, P('c:a/b').with_name, '//My/Share')
def test_with_name_empty(self):
P = self.cls
self.assertEqual(P('').with_name('d.xml'), P('d.xml'))
self.assertEqual(P('.').with_name('d.xml'), P('d.xml'))
self.assertEqual(P('/').with_name('d.xml'), P('/d.xml'))
self.assertEqual(P('a/b').with_name(''), P('a/'))
self.assertEqual(P('a/b').with_name('.'), P('a/.'))
def test_with_name_seps(self):
P = self.cls
self.assertRaises(ValueError, P('a/b').with_name, '/c')
self.assertRaises(ValueError, P('a/b').with_name, 'c/')
self.assertRaises(ValueError, P('a/b').with_name, 'c/d')
def test_with_stem_common(self):
P = self.cls
self.assertEqual(P('a/b').with_stem('d'), P('a/d'))
self.assertEqual(P('/a/b').with_stem('d'), P('/a/d'))
self.assertEqual(P('a/b.py').with_stem('d'), P('a/d.py'))
self.assertEqual(P('/a/b.py').with_stem('d'), P('/a/d.py'))
self.assertEqual(P('/a/b.tar.gz').with_stem('d'), P('/a/d.gz'))
self.assertEqual(P('a/Dot ending.').with_stem('d'), P('a/d'))
self.assertEqual(P('/a/Dot ending.').with_stem('d'), P('/a/d'))
@needs_windows
def test_with_stem_windows(self):
P = self.cls
self.assertEqual(P('c:a/b').with_stem('d'), P('c:a/d'))
self.assertEqual(P('c:/a/b').with_stem('d'), P('c:/a/d'))
self.assertEqual(P('c:a/Dot ending.').with_stem('d'), P('c:a/d'))
self.assertEqual(P('c:/a/Dot ending.').with_stem('d'), P('c:/a/d'))
self.assertRaises(ValueError, P('c:').with_stem, 'd')
self.assertRaises(ValueError, P('c:/').with_stem, 'd')
self.assertRaises(ValueError, P('//My/Share').with_stem, 'd')
self.assertEqual(str(P('a').with_stem('d:')), '.\\d:')
self.assertEqual(str(P('a').with_stem('d:e')), '.\\d:e')
self.assertEqual(P('c:a/b').with_stem('d:'), P('c:a/d:'))
self.assertEqual(P('c:a/b').with_stem('d:e'), P('c:a/d:e'))
self.assertRaises(ValueError, P('c:a/b').with_stem, 'd:/e')
self.assertRaises(ValueError, P('c:a/b').with_stem, '//My/Share')
def test_with_stem_empty(self):
P = self.cls
self.assertEqual(P('').with_stem('d'), P('d'))
self.assertEqual(P('.').with_stem('d'), P('d'))
self.assertEqual(P('/').with_stem('d'), P('/d'))
self.assertEqual(P('a/b').with_stem(''), P('a/'))
self.assertEqual(P('a/b').with_stem('.'), P('a/.'))
def test_with_stem_seps(self):
P = self.cls
self.assertRaises(ValueError, P('a/b').with_stem, '/c')
self.assertRaises(ValueError, P('a/b').with_stem, 'c/')
self.assertRaises(ValueError, P('a/b').with_stem, 'c/d')
def test_with_suffix_common(self):
P = self.cls
self.assertEqual(P('a/b').with_suffix('.gz'), P('a/b.gz'))
self.assertEqual(P('/a/b').with_suffix('.gz'), P('/a/b.gz'))
self.assertEqual(P('a/b.py').with_suffix('.gz'), P('a/b.gz'))
self.assertEqual(P('/a/b.py').with_suffix('.gz'), P('/a/b.gz'))
# Stripping suffix.
self.assertEqual(P('a/b.py').with_suffix(''), P('a/b'))
self.assertEqual(P('/a/b').with_suffix(''), P('/a/b'))
@needs_windows
def test_with_suffix_windows(self):
P = self.cls
self.assertEqual(P('c:a/b').with_suffix('.gz'), P('c:a/b.gz'))
self.assertEqual(P('c:/a/b').with_suffix('.gz'), P('c:/a/b.gz'))
self.assertEqual(P('c:a/b.py').with_suffix('.gz'), P('c:a/b.gz'))
self.assertEqual(P('c:/a/b.py').with_suffix('.gz'), P('c:/a/b.gz'))
# Path doesn't have a "filename" component.
self.assertRaises(ValueError, P('').with_suffix, '.gz')
self.assertRaises(ValueError, P('.').with_suffix, '.gz')
self.assertRaises(ValueError, P('/').with_suffix, '.gz')
self.assertRaises(ValueError, P('//My/Share').with_suffix, '.gz')
# Invalid suffix.
self.assertRaises(ValueError, P('c:a/b').with_suffix, 'gz')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '/')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\')
self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '/.gz')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '\\.gz')
self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c:.gz')
self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c/d')
self.assertRaises(ValueError, P('c:a/b').with_suffix, 'c\\d')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c/d')
self.assertRaises(ValueError, P('c:a/b').with_suffix, '.c\\d')
def test_with_suffix_empty(self):
P = self.cls
# Path doesn't have a "filename" component.
self.assertEqual(P('').with_suffix('.gz'), P('.gz'))
self.assertEqual(P('.').with_suffix('.gz'), P('..gz'))
self.assertEqual(P('/').with_suffix('.gz'), P('/.gz'))
def test_with_suffix_seps(self):
P = self.cls
# Invalid suffix.
self.assertRaises(ValueError, P('a/b').with_suffix, 'gz')
self.assertRaises(ValueError, P('a/b').with_suffix, '/')
self.assertRaises(ValueError, P('a/b').with_suffix, '.')
self.assertRaises(ValueError, P('a/b').with_suffix, '/.gz')
self.assertRaises(ValueError, P('a/b').with_suffix, 'c/d')
self.assertRaises(ValueError, P('a/b').with_suffix, '.c/.d')
self.assertRaises(ValueError, P('a/b').with_suffix, './.d')
self.assertRaises(ValueError, P('a/b').with_suffix, '.d/.')
def test_relative_to_common(self):
P = self.cls
p = P('a/b')
self.assertRaises(TypeError, p.relative_to)
self.assertRaises(TypeError, p.relative_to, b'a')
self.assertEqual(p.relative_to(P('')), P('a/b'))
self.assertEqual(p.relative_to(''), P('a/b'))
self.assertEqual(p.relative_to(P('a')), P('b'))
self.assertEqual(p.relative_to('a'), P('b'))
self.assertEqual(p.relative_to('a/'), P('b'))
self.assertEqual(p.relative_to(P('a/b')), P(''))
self.assertEqual(p.relative_to('a/b'), P(''))
self.assertEqual(p.relative_to(P(''), walk_up=True), P('a/b'))
self.assertEqual(p.relative_to('', walk_up=True), P('a/b'))
self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b'))
self.assertEqual(p.relative_to('a', walk_up=True), P('b'))
self.assertEqual(p.relative_to('a/', walk_up=True), P('b'))
self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P(''))
self.assertEqual(p.relative_to('a/b', walk_up=True), P(''))
self.assertEqual(p.relative_to(P('a/c'), walk_up=True), P('../b'))
self.assertEqual(p.relative_to('a/c', walk_up=True), P('../b'))
self.assertEqual(p.relative_to(P('a/b/c'), walk_up=True), P('..'))
self.assertEqual(p.relative_to('a/b/c', walk_up=True), P('..'))
self.assertEqual(p.relative_to(P('c'), walk_up=True), P('../a/b'))
self.assertEqual(p.relative_to('c', walk_up=True), P('../a/b'))
# Unrelated paths.
self.assertRaises(ValueError, p.relative_to, P('c'))
self.assertRaises(ValueError, p.relative_to, P('a/b/c'))
self.assertRaises(ValueError, p.relative_to, P('a/c'))
self.assertRaises(ValueError, p.relative_to, P('/a'))
self.assertRaises(ValueError, p.relative_to, P("../a"))
self.assertRaises(ValueError, p.relative_to, P("a/.."))
self.assertRaises(ValueError, p.relative_to, P("/a/.."))
self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('/a'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True)
p = P('/a/b')
self.assertEqual(p.relative_to(P('/')), P('a/b'))
self.assertEqual(p.relative_to('/'), P('a/b'))
self.assertEqual(p.relative_to(P('/a')), P('b'))
self.assertEqual(p.relative_to('/a'), P('b'))
self.assertEqual(p.relative_to('/a/'), P('b'))
self.assertEqual(p.relative_to(P('/a/b')), P(''))
self.assertEqual(p.relative_to('/a/b'), P(''))
self.assertEqual(p.relative_to(P('/'), walk_up=True), P('a/b'))
self.assertEqual(p.relative_to('/', walk_up=True), P('a/b'))
self.assertEqual(p.relative_to(P('/a'), walk_up=True), P('b'))
self.assertEqual(p.relative_to('/a', walk_up=True), P('b'))
self.assertEqual(p.relative_to('/a/', walk_up=True), P('b'))
self.assertEqual(p.relative_to(P('/a/b'), walk_up=True), P(''))
self.assertEqual(p.relative_to('/a/b', walk_up=True), P(''))
self.assertEqual(p.relative_to(P('/a/c'), walk_up=True), P('../b'))
self.assertEqual(p.relative_to('/a/c', walk_up=True), P('../b'))
self.assertEqual(p.relative_to(P('/a/b/c'), walk_up=True), P('..'))
self.assertEqual(p.relative_to('/a/b/c', walk_up=True), P('..'))
self.assertEqual(p.relative_to(P('/c'), walk_up=True), P('../a/b'))
self.assertEqual(p.relative_to('/c', walk_up=True), P('../a/b'))
# Unrelated paths.
self.assertRaises(ValueError, p.relative_to, P('/c'))
self.assertRaises(ValueError, p.relative_to, P('/a/b/c'))
self.assertRaises(ValueError, p.relative_to, P('/a/c'))
self.assertRaises(ValueError, p.relative_to, P(''))
self.assertRaises(ValueError, p.relative_to, '')
self.assertRaises(ValueError, p.relative_to, P('a'))
self.assertRaises(ValueError, p.relative_to, P("../a"))
self.assertRaises(ValueError, p.relative_to, P("a/.."))
self.assertRaises(ValueError, p.relative_to, P("/a/.."))
self.assertRaises(ValueError, p.relative_to, P(''), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('a'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("../a"), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("a/.."), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P("/a/.."), walk_up=True)
@needs_windows
def test_relative_to_windows(self):
P = self.cls
p = P('C:Foo/Bar')
self.assertEqual(p.relative_to(P('c:')), P('Foo/Bar'))
self.assertEqual(p.relative_to('c:'), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('c:foO')), P('Bar'))
self.assertEqual(p.relative_to('c:foO'), P('Bar'))
self.assertEqual(p.relative_to('c:foO/'), P('Bar'))
self.assertEqual(p.relative_to(P('c:foO/baR')), P())
self.assertEqual(p.relative_to('c:foO/baR'), P())
self.assertEqual(p.relative_to(P('c:'), walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to('c:', walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('c:foO'), walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('c:foO', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('c:foO/', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to(P('c:foO/baR'), walk_up=True), P())
self.assertEqual(p.relative_to('c:foO/baR', walk_up=True), P())
self.assertEqual(p.relative_to(P('C:Foo/Bar/Baz'), walk_up=True), P('..'))
self.assertEqual(p.relative_to(P('C:Foo/Baz'), walk_up=True), P('../Bar'))
self.assertEqual(p.relative_to(P('C:Baz/Bar'), walk_up=True), P('../../Foo/Bar'))
# Unrelated paths.
self.assertRaises(ValueError, p.relative_to, P())
self.assertRaises(ValueError, p.relative_to, '')
self.assertRaises(ValueError, p.relative_to, P('d:'))
self.assertRaises(ValueError, p.relative_to, P('/'))
self.assertRaises(ValueError, p.relative_to, P('Foo'))
self.assertRaises(ValueError, p.relative_to, P('/Foo'))
self.assertRaises(ValueError, p.relative_to, P('C:/Foo'))
self.assertRaises(ValueError, p.relative_to, P('C:Foo/Bar/Baz'))
self.assertRaises(ValueError, p.relative_to, P('C:Foo/Baz'))
self.assertRaises(ValueError, p.relative_to, P(), walk_up=True)
self.assertRaises(ValueError, p.relative_to, '', walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('C:/Foo'), walk_up=True)
p = P('C:/Foo/Bar')
self.assertEqual(p.relative_to(P('c:/')), P('Foo/Bar'))
self.assertEqual(p.relative_to('c:/'), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('c:/foO')), P('Bar'))
self.assertEqual(p.relative_to('c:/foO'), P('Bar'))
self.assertEqual(p.relative_to('c:/foO/'), P('Bar'))
self.assertEqual(p.relative_to(P('c:/foO/baR')), P())
self.assertEqual(p.relative_to('c:/foO/baR'), P())
self.assertEqual(p.relative_to(P('c:/'), walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to('c:/', walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('c:/foO'), walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('c:/foO', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('c:/foO/', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to(P('c:/foO/baR'), walk_up=True), P())
self.assertEqual(p.relative_to('c:/foO/baR', walk_up=True), P())
self.assertEqual(p.relative_to('C:/Baz', walk_up=True), P('../Foo/Bar'))
self.assertEqual(p.relative_to('C:/Foo/Bar/Baz', walk_up=True), P('..'))
self.assertEqual(p.relative_to('C:/Foo/Baz', walk_up=True), P('../Bar'))
# Unrelated paths.
self.assertRaises(ValueError, p.relative_to, 'c:')
self.assertRaises(ValueError, p.relative_to, P('c:'))
self.assertRaises(ValueError, p.relative_to, P('C:/Baz'))
self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Bar/Baz'))
self.assertRaises(ValueError, p.relative_to, P('C:/Foo/Baz'))
self.assertRaises(ValueError, p.relative_to, P('C:Foo'))
self.assertRaises(ValueError, p.relative_to, P('d:'))
self.assertRaises(ValueError, p.relative_to, P('d:/'))
self.assertRaises(ValueError, p.relative_to, P('/'))
self.assertRaises(ValueError, p.relative_to, P('/Foo'))
self.assertRaises(ValueError, p.relative_to, P('//C/Foo'))
self.assertRaises(ValueError, p.relative_to, 'c:', walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('c:'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('C:Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('d:'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('d:/'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('/'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('/Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('//C/Foo'), walk_up=True)
# UNC paths.
p = P('//Server/Share/Foo/Bar')
self.assertEqual(p.relative_to(P('//sErver/sHare')), P('Foo/Bar'))
self.assertEqual(p.relative_to('//sErver/sHare'), P('Foo/Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/'), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('//sErver/sHare/Foo')), P('Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/Foo'), P('Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/Foo/'), P('Bar'))
self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar')), P())
self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar'), P())
self.assertEqual(p.relative_to(P('//sErver/sHare'), walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to('//sErver/sHare', walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/', walk_up=True), P('Foo/Bar'))
self.assertEqual(p.relative_to(P('//sErver/sHare/Foo'), walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/Foo', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/Foo/', walk_up=True), P('Bar'))
self.assertEqual(p.relative_to(P('//sErver/sHare/Foo/Bar'), walk_up=True), P())
self.assertEqual(p.relative_to('//sErver/sHare/Foo/Bar', walk_up=True), P())
self.assertEqual(p.relative_to(P('//sErver/sHare/bar'), walk_up=True), P('../Foo/Bar'))
self.assertEqual(p.relative_to('//sErver/sHare/bar', walk_up=True), P('../Foo/Bar'))
# Unrelated paths.
self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'))
self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'))
self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'))
self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'))
self.assertRaises(ValueError, p.relative_to, P('/Server/Share/Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('c:/Server/Share/Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('//z/Share/Foo'), walk_up=True)
self.assertRaises(ValueError, p.relative_to, P('//Server/z/Foo'), walk_up=True)
def test_is_relative_to_common(self):
P = self.cls
p = P('a/b')
self.assertRaises(TypeError, p.is_relative_to)
self.assertRaises(TypeError, p.is_relative_to, b'a')
self.assertTrue(p.is_relative_to(P('')))
self.assertTrue(p.is_relative_to(''))
self.assertTrue(p.is_relative_to(P('a')))
self.assertTrue(p.is_relative_to('a/'))
self.assertTrue(p.is_relative_to(P('a/b')))
self.assertTrue(p.is_relative_to('a/b'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P('c')))
self.assertFalse(p.is_relative_to(P('a/b/c')))
self.assertFalse(p.is_relative_to(P('a/c')))
self.assertFalse(p.is_relative_to(P('/a')))
p = P('/a/b')
self.assertTrue(p.is_relative_to(P('/')))
self.assertTrue(p.is_relative_to('/'))
self.assertTrue(p.is_relative_to(P('/a')))
self.assertTrue(p.is_relative_to('/a'))
self.assertTrue(p.is_relative_to('/a/'))
self.assertTrue(p.is_relative_to(P('/a/b')))
self.assertTrue(p.is_relative_to('/a/b'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P('/c')))
self.assertFalse(p.is_relative_to(P('/a/b/c')))
self.assertFalse(p.is_relative_to(P('/a/c')))
self.assertFalse(p.is_relative_to(P('')))
self.assertFalse(p.is_relative_to(''))
self.assertFalse(p.is_relative_to(P('a')))
@needs_windows
def test_is_relative_to_windows(self):
P = self.cls
p = P('C:Foo/Bar')
self.assertTrue(p.is_relative_to(P('c:')))
self.assertTrue(p.is_relative_to('c:'))
self.assertTrue(p.is_relative_to(P('c:foO')))
self.assertTrue(p.is_relative_to('c:foO'))
self.assertTrue(p.is_relative_to('c:foO/'))
self.assertTrue(p.is_relative_to(P('c:foO/baR')))
self.assertTrue(p.is_relative_to('c:foO/baR'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P()))
self.assertFalse(p.is_relative_to(''))
self.assertFalse(p.is_relative_to(P('d:')))
self.assertFalse(p.is_relative_to(P('/')))
self.assertFalse(p.is_relative_to(P('Foo')))
self.assertFalse(p.is_relative_to(P('/Foo')))
self.assertFalse(p.is_relative_to(P('C:/Foo')))
self.assertFalse(p.is_relative_to(P('C:Foo/Bar/Baz')))
self.assertFalse(p.is_relative_to(P('C:Foo/Baz')))
p = P('C:/Foo/Bar')
self.assertTrue(p.is_relative_to(P('c:/')))
self.assertTrue(p.is_relative_to(P('c:/foO')))
self.assertTrue(p.is_relative_to('c:/foO/'))
self.assertTrue(p.is_relative_to(P('c:/foO/baR')))
self.assertTrue(p.is_relative_to('c:/foO/baR'))
# Unrelated paths.
self.assertFalse(p.is_relative_to('c:'))
self.assertFalse(p.is_relative_to(P('C:/Baz')))
self.assertFalse(p.is_relative_to(P('C:/Foo/Bar/Baz')))
self.assertFalse(p.is_relative_to(P('C:/Foo/Baz')))
self.assertFalse(p.is_relative_to(P('C:Foo')))
self.assertFalse(p.is_relative_to(P('d:')))
self.assertFalse(p.is_relative_to(P('d:/')))
self.assertFalse(p.is_relative_to(P('/')))
self.assertFalse(p.is_relative_to(P('/Foo')))
self.assertFalse(p.is_relative_to(P('//C/Foo')))
# UNC paths.
p = P('//Server/Share/Foo/Bar')
self.assertTrue(p.is_relative_to(P('//sErver/sHare')))
self.assertTrue(p.is_relative_to('//sErver/sHare'))
self.assertTrue(p.is_relative_to('//sErver/sHare/'))
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo')))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo'))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/'))
self.assertTrue(p.is_relative_to(P('//sErver/sHare/Foo/Bar')))
self.assertTrue(p.is_relative_to('//sErver/sHare/Foo/Bar'))
# Unrelated paths.
self.assertFalse(p.is_relative_to(P('/Server/Share/Foo')))
self.assertFalse(p.is_relative_to(P('c:/Server/Share/Foo')))
self.assertFalse(p.is_relative_to(P('//z/Share/Foo')))
self.assertFalse(p.is_relative_to(P('//Server/z/Foo')))
@needs_posix
def test_is_absolute_posix(self):
P = self.cls
self.assertFalse(P('').is_absolute())
self.assertFalse(P('a').is_absolute())
self.assertFalse(P('a/b/').is_absolute())
self.assertTrue(P('/').is_absolute())
self.assertTrue(P('/a').is_absolute())
self.assertTrue(P('/a/b/').is_absolute())
self.assertTrue(P('//a').is_absolute())
self.assertTrue(P('//a/b').is_absolute())
@needs_windows
def test_is_absolute_windows(self):
P = self.cls
# Under NT, only paths with both a drive and a root are absolute.
self.assertFalse(P().is_absolute())
self.assertFalse(P('a').is_absolute())
self.assertFalse(P('a/b/').is_absolute())
self.assertFalse(P('/').is_absolute())
self.assertFalse(P('/a').is_absolute())
self.assertFalse(P('/a/b/').is_absolute())
self.assertFalse(P('c:').is_absolute())
self.assertFalse(P('c:a').is_absolute())
self.assertFalse(P('c:a/b/').is_absolute())
self.assertTrue(P('c:/').is_absolute())
self.assertTrue(P('c:/a').is_absolute())
self.assertTrue(P('c:/a/b/').is_absolute())
# UNC paths are absolute by definition.
self.assertTrue(P('//').is_absolute())
self.assertTrue(P('//a').is_absolute())
self.assertTrue(P('//a/b').is_absolute())
self.assertTrue(P('//a/b/').is_absolute())
self.assertTrue(P('//a/b/c').is_absolute())
self.assertTrue(P('//a/b/c/d').is_absolute())
self.assertTrue(P('//?/UNC/').is_absolute())
self.assertTrue(P('//?/UNC/spam').is_absolute())
#
# Tests for the virtual classes.
#
class PathBaseTest(PurePathBaseTest):
cls = PathBase
def test_unsupported_operation(self):
P = self.cls
p = self.cls('')
e = UnsupportedOperation
self.assertRaises(e, p.stat)
self.assertRaises(e, p.lstat)
self.assertRaises(e, p.exists)
self.assertRaises(e, p.samefile, 'foo')
self.assertRaises(e, p.is_dir)
self.assertRaises(e, p.is_file)
self.assertRaises(e, p.is_mount)
self.assertRaises(e, p.is_symlink)
self.assertRaises(e, p.is_block_device)
self.assertRaises(e, p.is_char_device)
self.assertRaises(e, p.is_fifo)
self.assertRaises(e, p.is_socket)
self.assertRaises(e, p.open)
self.assertRaises(e, p.read_bytes)
self.assertRaises(e, p.read_text)
self.assertRaises(e, p.write_bytes, b'foo')
self.assertRaises(e, p.write_text, 'foo')
self.assertRaises(e, p.iterdir)
self.assertRaises(e, p.glob, '*')
self.assertRaises(e, p.rglob, '*')
self.assertRaises(e, lambda: list(p.walk()))
self.assertRaises(e, p.absolute)
self.assertRaises(e, P.cwd)
self.assertRaises(e, p.expanduser)
self.assertRaises(e, p.home)
self.assertRaises(e, p.readlink)
self.assertRaises(e, p.symlink_to, 'foo')
self.assertRaises(e, p.hardlink_to, 'foo')
self.assertRaises(e, p.mkdir)
self.assertRaises(e, p.touch)
self.assertRaises(e, p.rename, 'foo')
self.assertRaises(e, p.replace, 'foo')
self.assertRaises(e, p.chmod, 0o755)
self.assertRaises(e, p.lchmod, 0o755)
self.assertRaises(e, p.unlink)
self.assertRaises(e, p.rmdir)
self.assertRaises(e, p.owner)
self.assertRaises(e, p.group)
self.assertRaises(e, p.as_uri)
def test_as_uri_common(self):
e = UnsupportedOperation
self.assertRaises(e, self.cls('').as_uri)
def test_fspath_common(self):
self.assertRaises(TypeError, os.fspath, self.cls(''))
def test_as_bytes_common(self):
self.assertRaises(TypeError, bytes, self.cls(''))
class DummyPathIO(io.BytesIO):
"""
Used by DummyPath to implement `open('w')`
"""
def __init__(self, files, path):
super().__init__()
self.files = files
self.path = path
def close(self):
self.files[self.path] = self.getvalue()
super().close()
DummyPathStatResult = collections.namedtuple(
'DummyPathStatResult',
'st_mode st_ino st_dev st_nlink st_uid st_gid st_size st_atime st_mtime st_ctime')
class DummyPath(PathBase):
"""
Simple implementation of PathBase that keeps files and directories in
memory.
"""
__slots__ = ()
pathmod = posixpath
_files = {}
_directories = {}
_symlinks = {}
def __eq__(self, other):
if not isinstance(other, DummyPath):
return NotImplemented
return str(self) == str(other)
def __hash__(self):
return hash(str(self))
def __repr__(self):
return "{}({!r})".format(self.__class__.__name__, self.as_posix())
def stat(self, *, follow_symlinks=True):
if follow_symlinks:
path = str(self.resolve())
else:
path = str(self.parent.resolve() / self.name)
if path in self._files:
st_mode = stat.S_IFREG
elif path in self._directories:
st_mode = stat.S_IFDIR
elif path in self._symlinks:
st_mode = stat.S_IFLNK
else:
raise FileNotFoundError(errno.ENOENT, "Not found", str(self))
return DummyPathStatResult(st_mode, hash(str(self)), 0, 0, 0, 0, 0, 0, 0, 0)
def open(self, mode='r', buffering=-1, encoding=None,
errors=None, newline=None):
if buffering != -1:
raise NotImplementedError
path_obj = self.resolve()
path = str(path_obj)
name = path_obj.name
parent = str(path_obj.parent)
if path in self._directories:
raise IsADirectoryError(errno.EISDIR, "Is a directory", path)
text = 'b' not in mode
mode = ''.join(c for c in mode if c not in 'btU')
if mode == 'r':
if path not in self._files:
raise FileNotFoundError(errno.ENOENT, "File not found", path)
stream = io.BytesIO(self._files[path])
elif mode == 'w':
if parent not in self._directories:
raise FileNotFoundError(errno.ENOENT, "File not found", parent)
stream = DummyPathIO(self._files, path)
self._files[path] = b''
self._directories[parent].add(name)
else:
raise NotImplementedError
if text:
stream = io.TextIOWrapper(stream, encoding=encoding, errors=errors, newline=newline)
return stream
def iterdir(self):
path = str(self.resolve())
if path in self._files:
raise NotADirectoryError(errno.ENOTDIR, "Not a directory", path)
elif path in self._directories:
return (self / name for name in self._directories[path])
else:
raise FileNotFoundError(errno.ENOENT, "File not found", path)
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
path = str(self.resolve())
if path in self._directories:
if exist_ok:
return
else:
raise FileExistsError(errno.EEXIST, "File exists", path)
try:
if self.name:
self._directories[str(self.parent)].add(self.name)
self._directories[path] = set()
except KeyError:
if not parents:
raise FileNotFoundError(errno.ENOENT, "File not found", str(self.parent)) from None
self.parent.mkdir(parents=True, exist_ok=True)
self.mkdir(mode, parents=False, exist_ok=exist_ok)
class DummyPathTest(DummyPurePathTest):
"""Tests for PathBase methods that use stat(), open() and iterdir()."""
cls = DummyPath
can_symlink = False
# (self.base)
# |
# |-- brokenLink -> non-existing
# |-- dirA
# | `-- linkC -> ../dirB
# |-- dirB
# | |-- fileB
# | `-- linkD -> ../dirB
# |-- dirC
# | |-- dirD
# | | `-- fileD
# | `-- fileC
# | `-- novel.txt
# |-- dirE # No permissions
# |-- fileA
# |-- linkA -> fileA
# |-- linkB -> dirB
# `-- brokenLinkLoop -> brokenLinkLoop
#
def setUp(self):
super().setUp()
name = self.id().split('.')[-1]
if name in _tests_needing_symlinks and not self.can_symlink:
self.skipTest('requires symlinks')
pathmod = self.cls.pathmod
p = self.cls(self.base)
p.mkdir(parents=True)
p.joinpath('dirA').mkdir()
p.joinpath('dirB').mkdir()
p.joinpath('dirC').mkdir()
p.joinpath('dirC', 'dirD').mkdir()
p.joinpath('dirE').mkdir()
with p.joinpath('fileA').open('wb') as f:
f.write(b"this is file A\n")
with p.joinpath('dirB', 'fileB').open('wb') as f:
f.write(b"this is file B\n")
with p.joinpath('dirC', 'fileC').open('wb') as f:
f.write(b"this is file C\n")
with p.joinpath('dirC', 'novel.txt').open('wb') as f:
f.write(b"this is a novel\n")
with p.joinpath('dirC', 'dirD', 'fileD').open('wb') as f:
f.write(b"this is file D\n")
if self.can_symlink:
p.joinpath('linkA').symlink_to('fileA')
p.joinpath('brokenLink').symlink_to('non-existing')
p.joinpath('linkB').symlink_to('dirB')
p.joinpath('dirA', 'linkC').symlink_to(pathmod.join('..', 'dirB'))
p.joinpath('dirB', 'linkD').symlink_to(pathmod.join('..', 'dirB'))
p.joinpath('brokenLinkLoop').symlink_to('brokenLinkLoop')
def tearDown(self):
cls = self.cls
cls._files.clear()
cls._directories.clear()
cls._symlinks.clear()
def tempdir(self):
path = self.cls(self.base).with_name('tmp-dirD')
path.mkdir()
return path
def assertFileNotFound(self, func, *args, **kwargs):
with self.assertRaises(FileNotFoundError) as cm:
func(*args, **kwargs)
self.assertEqual(cm.exception.errno, errno.ENOENT)
def assertEqualNormCase(self, path_a, path_b):
normcase = self.pathmod.normcase
self.assertEqual(normcase(path_a), normcase(path_b))
def test_samefile(self):
pathmod = self.pathmod
fileA_path = pathmod.join(self.base, 'fileA')
fileB_path = pathmod.join(self.base, 'dirB', 'fileB')
p = self.cls(fileA_path)
pp = self.cls(fileA_path)
q = self.cls(fileB_path)
self.assertTrue(p.samefile(fileA_path))
self.assertTrue(p.samefile(pp))
self.assertFalse(p.samefile(fileB_path))
self.assertFalse(p.samefile(q))
# Test the non-existent file case
non_existent = pathmod.join(self.base, 'foo')
r = self.cls(non_existent)
self.assertRaises(FileNotFoundError, p.samefile, r)
self.assertRaises(FileNotFoundError, p.samefile, non_existent)
self.assertRaises(FileNotFoundError, r.samefile, p)
self.assertRaises(FileNotFoundError, r.samefile, non_existent)
self.assertRaises(FileNotFoundError, r.samefile, r)
self.assertRaises(FileNotFoundError, r.samefile, non_existent)
def test_exists(self):
P = self.cls
p = P(self.base)
self.assertIs(True, p.exists())
self.assertIs(True, (p / 'dirA').exists())
self.assertIs(True, (p / 'fileA').exists())
self.assertIs(False, (p / 'fileA' / 'bah').exists())
if self.can_symlink:
self.assertIs(True, (p / 'linkA').exists())
self.assertIs(True, (p / 'linkB').exists())
self.assertIs(True, (p / 'linkB' / 'fileB').exists())
self.assertIs(False, (p / 'linkA' / 'bah').exists())
self.assertIs(False, (p / 'brokenLink').exists())
self.assertIs(True, (p / 'brokenLink').exists(follow_symlinks=False))
self.assertIs(False, (p / 'foo').exists())
self.assertIs(False, P('/xyzzy').exists())
self.assertIs(False, P(self.base + '\udfff').exists())
self.assertIs(False, P(self.base + '\x00').exists())
def test_open_common(self):
p = self.cls(self.base)
with (p / 'fileA').open('r') as f:
self.assertIsInstance(f, io.TextIOBase)
self.assertEqual(f.read(), "this is file A\n")
with (p / 'fileA').open('rb') as f:
self.assertIsInstance(f, io.BufferedIOBase)
self.assertEqual(f.read().strip(), b"this is file A")
def test_read_write_bytes(self):
p = self.cls(self.base)
(p / 'fileA').write_bytes(b'abcdefg')
self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
# Check that trying to write str does not truncate the file.
self.assertRaises(TypeError, (p / 'fileA').write_bytes, 'somestr')
self.assertEqual((p / 'fileA').read_bytes(), b'abcdefg')
def test_read_write_text(self):
p = self.cls(self.base)
(p / 'fileA').write_text('äbcdefg', encoding='latin-1')
self.assertEqual((p / 'fileA').read_text(
encoding='utf-8', errors='ignore'), 'bcdefg')
# Check that trying to write bytes does not truncate the file.
self.assertRaises(TypeError, (p / 'fileA').write_text, b'somebytes')
self.assertEqual((p / 'fileA').read_text(encoding='latin-1'), 'äbcdefg')
def test_read_text_with_newlines(self):
p = self.cls(self.base)
# Check that `\n` character change nothing
(p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
self.assertEqual((p / 'fileA').read_text(newline='\n'),
'abcde\r\nfghlk\n\rmnopq')
# Check that `\r` character replaces `\n`
(p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
self.assertEqual((p / 'fileA').read_text(newline='\r'),
'abcde\r\nfghlk\n\rmnopq')
# Check that `\r\n` character replaces `\n`
(p / 'fileA').write_bytes(b'abcde\r\nfghlk\n\rmnopq')
self.assertEqual((p / 'fileA').read_text(newline='\r\n'),
'abcde\r\nfghlk\n\rmnopq')
def test_write_text_with_newlines(self):
p = self.cls(self.base)
# Check that `\n` character change nothing
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\n')
self.assertEqual((p / 'fileA').read_bytes(),
b'abcde\r\nfghlk\n\rmnopq')
# Check that `\r` character replaces `\n`
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r')
self.assertEqual((p / 'fileA').read_bytes(),
b'abcde\r\rfghlk\r\rmnopq')
# Check that `\r\n` character replaces `\n`
(p / 'fileA').write_text('abcde\r\nfghlk\n\rmnopq', newline='\r\n')
self.assertEqual((p / 'fileA').read_bytes(),
b'abcde\r\r\nfghlk\r\n\rmnopq')
# Check that no argument passed will change `\n` to `os.linesep`
os_linesep_byte = bytes(os.linesep, encoding='ascii')
(p / 'fileA').write_text('abcde\nfghlk\n\rmnopq')
self.assertEqual((p / 'fileA').read_bytes(),
b'abcde' + os_linesep_byte + b'fghlk' + os_linesep_byte + b'\rmnopq')
def test_iterdir(self):
P = self.cls
p = P(self.base)
it = p.iterdir()
paths = set(it)
expected = ['dirA', 'dirB', 'dirC', 'dirE', 'fileA']
if self.can_symlink:
expected += ['linkA', 'linkB', 'brokenLink', 'brokenLinkLoop']
self.assertEqual(paths, { P(self.base, q) for q in expected })
@needs_symlinks
def test_iterdir_symlink(self):
# __iter__ on a symlink to a directory.
P = self.cls
p = P(self.base, 'linkB')
paths = set(p.iterdir())
expected = { P(self.base, 'linkB', q) for q in ['fileB', 'linkD'] }
self.assertEqual(paths, expected)
def test_iterdir_nodir(self):
# __iter__ on something that is not a directory.
p = self.cls(self.base, 'fileA')
with self.assertRaises(OSError) as cm:
p.iterdir()
# ENOENT or EINVAL under Windows, ENOTDIR otherwise
# (see issue #12802).
self.assertIn(cm.exception.errno, (errno.ENOTDIR,
errno.ENOENT, errno.EINVAL))
def test_glob_common(self):
def _check(glob, expected):
self.assertEqual(set(glob), { P(self.base, q) for q in expected })
P = self.cls
p = P(self.base)
it = p.glob("fileA")
self.assertIsInstance(it, collections.abc.Iterator)
_check(it, ["fileA"])
_check(p.glob("fileB"), [])
_check(p.glob("dir*/file*"), ["dirB/fileB", "dirC/fileC"])
if not self.can_symlink:
_check(p.glob("*A"), ['dirA', 'fileA'])
else:
_check(p.glob("*A"), ['dirA', 'fileA', 'linkA'])
if not self.can_symlink:
_check(p.glob("*B/*"), ['dirB/fileB'])
else:
_check(p.glob("*B/*"), ['dirB/fileB', 'dirB/linkD',
'linkB/fileB', 'linkB/linkD'])
if not self.can_symlink:
_check(p.glob("*/fileB"), ['dirB/fileB'])
else:
_check(p.glob("*/fileB"), ['dirB/fileB', 'linkB/fileB'])
if self.can_symlink:
_check(p.glob("brokenLink"), ['brokenLink'])
if not self.can_symlink:
_check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/"])
else:
_check(p.glob("*/"), ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
@needs_posix
def test_glob_posix(self):
P = self.cls
p = P(self.base)
given = set(p.glob("FILEa"))
expect = set()
self.assertEqual(given, expect)
self.assertEqual(set(p.glob("FILEa*")), set())
@needs_windows
def test_glob_windows(self):
P = self.cls
p = P(self.base)
self.assertEqual(set(p.glob("FILEa")), { P(self.base, "fileA") })
self.assertEqual(set(p.glob("*a\\")), { P(self.base, "dirA/") })
self.assertEqual(set(p.glob("F*a")), { P(self.base, "fileA") })
self.assertEqual(set(map(str, p.glob("FILEa"))), {f"{p}\\fileA"})
self.assertEqual(set(map(str, p.glob("F*a"))), {f"{p}\\fileA"})
def test_glob_empty_pattern(self):
P = self.cls
p = P(self.base)
self.assertEqual(list(p.glob("")), [p])
self.assertEqual(list(p.glob(".")), [p / "."])
self.assertEqual(list(p.glob("./")), [p / "./"])
def test_glob_case_sensitive(self):
P = self.cls
def _check(path, pattern, case_sensitive, expected):
actual = {str(q) for q in path.glob(pattern, case_sensitive=case_sensitive)}
expected = {str(P(self.base, q)) for q in expected}
self.assertEqual(actual, expected)
path = P(self.base)
_check(path, "DIRB/FILE*", True, [])
_check(path, "DIRB/FILE*", False, ["dirB/fileB"])
_check(path, "dirb/file*", True, [])
_check(path, "dirb/file*", False, ["dirB/fileB"])
@needs_symlinks
def test_glob_follow_symlinks_common(self):
def _check(path, glob, expected):
actual = {path for path in path.glob(glob, follow_symlinks=True)
if path.parts.count("linkD") <= 1} # exclude symlink loop.
self.assertEqual(actual, { P(self.base, q) for q in expected })
P = self.cls
p = P(self.base)
_check(p, "fileB", [])
_check(p, "dir*/file*", ["dirB/fileB", "dirC/fileC"])
_check(p, "*A", ["dirA", "fileA", "linkA"])
_check(p, "*B/*", ["dirB/fileB", "dirB/linkD", "linkB/fileB", "linkB/linkD"])
_check(p, "*/fileB", ["dirB/fileB", "linkB/fileB"])
_check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/", "linkB/"])
_check(p, "dir*/*/..", ["dirC/dirD/..", "dirA/linkC/..", "dirB/linkD/.."])
_check(p, "dir*/**/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
"dirC/", "dirC/dirD/", "dirE/"])
_check(p, "dir*/**/..", ["dirA/..", "dirA/linkC/..", "dirB/..",
"dirB/linkD/..", "dirA/linkC/linkD/..",
"dirC/..", "dirC/dirD/..", "dirE/.."])
_check(p, "dir*/*/**/", ["dirA/linkC/", "dirA/linkC/linkD/", "dirB/linkD/", "dirC/dirD/"])
_check(p, "dir*/*/**/..", ["dirA/linkC/..", "dirA/linkC/linkD/..",
"dirB/linkD/..", "dirC/dirD/.."])
_check(p, "dir*/**/fileC", ["dirC/fileC"])
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
_check(p, "*/dirD/**/", ["dirC/dirD/"])
@needs_symlinks
def test_glob_no_follow_symlinks_common(self):
def _check(path, glob, expected):
actual = {path for path in path.glob(glob, follow_symlinks=False)}
self.assertEqual(actual, { P(self.base, q) for q in expected })
P = self.cls
p = P(self.base)
_check(p, "fileB", [])
_check(p, "dir*/file*", ["dirB/fileB", "dirC/fileC"])
_check(p, "*A", ["dirA", "fileA", "linkA"])
_check(p, "*B/*", ["dirB/fileB", "dirB/linkD"])
_check(p, "*/fileB", ["dirB/fileB"])
_check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirE/"])
_check(p, "dir*/*/..", ["dirC/dirD/.."])
_check(p, "dir*/**/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"])
_check(p, "dir*/**/..", ["dirA/..", "dirB/..", "dirC/..", "dirC/dirD/..", "dirE/.."])
_check(p, "dir*/*/**/", ["dirC/dirD/"])
_check(p, "dir*/*/**/..", ["dirC/dirD/.."])
_check(p, "dir*/**/fileC", ["dirC/fileC"])
_check(p, "dir*/*/../dirD/**/", ["dirC/dirD/../dirD/"])
_check(p, "*/dirD/**/", ["dirC/dirD/"])
def test_rglob_common(self):
def _check(glob, expected):
self.assertEqual(set(glob), {P(self.base, q) for q in expected})
P = self.cls
p = P(self.base)
it = p.rglob("fileA")
self.assertIsInstance(it, collections.abc.Iterator)
_check(it, ["fileA"])
_check(p.rglob("fileB"), ["dirB/fileB"])
_check(p.rglob("**/fileB"), ["dirB/fileB"])
_check(p.rglob("*/fileA"), [])
if not self.can_symlink:
_check(p.rglob("*/fileB"), ["dirB/fileB"])
else:
_check(p.rglob("*/fileB"), ["dirB/fileB", "dirB/linkD/fileB",
"linkB/fileB", "dirA/linkC/fileB"])
_check(p.rglob("file*"), ["fileA", "dirB/fileB",
"dirC/fileC", "dirC/dirD/fileD"])
if not self.can_symlink:
_check(p.rglob("*/"), [
"dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/",
])
else:
_check(p.rglob("*/"), [
"dirA/", "dirA/linkC/", "dirB/", "dirB/linkD/", "dirC/",
"dirC/dirD/", "dirE/", "linkB/",
])
_check(p.rglob(""), ["", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"])
p = P(self.base, "dirC")
_check(p.rglob("*"), ["dirC/fileC", "dirC/novel.txt",
"dirC/dirD", "dirC/dirD/fileD"])
_check(p.rglob("file*"), ["dirC/fileC", "dirC/dirD/fileD"])
_check(p.rglob("**/file*"), ["dirC/fileC", "dirC/dirD/fileD"])
_check(p.rglob("dir*/**/"), ["dirC/dirD/"])
_check(p.rglob("*/*"), ["dirC/dirD/fileD"])
_check(p.rglob("*/"), ["dirC/dirD/"])
_check(p.rglob(""), ["dirC/", "dirC/dirD/"])
_check(p.rglob("**/"), ["dirC/", "dirC/dirD/"])
# gh-91616, a re module regression
_check(p.rglob("*.txt"), ["dirC/novel.txt"])
_check(p.rglob("*.*"), ["dirC/novel.txt"])
@needs_posix
def test_rglob_posix(self):
P = self.cls
p = P(self.base, "dirC")
given = set(p.rglob("FILEd"))
expect = set()
self.assertEqual(given, expect)
self.assertEqual(set(p.rglob("FILEd*")), set())
@needs_windows
def test_rglob_windows(self):
P = self.cls
p = P(self.base, "dirC")
self.assertEqual(set(p.rglob("FILEd")), { P(self.base, "dirC/dirD/fileD") })
self.assertEqual(set(p.rglob("*\\")), { P(self.base, "dirC/dirD/") })
self.assertEqual(set(map(str, p.rglob("FILEd"))), {f"{p}\\dirD\\fileD"})
@needs_symlinks
def test_rglob_follow_symlinks_common(self):
def _check(path, glob, expected):
actual = {path for path in path.rglob(glob, follow_symlinks=True)
if path.parts.count("linkD") <= 1} # exclude symlink loop.
self.assertEqual(actual, { P(self.base, q) for q in expected })
P = self.cls
p = P(self.base)
_check(p, "fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB",
"dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB"])
_check(p, "*/fileA", [])
_check(p, "*/fileB", ["dirB/fileB", "dirA/linkC/fileB", "linkB/fileB",
"dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB"])
_check(p, "file*", ["fileA", "dirA/linkC/fileB", "dirB/fileB",
"dirA/linkC/linkD/fileB", "dirB/linkD/fileB", "linkB/linkD/fileB",
"dirC/fileC", "dirC/dirD/fileD", "linkB/fileB"])
_check(p, "*/", ["dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
"dirC/", "dirC/dirD/", "dirE/", "linkB/", "linkB/linkD/"])
_check(p, "", ["", "dirA/", "dirA/linkC/", "dirA/linkC/linkD/", "dirB/", "dirB/linkD/",
"dirC/", "dirE/", "dirC/dirD/", "linkB/", "linkB/linkD/"])
p = P(self.base, "dirC")
_check(p, "*", ["dirC/fileC", "dirC/novel.txt",
"dirC/dirD", "dirC/dirD/fileD"])
_check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"])
_check(p, "*/*", ["dirC/dirD/fileD"])
_check(p, "*/", ["dirC/dirD/"])
_check(p, "", ["dirC/", "dirC/dirD/"])
# gh-91616, a re module regression
_check(p, "*.txt", ["dirC/novel.txt"])
_check(p, "*.*", ["dirC/novel.txt"])
@needs_symlinks
def test_rglob_no_follow_symlinks_common(self):
def _check(path, glob, expected):
actual = {path for path in path.rglob(glob, follow_symlinks=False)}
self.assertEqual(actual, { P(self.base, q) for q in expected })
P = self.cls
p = P(self.base)
_check(p, "fileB", ["dirB/fileB"])
_check(p, "*/fileA", [])
_check(p, "*/fileB", ["dirB/fileB"])
_check(p, "file*", ["fileA", "dirB/fileB", "dirC/fileC", "dirC/dirD/fileD", ])
_check(p, "*/", ["dirA/", "dirB/", "dirC/", "dirC/dirD/", "dirE/"])
_check(p, "", ["", "dirA/", "dirB/", "dirC/", "dirE/", "dirC/dirD/"])
p = P(self.base, "dirC")
_check(p, "*", ["dirC/fileC", "dirC/novel.txt",
"dirC/dirD", "dirC/dirD/fileD"])
_check(p, "file*", ["dirC/fileC", "dirC/dirD/fileD"])
_check(p, "*/*", ["dirC/dirD/fileD"])
_check(p, "*/", ["dirC/dirD/"])
_check(p, "", ["dirC/", "dirC/dirD/"])
# gh-91616, a re module regression
_check(p, "*.txt", ["dirC/novel.txt"])
_check(p, "*.*", ["dirC/novel.txt"])
@needs_symlinks
def test_rglob_symlink_loop(self):
# Don't get fooled by symlink loops (Issue #26012).
P = self.cls
p = P(self.base)
given = set(p.rglob('*'))
expect = {'brokenLink',
'dirA', 'dirA/linkC',
'dirB', 'dirB/fileB', 'dirB/linkD',
'dirC', 'dirC/dirD', 'dirC/dirD/fileD',
'dirC/fileC', 'dirC/novel.txt',
'dirE',
'fileA',
'linkA',
'linkB',
'brokenLinkLoop',
}
self.assertEqual(given, {p / x for x in expect})
def test_glob_dotdot(self):
# ".." is not special in globs.
P = self.cls
p = P(self.base)
self.assertEqual(set(p.glob("..")), { P(self.base, "..") })
self.assertEqual(set(p.glob("../..")), { P(self.base, "..", "..") })
self.assertEqual(set(p.glob("dirA/..")), { P(self.base, "dirA", "..") })
self.assertEqual(set(p.glob("dirA/../file*")), { P(self.base, "dirA/../fileA") })
self.assertEqual(set(p.glob("dirA/../file*/..")), set())
self.assertEqual(set(p.glob("../xyzzy")), set())
self.assertEqual(set(p.glob("xyzzy/..")), set())
self.assertEqual(set(p.glob("/".join([".."] * 50))), { P(self.base, *[".."] * 50)})
@needs_symlinks
def test_glob_permissions(self):
# See bpo-38894
P = self.cls
base = P(self.base) / 'permissions'
base.mkdir()
for i in range(100):
link = base / f"link{i}"
if i % 2:
link.symlink_to(P(self.base, "dirE", "nonexistent"))
else:
link.symlink_to(P(self.base, "dirC"))
self.assertEqual(len(set(base.glob("*"))), 100)
self.assertEqual(len(set(base.glob("*/"))), 50)
self.assertEqual(len(set(base.glob("*/fileC"))), 50)
self.assertEqual(len(set(base.glob("*/file*"))), 50)
@needs_symlinks
def test_glob_long_symlink(self):
# See gh-87695
base = self.cls(self.base) / 'long_symlink'
base.mkdir()
bad_link = base / 'bad_link'
bad_link.symlink_to("bad" * 200)
self.assertEqual(sorted(base.glob('**/*')), [bad_link])
@needs_symlinks
def test_readlink(self):
P = self.cls(self.base)
self.assertEqual((P / 'linkA').readlink(), self.cls('fileA'))
self.assertEqual((P / 'brokenLink').readlink(),
self.cls('non-existing'))
self.assertEqual((P / 'linkB').readlink(), self.cls('dirB'))
self.assertEqual((P / 'linkB' / 'linkD').readlink(), self.cls('../dirB'))
with self.assertRaises(OSError):
(P / 'fileA').readlink()
@unittest.skipIf(hasattr(os, "readlink"), "os.readlink() is present")
def test_readlink_unsupported(self):
P = self.cls(self.base)
p = P / 'fileA'
with self.assertRaises(UnsupportedOperation):
q.readlink(p)
def _check_resolve(self, p, expected, strict=True):
q = p.resolve(strict)
self.assertEqual(q, expected)
# This can be used to check both relative and absolute resolutions.
_check_resolve_relative = _check_resolve_absolute = _check_resolve
@needs_symlinks
def test_resolve_common(self):
P = self.cls
p = P(self.base, 'foo')
with self.assertRaises(OSError) as cm:
p.resolve(strict=True)
self.assertEqual(cm.exception.errno, errno.ENOENT)
# Non-strict
pathmod = self.pathmod
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(self.base, 'foo'))
p = P(self.base, 'foo', 'in', 'spam')
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(self.base, 'foo', 'in', 'spam'))
p = P(self.base, '..', 'foo', 'in', 'spam')
self.assertEqualNormCase(str(p.resolve(strict=False)),
pathmod.join(pathmod.dirname(self.base), 'foo', 'in', 'spam'))
# These are all relative symlinks.
p = P(self.base, 'dirB', 'fileB')
self._check_resolve_relative(p, p)
p = P(self.base, 'linkA')
self._check_resolve_relative(p, P(self.base, 'fileA'))
p = P(self.base, 'dirA', 'linkC', 'fileB')
self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB'))
p = P(self.base, 'dirB', 'linkD', 'fileB')
self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB'))
# Non-strict
p = P(self.base, 'dirA', 'linkC', 'fileB', 'foo', 'in', 'spam')
self._check_resolve_relative(p, P(self.base, 'dirB', 'fileB', 'foo', 'in',
'spam'), False)
p = P(self.base, 'dirA', 'linkC', '..', 'foo', 'in', 'spam')
if self.cls.pathmod is not posixpath:
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
# resolves to 'dirA' without resolving linkY first.
self._check_resolve_relative(p, P(self.base, 'dirA', 'foo', 'in',
'spam'), False)
else:
# In Posix, if linkY points to dirB, 'dirA/linkY/..'
# resolves to 'dirB/..' first before resolving to parent of dirB.
self._check_resolve_relative(p, P(self.base, 'foo', 'in', 'spam'), False)
# Now create absolute symlinks.
d = self.tempdir()
P(self.base, 'dirA', 'linkX').symlink_to(d)
P(self.base, str(d), 'linkY').symlink_to(self.pathmod.join(self.base, 'dirB'))
p = P(self.base, 'dirA', 'linkX', 'linkY', 'fileB')
self._check_resolve_absolute(p, P(self.base, 'dirB', 'fileB'))
# Non-strict
p = P(self.base, 'dirA', 'linkX', 'linkY', 'foo', 'in', 'spam')
self._check_resolve_relative(p, P(self.base, 'dirB', 'foo', 'in', 'spam'),
False)
p = P(self.base, 'dirA', 'linkX', 'linkY', '..', 'foo', 'in', 'spam')
if self.cls.pathmod is not posixpath:
# In Windows, if linkY points to dirB, 'dirA\linkY\..'
# resolves to 'dirA' without resolving linkY first.
self._check_resolve_relative(p, P(d, 'foo', 'in', 'spam'), False)
else:
# In Posix, if linkY points to dirB, 'dirA/linkY/..'
# resolves to 'dirB/..' first before resolving to parent of dirB.
self._check_resolve_relative(p, P(self.base, 'foo', 'in', 'spam'), False)
@needs_symlinks
def test_resolve_dot(self):
# See http://web.archive.org/web/20200623062557/https://bitbucket.org/pitrou/pathlib/issues/9/
pathmod = self.pathmod
p = self.cls(self.base)
p.joinpath('0').symlink_to('.', target_is_directory=True)
p.joinpath('1').symlink_to(pathmod.join('0', '0'), target_is_directory=True)
p.joinpath('2').symlink_to(pathmod.join('1', '1'), target_is_directory=True)
q = p / '2'
self.assertEqual(q.resolve(strict=True), p)
r = q / '3' / '4'
self.assertRaises(FileNotFoundError, r.resolve, strict=True)
# Non-strict
self.assertEqual(r.resolve(strict=False), p / '3' / '4')
def _check_symlink_loop(self, *args):
path = self.cls(*args)
with self.assertRaises(OSError) as cm:
path.resolve(strict=True)
self.assertEqual(cm.exception.errno, errno.ELOOP)
@needs_posix
@needs_symlinks
def test_resolve_loop(self):
# Loops with relative symlinks.
self.cls(self.base, 'linkX').symlink_to('linkX/inside')
self._check_symlink_loop(self.base, 'linkX')
self.cls(self.base, 'linkY').symlink_to('linkY')
self._check_symlink_loop(self.base, 'linkY')
self.cls(self.base, 'linkZ').symlink_to('linkZ/../linkZ')
self._check_symlink_loop(self.base, 'linkZ')
# Non-strict
p = self.cls(self.base, 'linkZ', 'foo')
self.assertEqual(p.resolve(strict=False), p)
# Loops with absolute symlinks.
self.cls(self.base, 'linkU').symlink_to(self.pathmod.join(self.base, 'linkU/inside'))
self._check_symlink_loop(self.base, 'linkU')
self.cls(self.base, 'linkV').symlink_to(self.pathmod.join(self.base, 'linkV'))
self._check_symlink_loop(self.base, 'linkV')
self.cls(self.base, 'linkW').symlink_to(self.pathmod.join(self.base, 'linkW/../linkW'))
self._check_symlink_loop(self.base, 'linkW')
# Non-strict
q = self.cls(self.base, 'linkW', 'foo')
self.assertEqual(q.resolve(strict=False), q)
def test_stat(self):
statA = self.cls(self.base).joinpath('fileA').stat()
statB = self.cls(self.base).joinpath('dirB', 'fileB').stat()
statC = self.cls(self.base).joinpath('dirC').stat()
# st_mode: files are the same, directory differs.
self.assertIsInstance(statA.st_mode, int)
self.assertEqual(statA.st_mode, statB.st_mode)
self.assertNotEqual(statA.st_mode, statC.st_mode)
self.assertNotEqual(statB.st_mode, statC.st_mode)
# st_ino: all different,
self.assertIsInstance(statA.st_ino, int)
self.assertNotEqual(statA.st_ino, statB.st_ino)
self.assertNotEqual(statA.st_ino, statC.st_ino)
self.assertNotEqual(statB.st_ino, statC.st_ino)
# st_dev: all the same.
self.assertIsInstance(statA.st_dev, int)
self.assertEqual(statA.st_dev, statB.st_dev)
self.assertEqual(statA.st_dev, statC.st_dev)
# other attributes not used by pathlib.
@needs_symlinks
def test_stat_no_follow_symlinks(self):
p = self.cls(self.base) / 'linkA'
st = p.stat()
self.assertNotEqual(st, p.stat(follow_symlinks=False))
def test_stat_no_follow_symlinks_nosymlink(self):
p = self.cls(self.base) / 'fileA'
st = p.stat()
self.assertEqual(st, p.stat(follow_symlinks=False))
@needs_symlinks
def test_lstat(self):
p = self.cls(self.base)/ 'linkA'
st = p.stat()
self.assertNotEqual(st, p.lstat())
def test_lstat_nosymlink(self):
p = self.cls(self.base) / 'fileA'
st = p.stat()
self.assertEqual(st, p.lstat())
def test_is_dir(self):
P = self.cls(self.base)
self.assertTrue((P / 'dirA').is_dir())
self.assertFalse((P / 'fileA').is_dir())
self.assertFalse((P / 'non-existing').is_dir())
self.assertFalse((P / 'fileA' / 'bah').is_dir())
if self.can_symlink:
self.assertFalse((P / 'linkA').is_dir())
self.assertTrue((P / 'linkB').is_dir())
self.assertFalse((P/ 'brokenLink').is_dir())
self.assertFalse((P / 'dirA\udfff').is_dir())
self.assertFalse((P / 'dirA\x00').is_dir())
def test_is_dir_no_follow_symlinks(self):
P = self.cls(self.base)
self.assertTrue((P / 'dirA').is_dir(follow_symlinks=False))
self.assertFalse((P / 'fileA').is_dir(follow_symlinks=False))
self.assertFalse((P / 'non-existing').is_dir(follow_symlinks=False))
self.assertFalse((P / 'fileA' / 'bah').is_dir(follow_symlinks=False))
if self.can_symlink:
self.assertFalse((P / 'linkA').is_dir(follow_symlinks=False))
self.assertFalse((P / 'linkB').is_dir(follow_symlinks=False))
self.assertFalse((P/ 'brokenLink').is_dir(follow_symlinks=False))
self.assertFalse((P / 'dirA\udfff').is_dir(follow_symlinks=False))
self.assertFalse((P / 'dirA\x00').is_dir(follow_symlinks=False))
def test_is_file(self):
P = self.cls(self.base)
self.assertTrue((P / 'fileA').is_file())
self.assertFalse((P / 'dirA').is_file())
self.assertFalse((P / 'non-existing').is_file())
self.assertFalse((P / 'fileA' / 'bah').is_file())
if self.can_symlink:
self.assertTrue((P / 'linkA').is_file())
self.assertFalse((P / 'linkB').is_file())
self.assertFalse((P/ 'brokenLink').is_file())
self.assertFalse((P / 'fileA\udfff').is_file())
self.assertFalse((P / 'fileA\x00').is_file())
def test_is_file_no_follow_symlinks(self):
P = self.cls(self.base)
self.assertTrue((P / 'fileA').is_file(follow_symlinks=False))
self.assertFalse((P / 'dirA').is_file(follow_symlinks=False))
self.assertFalse((P / 'non-existing').is_file(follow_symlinks=False))
self.assertFalse((P / 'fileA' / 'bah').is_file(follow_symlinks=False))
if self.can_symlink:
self.assertFalse((P / 'linkA').is_file(follow_symlinks=False))
self.assertFalse((P / 'linkB').is_file(follow_symlinks=False))
self.assertFalse((P/ 'brokenLink').is_file(follow_symlinks=False))
self.assertFalse((P / 'fileA\udfff').is_file(follow_symlinks=False))
self.assertFalse((P / 'fileA\x00').is_file(follow_symlinks=False))
def test_is_mount(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_mount())
self.assertFalse((P / 'dirA').is_mount())
self.assertFalse((P / 'non-existing').is_mount())
self.assertFalse((P / 'fileA' / 'bah').is_mount())
if self.can_symlink:
self.assertFalse((P / 'linkA').is_mount())
def test_is_symlink(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_symlink())
self.assertFalse((P / 'dirA').is_symlink())
self.assertFalse((P / 'non-existing').is_symlink())
self.assertFalse((P / 'fileA' / 'bah').is_symlink())
if self.can_symlink:
self.assertTrue((P / 'linkA').is_symlink())
self.assertTrue((P / 'linkB').is_symlink())
self.assertTrue((P/ 'brokenLink').is_symlink())
self.assertIs((P / 'fileA\udfff').is_file(), False)
self.assertIs((P / 'fileA\x00').is_file(), False)
if self.can_symlink:
self.assertIs((P / 'linkA\udfff').is_file(), False)
self.assertIs((P / 'linkA\x00').is_file(), False)
def test_is_junction_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_junction())
self.assertFalse((P / 'dirA').is_junction())
self.assertFalse((P / 'non-existing').is_junction())
self.assertFalse((P / 'fileA' / 'bah').is_junction())
self.assertFalse((P / 'fileA\udfff').is_junction())
self.assertFalse((P / 'fileA\x00').is_junction())
def test_is_fifo_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_fifo())
self.assertFalse((P / 'dirA').is_fifo())
self.assertFalse((P / 'non-existing').is_fifo())
self.assertFalse((P / 'fileA' / 'bah').is_fifo())
self.assertIs((P / 'fileA\udfff').is_fifo(), False)
self.assertIs((P / 'fileA\x00').is_fifo(), False)
def test_is_socket_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_socket())
self.assertFalse((P / 'dirA').is_socket())
self.assertFalse((P / 'non-existing').is_socket())
self.assertFalse((P / 'fileA' / 'bah').is_socket())
self.assertIs((P / 'fileA\udfff').is_socket(), False)
self.assertIs((P / 'fileA\x00').is_socket(), False)
def test_is_block_device_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_block_device())
self.assertFalse((P / 'dirA').is_block_device())
self.assertFalse((P / 'non-existing').is_block_device())
self.assertFalse((P / 'fileA' / 'bah').is_block_device())
self.assertIs((P / 'fileA\udfff').is_block_device(), False)
self.assertIs((P / 'fileA\x00').is_block_device(), False)
def test_is_char_device_false(self):
P = self.cls(self.base)
self.assertFalse((P / 'fileA').is_char_device())
self.assertFalse((P / 'dirA').is_char_device())
self.assertFalse((P / 'non-existing').is_char_device())
self.assertFalse((P / 'fileA' / 'bah').is_char_device())
self.assertIs((P / 'fileA\udfff').is_char_device(), False)
self.assertIs((P / 'fileA\x00').is_char_device(), False)
def _check_complex_symlinks(self, link0_target):
# Test solving a non-looping chain of symlinks (issue #19887).
pathmod = self.pathmod
P = self.cls(self.base)
P.joinpath('link1').symlink_to(pathmod.join('link0', 'link0'), target_is_directory=True)
P.joinpath('link2').symlink_to(pathmod.join('link1', 'link1'), target_is_directory=True)
P.joinpath('link3').symlink_to(pathmod.join('link2', 'link2'), target_is_directory=True)
P.joinpath('link0').symlink_to(link0_target, target_is_directory=True)
# Resolve absolute paths.
p = (P / 'link0').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = (P / 'link1').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = (P / 'link2').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = (P / 'link3').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
# Resolve relative paths.
try:
self.cls('').absolute()
except UnsupportedOperation:
return
old_path = os.getcwd()
os.chdir(self.base)
try:
p = self.cls('link0').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = self.cls('link1').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = self.cls('link2').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
p = self.cls('link3').resolve()
self.assertEqual(p, P)
self.assertEqualNormCase(str(p), self.base)
finally:
os.chdir(old_path)
@needs_symlinks
def test_complex_symlinks_absolute(self):
self._check_complex_symlinks(self.base)
@needs_symlinks
def test_complex_symlinks_relative(self):
self._check_complex_symlinks('.')
@needs_symlinks
def test_complex_symlinks_relative_dot_dot(self):
self._check_complex_symlinks(self.pathmod.join('dirA', '..'))
def setUpWalk(self):
# Build:
# TESTFN/
# TEST1/ a file kid and two directory kids
# tmp1
# SUB1/ a file kid and a directory kid
# tmp2
# SUB11/ no kids
# SUB2/ a file kid and a dirsymlink kid
# tmp3
# link/ a symlink to TEST2
# broken_link
# broken_link2
# TEST2/
# tmp4 a lone file
self.walk_path = self.cls(self.base, "TEST1")
self.sub1_path = self.walk_path / "SUB1"
self.sub11_path = self.sub1_path / "SUB11"
self.sub2_path = self.walk_path / "SUB2"
tmp1_path = self.walk_path / "tmp1"
tmp2_path = self.sub1_path / "tmp2"
tmp3_path = self.sub2_path / "tmp3"
self.link_path = self.sub2_path / "link"
t2_path = self.cls(self.base, "TEST2")
tmp4_path = self.cls(self.base, "TEST2", "tmp4")
broken_link_path = self.sub2_path / "broken_link"
broken_link2_path = self.sub2_path / "broken_link2"
self.sub11_path.mkdir(parents=True)
self.sub2_path.mkdir(parents=True)
t2_path.mkdir(parents=True)
for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path:
with path.open("w", encoding='utf-8') as f:
f.write(f"I'm {path} and proud of it. Blame test_pathlib.\n")
if self.can_symlink:
self.link_path.symlink_to(t2_path)
broken_link_path.symlink_to('broken')
broken_link2_path.symlink_to(self.cls('tmp3', 'broken'))
self.sub2_tree = (self.sub2_path, [], ["broken_link", "broken_link2", "link", "tmp3"])
else:
self.sub2_tree = (self.sub2_path, [], ["tmp3"])
def test_walk_topdown(self):
self.setUpWalk()
walker = self.walk_path.walk()
entry = next(walker)
entry[1].sort() # Ensure we visit SUB1 before SUB2
self.assertEqual(entry, (self.walk_path, ["SUB1", "SUB2"], ["tmp1"]))
entry = next(walker)
self.assertEqual(entry, (self.sub1_path, ["SUB11"], ["tmp2"]))
entry = next(walker)
self.assertEqual(entry, (self.sub11_path, [], []))
entry = next(walker)
entry[1].sort()
entry[2].sort()
self.assertEqual(entry, self.sub2_tree)
with self.assertRaises(StopIteration):
next(walker)
def test_walk_prune(self):
self.setUpWalk()
# Prune the search.
all = []
for root, dirs, files in self.walk_path.walk():
all.append((root, dirs, files))
if 'SUB1' in dirs:
# Note that this also mutates the dirs we appended to all!
dirs.remove('SUB1')
self.assertEqual(len(all), 2)
self.assertEqual(all[0], (self.walk_path, ["SUB2"], ["tmp1"]))
all[1][-1].sort()
all[1][1].sort()
self.assertEqual(all[1], self.sub2_tree)
def test_walk_bottom_up(self):
self.setUpWalk()
seen_testfn = seen_sub1 = seen_sub11 = seen_sub2 = False
for path, dirnames, filenames in self.walk_path.walk(top_down=False):
if path == self.walk_path:
self.assertFalse(seen_testfn)
self.assertTrue(seen_sub1)
self.assertTrue(seen_sub2)
self.assertEqual(sorted(dirnames), ["SUB1", "SUB2"])
self.assertEqual(filenames, ["tmp1"])
seen_testfn = True
elif path == self.sub1_path:
self.assertFalse(seen_testfn)
self.assertFalse(seen_sub1)
self.assertTrue(seen_sub11)
self.assertEqual(dirnames, ["SUB11"])
self.assertEqual(filenames, ["tmp2"])
seen_sub1 = True
elif path == self.sub11_path:
self.assertFalse(seen_sub1)
self.assertFalse(seen_sub11)
self.assertEqual(dirnames, [])
self.assertEqual(filenames, [])
seen_sub11 = True
elif path == self.sub2_path:
self.assertFalse(seen_testfn)
self.assertFalse(seen_sub2)
self.assertEqual(sorted(dirnames), sorted(self.sub2_tree[1]))
self.assertEqual(sorted(filenames), sorted(self.sub2_tree[2]))
seen_sub2 = True
else:
raise AssertionError(f"Unexpected path: {path}")
self.assertTrue(seen_testfn)
@needs_symlinks
def test_walk_follow_symlinks(self):
self.setUpWalk()
walk_it = self.walk_path.walk(follow_symlinks=True)
for root, dirs, files in walk_it:
if root == self.link_path:
self.assertEqual(dirs, [])
self.assertEqual(files, ["tmp4"])
break
else:
self.fail("Didn't follow symlink with follow_symlinks=True")
@needs_symlinks
def test_walk_symlink_location(self):
self.setUpWalk()
# Tests whether symlinks end up in filenames or dirnames depending
# on the `follow_symlinks` argument.
walk_it = self.walk_path.walk(follow_symlinks=False)
for root, dirs, files in walk_it:
if root == self.sub2_path:
self.assertIn("link", files)
break
else:
self.fail("symlink not found")
walk_it = self.walk_path.walk(follow_symlinks=True)
for root, dirs, files in walk_it:
if root == self.sub2_path:
self.assertIn("link", dirs)
break
else:
self.fail("symlink not found")
class DummyPathWithSymlinks(DummyPath):
__slots__ = ()
# Reduce symlink traversal limit to make tests run faster.
_max_symlinks = 20
def readlink(self):
path = str(self.parent.resolve() / self.name)
if path in self._symlinks:
return self.with_segments(self._symlinks[path])
elif path in self._files or path in self._directories:
raise OSError(errno.EINVAL, "Not a symlink", path)
else:
raise FileNotFoundError(errno.ENOENT, "File not found", path)
def symlink_to(self, target, target_is_directory=False):
self._directories[str(self.parent)].add(self.name)
self._symlinks[str(self)] = str(target)
class DummyPathWithSymlinksTest(DummyPathTest):
cls = DummyPathWithSymlinks
can_symlink = True
if __name__ == "__main__":
unittest.main()