Skip to content

Commit efa0098

Browse files
authored
Fix relative path resolution in pathlib (#82)
* Fix relative path resolution in pathlib * Add test
1 parent 87bec84 commit efa0098

File tree

4 files changed

+58
-12
lines changed

4 files changed

+58
-12
lines changed

docs/src/markdown/changelog.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## 5.0.2
44

55
- **FIX**: Fix case where a `GLOBSTAR` pattern, followed by a slash, was not disabling `MATCHBASE`.
6+
- **FIX**: Fix `pathlib` relative path resolution in glob implementations.
67

78
## 5.0.1
89

tests/test_pathlib.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
11
"""Test `pathlib`."""
2+
import contextlib
23
import pytest
34
import unittest
45
import os
56
from wcmatch import pathlib, glob
67
import pathlib as pypathlib
78
import pickle
9+
import warnings
10+
11+
12+
@contextlib.contextmanager
13+
def change_cwd(path, quiet=False):
14+
"""
15+
Return a context manager that changes the current working directory.
16+
17+
Arguments:
18+
path: the directory to use as the temporary current working directory.
19+
quiet: if False (the default), the context manager raises an exception
20+
on error. Otherwise, it issues only a warning and keeps the current
21+
working directory the same.
22+
23+
"""
24+
25+
saved_dir = os.getcwd()
26+
try:
27+
os.chdir(path)
28+
except OSError:
29+
if not quiet:
30+
raise
31+
warnings.warn('tests may fail, unable to change CWD to: ' + path,
32+
RuntimeWarning, stacklevel=3)
33+
try:
34+
yield os.getcwd()
35+
finally:
36+
os.chdir(saved_dir)
837

938

1039
class TestGlob(unittest.TestCase):
@@ -16,6 +45,16 @@ class TestGlob(unittest.TestCase):
1645
1746
"""
1847

48+
def test_relative(self):
49+
"""Test relative path."""
50+
51+
abspath = os.path.abspath('.')
52+
p = pathlib.Path(abspath)
53+
with change_cwd(os.path.dirname(abspath)):
54+
results = list(p.glob('docs/**/*.md', flags=pathlib.GLOBSTAR))
55+
self.assertTrue(len(results))
56+
self.assertTrue(all([file.suffix == '.md' for file in results]))
57+
1958
def test_glob(self):
2059
"""Test globbing function."""
2160

wcmatch/__meta__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,5 +186,5 @@ def parse_version(ver, pre=False):
186186
return Version(major, minor, micro, release, pre, post, dev)
187187

188188

189-
__version_info__ = Version(5, 0, 1, "final")
189+
__version_info__ = Version(5, 0, 2, "final")
190190
__version__ = __version_info__._get_canonical()

wcmatch/glob.py

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def __init__(self, pattern, flags=0, curdir=None):
112112

113113
self.is_bytes = isinstance(pattern[0], bytes)
114114
self.current = b'.' if self.is_bytes else '.'
115-
self.curdir = curdir
115+
self.curdir = curdir if curdir is not None else self.current
116116
self.mark = bool(flags & MARK)
117117
if self.mark:
118118
flags ^= MARK
@@ -228,7 +228,7 @@ def _get_matcher(self, target):
228228
def _iter(self, curdir, dir_only, deep):
229229
"""Iterate the directory."""
230230

231-
scandir = self.current if not curdir else curdir
231+
scandir = self.curdir if not curdir else curdir
232232

233233
# Python will never return . or .., so fake it.
234234
for special in self.specials:
@@ -381,7 +381,7 @@ def _glob(self, curdir, this, rest):
381381
else:
382382
yield path, is_dir
383383

384-
def _get_starting_paths(self, curdir, dir_only):
384+
def _get_starting_paths(self, curdir, dir_only, base):
385385
"""
386386
Get the starting location.
387387
@@ -394,14 +394,19 @@ def _get_starting_paths(self, curdir, dir_only):
394394
results = [(curdir, True)]
395395

396396
if not self._is_parent(curdir) and not self._is_this(curdir):
397-
fullpath = os.path.abspath(curdir)
397+
fullpath = os.path.abspath(os.path.join(base, curdir))
398398
basename = os.path.basename(fullpath)
399399
dirname = os.path.dirname(fullpath)
400400
if basename:
401401
matcher = self._get_matcher(basename)
402-
results = [
403-
(os.path.basename(name), is_dir) for name, is_dir in self._glob_dir(dirname, matcher, dir_only)
404-
]
402+
if base not in ('.', b'.'):
403+
results = [
404+
(name, is_dir) for name, is_dir in self._glob_dir(dirname, matcher, dir_only)
405+
]
406+
else:
407+
results = [
408+
(os.path.basename(name), is_dir) for name, is_dir in self._glob_dir(dirname, matcher, dir_only)
409+
]
405410

406411
return results
407412

@@ -414,9 +419,10 @@ def glob(self):
414419
"""Starts off the glob iterator."""
415420

416421
if self.is_bytes:
417-
curdir = os.fsencode(os.curdir) if self.curdir is None else self.curdir
422+
curdir = self.curdir
418423
else:
419-
curdir = os.curdir if self.curdir is None else self.curdir
424+
curdir = self.curdir
425+
base = curdir
420426

421427
for pattern in self.pattern:
422428
# If the pattern ends with `/` we return the files ending with `/`.
@@ -431,13 +437,13 @@ def glob(self):
431437
curdir = this[0]
432438

433439
# Abort if we cannot find the drive, or if current directory is empty
434-
if not curdir or (this.is_drive and not os.path.lexists(curdir)):
440+
if not curdir or (this.is_drive and not os.path.lexists(os.path.join(base, curdir))):
435441
continue
436442

437443
# Make sure case matches, but running case insensitive
438444
# on a case sensitive file system may return more than
439445
# one starting location.
440-
results = [(curdir, True)] if this.is_drive else self._get_starting_paths(curdir, dir_only)
446+
results = [(curdir, True)] if this.is_drive else self._get_starting_paths(curdir, dir_only, base)
441447
if not results:
442448
continue
443449

0 commit comments

Comments
 (0)