Skip to content

Commit

Permalink
Fixed handling of alternative path separator in some functions
Browse files Browse the repository at this point in the history
- affects os.path.split(), os.path.splitdrive() and glob.glob()
- fixes pytest-dev#632
  • Loading branch information
mrbean-bremen committed Sep 1, 2021
1 parent 7419c61 commit ed6b767
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 81 deletions.
5 changes: 5 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@ The released versions correspond to PyPi releases.

## Version 4.6.0 (as yet unreleased)

### Fixes
* fixed handling of alternative path separator in `os.path.split`,
`os.path.splitdrive` and `glob.glob`
(see [#632](../../issues/632))

## [Version 4.5.1](https://pypi.python.org/pypi/pyfakefs/4.5.1) (2021-08-29)
This is a bugfix release.

Expand Down
48 changes: 18 additions & 30 deletions pyfakefs/fake_filesystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -1587,31 +1587,18 @@ def splitpath(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]:
(str) A duple (pathname, basename) for which pathname does not
end with a slash, and basename does not contain a slash.
"""
full_path = path
path = self.normcase(path)
sep: AnyStr = self.get_path_separator(path)
path = make_string_path(path)
sep = self.get_path_separator(path)
alt_sep = self._alternative_path_separator(path)
seps = sep if alt_sep is None else sep + alt_sep
drive, path = self.splitdrive(path)
path_components: List[AnyStr] = path.split(sep)
if not path_components:
return drive, matching_string(path, '')
basename = path_components.pop()
if not path_components:
return drive, basename
for component in path_components:
if component:
# The path is not the root; it contains a non-separator
# component. Strip all trailing separators.
while not path_components[-1]:
path_components.pop()
if not path_components:
return drive, basename
return drive + sep.join(path_components), basename
# Root path. Collapse all leading separators.
if drive and not basename:
return full_path, basename
if drive and len(drive) == 2:
drive += sep
return drive or sep, basename
i = len(path)
while i and path[i-1] not in seps:
i -= 1
head, tail = path[:i], path[i:] # now tail has no slashes
# remove trailing slashes from head, unless it's all slashes
head = head.rstrip(seps) or head
return drive + head, tail

def splitdrive(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]:
"""Splits the path into the drive part and the rest of the path.
Expand All @@ -1630,17 +1617,17 @@ def splitdrive(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]:
path_str = make_string_path(path)
if self.is_windows_fs:
if len(path_str) >= 2:
path_str = self.normcase(path_str)
norm_str = self.normcase(path_str)
sep = self.get_path_separator(path_str)
# UNC path_str handling
if (path_str[0:2] == sep * 2) and (
path_str[2:3] != sep):
if (norm_str[0:2] == sep * 2) and (
norm_str[2:3] != sep):
# UNC path_str handling - splits off the mount point
# instead of the drive
sep_index = path_str.find(sep, 2)
sep_index = norm_str.find(sep, 2)
if sep_index == -1:
return path_str[:0], path_str
sep_index2 = path_str.find(sep, sep_index + 1)
sep_index2 = norm_str.find(sep, sep_index + 1)
if sep_index2 == sep_index + 1:
return path_str[:0], path_str
if sep_index2 == -1:
Expand Down Expand Up @@ -2430,7 +2417,8 @@ def create_dir(self, directory_path: AnyPath,
dir_path = self.make_string_path(directory_path)
dir_path = self.absnormpath(dir_path)
self._auto_mount_drive_if_needed(dir_path)
if self.exists(dir_path, check_link=True):
if (self.exists(dir_path, check_link=True) and
dir_path not in self.mount_points):
self.raise_os_error(errno.EEXIST, dir_path)
path_components = self._path_components(dir_path)
current_dir = self.root
Expand Down
2 changes: 1 addition & 1 deletion pyfakefs/tests/example.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def get_glob(glob_path):
>>> import sys
>>> if sys.platform.startswith('win'):
... # Windows style path
... file_names == [r'\test\file1.txt', r'\test\file2.txt']
... file_names == [r'/test\file1.txt', r'/test\file2.txt']
... else:
... # UNIX style path
... file_names == ['/test/file1.txt', '/test/file2.txt']
Expand Down
2 changes: 1 addition & 1 deletion pyfakefs/tests/example_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def test_get_globs(self):
matching_paths = sorted(example.get_glob('/test/dir1/dir*'))
if is_windows:
self.assertEqual(matching_paths,
[r'\test\dir1\dir2a', r'\test\dir1\dir2b'])
[r'/test/dir1\dir2a', r'/test/dir1\dir2b'])
else:
self.assertEqual(matching_paths,
['/test/dir1/dir2a', '/test/dir1/dir2b'])
Expand Down
6 changes: 3 additions & 3 deletions pyfakefs/tests/fake_filesystem_glob_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def test_glob_empty(self):
self.assertEqual(glob.glob(''), [])

def test_glob_star(self):
basedir = os.sep + 'xyzzy'
basedir = '/xyzzy'
self.assertEqual([os.path.join(basedir, 'subdir'),
os.path.join(basedir, 'subdir2'),
os.path.join(basedir, 'subfile')],
Expand All @@ -46,7 +46,7 @@ def test_glob_exact(self):
self.assertEqual(['/xyzzy/subfile'], glob.glob('/xyzzy/subfile'))

def test_glob_question(self):
basedir = os.sep + 'xyzzy'
basedir = '/xyzzy'
self.assertEqual([os.path.join(basedir, 'subdir'),
os.path.join(basedir, 'subdir2'),
os.path.join(basedir, 'subfile')],
Expand All @@ -60,7 +60,7 @@ def test_non_existent_path(self):
self.assertEqual([], glob.glob('nonexistent'))

def test_magic_dir(self):
self.assertEqual([os.sep + '[Temp]'], glob.glob('/*emp*'))
self.assertEqual(['/[Temp]'], glob.glob('/*emp*'))

def test_glob1(self):
self.assertEqual(['[Temp]'], glob.glob1('/', '*Tem*'))
Expand Down
122 changes: 88 additions & 34 deletions pyfakefs/tests/fake_filesystem_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ def check_abspath(self, is_windows):
self.filesystem.create_file(abspath)
self.assertEqual(abspath, self.path.abspath(abspath))
self.assertEqual(abspath, self.path.abspath(filename))
self.assertEqual(abspath, self.path.abspath(u'..!%s' % filename))
self.assertEqual(abspath, self.path.abspath('..!%s' % filename))

def test_abspath_windows(self):
self.check_abspath(is_windows=True)
Expand Down Expand Up @@ -1268,9 +1268,9 @@ def test_eliminate_trailing_separators_from_head(self):
self.assertEqual(('|a|b', 'c'), self.filesystem.splitpath('|a|b|c'))

def test_root_separator_is_not_stripped(self):
self.assertEqual(('|', ''), self.filesystem.splitpath('|||'))
self.assertEqual(('|||', ''), self.filesystem.splitpath('|||'))
self.assertEqual(('|', 'a'), self.filesystem.splitpath('|a'))
self.assertEqual(('|', 'a'), self.filesystem.splitpath('|||a'))
self.assertEqual(('|||', 'a'), self.filesystem.splitpath('|||a'))

def test_empty_tail_if_path_ends_in_separator(self):
self.assertEqual(('a|b', ''), self.filesystem.splitpath('a|b|'))
Expand Down Expand Up @@ -1401,6 +1401,7 @@ def test_exists_with_mixed_separators(self):
class DriveLetterSupportTest(TestCase):
def setUp(self):
self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!')
self.filesystem.alternative_path_separator = '^'
self.filesystem.is_windows_fs = True

def test_initial_value(self):
Expand All @@ -1419,11 +1420,11 @@ def test_collapse_unc_path(self):
self.filesystem.normpath('!!foo!bar!!baz!!'))

def test_normalize_path_str(self):
self.filesystem.cwd = u''
self.assertEqual(u'c:!foo!bar',
self.filesystem.absnormpath(u'c:!foo!!bar'))
self.filesystem.cwd = u'c:!foo'
self.assertEqual(u'c:!foo!bar', self.filesystem.absnormpath(u'bar'))
self.filesystem.cwd = ''
self.assertEqual('c:!foo!bar',
self.filesystem.absnormpath('c:!foo!!bar'))
self.filesystem.cwd = 'c:!foo'
self.assertEqual('c:!foo!bar', self.filesystem.absnormpath('bar'))

def test_normalize_path_bytes(self):
self.filesystem.cwd = b''
Expand All @@ -1433,20 +1434,30 @@ def test_normalize_path_bytes(self):
self.assertEqual(b'c:!foo!bar', self.filesystem.absnormpath(b'bar'))

def test_split_path_str(self):
self.assertEqual((u'c:!foo', u'bar'),
self.filesystem.splitpath(u'c:!foo!bar'))
self.assertEqual((u'c:!', u'foo'),
self.filesystem.splitpath(u'c:!foo'))
self.assertEqual((u'!foo', u'bar'),
self.filesystem.splitpath(u'!foo!bar'))
self.assertEqual((u'!', u'foo'),
self.filesystem.splitpath(u'!foo'))
self.assertEqual((u'c:foo', u'bar'),
self.filesystem.splitpath(u'c:foo!bar'))
self.assertEqual((u'c:', u'foo'),
self.filesystem.splitpath(u'c:foo'))
self.assertEqual((u'foo', u'bar'),
self.filesystem.splitpath(u'foo!bar'))
self.assertEqual(('c:!foo', 'bar'),
self.filesystem.splitpath('c:!foo!bar'))
self.assertEqual(('c:!', 'foo'),
self.filesystem.splitpath('c:!foo'))
self.assertEqual(('!foo', 'bar'),
self.filesystem.splitpath('!foo!bar'))
self.assertEqual(('!', 'foo'),
self.filesystem.splitpath('!foo'))
self.assertEqual(('c:foo', 'bar'),
self.filesystem.splitpath('c:foo!bar'))
self.assertEqual(('c:', 'foo'),
self.filesystem.splitpath('c:foo'))
self.assertEqual(('foo', 'bar'),
self.filesystem.splitpath('foo!bar'))

def test_split_with_alt_separator(self):
self.assertEqual(('a^b', 'c'), self.filesystem.splitpath('a^b^c'))
self.assertEqual(('a^b!c', 'd'), self.filesystem.splitpath('a^b!c^d'))
self.assertEqual(('a^b!c', 'd'), self.filesystem.splitpath('a^b!c!d'))
self.assertEqual((b'a^b', b'c'), self.filesystem.splitpath(b'a^b^c'))
self.assertEqual((b'a^b!c', b'd'),
self.filesystem.splitpath(b'a^b!c^d'))
self.assertEqual((b'a^b!c', b'd'),
self.filesystem.splitpath(b'a^b!c!d'))

def test_split_path_bytes(self):
self.assertEqual((b'c:!foo', b'bar'),
Expand Down Expand Up @@ -1477,21 +1488,35 @@ def test_get_path_components(self):
self.assertEqual(['c:'], self.filesystem._path_components('c:'))

def test_split_drive_str(self):
self.assertEqual((u'c:', u'!foo!bar'),
self.filesystem.splitdrive(u'c:!foo!bar'))
self.assertEqual((u'', u'!foo!bar'),
self.filesystem.splitdrive(u'!foo!bar'))
self.assertEqual((u'c:', u'foo!bar'),
self.filesystem.splitdrive(u'c:foo!bar'))
self.assertEqual((u'', u'foo!bar'),
self.filesystem.splitdrive(u'foo!bar'))
self.assertEqual(('c:', '!foo!bar'),
self.filesystem.splitdrive('c:!foo!bar'))
self.assertEqual(('', '!foo!bar'),
self.filesystem.splitdrive('!foo!bar'))
self.assertEqual(('c:', 'foo!bar'),
self.filesystem.splitdrive('c:foo!bar'))
self.assertEqual(('', 'foo!bar'),
self.filesystem.splitdrive('foo!bar'))

def test_split_drive_bytes(self):
self.assertEqual((b'c:', b'!foo!bar'),
self.filesystem.splitdrive(b'c:!foo!bar'))
self.assertEqual((b'', b'!foo!bar'),
self.filesystem.splitdrive(b'!foo!bar'))

def test_split_drive_alt_sep(self):
self.assertEqual(('c:', '^foo^bar'),
self.filesystem.splitdrive('c:^foo^bar'))
self.assertEqual(('', 'foo^bar'),
self.filesystem.splitdrive('foo^bar'))
self.assertEqual(('', 'foo^bar!baz'),
self.filesystem.splitdrive('foo^bar!baz'))
self.assertEqual((b'c:', b'^foo^bar'),
self.filesystem.splitdrive(b'c:^foo^bar'))
self.assertEqual((b'', b'^foo^bar'),
self.filesystem.splitdrive(b'^foo^bar'))
self.assertEqual((b'', b'^foo^bar!baz'),
self.filesystem.splitdrive(b'^foo^bar!baz'))

def test_split_drive_with_unc_path(self):
self.assertEqual(('!!foo!bar', '!baz'),
self.filesystem.splitdrive('!!foo!bar!baz'))
Expand All @@ -1501,6 +1526,15 @@ def test_split_drive_with_unc_path(self):
self.assertEqual(('!!foo!bar', '!!'),
self.filesystem.splitdrive('!!foo!bar!!'))

def test_split_drive_with_unc_path_alt_sep(self):
self.assertEqual(('^^foo^bar', '!baz'),
self.filesystem.splitdrive('^^foo^bar!baz'))
self.assertEqual(('', '^^foo'), self.filesystem.splitdrive('^^foo'))
self.assertEqual(('', '^^foo^^bar'),
self.filesystem.splitdrive('^^foo^^bar'))
self.assertEqual(('^^foo^bar', '^^'),
self.filesystem.splitdrive('^^foo^bar^^'))

def test_split_path_with_drive(self):
self.assertEqual(('d:!foo', 'baz'),
self.filesystem.splitpath('d:!foo!baz'))
Expand All @@ -1513,14 +1547,34 @@ def test_split_path_with_drive(self):
self.assertEqual(('c:!!', ''),
self.filesystem.splitpath('c:!!'))

def test_split_path_with_drive_alt_sep(self):
self.assertEqual(('d:^foo', 'baz'),
self.filesystem.splitpath('d:^foo^baz'))
self.assertEqual(('d:^foo^baz', ''),
self.filesystem.splitpath('d:^foo^baz^'))
self.assertEqual(('c:', ''),
self.filesystem.splitpath('c:'))
self.assertEqual(('c:^', ''),
self.filesystem.splitpath('c:^'))
self.assertEqual(('c:^^', ''),
self.filesystem.splitpath('c:^^'))

def test_split_path_with_unc_path(self):
self.assertEqual(('!!foo!bar', 'baz'),
self.assertEqual(('!!foo!bar!', 'baz'),
self.filesystem.splitpath('!!foo!bar!baz'))
self.assertEqual(('!!foo!bar', ''),
self.filesystem.splitpath('!!foo!bar'))
self.assertEqual(('!!foo!bar!!', ''),
self.filesystem.splitpath('!!foo!bar!!'))

def test_split_path_with_unc_path_alt_sep(self):
self.assertEqual(('^^foo^bar^', 'baz'),
self.filesystem.splitpath('^^foo^bar^baz'))
self.assertEqual(('^^foo^bar', ''),
self.filesystem.splitpath('^^foo^bar'))
self.assertEqual(('^^foo^bar^^', ''),
self.filesystem.splitpath('^^foo^bar^^'))


class DiskSpaceTest(TestCase):
def setUp(self):
Expand Down Expand Up @@ -1563,16 +1617,16 @@ def test_file_system_size_after_binary_file_creation(self):
self.assertEqual((100, 5, 95), self.filesystem.get_disk_usage())

def test_file_system_size_after_ascii_string_file_creation(self):
self.filesystem.create_file('!foo!bar', contents=u'complicated')
self.filesystem.create_file('!foo!bar', contents='complicated')
self.assertEqual((100, 11, 89), self.filesystem.get_disk_usage())

def test_filesystem_size_after_2byte_unicode_file_creation(self):
self.filesystem.create_file('!foo!bar', contents=u'сложно',
self.filesystem.create_file('!foo!bar', contents='сложно',
encoding='utf-8')
self.assertEqual((100, 12, 88), self.filesystem.get_disk_usage())

def test_filesystem_size_after_3byte_unicode_file_creation(self):
self.filesystem.create_file('!foo!bar', contents=u'複雑',
self.filesystem.create_file('!foo!bar', contents='複雑',
encoding='utf-8')
self.assertEqual((100, 6, 94), self.filesystem.get_disk_usage())

Expand Down
23 changes: 11 additions & 12 deletions pyfakefs/tests/fake_filesystem_unittest_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,8 @@ def test_open(self):
self.assertTrue(self.fs.exists('/fake_file.txt'))
with open('/fake_file.txt') as f:
content = f.read()
self.assertEqual(content, 'This test file was created using the '
'open() function.\n')
self.assertEqual('This test file was created using the '
'open() function.\n', content)

def test_io_open(self):
"""Fake io module is bound"""
Expand All @@ -109,8 +109,8 @@ def test_io_open(self):
self.assertTrue(self.fs.exists('/fake_file.txt'))
with open('/fake_file.txt') as f:
content = f.read()
self.assertEqual(content, 'This test file was created using the '
'io.open() function.\n')
self.assertEqual('This test file was created using the '
'io.open() function.\n', content)

def test_os(self):
"""Fake os module is bound"""
Expand All @@ -121,22 +121,21 @@ def test_os(self):
def test_glob(self):
"""Fake glob module is bound"""
is_windows = sys.platform.startswith('win')
self.assertEqual(glob.glob('/test/dir1/dir*'),
[])
self.assertEqual([], glob.glob('/test/dir1/dir*'))
self.fs.create_dir('/test/dir1/dir2a')
matching_paths = glob.glob('/test/dir1/dir*')
if is_windows:
self.assertEqual(matching_paths, [r'\test\dir1\dir2a'])
self.assertEqual([r'/test/dir1\dir2a'], matching_paths)
else:
self.assertEqual(matching_paths, ['/test/dir1/dir2a'])
self.assertEqual(['/test/dir1/dir2a'], matching_paths)
self.fs.create_dir('/test/dir1/dir2b')
matching_paths = sorted(glob.glob('/test/dir1/dir*'))
if is_windows:
self.assertEqual(matching_paths,
[r'\test\dir1\dir2a', r'\test\dir1\dir2b'])
self.assertEqual([r'/test/dir1\dir2a', r'/test/dir1\dir2b'],
matching_paths)
else:
self.assertEqual(matching_paths,
['/test/dir1/dir2a', '/test/dir1/dir2b'])
self.assertEqual(['/test/dir1/dir2a', '/test/dir1/dir2b'],
matching_paths)

def test_shutil(self):
"""Fake shutil module is bound"""
Expand Down

0 comments on commit ed6b767

Please sign in to comment.