Skip to content

Commit

Permalink
Fixed scandir iterator for Python 2
Browse files Browse the repository at this point in the history
- adapted example test to work with scandir module
- fixed scandir test to work with scandir module
- see #332
  • Loading branch information
mrbean-bremen committed Dec 14, 2017
1 parent 6c685a4 commit a848aa8
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 41 deletions.
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ This version of pyfakefs does not support Python 2.6. Python 2.6 users
must use pyfakefs 3.3 or earlier.

#### New Features
* Added support to fake out backported `scandir` module ([#332](../../issues/332))

#### Infrastructure
* Removed Python 2.6 support [#293](../../issues/293)

#### Fixes
* `os.path.split()` and `os.path.dirname()` gave incorrect results under Windows ([#335](../../issues/335))

## [Version 3.3](https://pypi.python.org/pypi/pyfakefs/3.3)

Expand Down
14 changes: 12 additions & 2 deletions example.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
import glob
import shutil

try:
import scandir
has_scandir = True
except ImportError:
has_scandir = False


def create_file(path):
"""Create the specified file and add some content to it. Use the `open()`
Expand Down Expand Up @@ -131,9 +137,13 @@ def rm_tree(path):
"""Delete the specified file hierarchy."""
shutil.rmtree(path)

def scandir(path):

def scan_dir(path):
"""Return a list of directory entries for the given path."""
return list(os.scandir(path))
if has_scandir:
return list(scandir.scandir(path))
else:
return list(os.scandir(path))

def file_contents(path):
"""Return the contents of the given path as byte array."""
Expand Down
24 changes: 21 additions & 3 deletions example_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import unittest

from pyfakefs import fake_filesystem_unittest
from pyfakefs.fake_filesystem_unittest import has_scandir

# The module under test is pyfakefs/example
import example
Expand Down Expand Up @@ -126,8 +127,9 @@ def test_rm_tree(self):
example.rm_tree('/test/dir1')
self.assertFalse(os.path.exists('/test/dir1'))

@unittest.skipIf(sys.version_info < (3, 5), 'os.scandir was introduced in Python 3.5')
def test_scandir(self):
@unittest.skipIf(sys.version_info < (3, 5),
'os.scandir was introduced in Python 3.5')
def test_os_scandir(self):
"""Test example.scandir() which uses `os.scandir()`.
The os module has been replaced with the fake os module so the
Expand All @@ -138,13 +140,29 @@ def test_scandir(self):
self.fs.CreateFile('/linktest/linked')
self.fs.CreateLink('/test/linked_file', '/linktest/linked')

entries = sorted(example.scandir('/test'), key=lambda e: e.name)
entries = sorted(example.scan_dir('/test'), key=lambda e: e.name)
self.assertEqual(3, len(entries))
self.assertEqual('linked_file', entries[1].name)
self.assertTrue(entries[0].is_dir())
self.assertTrue(entries[1].is_symlink())
self.assertTrue(entries[2].is_file())

@unittest.skipIf(not has_scandir, 'Testing only if scandir module is installed')
def test_scandir_scandir(self):
"""Test example.scandir() which uses `scandir.scandir()`.
The scandir module has been replaced with the fake_scandir module so the
fake filesystem path entries are returned instead of `scandir.DirEntry` objects.
"""
self.fs.CreateFile('/test/text.txt')
self.fs.CreateDirectory('/test/dir')

entries = sorted(example.scan_dir('/test'), key=lambda e: e.name)
self.assertEqual(2, len(entries))
self.assertEqual('text.txt', entries[1].name)
self.assertTrue(entries[0].is_dir())
self.assertTrue(entries[1].is_file())

def test_real_file_access(self):
"""Test `example.file_contents()` for a real file after adding it using `add_real_file()`."""
filename = __file__
Expand Down
87 changes: 52 additions & 35 deletions fake_filesystem_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -4823,13 +4823,16 @@ def testOpen(self):
class FakeScandirTest(FakeOsModuleTestBase):
def setUp(self):
super(FakeScandirTest, self).setUp()
self.skipIfSymlinkNotSupported()

self.supports_symlinks = (not self.is_windows or
not self.useRealFs() and not self.is_python2)

if has_scandir:
if self.useRealFs():
from scandir import scandir
else:
from fake_scandir import scandir
import pyfakefs.fake_scandir
scandir = lambda p: pyfakefs.fake_scandir.scandir(self.filesystem, p)
else:
scandir = self.os.scandir

Expand All @@ -4845,64 +4848,76 @@ def setUp(self):
self.file_path = self.os.path.join(directory, 'file')
self.createFile(self.file_path, contents=b'b' * 50)
self.file_link_path = self.os.path.join(directory, 'link_file')
self.createLink(self.file_link_path, self.linked_file_path)
self.dir_link_path = self.os.path.join(directory, 'link_dir')
self.createLink(self.dir_link_path, self.linked_dir_path)
if self.supports_symlinks:
self.createLink(self.file_link_path, self.linked_file_path)
self.dir_link_path = self.os.path.join(directory, 'link_dir')
self.createLink(self.dir_link_path, self.linked_dir_path)
self.dir_entries = [entry for entry in scandir(directory)]
self.dir_entries = sorted(self.dir_entries,
key=lambda entry: entry.name)

def testPaths(self):
self.assertEqual(4, len(self.dir_entries))
sorted_names = ['dir', 'file', 'link_dir', 'link_file']
sorted_names = ['dir', 'file']
if self.supports_symlinks:
sorted_names.extend(['link_dir', 'link_file'])

self.assertEqual(len(sorted_names), len(self.dir_entries))
self.assertEqual(sorted_names,
[entry.name for entry in self.dir_entries])
self.assertEqual(self.dir_path, self.dir_entries[0].path)

def testIsfile(self):
self.assertFalse(self.dir_entries[0].is_file())
self.assertTrue(self.dir_entries[1].is_file())
self.assertFalse(self.dir_entries[2].is_file())
self.assertFalse(self.dir_entries[2].is_file(follow_symlinks=False))
self.assertTrue(self.dir_entries[3].is_file())
self.assertFalse(self.dir_entries[3].is_file(follow_symlinks=False))
if self.supports_symlinks:
self.assertFalse(self.dir_entries[2].is_file())
self.assertFalse(self.dir_entries[2].is_file(follow_symlinks=False))
self.assertTrue(self.dir_entries[3].is_file())
self.assertFalse(self.dir_entries[3].is_file(follow_symlinks=False))

def testIsdir(self):
self.assertTrue(self.dir_entries[0].is_dir())
self.assertFalse(self.dir_entries[1].is_dir())
self.assertTrue(self.dir_entries[2].is_dir())
self.assertFalse(self.dir_entries[2].is_dir(follow_symlinks=False))
self.assertFalse(self.dir_entries[3].is_dir())
self.assertFalse(self.dir_entries[3].is_dir(follow_symlinks=False))
if self.supports_symlinks:
self.assertTrue(self.dir_entries[2].is_dir())
self.assertFalse(self.dir_entries[2].is_dir(follow_symlinks=False))
self.assertFalse(self.dir_entries[3].is_dir())
self.assertFalse(self.dir_entries[3].is_dir(follow_symlinks=False))

def testIsLink(self):
self.assertFalse(self.dir_entries[0].is_symlink())
self.assertFalse(self.dir_entries[1].is_symlink())
self.assertTrue(self.dir_entries[2].is_symlink())
self.assertTrue(self.dir_entries[3].is_symlink())
if self.supports_symlinks:
self.assertFalse(self.dir_entries[0].is_symlink())
self.assertFalse(self.dir_entries[1].is_symlink())
self.assertTrue(self.dir_entries[2].is_symlink())
self.assertTrue(self.dir_entries[3].is_symlink())

def testInode(self):
if has_scandir and self.is_windows and self.useRealFs():
self.skipTest('inode seems not to work in scandir module under Windows')
self.assertEqual(self.os.stat(self.dir_path).st_ino,
self.dir_entries[0].inode())
self.assertEqual(self.os.stat(self.file_path).st_ino,
self.dir_entries[1].inode())
self.assertEqual(self.os.lstat(self.dir_link_path).st_ino,
self.dir_entries[2].inode())
self.assertEqual(self.os.lstat(self.file_link_path).st_ino,
self.dir_entries[3].inode())
if self.supports_symlinks:
self.assertEqual(self.os.lstat(self.dir_link_path).st_ino,
self.dir_entries[2].inode())
self.assertEqual(self.os.lstat(self.file_link_path).st_ino,
self.dir_entries[3].inode())

def checkStat(self, expected_size):
self.assertEqual(50, self.dir_entries[1].stat().st_size)
self.assertEqual(10, self.dir_entries[3].stat().st_size)
self.assertEqual(expected_size,
self.dir_entries[3].stat(
follow_symlinks=False).st_size)
self.assertEqual(
self.os.stat(self.dir_path).st_ctime,
self.dir_entries[0].stat().st_ctime)
self.assertEqual(
self.os.stat(self.linked_dir_path).st_mtime,
self.dir_entries[2].stat().st_mtime)

if self.supports_symlinks:
self.assertEqual(10, self.dir_entries[3].stat().st_size)
self.assertEqual(expected_size,
self.dir_entries[3].stat(
follow_symlinks=False).st_size)
self.assertEqual(
self.os.stat(self.linked_dir_path).st_mtime,
self.dir_entries[2].stat().st_mtime)

@unittest.skipIf(TestCase.is_windows, 'POSIX specific behavior')
def testStatPosix(self):
Expand All @@ -4915,13 +4930,15 @@ def testStatWindows(self):
def testIndexAccessToStatTimesReturnsInt(self):
self.assertEqual(self.os.stat(self.dir_path)[stat.ST_CTIME],
int(self.dir_entries[0].stat().st_ctime))
self.assertEqual(self.os.stat(self.linked_dir_path)[stat.ST_MTIME],
int(self.dir_entries[2].stat().st_mtime))
if self.supports_symlinks:
self.assertEqual(self.os.stat(self.linked_dir_path)[stat.ST_MTIME],
int(self.dir_entries[2].stat().st_mtime))

def testStatInoDev(self):
file_stat = self.os.stat(self.linked_file_path)
self.assertEqual(file_stat.st_ino, self.dir_entries[3].stat().st_ino)
self.assertEqual(file_stat.st_dev, self.dir_entries[3].stat().st_dev)
if self.supports_symlinks:
file_stat = self.os.stat(self.linked_file_path)
self.assertEqual(file_stat.st_ino, self.dir_entries[3].stat().st_ino)
self.assertEqual(file_stat.st_dev, self.dir_entries[3].stat().st_dev)


class RealScandirTest(FakeScandirTest):
Expand Down
8 changes: 7 additions & 1 deletion pyfakefs/fake_scandir.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,14 +97,20 @@ def __iter__(self):
return self

def __next__(self):
entry = self.contents_iter.__next__()
try:
entry = self.contents_iter.next()
except AttributeError:
entry = self.contents_iter.__next__()
dir_entry = DirEntry(self.filesystem)
dir_entry.name = entry
dir_entry.path = self.filesystem.JoinPaths(self.path, dir_entry.name)
dir_entry._isdir = self.filesystem.IsDir(dir_entry.path)
dir_entry._islink = self.filesystem.IsLink(dir_entry.path)
return dir_entry

# satisfy both Python 2 and 3
next = __next__

if sys.version_info >= (3, 6):
def __enter__(self):
return self
Expand Down

0 comments on commit a848aa8

Please sign in to comment.