| """ |
| Tests for pathlib.types._JoinablePath |
| """ |
| |
| import unittest |
| import threading |
| from test.support import threading_helper |
| |
| from .support import is_pypi |
| from .support.lexical_path import LexicalPath |
| |
| if is_pypi: |
| from pathlib_abc import _PathParser, _JoinablePath |
| else: |
| from pathlib.types import _PathParser, _JoinablePath |
| |
| |
| class JoinTestBase: |
| def test_is_joinable(self): |
| p = self.cls() |
| self.assertIsInstance(p, _JoinablePath) |
| |
| def test_parser(self): |
| self.assertIsInstance(self.cls.parser, _PathParser) |
| |
| def test_constructor(self): |
| P = self.cls |
| p = P('a') |
| self.assertIsInstance(p, P) |
| P() |
| P('a', 'b', 'c') |
| P('/a', 'b', 'c') |
| P('a/b/c') |
| P('/a/b/c') |
| |
| def test_with_segments(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.parent.session_id) |
| for parent in p.parents: |
| self.assertEqual(42, parent.session_id) |
| |
| def test_join(self): |
| P = self.cls |
| sep = self.cls.parser.sep |
| p = P(f'a{sep}b') |
| pp = p.joinpath('c') |
| self.assertEqual(pp, P(f'a{sep}b{sep}c')) |
| self.assertIs(type(pp), type(p)) |
| pp = p.joinpath('c', 'd') |
| self.assertEqual(pp, P(f'a{sep}b{sep}c{sep}d')) |
| pp = p.joinpath(f'{sep}c') |
| self.assertEqual(pp, P(f'{sep}c')) |
| |
| def test_div(self): |
| # Basically the same as joinpath(). |
| P = self.cls |
| sep = self.cls.parser.sep |
| p = P(f'a{sep}b') |
| pp = p / 'c' |
| self.assertEqual(pp, P(f'a{sep}b{sep}c')) |
| self.assertIs(type(pp), type(p)) |
| pp = p / f'c{sep}d' |
| self.assertEqual(pp, P(f'a{sep}b{sep}c{sep}d')) |
| pp = p / 'c' / 'd' |
| self.assertEqual(pp, P(f'a{sep}b{sep}c{sep}d')) |
| pp = 'c' / p / 'd' |
| self.assertEqual(pp, P(f'c{sep}a{sep}b{sep}d')) |
| pp = p/ f'{sep}c' |
| self.assertEqual(pp, P(f'{sep}c')) |
| |
| def test_full_match(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')) |
| # 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(self): |
| # `parts` returns a tuple. |
| sep = self.cls.parser.sep |
| P = self.cls |
| p = P(f'a{sep}b') |
| parts = p.parts |
| self.assertEqual(parts, ('a', 'b')) |
| # When the path is absolute, the anchor is a separate part. |
| p = P(f'{sep}a{sep}b') |
| parts = p.parts |
| self.assertEqual(parts, (sep, 'a', 'b')) |
| |
| @threading_helper.requires_working_threading() |
| def test_parts_multithreaded(self): |
| P = self.cls |
| |
| NUM_THREADS = 10 |
| NUM_ITERS = 10 |
| |
| for _ in range(NUM_ITERS): |
| b = threading.Barrier(NUM_THREADS) |
| path = P('a') / 'b' / 'c' / 'd' / 'e' |
| expected = ('a', 'b', 'c', 'd', 'e') |
| |
| def check_parts(): |
| b.wait() |
| self.assertEqual(path.parts, expected) |
| |
| threads = [threading.Thread(target=check_parts) for _ in range(NUM_THREADS)] |
| with threading_helper.start_threads(threads): |
| pass |
| |
| def test_parent(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('/')) |
| |
| def test_parents(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] |
| |
| def test_anchor(self): |
| P = self.cls |
| sep = self.cls.parser.sep |
| self.assertEqual(P('').anchor, '') |
| self.assertEqual(P(f'a{sep}b').anchor, '') |
| self.assertEqual(P(sep).anchor, sep) |
| self.assertEqual(P(f'{sep}a{sep}b').anchor, sep) |
| |
| def test_name(self): |
| P = self.cls |
| self.assertEqual(P('').name, '') |
| 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') |
| |
| def test_suffix(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/trailing.dot.').suffix, '.') |
| self.assertEqual(P('/a/trailing.dot.').suffix, '.') |
| self.assertEqual(P('a/..d.o.t..').suffix, '.') |
| self.assertEqual(P('a/inn.er..dots').suffix, '.dots') |
| self.assertEqual(P('photo').suffix, '') |
| self.assertEqual(P('photo.jpg').suffix, '.jpg') |
| |
| def test_suffixes(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/trailing.dot.').suffixes, ['.dot', '.']) |
| self.assertEqual(P('/a/trailing.dot.').suffixes, ['.dot', '.']) |
| self.assertEqual(P('a/..d.o.t..').suffixes, ['.o', '.t', '.', '.']) |
| self.assertEqual(P('a/inn.er..dots').suffixes, ['.er', '.', '.dots']) |
| self.assertEqual(P('photo').suffixes, []) |
| self.assertEqual(P('photo.jpg').suffixes, ['.jpg']) |
| |
| def test_stem(self): |
| P = self.cls |
| self.assertEqual(P('..').stem, '..') |
| 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/trailing.dot.').stem, 'trailing.dot') |
| self.assertEqual(P('a/..d.o.t..').stem, '..d.o.t.') |
| self.assertEqual(P('a/inn.er..dots').stem, 'inn.er.') |
| self.assertEqual(P('photo').stem, 'photo') |
| self.assertEqual(P('photo.jpg').stem, 'photo') |
| |
| def test_with_name(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')) |
| 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(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.')) |
| self.assertRaises(ValueError, P('foo.gz').with_stem, '') |
| self.assertRaises(ValueError, P('/a/b/foo.gz').with_stem, '') |
| 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(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')) |
| # Single dot |
| self.assertEqual(P('a/b').with_suffix('.'), P('a/b.')) |
| self.assertEqual(P('/a/b').with_suffix('.'), P('/a/b.')) |
| self.assertEqual(P('a/b.py').with_suffix('.'), P('a/b.')) |
| self.assertEqual(P('/a/b.py').with_suffix('.'), P('/a/b.')) |
| # Path doesn't have a "filename" component. |
| self.assertRaises(ValueError, P('').with_suffix, '.gz') |
| self.assertRaises(ValueError, P('/').with_suffix, '.gz') |
| # 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, '/.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/.') |
| self.assertRaises(TypeError, P('a/b').with_suffix, None) |
| |
| def test_relative_to(self): |
| P = self.cls |
| p = P('a/b') |
| self.assertEqual(p.relative_to(P('')), P('a', 'b')) |
| self.assertEqual(p.relative_to(P('a')), P('b')) |
| self.assertEqual(p.relative_to(P('a/b')), P('')) |
| self.assertEqual(p.relative_to(P(''), walk_up=True), P('a', 'b')) |
| self.assertEqual(p.relative_to(P('a'), walk_up=True), P('b')) |
| self.assertEqual(p.relative_to(P('a/b'), walk_up=True), P('')) |
| self.assertEqual(p.relative_to(P('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(P('c'), walk_up=True), P('..', 'a', 'b')) |
| 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) |
| class Q(self.cls): |
| __eq__ = object.__eq__ |
| __hash__ = object.__hash__ |
| q = Q('a/b') |
| self.assertTrue(q.relative_to(q)) |
| self.assertRaises(ValueError, q.relative_to, Q('')) |
| self.assertRaises(ValueError, q.relative_to, Q('a')) |
| self.assertRaises(ValueError, q.relative_to, Q('a'), walk_up=True) |
| self.assertRaises(ValueError, q.relative_to, Q('a/b')) |
| self.assertRaises(ValueError, q.relative_to, Q('c')) |
| |
| def test_is_relative_to(self): |
| P = self.cls |
| p = P('a/b') |
| self.assertTrue(p.is_relative_to(P(''))) |
| self.assertTrue(p.is_relative_to(P('a'))) |
| self.assertTrue(p.is_relative_to(P('a/b'))) |
| 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'))) |
| class Q(self.cls): |
| __eq__ = object.__eq__ |
| __hash__ = object.__hash__ |
| q = Q('a/b') |
| self.assertTrue(q.is_relative_to(q)) |
| self.assertFalse(q.is_relative_to(Q(''))) |
| self.assertFalse(q.is_relative_to(Q('a'))) |
| self.assertFalse(q.is_relative_to(Q('a/b'))) |
| self.assertFalse(q.is_relative_to(Q('c'))) |
| |
| |
| class LexicalPathJoinTest(JoinTestBase, unittest.TestCase): |
| cls = LexicalPath |
| |
| |
| if not is_pypi: |
| from pathlib import PurePath, Path |
| |
| class PurePathJoinTest(JoinTestBase, unittest.TestCase): |
| cls = PurePath |
| |
| class PathJoinTest(JoinTestBase, unittest.TestCase): |
| cls = Path |
| |
| |
| if __name__ == "__main__": |
| unittest.main() |