123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336 |
- """Tests for scandir.scandir()."""
- from __future__ import unicode_literals
- import os
- import shutil
- import sys
- import time
- import unittest
- import yatest.common
- try:
- import scandir
- has_scandir = True
- except ImportError:
- has_scandir = False
- FILE_ATTRIBUTE_DIRECTORY = 16
- IS_PY3 = sys.version_info >= (3, 0)
- if IS_PY3:
- int_types = int
- else:
- int_types = (int, long)
- str = unicode
- if hasattr(os, 'symlink'):
- try:
- #link_name = os.path.join(os.path.dirname(__file__), '_testlink')
- #os.symlink(__file__, link_name)
- #os.remove(link_name)
- symlinks_supported = True
- except NotImplementedError:
- # Windows versions before Vista don't support symbolic links
- symlinks_supported = False
- else:
- symlinks_supported = False
- def create_file(path, contents='1234'):
- with open(path, 'w') as f:
- f.write(contents)
- def setup_main():
- join = os.path.join
- os.mkdir(TEST_PATH)
- os.mkdir(join(TEST_PATH, 'subdir'))
- create_file(join(TEST_PATH, 'file1.txt'))
- create_file(join(TEST_PATH, 'file2.txt'), contents='12345678')
- os.mkdir(join(TEST_PATH, 'subdir', 'unidir\u018F'))
- create_file(join(TEST_PATH, 'subdir', 'file1.txt'))
- create_file(join(TEST_PATH, 'subdir', 'unicod\u018F.txt'))
- create_file(join(TEST_PATH, 'subdir', 'unidir\u018F', 'file1.txt'))
- os.mkdir(join(TEST_PATH, 'linkdir'))
- def setup_symlinks():
- join = os.path.join
- os.mkdir(join(TEST_PATH, 'linkdir', 'linksubdir'))
- create_file(join(TEST_PATH, 'linkdir', 'file1.txt'))
- os.symlink(os.path.abspath(join(TEST_PATH, 'linkdir', 'file1.txt')),
- join(TEST_PATH, 'linkdir', 'link_to_file'))
- dir_name = os.path.abspath(join(TEST_PATH, 'linkdir', 'linksubdir'))
- dir_link = join(TEST_PATH, 'linkdir', 'link_to_dir')
- if IS_PY3:
- os.symlink(dir_name, dir_link, target_is_directory=True)
- else:
- os.symlink(dir_name, dir_link)
- def teardown():
- try:
- shutil.rmtree(TEST_PATH)
- except OSError:
- # why does the above fail sometimes?
- time.sleep(0.1)
- shutil.rmtree(TEST_PATH)
- class TestMixin(unittest.TestCase):
- def setUp(self):
- global TEST_PATH
- TEST_PATH = yatest.common.test_output_path('../test')
- if not os.path.exists(TEST_PATH):
- setup_main()
- if symlinks_supported and not os.path.exists(
- os.path.join(TEST_PATH, 'linkdir', 'linksubdir')):
- setup_symlinks()
- if not hasattr(unittest.TestCase, 'skipTest'):
- def skipTest(self, reason):
- sys.stdout.write('skipped {0!r} '.format(reason))
- def test_basic(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- entries = sorted(self.scandir_func(TEST_PATH), key=lambda e: e.name)
- self.assertEqual([(e.name, e.is_dir()) for e in entries],
- [('file1.txt', False), ('file2.txt', False),
- ('linkdir', True), ('subdir', True)])
- self.assertEqual([e.path for e in entries],
- [os.path.join(TEST_PATH, e.name) for e in entries])
- def test_dir_entry(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- entries = dict((e.name, e) for e in self.scandir_func(TEST_PATH))
- e = entries['file1.txt']
- self.assertEqual([e.is_dir(), e.is_file(), e.is_symlink()], [False, True, False])
- e = entries['file2.txt']
- self.assertEqual([e.is_dir(), e.is_file(), e.is_symlink()], [False, True, False])
- e = entries['subdir']
- self.assertEqual([e.is_dir(), e.is_file(), e.is_symlink()], [True, False, False])
- self.assertEqual(entries['file1.txt'].stat().st_size, 4)
- self.assertEqual(entries['file2.txt'].stat().st_size, 8)
- def test_stat(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- entries = list(self.scandir_func(TEST_PATH))
- for entry in entries:
- os_stat = os.stat(os.path.join(TEST_PATH, entry.name))
- scandir_stat = entry.stat()
- self.assertEqual(os_stat.st_mode, scandir_stat.st_mode)
- # TODO: be nice to figure out why these aren't identical on Windows and on PyPy
- # * Windows: they seem to be a few microseconds to tens of seconds out
- # * PyPy: for some reason os_stat's times are nanosecond, scandir's are not
- self.assertAlmostEqual(os_stat.st_mtime, scandir_stat.st_mtime, delta=1)
- self.assertAlmostEqual(os_stat.st_ctime, scandir_stat.st_ctime, delta=1)
- if entry.is_file():
- self.assertEqual(os_stat.st_size, scandir_stat.st_size)
- def test_returns_iter(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- it = self.scandir_func(TEST_PATH)
- entry = next(it)
- assert hasattr(entry, 'name')
- def check_file_attributes(self, result):
- self.assertTrue(hasattr(result, 'st_file_attributes'))
- self.assertTrue(isinstance(result.st_file_attributes, int_types))
- self.assertTrue(0 <= result.st_file_attributes <= 0xFFFFFFFF)
- def test_file_attributes(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- if sys.platform != 'win32' or not self.has_file_attributes:
- # st_file_attributes is Win32 specific
- return self.skipTest('st_file_attributes not supported')
- entries = dict((e.name, e) for e in self.scandir_func(TEST_PATH))
- # test st_file_attributes on a file (FILE_ATTRIBUTE_DIRECTORY not set)
- result = entries['file1.txt'].stat()
- self.check_file_attributes(result)
- self.assertEqual(result.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY, 0)
- # test st_file_attributes on a directory (FILE_ATTRIBUTE_DIRECTORY set)
- result = entries['subdir'].stat()
- self.check_file_attributes(result)
- self.assertEqual(result.st_file_attributes & FILE_ATTRIBUTE_DIRECTORY,
- FILE_ATTRIBUTE_DIRECTORY)
- def test_path(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- entries = sorted(self.scandir_func(TEST_PATH), key=lambda e: e.name)
- self.assertEqual([os.path.basename(e.name) for e in entries],
- ['file1.txt', 'file2.txt', 'linkdir', 'subdir'])
- self.assertEqual([os.path.normpath(os.path.join(TEST_PATH, e.name)) for e in entries],
- [os.path.normpath(e.path) for e in entries])
- def test_symlink(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- if not symlinks_supported:
- return self.skipTest('symbolic links not supported')
- entries = sorted(self.scandir_func(os.path.join(TEST_PATH, 'linkdir')),
- key=lambda e: e.name)
- self.assertEqual([(e.name, e.is_symlink()) for e in entries],
- [('file1.txt', False),
- ('link_to_dir', True),
- ('link_to_file', True),
- ('linksubdir', False)])
- self.assertEqual([(e.name, e.is_file(), e.is_file(follow_symlinks=False))
- for e in entries],
- [('file1.txt', True, True),
- ('link_to_dir', False, False),
- ('link_to_file', True, False),
- ('linksubdir', False, False)])
- self.assertEqual([(e.name, e.is_dir(), e.is_dir(follow_symlinks=False))
- for e in entries],
- [('file1.txt', False, False),
- ('link_to_dir', True, False),
- ('link_to_file', False, False),
- ('linksubdir', True, True)])
- def test_bytes(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- # Check that unicode filenames are returned correctly as bytes in output
- path = os.path.join(TEST_PATH, 'subdir').encode(sys.getfilesystemencoding(), 'replace')
- self.assertTrue(isinstance(path, bytes))
- # Python 3.6 on Windows fixes the bytes filename thing by using UTF-8
- if IS_PY3 and sys.platform == 'win32':
- if not (sys.version_info >= (3, 6) and self.scandir_func == os.scandir):
- self.assertRaises(TypeError, self.scandir_func, path)
- return
- entries = [e for e in self.scandir_func(path) if e.name.startswith(b'unicod')]
- self.assertEqual(len(entries), 1)
- entry = entries[0]
- self.assertTrue(isinstance(entry.name, bytes))
- self.assertTrue(isinstance(entry.path, bytes))
- # b'unicod?.txt' on Windows, b'unicod\xc6\x8f.txt' (UTF-8) or similar on POSIX
- entry_name = 'unicod\u018f.txt'.encode(sys.getfilesystemencoding(), 'replace')
- self.assertEqual(entry.name, entry_name)
- self.assertEqual(entry.path, os.path.join(path, entry_name))
- def test_unicode(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- # Check that unicode filenames are returned correctly as (unicode) str in output
- path = os.path.join(TEST_PATH, 'subdir')
- if not IS_PY3:
- path = path.decode(sys.getfilesystemencoding(), 'replace')
- self.assertTrue(isinstance(path, str))
- entries = [e for e in self.scandir_func(path) if e.name.startswith('unicod')]
- self.assertEqual(len(entries), 1)
- entry = entries[0]
- self.assertTrue(isinstance(entry.name, str))
- self.assertTrue(isinstance(entry.path, str))
- entry_name = 'unicod\u018f.txt'
- self.assertEqual(entry.name, entry_name)
- self.assertEqual(entry.path, os.path.join(path, 'unicod\u018f.txt'))
- # Check that it handles unicode input properly
- path = os.path.join(TEST_PATH, 'subdir', 'unidir\u018f')
- self.assertTrue(isinstance(path, str))
- entries = list(self.scandir_func(path))
- self.assertEqual(len(entries), 1)
- entry = entries[0]
- self.assertTrue(isinstance(entry.name, str))
- self.assertTrue(isinstance(entry.path, str))
- self.assertEqual(entry.name, 'file1.txt')
- self.assertEqual(entry.path, os.path.join(path, 'file1.txt'))
- def test_walk_unicode_handling(self):
- if not hasattr(self, 'scandir_func'):
- self.skipTest('skip mixin')
- encoding = sys.getfilesystemencoding()
- dirname_unicode = u'test_unicode_dir'
- dirname_bytes = dirname_unicode.encode(encoding)
- dirpath = os.path.join(TEST_PATH.encode(encoding), dirname_bytes)
- try:
- os.makedirs(dirpath)
- if sys.platform != 'win32':
- # test bytes
- self.assertTrue(isinstance(dirpath, bytes))
- for (path, dirs, files) in scandir.walk(dirpath):
- self.assertTrue(isinstance(path, bytes))
- # test unicode
- text_type = str if IS_PY3 else unicode
- dirpath_unicode = text_type(dirpath, encoding)
- self.assertTrue(isinstance(dirpath_unicode, text_type))
- for (path, dirs, files) in scandir.walk(dirpath_unicode):
- self.assertTrue(isinstance(path, text_type))
- finally:
- shutil.rmtree(dirpath)
- if has_scandir:
- class TestScandirGeneric(TestMixin, unittest.TestCase):
- def setUp(self):
- self.scandir_func = scandir.scandir_generic
- self.has_file_attributes = False
- TestMixin.setUp(self)
- if getattr(scandir, 'scandir_python', None):
- class TestScandirPython(TestMixin, unittest.TestCase):
- def setUp(self):
- self.scandir_func = scandir.scandir_python
- self.has_file_attributes = True
- TestMixin.setUp(self)
- if getattr(scandir, 'scandir_c', None):
- class TestScandirC(TestMixin, unittest.TestCase):
- def setUp(self):
- self.scandir_func = scandir.scandir_c
- self.has_file_attributes = True
- TestMixin.setUp(self)
- class TestScandirDirEntry(unittest.TestCase):
- def setUp(self):
- if not os.path.exists(TEST_PATH):
- setup_main()
- def test_iter_returns_dir_entry(self):
- it = scandir.scandir(TEST_PATH)
- entry = next(it)
- assert isinstance(entry, scandir.DirEntry)
- if hasattr(os, 'scandir'):
- class TestScandirOS(TestMixin, unittest.TestCase):
- def setUp(self):
- self.scandir_func = os.scandir
- self.has_file_attributes = True
- TestMixin.setUp(self)
|