mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-25 05:41:12 +00:00
Backed out 3 changesets (bug 1494069) for blocking 1498215. a=backout
Backed out changeset 9752f179b9c3 (bug 1494069) Backed out changeset fe0fb280dbfc (bug 1494069) Backed out changeset a2956764213e (bug 1494069)
This commit is contained in:
parent
53217795fe
commit
f00a0ea9cc
@ -31,7 +31,6 @@ mozilla.pth:third_party/python/pystache
|
||||
mozilla.pth:third_party/python/pyyaml/lib
|
||||
mozilla.pth:third_party/python/requests
|
||||
mozilla.pth:third_party/python/requests-unixsocket
|
||||
mozilla.pth:third_party/python/scandir
|
||||
mozilla.pth:third_party/python/slugid
|
||||
mozilla.pth:third_party/python/py
|
||||
mozilla.pth:third_party/python/pytest/src
|
||||
|
@ -9,15 +9,10 @@ import os
|
||||
from mozpack import path as mozpath
|
||||
from mozpack.files import FileFinder
|
||||
|
||||
try:
|
||||
from os import scandir, walk
|
||||
except ImportError:
|
||||
from scandir import scandir, walk
|
||||
|
||||
|
||||
class FilterPath(object):
|
||||
"""Helper class to make comparing and matching file paths easier."""
|
||||
def __init__(self, path):
|
||||
def __init__(self, path, exclude=None):
|
||||
self.path = os.path.normpath(path)
|
||||
self._finder = None
|
||||
|
||||
@ -72,63 +67,6 @@ class FilterPath(object):
|
||||
return repr(self.path)
|
||||
|
||||
|
||||
def collapse(paths, base=None, dotfiles=False):
|
||||
"""Given an iterable of paths, collapse them into the smallest possible set
|
||||
of paths that contain the original set (without containing any extra paths).
|
||||
|
||||
For example, if directory 'a' contains two files b.txt and c.txt, calling:
|
||||
|
||||
collapse(['a/b.txt', 'a/c.txt'])
|
||||
|
||||
returns ['a']. But if a third file d.txt also exists, then it will return
|
||||
['a/b.txt', 'a/c.txt'] since ['a'] would also include that extra file.
|
||||
|
||||
:param paths: An iterable of paths (files and directories) to collapse.
|
||||
:returns: The smallest set of paths (files and directories) that contain
|
||||
the original set of paths and only the original set.
|
||||
"""
|
||||
if not paths:
|
||||
if not base:
|
||||
return []
|
||||
|
||||
# Need to test whether directory chain is empty. If it is then bubble
|
||||
# the base back up so that it counts as 'covered'.
|
||||
for _, _, names in walk(base):
|
||||
if names:
|
||||
return []
|
||||
return [base]
|
||||
|
||||
if not base:
|
||||
paths = map(mozpath.abspath, paths)
|
||||
base = mozpath.commonprefix(paths)
|
||||
|
||||
if not os.path.isdir(base):
|
||||
base = os.path.dirname(base)
|
||||
|
||||
covered = set()
|
||||
full = set()
|
||||
for entry in scandir(base):
|
||||
if not dotfiles and entry.name[0] == '.':
|
||||
continue
|
||||
|
||||
path = mozpath.normpath(entry.path)
|
||||
full.add(path)
|
||||
|
||||
if path in paths:
|
||||
# This path was explicitly specified, so just bubble it back up
|
||||
# without recursing down into it (if it was a directory).
|
||||
covered.add(path)
|
||||
elif entry.is_dir():
|
||||
new_paths = [p for p in paths if p.startswith(path)]
|
||||
covered.update(collapse(new_paths, base=path, dotfiles=dotfiles))
|
||||
|
||||
if full == covered:
|
||||
# Every file under this base was covered, so we can collapse them all
|
||||
# up into the base path.
|
||||
return [base]
|
||||
return covered
|
||||
|
||||
|
||||
def filterpaths(paths, linter, **lintargs):
|
||||
"""Filters a list of paths.
|
||||
|
||||
@ -205,7 +143,7 @@ def filterpaths(paths, linter, **lintargs):
|
||||
discard.add(path.join(p))
|
||||
|
||||
# Only pass paths we couldn't exclude here to the underlying linter
|
||||
lintargs['exclude'] = collapse([f.path for f in discard])
|
||||
lintargs['exclude'] = [f.path for f in discard]
|
||||
return [f.path for f in keep]
|
||||
|
||||
|
||||
|
@ -2,11 +2,10 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
from __future__ import absolute_import, print_function
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
from copy import deepcopy
|
||||
from fnmatch import fnmatch
|
||||
|
||||
import mozunit
|
||||
import pytest
|
||||
@ -103,29 +102,5 @@ def test_filterpaths(filterpaths, test):
|
||||
assert_paths(paths, expected)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('paths,expected', [
|
||||
(['subdir1/*'], ['subdir1']),
|
||||
(['subdir2/*'], ['subdir2']),
|
||||
(['subdir1/*.*', 'subdir1/subdir3/*', 'subdir2/*'], ['subdir1', 'subdir2']),
|
||||
([root + '/*', 'subdir1/*.*', 'subdir1/subdir3/*', 'subdir2/*'], [root]),
|
||||
(['subdir1/b.py', 'subdir1/subdir3'], ['subdir1/b.py', 'subdir1/subdir3']),
|
||||
(['subdir1/b.py', 'subdir1/b.js'], ['subdir1/b.py', 'subdir1/b.js']),
|
||||
])
|
||||
def test_collapse(paths, expected):
|
||||
inputs = []
|
||||
for path in paths:
|
||||
base, name = os.path.split(path)
|
||||
if '*' in name:
|
||||
for n in os.listdir(base):
|
||||
if not fnmatch(n, name):
|
||||
continue
|
||||
inputs.append(os.path.join(base, n))
|
||||
else:
|
||||
inputs.append(path)
|
||||
|
||||
print("inputs: {}".format(inputs))
|
||||
assert_paths(pathutils.collapse(inputs), expected)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
mozunit.main()
|
||||
|
1
third_party/python/requirements.in
vendored
1
third_party/python/requirements.in
vendored
@ -8,7 +8,6 @@ psutil==5.4.3
|
||||
pytest==3.6.2
|
||||
python-hglib==2.4
|
||||
requests==2.9.1
|
||||
scandir==1.9.0
|
||||
six==1.10.0
|
||||
virtualenv==15.2.0
|
||||
voluptuous==0.11.5
|
||||
|
12
third_party/python/requirements.txt
vendored
12
third_party/python/requirements.txt
vendored
@ -63,18 +63,6 @@ python-hglib==2.4 \
|
||||
requests==2.9.1 \
|
||||
--hash=sha256:113fbba5531a9e34945b7d36b33a084e8ba5d0664b703c81a7c572d91919a5b8 \
|
||||
--hash=sha256:c577815dd00f1394203fc44eb979724b098f88264a9ef898ee45b8e5e9cf587f
|
||||
scandir==1.9.0 \
|
||||
--hash=sha256:04b8adb105f2ed313a7c2ef0f1cf7aff4871aa7a1883fa4d8c44b5551ab052d6 \
|
||||
--hash=sha256:1444134990356c81d12f30e4b311379acfbbcd03e0bab591de2696a3b126d58e \
|
||||
--hash=sha256:1b5c314e39f596875e5a95dd81af03730b338c277c54a454226978d5ba95dbb6 \
|
||||
--hash=sha256:346619f72eb0ddc4cf355ceffd225fa52506c92a2ff05318cfabd02a144e7c4e \
|
||||
--hash=sha256:44975e209c4827fc18a3486f257154d34ec6eaec0f90fef0cca1caa482db7064 \
|
||||
--hash=sha256:61859fd7e40b8c71e609c202db5b0c1dbec0d5c7f1449dec2245575bdc866792 \
|
||||
--hash=sha256:a5e232a0bf188362fa00123cc0bb842d363a292de7126126df5527b6a369586a \
|
||||
--hash=sha256:c14701409f311e7a9b7ec8e337f0815baf7ac95776cc78b419a1e6d49889a383 \
|
||||
--hash=sha256:c7708f29d843fc2764310732e41f0ce27feadde453261859ec0fca7865dfc41b \
|
||||
--hash=sha256:c9009c527929f6e25604aec39b0a43c3f831d2947d89d6caaab22f057b7055c8 \
|
||||
--hash=sha256:f5c71e29b4e2af7ccdc03a020c626ede51da471173b4a6ad1e904f2b2e04b4bd
|
||||
six==1.10.0 \
|
||||
--hash=sha256:0ff78c403d9bccf5a425a6d31a12aa6b47f1c21ca4dc2573a7e2f32a97335eb1 \
|
||||
--hash=sha256:105f8d68616f8248e24bf0e9372ef04d3cc10104f1980f54d57b2ce73a5ad56a
|
||||
|
27
third_party/python/scandir/LICENSE.txt
vendored
27
third_party/python/scandir/LICENSE.txt
vendored
@ -1,27 +0,0 @@
|
||||
Copyright (c) 2012, Ben Hoyt
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name of Ben Hoyt nor the names of its contributors may be used
|
||||
to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
6
third_party/python/scandir/MANIFEST.in
vendored
6
third_party/python/scandir/MANIFEST.in
vendored
@ -1,6 +0,0 @@
|
||||
include *.py
|
||||
include *.c
|
||||
include *.h
|
||||
include *.txt
|
||||
include *.rst
|
||||
include test/*.py
|
238
third_party/python/scandir/PKG-INFO
vendored
238
third_party/python/scandir/PKG-INFO
vendored
@ -1,238 +0,0 @@
|
||||
Metadata-Version: 1.1
|
||||
Name: scandir
|
||||
Version: 1.9.0
|
||||
Summary: scandir, a better directory iterator and faster os.walk()
|
||||
Home-page: https://github.com/benhoyt/scandir
|
||||
Author: Ben Hoyt
|
||||
Author-email: benhoyt@gmail.com
|
||||
License: New BSD License
|
||||
Description-Content-Type: UNKNOWN
|
||||
Description:
|
||||
scandir, a better directory iterator and faster os.walk()
|
||||
=========================================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/scandir.svg
|
||||
:target: https://pypi.python.org/pypi/scandir
|
||||
:alt: scandir on PyPI (Python Package Index)
|
||||
|
||||
.. image:: https://travis-ci.org/benhoyt/scandir.svg?branch=master
|
||||
:target: https://travis-ci.org/benhoyt/scandir
|
||||
:alt: Travis CI tests (Linux)
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/github/benhoyt/scandir?branch=master&svg=true
|
||||
:target: https://ci.appveyor.com/project/benhoyt/scandir
|
||||
:alt: Appveyor tests (Windows)
|
||||
|
||||
|
||||
``scandir()`` is a directory iteration function like ``os.listdir()``,
|
||||
except that instead of returning a list of bare filenames, it yields
|
||||
``DirEntry`` objects that include file type and stat information along
|
||||
with the name. Using ``scandir()`` increases the speed of ``os.walk()``
|
||||
by 2-20 times (depending on the platform and file system) by avoiding
|
||||
unnecessary calls to ``os.stat()`` in most cases.
|
||||
|
||||
|
||||
Now included in a Python near you!
|
||||
----------------------------------
|
||||
|
||||
``scandir`` has been included in the Python 3.5 standard library as
|
||||
``os.scandir()``, and the related performance improvements to
|
||||
``os.walk()`` have also been included. So if you're lucky enough to be
|
||||
using Python 3.5 (release date September 13, 2015) you get the benefit
|
||||
immediately, otherwise just
|
||||
`download this module from PyPI <https://pypi.python.org/pypi/scandir>`_,
|
||||
install it with ``pip install scandir``, and then do something like
|
||||
this in your code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Use the built-in version of scandir/walk if possible, otherwise
|
||||
# use the scandir module version
|
||||
try:
|
||||
from os import scandir, walk
|
||||
except ImportError:
|
||||
from scandir import scandir, walk
|
||||
|
||||
`PEP 471 <https://www.python.org/dev/peps/pep-0471/>`_, which is the
|
||||
PEP that proposes including ``scandir`` in the Python standard library,
|
||||
was `accepted <https://mail.python.org/pipermail/python-dev/2014-July/135561.html>`_
|
||||
in July 2014 by Victor Stinner, the BDFL-delegate for the PEP.
|
||||
|
||||
This ``scandir`` module is intended to work on Python 2.6+ and Python
|
||||
3.2+ (and it has been tested on those versions).
|
||||
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
Python's built-in ``os.walk()`` is significantly slower than it needs to be,
|
||||
because -- in addition to calling ``listdir()`` on each directory -- it calls
|
||||
``stat()`` on each file to determine whether the filename is a directory or not.
|
||||
But both ``FindFirstFile`` / ``FindNextFile`` on Windows and ``readdir`` on Linux/OS
|
||||
X already tell you whether the files returned are directories or not, so
|
||||
no further ``stat`` system calls are needed. In short, you can reduce the number
|
||||
of system calls from about 2N to N, where N is the total number of files and
|
||||
directories in the tree.
|
||||
|
||||
In practice, removing all those extra system calls makes ``os.walk()`` about
|
||||
**7-50 times as fast on Windows, and about 3-10 times as fast on Linux and Mac OS
|
||||
X.** So we're not talking about micro-optimizations. See more benchmarks
|
||||
in the "Benchmarks" section below.
|
||||
|
||||
Somewhat relatedly, many people have also asked for a version of
|
||||
``os.listdir()`` that yields filenames as it iterates instead of returning them
|
||||
as one big list. This improves memory efficiency for iterating very large
|
||||
directories.
|
||||
|
||||
So as well as a faster ``walk()``, scandir adds a new ``scandir()`` function.
|
||||
They're pretty easy to use, but see "The API" below for the full docs.
|
||||
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Below are results showing how many times as fast ``scandir.walk()`` is than
|
||||
``os.walk()`` on various systems, found by running ``benchmark.py`` with no
|
||||
arguments:
|
||||
|
||||
==================== ============== =============
|
||||
System version Python version Times as fast
|
||||
==================== ============== =============
|
||||
Windows 7 64-bit 2.7.7 64-bit 10.4
|
||||
Windows 7 64-bit SSD 2.7.7 64-bit 10.3
|
||||
Windows 7 64-bit NFS 2.7.6 64-bit 36.8
|
||||
Windows 7 64-bit SSD 3.4.1 64-bit 9.9
|
||||
Windows 7 64-bit SSD 3.5.0 64-bit 9.5
|
||||
CentOS 6.2 64-bit 2.6.6 64-bit 3.9
|
||||
Ubuntu 14.04 64-bit 2.7.6 64-bit 5.8
|
||||
Mac OS X 10.9.3 2.7.5 64-bit 3.8
|
||||
==================== ============== =============
|
||||
|
||||
All of the above tests were done using the fast C version of scandir
|
||||
(source code in ``_scandir.c``).
|
||||
|
||||
Note that the gains are less than the above on smaller directories and greater
|
||||
on larger directories. This is why ``benchmark.py`` creates a test directory
|
||||
tree with a standardized size.
|
||||
|
||||
|
||||
The API
|
||||
-------
|
||||
|
||||
walk()
|
||||
~~~~~~
|
||||
|
||||
The API for ``scandir.walk()`` is exactly the same as ``os.walk()``, so just
|
||||
`read the Python docs <https://docs.python.org/3.5/library/os.html#os.walk>`_.
|
||||
|
||||
scandir()
|
||||
~~~~~~~~~
|
||||
|
||||
The full docs for ``scandir()`` and the ``DirEntry`` objects it yields are
|
||||
available in the `Python documentation here <https://docs.python.org/3.5/library/os.html#os.scandir>`_.
|
||||
But below is a brief summary as well.
|
||||
|
||||
scandir(path='.') -> iterator of DirEntry objects for given path
|
||||
|
||||
Like ``listdir``, ``scandir`` calls the operating system's directory
|
||||
iteration system calls to get the names of the files in the given
|
||||
``path``, but it's different from ``listdir`` in two ways:
|
||||
|
||||
* Instead of returning bare filename strings, it returns lightweight
|
||||
``DirEntry`` objects that hold the filename string and provide
|
||||
simple methods that allow access to the additional data the
|
||||
operating system may have returned.
|
||||
|
||||
* It returns a generator instead of a list, so that ``scandir`` acts
|
||||
as a true iterator instead of returning the full list immediately.
|
||||
|
||||
``scandir()`` yields a ``DirEntry`` object for each file and
|
||||
sub-directory in ``path``. Just like ``listdir``, the ``'.'``
|
||||
and ``'..'`` pseudo-directories are skipped, and the entries are
|
||||
yielded in system-dependent order. Each ``DirEntry`` object has the
|
||||
following attributes and methods:
|
||||
|
||||
* ``name``: the entry's filename, relative to the scandir ``path``
|
||||
argument (corresponds to the return values of ``os.listdir``)
|
||||
|
||||
* ``path``: the entry's full path name (not necessarily an absolute
|
||||
path) -- the equivalent of ``os.path.join(scandir_path, entry.name)``
|
||||
|
||||
* ``is_dir(*, follow_symlinks=True)``: similar to
|
||||
``pathlib.Path.is_dir()``, but the return value is cached on the
|
||||
``DirEntry`` object; doesn't require a system call in most cases;
|
||||
don't follow symbolic links if ``follow_symlinks`` is False
|
||||
|
||||
* ``is_file(*, follow_symlinks=True)``: similar to
|
||||
``pathlib.Path.is_file()``, but the return value is cached on the
|
||||
``DirEntry`` object; doesn't require a system call in most cases;
|
||||
don't follow symbolic links if ``follow_symlinks`` is False
|
||||
|
||||
* ``is_symlink()``: similar to ``pathlib.Path.is_symlink()``, but the
|
||||
return value is cached on the ``DirEntry`` object; doesn't require a
|
||||
system call in most cases
|
||||
|
||||
* ``stat(*, follow_symlinks=True)``: like ``os.stat()``, but the
|
||||
return value is cached on the ``DirEntry`` object; does not require a
|
||||
system call on Windows (except for symlinks); don't follow symbolic links
|
||||
(like ``os.lstat()``) if ``follow_symlinks`` is False
|
||||
|
||||
* ``inode()``: return the inode number of the entry; the return value
|
||||
is cached on the ``DirEntry`` object
|
||||
|
||||
Here's a very simple example of ``scandir()`` showing use of the
|
||||
``DirEntry.name`` attribute and the ``DirEntry.is_dir()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def subdirs(path):
|
||||
"""Yield directory names not starting with '.' under given path."""
|
||||
for entry in os.scandir(path):
|
||||
if not entry.name.startswith('.') and entry.is_dir():
|
||||
yield entry.name
|
||||
|
||||
This ``subdirs()`` function will be significantly faster with scandir
|
||||
than ``os.listdir()`` and ``os.path.isdir()`` on both Windows and POSIX
|
||||
systems, especially on medium-sized or large directories.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
* `The Python docs for scandir <https://docs.python.org/3.5/library/os.html#os.scandir>`_
|
||||
* `PEP 471 <https://www.python.org/dev/peps/pep-0471/>`_, the
|
||||
(now-accepted) Python Enhancement Proposal that proposed adding
|
||||
``scandir`` to the standard library -- a lot of details here,
|
||||
including rejected ideas and previous discussion
|
||||
|
||||
|
||||
Flames, comments, bug reports
|
||||
-----------------------------
|
||||
|
||||
Please send flames, comments, and questions about scandir to Ben Hoyt:
|
||||
|
||||
http://benhoyt.com/
|
||||
|
||||
File bug reports for the version in the Python 3.5 standard library
|
||||
`here <https://docs.python.org/3.5/bugs.html>`_, or file bug reports
|
||||
or feature requests for this module at the GitHub project page:
|
||||
|
||||
https://github.com/benhoyt/scandir
|
||||
|
||||
Platform: UNKNOWN
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: System :: Filesystems
|
||||
Classifier: Topic :: System :: Operating System
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.6
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: Implementation :: CPython
|
211
third_party/python/scandir/README.rst
vendored
211
third_party/python/scandir/README.rst
vendored
@ -1,211 +0,0 @@
|
||||
|
||||
scandir, a better directory iterator and faster os.walk()
|
||||
=========================================================
|
||||
|
||||
.. image:: https://img.shields.io/pypi/v/scandir.svg
|
||||
:target: https://pypi.python.org/pypi/scandir
|
||||
:alt: scandir on PyPI (Python Package Index)
|
||||
|
||||
.. image:: https://travis-ci.org/benhoyt/scandir.svg?branch=master
|
||||
:target: https://travis-ci.org/benhoyt/scandir
|
||||
:alt: Travis CI tests (Linux)
|
||||
|
||||
.. image:: https://ci.appveyor.com/api/projects/status/github/benhoyt/scandir?branch=master&svg=true
|
||||
:target: https://ci.appveyor.com/project/benhoyt/scandir
|
||||
:alt: Appveyor tests (Windows)
|
||||
|
||||
|
||||
``scandir()`` is a directory iteration function like ``os.listdir()``,
|
||||
except that instead of returning a list of bare filenames, it yields
|
||||
``DirEntry`` objects that include file type and stat information along
|
||||
with the name. Using ``scandir()`` increases the speed of ``os.walk()``
|
||||
by 2-20 times (depending on the platform and file system) by avoiding
|
||||
unnecessary calls to ``os.stat()`` in most cases.
|
||||
|
||||
|
||||
Now included in a Python near you!
|
||||
----------------------------------
|
||||
|
||||
``scandir`` has been included in the Python 3.5 standard library as
|
||||
``os.scandir()``, and the related performance improvements to
|
||||
``os.walk()`` have also been included. So if you're lucky enough to be
|
||||
using Python 3.5 (release date September 13, 2015) you get the benefit
|
||||
immediately, otherwise just
|
||||
`download this module from PyPI <https://pypi.python.org/pypi/scandir>`_,
|
||||
install it with ``pip install scandir``, and then do something like
|
||||
this in your code:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# Use the built-in version of scandir/walk if possible, otherwise
|
||||
# use the scandir module version
|
||||
try:
|
||||
from os import scandir, walk
|
||||
except ImportError:
|
||||
from scandir import scandir, walk
|
||||
|
||||
`PEP 471 <https://www.python.org/dev/peps/pep-0471/>`_, which is the
|
||||
PEP that proposes including ``scandir`` in the Python standard library,
|
||||
was `accepted <https://mail.python.org/pipermail/python-dev/2014-July/135561.html>`_
|
||||
in July 2014 by Victor Stinner, the BDFL-delegate for the PEP.
|
||||
|
||||
This ``scandir`` module is intended to work on Python 2.6+ and Python
|
||||
3.2+ (and it has been tested on those versions).
|
||||
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
Python's built-in ``os.walk()`` is significantly slower than it needs to be,
|
||||
because -- in addition to calling ``listdir()`` on each directory -- it calls
|
||||
``stat()`` on each file to determine whether the filename is a directory or not.
|
||||
But both ``FindFirstFile`` / ``FindNextFile`` on Windows and ``readdir`` on Linux/OS
|
||||
X already tell you whether the files returned are directories or not, so
|
||||
no further ``stat`` system calls are needed. In short, you can reduce the number
|
||||
of system calls from about 2N to N, where N is the total number of files and
|
||||
directories in the tree.
|
||||
|
||||
In practice, removing all those extra system calls makes ``os.walk()`` about
|
||||
**7-50 times as fast on Windows, and about 3-10 times as fast on Linux and Mac OS
|
||||
X.** So we're not talking about micro-optimizations. See more benchmarks
|
||||
in the "Benchmarks" section below.
|
||||
|
||||
Somewhat relatedly, many people have also asked for a version of
|
||||
``os.listdir()`` that yields filenames as it iterates instead of returning them
|
||||
as one big list. This improves memory efficiency for iterating very large
|
||||
directories.
|
||||
|
||||
So as well as a faster ``walk()``, scandir adds a new ``scandir()`` function.
|
||||
They're pretty easy to use, but see "The API" below for the full docs.
|
||||
|
||||
|
||||
Benchmarks
|
||||
----------
|
||||
|
||||
Below are results showing how many times as fast ``scandir.walk()`` is than
|
||||
``os.walk()`` on various systems, found by running ``benchmark.py`` with no
|
||||
arguments:
|
||||
|
||||
==================== ============== =============
|
||||
System version Python version Times as fast
|
||||
==================== ============== =============
|
||||
Windows 7 64-bit 2.7.7 64-bit 10.4
|
||||
Windows 7 64-bit SSD 2.7.7 64-bit 10.3
|
||||
Windows 7 64-bit NFS 2.7.6 64-bit 36.8
|
||||
Windows 7 64-bit SSD 3.4.1 64-bit 9.9
|
||||
Windows 7 64-bit SSD 3.5.0 64-bit 9.5
|
||||
CentOS 6.2 64-bit 2.6.6 64-bit 3.9
|
||||
Ubuntu 14.04 64-bit 2.7.6 64-bit 5.8
|
||||
Mac OS X 10.9.3 2.7.5 64-bit 3.8
|
||||
==================== ============== =============
|
||||
|
||||
All of the above tests were done using the fast C version of scandir
|
||||
(source code in ``_scandir.c``).
|
||||
|
||||
Note that the gains are less than the above on smaller directories and greater
|
||||
on larger directories. This is why ``benchmark.py`` creates a test directory
|
||||
tree with a standardized size.
|
||||
|
||||
|
||||
The API
|
||||
-------
|
||||
|
||||
walk()
|
||||
~~~~~~
|
||||
|
||||
The API for ``scandir.walk()`` is exactly the same as ``os.walk()``, so just
|
||||
`read the Python docs <https://docs.python.org/3.5/library/os.html#os.walk>`_.
|
||||
|
||||
scandir()
|
||||
~~~~~~~~~
|
||||
|
||||
The full docs for ``scandir()`` and the ``DirEntry`` objects it yields are
|
||||
available in the `Python documentation here <https://docs.python.org/3.5/library/os.html#os.scandir>`_.
|
||||
But below is a brief summary as well.
|
||||
|
||||
scandir(path='.') -> iterator of DirEntry objects for given path
|
||||
|
||||
Like ``listdir``, ``scandir`` calls the operating system's directory
|
||||
iteration system calls to get the names of the files in the given
|
||||
``path``, but it's different from ``listdir`` in two ways:
|
||||
|
||||
* Instead of returning bare filename strings, it returns lightweight
|
||||
``DirEntry`` objects that hold the filename string and provide
|
||||
simple methods that allow access to the additional data the
|
||||
operating system may have returned.
|
||||
|
||||
* It returns a generator instead of a list, so that ``scandir`` acts
|
||||
as a true iterator instead of returning the full list immediately.
|
||||
|
||||
``scandir()`` yields a ``DirEntry`` object for each file and
|
||||
sub-directory in ``path``. Just like ``listdir``, the ``'.'``
|
||||
and ``'..'`` pseudo-directories are skipped, and the entries are
|
||||
yielded in system-dependent order. Each ``DirEntry`` object has the
|
||||
following attributes and methods:
|
||||
|
||||
* ``name``: the entry's filename, relative to the scandir ``path``
|
||||
argument (corresponds to the return values of ``os.listdir``)
|
||||
|
||||
* ``path``: the entry's full path name (not necessarily an absolute
|
||||
path) -- the equivalent of ``os.path.join(scandir_path, entry.name)``
|
||||
|
||||
* ``is_dir(*, follow_symlinks=True)``: similar to
|
||||
``pathlib.Path.is_dir()``, but the return value is cached on the
|
||||
``DirEntry`` object; doesn't require a system call in most cases;
|
||||
don't follow symbolic links if ``follow_symlinks`` is False
|
||||
|
||||
* ``is_file(*, follow_symlinks=True)``: similar to
|
||||
``pathlib.Path.is_file()``, but the return value is cached on the
|
||||
``DirEntry`` object; doesn't require a system call in most cases;
|
||||
don't follow symbolic links if ``follow_symlinks`` is False
|
||||
|
||||
* ``is_symlink()``: similar to ``pathlib.Path.is_symlink()``, but the
|
||||
return value is cached on the ``DirEntry`` object; doesn't require a
|
||||
system call in most cases
|
||||
|
||||
* ``stat(*, follow_symlinks=True)``: like ``os.stat()``, but the
|
||||
return value is cached on the ``DirEntry`` object; does not require a
|
||||
system call on Windows (except for symlinks); don't follow symbolic links
|
||||
(like ``os.lstat()``) if ``follow_symlinks`` is False
|
||||
|
||||
* ``inode()``: return the inode number of the entry; the return value
|
||||
is cached on the ``DirEntry`` object
|
||||
|
||||
Here's a very simple example of ``scandir()`` showing use of the
|
||||
``DirEntry.name`` attribute and the ``DirEntry.is_dir()`` method:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def subdirs(path):
|
||||
"""Yield directory names not starting with '.' under given path."""
|
||||
for entry in os.scandir(path):
|
||||
if not entry.name.startswith('.') and entry.is_dir():
|
||||
yield entry.name
|
||||
|
||||
This ``subdirs()`` function will be significantly faster with scandir
|
||||
than ``os.listdir()`` and ``os.path.isdir()`` on both Windows and POSIX
|
||||
systems, especially on medium-sized or large directories.
|
||||
|
||||
|
||||
Further reading
|
||||
---------------
|
||||
|
||||
* `The Python docs for scandir <https://docs.python.org/3.5/library/os.html#os.scandir>`_
|
||||
* `PEP 471 <https://www.python.org/dev/peps/pep-0471/>`_, the
|
||||
(now-accepted) Python Enhancement Proposal that proposed adding
|
||||
``scandir`` to the standard library -- a lot of details here,
|
||||
including rejected ideas and previous discussion
|
||||
|
||||
|
||||
Flames, comments, bug reports
|
||||
-----------------------------
|
||||
|
||||
Please send flames, comments, and questions about scandir to Ben Hoyt:
|
||||
|
||||
http://benhoyt.com/
|
||||
|
||||
File bug reports for the version in the Python 3.5 standard library
|
||||
`here <https://docs.python.org/3.5/bugs.html>`_, or file bug reports
|
||||
or feature requests for this module at the GitHub project page:
|
||||
|
||||
https://github.com/benhoyt/scandir
|
1833
third_party/python/scandir/_scandir.c
vendored
1833
third_party/python/scandir/_scandir.c
vendored
File diff suppressed because it is too large
Load Diff
192
third_party/python/scandir/benchmark.py
vendored
192
third_party/python/scandir/benchmark.py
vendored
@ -1,192 +0,0 @@
|
||||
"""Simple benchmark to compare the speed of scandir.walk() with os.walk()."""
|
||||
|
||||
import optparse
|
||||
import os
|
||||
import stat
|
||||
import sys
|
||||
import timeit
|
||||
|
||||
import warnings
|
||||
with warnings.catch_warnings(record=True):
|
||||
import scandir
|
||||
|
||||
DEPTH = 4
|
||||
NUM_DIRS = 5
|
||||
NUM_FILES = 50
|
||||
|
||||
|
||||
def os_walk_pre_35(top, topdown=True, onerror=None, followlinks=False):
|
||||
"""Pre Python 3.5 implementation of os.walk() that doesn't use scandir."""
|
||||
islink, join, isdir = os.path.islink, os.path.join, os.path.isdir
|
||||
|
||||
try:
|
||||
names = os.listdir(top)
|
||||
except OSError as err:
|
||||
if onerror is not None:
|
||||
onerror(err)
|
||||
return
|
||||
|
||||
dirs, nondirs = [], []
|
||||
for name in names:
|
||||
if isdir(join(top, name)):
|
||||
dirs.append(name)
|
||||
else:
|
||||
nondirs.append(name)
|
||||
|
||||
if topdown:
|
||||
yield top, dirs, nondirs
|
||||
for name in dirs:
|
||||
new_path = join(top, name)
|
||||
if followlinks or not islink(new_path):
|
||||
for x in os_walk_pre_35(new_path, topdown, onerror, followlinks):
|
||||
yield x
|
||||
if not topdown:
|
||||
yield top, dirs, nondirs
|
||||
|
||||
|
||||
def create_tree(path, depth=DEPTH):
|
||||
"""Create a directory tree at path with given depth, and NUM_DIRS and
|
||||
NUM_FILES at each level.
|
||||
"""
|
||||
os.mkdir(path)
|
||||
for i in range(NUM_FILES):
|
||||
filename = os.path.join(path, 'file{0:03}.txt'.format(i))
|
||||
with open(filename, 'wb') as f:
|
||||
f.write(b'foo')
|
||||
if depth <= 1:
|
||||
return
|
||||
for i in range(NUM_DIRS):
|
||||
dirname = os.path.join(path, 'dir{0:03}'.format(i))
|
||||
create_tree(dirname, depth - 1)
|
||||
|
||||
|
||||
def get_tree_size(path):
|
||||
"""Return total size of all files in directory tree at path."""
|
||||
size = 0
|
||||
try:
|
||||
for entry in scandir.scandir(path):
|
||||
if entry.is_symlink():
|
||||
pass
|
||||
elif entry.is_dir():
|
||||
size += get_tree_size(os.path.join(path, entry.name))
|
||||
else:
|
||||
size += entry.stat().st_size
|
||||
except OSError:
|
||||
pass
|
||||
return size
|
||||
|
||||
|
||||
def benchmark(path, get_size=False):
|
||||
sizes = {}
|
||||
|
||||
if get_size:
|
||||
def do_os_walk():
|
||||
size = 0
|
||||
for root, dirs, files in os.walk(path):
|
||||
for filename in files:
|
||||
fullname = os.path.join(root, filename)
|
||||
st = os.lstat(fullname)
|
||||
if not stat.S_ISLNK(st.st_mode):
|
||||
size += st.st_size
|
||||
sizes['os_walk'] = size
|
||||
|
||||
def do_scandir_walk():
|
||||
sizes['scandir_walk'] = get_tree_size(path)
|
||||
|
||||
else:
|
||||
def do_os_walk():
|
||||
for root, dirs, files in os.walk(path):
|
||||
pass
|
||||
|
||||
def do_scandir_walk():
|
||||
for root, dirs, files in scandir.walk(path):
|
||||
pass
|
||||
|
||||
# Run this once first to cache things, so we're not benchmarking I/O
|
||||
print("Priming the system's cache...")
|
||||
do_scandir_walk()
|
||||
|
||||
# Use the best of 3 time for each of them to eliminate high outliers
|
||||
os_walk_time = 1000000
|
||||
scandir_walk_time = 1000000
|
||||
N = 3
|
||||
for i in range(N):
|
||||
print('Benchmarking walks on {0}, repeat {1}/{2}...'.format(
|
||||
path, i + 1, N))
|
||||
os_walk_time = min(os_walk_time, timeit.timeit(do_os_walk, number=1))
|
||||
scandir_walk_time = min(scandir_walk_time,
|
||||
timeit.timeit(do_scandir_walk, number=1))
|
||||
|
||||
if get_size:
|
||||
if sizes['os_walk'] == sizes['scandir_walk']:
|
||||
equality = 'equal'
|
||||
else:
|
||||
equality = 'NOT EQUAL!'
|
||||
print('os.walk size {0}, scandir.walk size {1} -- {2}'.format(
|
||||
sizes['os_walk'], sizes['scandir_walk'], equality))
|
||||
|
||||
print('os.walk took {0:.3f}s, scandir.walk took {1:.3f}s -- {2:.1f}x as fast'.format(
|
||||
os_walk_time, scandir_walk_time, os_walk_time / scandir_walk_time))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
usage = """Usage: benchmark.py [-h] [tree_dir]
|
||||
|
||||
Create a large directory tree named "benchtree" (relative to this script) and
|
||||
benchmark os.walk() versus scandir.walk(). If tree_dir is specified, benchmark
|
||||
using it instead of creating a tree."""
|
||||
parser = optparse.OptionParser(usage=usage)
|
||||
parser.add_option('-s', '--size', action='store_true',
|
||||
help='get size of directory tree while walking')
|
||||
parser.add_option('-c', '--scandir', type='choice', choices=['best', 'generic', 'c', 'python', 'os'], default='best',
|
||||
help='version of scandir() to use, default "%default"')
|
||||
options, args = parser.parse_args()
|
||||
|
||||
if args:
|
||||
tree_dir = args[0]
|
||||
else:
|
||||
tree_dir = os.path.join(os.path.dirname(__file__), 'benchtree')
|
||||
if not os.path.exists(tree_dir):
|
||||
print('Creating tree at {0}: depth={1}, num_dirs={2}, num_files={3}'.format(
|
||||
tree_dir, DEPTH, NUM_DIRS, NUM_FILES))
|
||||
create_tree(tree_dir)
|
||||
|
||||
if options.scandir == 'generic':
|
||||
scandir.scandir = scandir.scandir_generic
|
||||
elif options.scandir == 'c':
|
||||
if scandir.scandir_c is None:
|
||||
print("ERROR: Compiled C version of scandir not found!")
|
||||
sys.exit(1)
|
||||
scandir.scandir = scandir.scandir_c
|
||||
elif options.scandir == 'python':
|
||||
if scandir.scandir_python is None:
|
||||
print("ERROR: Python version of scandir not found!")
|
||||
sys.exit(1)
|
||||
scandir.scandir = scandir.scandir_python
|
||||
elif options.scandir == 'os':
|
||||
if not hasattr(os, 'scandir'):
|
||||
print("ERROR: Python 3.5's os.scandir() not found!")
|
||||
sys.exit(1)
|
||||
scandir.scandir = os.scandir
|
||||
elif hasattr(os, 'scandir'):
|
||||
scandir.scandir = os.scandir
|
||||
|
||||
if scandir.scandir == getattr(os, 'scandir', None):
|
||||
print("Using Python 3.5's builtin os.scandir()")
|
||||
elif scandir.scandir == scandir.scandir_c:
|
||||
print('Using fast C version of scandir')
|
||||
elif scandir.scandir == scandir.scandir_python:
|
||||
print('Using slower ctypes version of scandir')
|
||||
elif scandir.scandir == scandir.scandir_generic:
|
||||
print('Using very slow generic version of scandir')
|
||||
else:
|
||||
print('ERROR: Unsure which version of scandir we are using!')
|
||||
sys.exit(1)
|
||||
|
||||
if hasattr(os, 'scandir'):
|
||||
os.walk = os_walk_pre_35
|
||||
print('Comparing against pre-Python 3.5 version of os.walk()')
|
||||
else:
|
||||
print('Comparing against builtin version of os.walk()')
|
||||
|
||||
benchmark(tree_dir, get_size=options.size)
|
48
third_party/python/scandir/osdefs.h
vendored
48
third_party/python/scandir/osdefs.h
vendored
@ -1,48 +0,0 @@
|
||||
// from CPython
|
||||
#ifndef Py_OSDEFS_H
|
||||
#define Py_OSDEFS_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/* Operating system dependencies */
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
#define SEP L'\\'
|
||||
#define ALTSEP L'/'
|
||||
#define MAXPATHLEN 256
|
||||
#define DELIM L';'
|
||||
#endif
|
||||
|
||||
/* Filename separator */
|
||||
#ifndef SEP
|
||||
#define SEP L'/'
|
||||
#endif
|
||||
|
||||
/* Max pathname length */
|
||||
#ifdef __hpux
|
||||
#include <sys/param.h>
|
||||
#include <limits.h>
|
||||
#ifndef PATH_MAX
|
||||
#define PATH_MAX MAXPATHLEN
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef MAXPATHLEN
|
||||
#if defined(PATH_MAX) && PATH_MAX > 1024
|
||||
#define MAXPATHLEN PATH_MAX
|
||||
#else
|
||||
#define MAXPATHLEN 1024
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Search path entry delimiter */
|
||||
#ifndef DELIM
|
||||
#define DELIM L':'
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* !Py_OSDEFS_H */
|
693
third_party/python/scandir/scandir.py
vendored
693
third_party/python/scandir/scandir.py
vendored
@ -1,693 +0,0 @@
|
||||
"""scandir, a better directory iterator and faster os.walk(), now in the Python 3.5 stdlib
|
||||
|
||||
scandir() is a generator version of os.listdir() that returns an
|
||||
iterator over files in a directory, and also exposes the extra
|
||||
information most OSes provide while iterating files in a directory
|
||||
(such as type and stat information).
|
||||
|
||||
This module also includes a version of os.walk() that uses scandir()
|
||||
to speed it up significantly.
|
||||
|
||||
See README.md or https://github.com/benhoyt/scandir for rationale and
|
||||
docs, or read PEP 471 (https://www.python.org/dev/peps/pep-0471/) for
|
||||
more details on its inclusion into Python 3.5
|
||||
|
||||
scandir is released under the new BSD 3-clause license. See
|
||||
LICENSE.txt for the full license text.
|
||||
"""
|
||||
|
||||
from __future__ import division
|
||||
|
||||
from errno import ENOENT
|
||||
from os import listdir, lstat, stat, strerror
|
||||
from os.path import join, islink
|
||||
from stat import S_IFDIR, S_IFLNK, S_IFREG
|
||||
import collections
|
||||
import sys
|
||||
|
||||
try:
|
||||
import _scandir
|
||||
except ImportError:
|
||||
_scandir = None
|
||||
|
||||
try:
|
||||
import ctypes
|
||||
except ImportError:
|
||||
ctypes = None
|
||||
|
||||
if _scandir is None and ctypes is None:
|
||||
import warnings
|
||||
warnings.warn("scandir can't find the compiled _scandir C module "
|
||||
"or ctypes, using slow generic fallback")
|
||||
|
||||
__version__ = '1.9.0'
|
||||
__all__ = ['scandir', 'walk']
|
||||
|
||||
# Windows FILE_ATTRIBUTE constants for interpreting the
|
||||
# FIND_DATA.dwFileAttributes member
|
||||
FILE_ATTRIBUTE_ARCHIVE = 32
|
||||
FILE_ATTRIBUTE_COMPRESSED = 2048
|
||||
FILE_ATTRIBUTE_DEVICE = 64
|
||||
FILE_ATTRIBUTE_DIRECTORY = 16
|
||||
FILE_ATTRIBUTE_ENCRYPTED = 16384
|
||||
FILE_ATTRIBUTE_HIDDEN = 2
|
||||
FILE_ATTRIBUTE_INTEGRITY_STREAM = 32768
|
||||
FILE_ATTRIBUTE_NORMAL = 128
|
||||
FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 8192
|
||||
FILE_ATTRIBUTE_NO_SCRUB_DATA = 131072
|
||||
FILE_ATTRIBUTE_OFFLINE = 4096
|
||||
FILE_ATTRIBUTE_READONLY = 1
|
||||
FILE_ATTRIBUTE_REPARSE_POINT = 1024
|
||||
FILE_ATTRIBUTE_SPARSE_FILE = 512
|
||||
FILE_ATTRIBUTE_SYSTEM = 4
|
||||
FILE_ATTRIBUTE_TEMPORARY = 256
|
||||
FILE_ATTRIBUTE_VIRTUAL = 65536
|
||||
|
||||
IS_PY3 = sys.version_info >= (3, 0)
|
||||
|
||||
if IS_PY3:
|
||||
unicode = str # Because Python <= 3.2 doesn't have u'unicode' syntax
|
||||
|
||||
|
||||
class GenericDirEntry(object):
|
||||
__slots__ = ('name', '_stat', '_lstat', '_scandir_path', '_path')
|
||||
|
||||
def __init__(self, scandir_path, name):
|
||||
self._scandir_path = scandir_path
|
||||
self.name = name
|
||||
self._stat = None
|
||||
self._lstat = None
|
||||
self._path = None
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
if self._path is None:
|
||||
self._path = join(self._scandir_path, self.name)
|
||||
return self._path
|
||||
|
||||
def stat(self, follow_symlinks=True):
|
||||
if follow_symlinks:
|
||||
if self._stat is None:
|
||||
self._stat = stat(self.path)
|
||||
return self._stat
|
||||
else:
|
||||
if self._lstat is None:
|
||||
self._lstat = lstat(self.path)
|
||||
return self._lstat
|
||||
|
||||
# The code duplication below is intentional: this is for slightly
|
||||
# better performance on systems that fall back to GenericDirEntry.
|
||||
# It avoids an additional attribute lookup and method call, which
|
||||
# are relatively slow on CPython.
|
||||
def is_dir(self, follow_symlinks=True):
|
||||
try:
|
||||
st = self.stat(follow_symlinks=follow_symlinks)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False # Path doesn't exist or is a broken symlink
|
||||
return st.st_mode & 0o170000 == S_IFDIR
|
||||
|
||||
def is_file(self, follow_symlinks=True):
|
||||
try:
|
||||
st = self.stat(follow_symlinks=follow_symlinks)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False # Path doesn't exist or is a broken symlink
|
||||
return st.st_mode & 0o170000 == S_IFREG
|
||||
|
||||
def is_symlink(self):
|
||||
try:
|
||||
st = self.stat(follow_symlinks=False)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False # Path doesn't exist or is a broken symlink
|
||||
return st.st_mode & 0o170000 == S_IFLNK
|
||||
|
||||
def inode(self):
|
||||
st = self.stat(follow_symlinks=False)
|
||||
return st.st_ino
|
||||
|
||||
def __str__(self):
|
||||
return '<{0}: {1!r}>'.format(self.__class__.__name__, self.name)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
|
||||
def _scandir_generic(path=unicode('.')):
|
||||
"""Like os.listdir(), but yield DirEntry objects instead of returning
|
||||
a list of names.
|
||||
"""
|
||||
for name in listdir(path):
|
||||
yield GenericDirEntry(path, name)
|
||||
|
||||
|
||||
if IS_PY3 and sys.platform == 'win32':
|
||||
def scandir_generic(path=unicode('.')):
|
||||
if isinstance(path, bytes):
|
||||
raise TypeError("os.scandir() doesn't support bytes path on Windows, use Unicode instead")
|
||||
return _scandir_generic(path)
|
||||
scandir_generic.__doc__ = _scandir_generic.__doc__
|
||||
else:
|
||||
scandir_generic = _scandir_generic
|
||||
|
||||
|
||||
scandir_c = None
|
||||
scandir_python = None
|
||||
|
||||
|
||||
if sys.platform == 'win32':
|
||||
if ctypes is not None:
|
||||
from ctypes import wintypes
|
||||
|
||||
# Various constants from windows.h
|
||||
INVALID_HANDLE_VALUE = ctypes.c_void_p(-1).value
|
||||
ERROR_FILE_NOT_FOUND = 2
|
||||
ERROR_NO_MORE_FILES = 18
|
||||
IO_REPARSE_TAG_SYMLINK = 0xA000000C
|
||||
|
||||
# Numer of seconds between 1601-01-01 and 1970-01-01
|
||||
SECONDS_BETWEEN_EPOCHS = 11644473600
|
||||
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
|
||||
# ctypes wrappers for (wide string versions of) FindFirstFile,
|
||||
# FindNextFile, and FindClose
|
||||
FindFirstFile = kernel32.FindFirstFileW
|
||||
FindFirstFile.argtypes = [
|
||||
wintypes.LPCWSTR,
|
||||
ctypes.POINTER(wintypes.WIN32_FIND_DATAW),
|
||||
]
|
||||
FindFirstFile.restype = wintypes.HANDLE
|
||||
|
||||
FindNextFile = kernel32.FindNextFileW
|
||||
FindNextFile.argtypes = [
|
||||
wintypes.HANDLE,
|
||||
ctypes.POINTER(wintypes.WIN32_FIND_DATAW),
|
||||
]
|
||||
FindNextFile.restype = wintypes.BOOL
|
||||
|
||||
FindClose = kernel32.FindClose
|
||||
FindClose.argtypes = [wintypes.HANDLE]
|
||||
FindClose.restype = wintypes.BOOL
|
||||
|
||||
Win32StatResult = collections.namedtuple('Win32StatResult', [
|
||||
'st_mode',
|
||||
'st_ino',
|
||||
'st_dev',
|
||||
'st_nlink',
|
||||
'st_uid',
|
||||
'st_gid',
|
||||
'st_size',
|
||||
'st_atime',
|
||||
'st_mtime',
|
||||
'st_ctime',
|
||||
'st_atime_ns',
|
||||
'st_mtime_ns',
|
||||
'st_ctime_ns',
|
||||
'st_file_attributes',
|
||||
])
|
||||
|
||||
def filetime_to_time(filetime):
|
||||
"""Convert Win32 FILETIME to time since Unix epoch in seconds."""
|
||||
total = filetime.dwHighDateTime << 32 | filetime.dwLowDateTime
|
||||
return total / 10000000 - SECONDS_BETWEEN_EPOCHS
|
||||
|
||||
def find_data_to_stat(data):
|
||||
"""Convert Win32 FIND_DATA struct to stat_result."""
|
||||
# First convert Win32 dwFileAttributes to st_mode
|
||||
attributes = data.dwFileAttributes
|
||||
st_mode = 0
|
||||
if attributes & FILE_ATTRIBUTE_DIRECTORY:
|
||||
st_mode |= S_IFDIR | 0o111
|
||||
else:
|
||||
st_mode |= S_IFREG
|
||||
if attributes & FILE_ATTRIBUTE_READONLY:
|
||||
st_mode |= 0o444
|
||||
else:
|
||||
st_mode |= 0o666
|
||||
if (attributes & FILE_ATTRIBUTE_REPARSE_POINT and
|
||||
data.dwReserved0 == IO_REPARSE_TAG_SYMLINK):
|
||||
st_mode ^= st_mode & 0o170000
|
||||
st_mode |= S_IFLNK
|
||||
|
||||
st_size = data.nFileSizeHigh << 32 | data.nFileSizeLow
|
||||
st_atime = filetime_to_time(data.ftLastAccessTime)
|
||||
st_mtime = filetime_to_time(data.ftLastWriteTime)
|
||||
st_ctime = filetime_to_time(data.ftCreationTime)
|
||||
|
||||
# Some fields set to zero per CPython's posixmodule.c: st_ino, st_dev,
|
||||
# st_nlink, st_uid, st_gid
|
||||
return Win32StatResult(st_mode, 0, 0, 0, 0, 0, st_size,
|
||||
st_atime, st_mtime, st_ctime,
|
||||
int(st_atime * 1000000000),
|
||||
int(st_mtime * 1000000000),
|
||||
int(st_ctime * 1000000000),
|
||||
attributes)
|
||||
|
||||
class Win32DirEntryPython(object):
|
||||
__slots__ = ('name', '_stat', '_lstat', '_find_data', '_scandir_path', '_path', '_inode')
|
||||
|
||||
def __init__(self, scandir_path, name, find_data):
|
||||
self._scandir_path = scandir_path
|
||||
self.name = name
|
||||
self._stat = None
|
||||
self._lstat = None
|
||||
self._find_data = find_data
|
||||
self._path = None
|
||||
self._inode = None
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
if self._path is None:
|
||||
self._path = join(self._scandir_path, self.name)
|
||||
return self._path
|
||||
|
||||
def stat(self, follow_symlinks=True):
|
||||
if follow_symlinks:
|
||||
if self._stat is None:
|
||||
if self.is_symlink():
|
||||
# It's a symlink, call link-following stat()
|
||||
self._stat = stat(self.path)
|
||||
else:
|
||||
# Not a symlink, stat is same as lstat value
|
||||
if self._lstat is None:
|
||||
self._lstat = find_data_to_stat(self._find_data)
|
||||
self._stat = self._lstat
|
||||
return self._stat
|
||||
else:
|
||||
if self._lstat is None:
|
||||
# Lazily convert to stat object, because it's slow
|
||||
# in Python, and often we only need is_dir() etc
|
||||
self._lstat = find_data_to_stat(self._find_data)
|
||||
return self._lstat
|
||||
|
||||
def is_dir(self, follow_symlinks=True):
|
||||
is_symlink = self.is_symlink()
|
||||
if follow_symlinks and is_symlink:
|
||||
try:
|
||||
return self.stat().st_mode & 0o170000 == S_IFDIR
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False
|
||||
elif is_symlink:
|
||||
return False
|
||||
else:
|
||||
return (self._find_data.dwFileAttributes &
|
||||
FILE_ATTRIBUTE_DIRECTORY != 0)
|
||||
|
||||
def is_file(self, follow_symlinks=True):
|
||||
is_symlink = self.is_symlink()
|
||||
if follow_symlinks and is_symlink:
|
||||
try:
|
||||
return self.stat().st_mode & 0o170000 == S_IFREG
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False
|
||||
elif is_symlink:
|
||||
return False
|
||||
else:
|
||||
return (self._find_data.dwFileAttributes &
|
||||
FILE_ATTRIBUTE_DIRECTORY == 0)
|
||||
|
||||
def is_symlink(self):
|
||||
return (self._find_data.dwFileAttributes &
|
||||
FILE_ATTRIBUTE_REPARSE_POINT != 0 and
|
||||
self._find_data.dwReserved0 == IO_REPARSE_TAG_SYMLINK)
|
||||
|
||||
def inode(self):
|
||||
if self._inode is None:
|
||||
self._inode = lstat(self.path).st_ino
|
||||
return self._inode
|
||||
|
||||
def __str__(self):
|
||||
return '<{0}: {1!r}>'.format(self.__class__.__name__, self.name)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def win_error(error, filename):
|
||||
exc = WindowsError(error, ctypes.FormatError(error))
|
||||
exc.filename = filename
|
||||
return exc
|
||||
|
||||
def _scandir_python(path=unicode('.')):
|
||||
"""Like os.listdir(), but yield DirEntry objects instead of returning
|
||||
a list of names.
|
||||
"""
|
||||
# Call FindFirstFile and handle errors
|
||||
if isinstance(path, bytes):
|
||||
is_bytes = True
|
||||
filename = join(path.decode('mbcs', 'strict'), '*.*')
|
||||
else:
|
||||
is_bytes = False
|
||||
filename = join(path, '*.*')
|
||||
data = wintypes.WIN32_FIND_DATAW()
|
||||
data_p = ctypes.byref(data)
|
||||
handle = FindFirstFile(filename, data_p)
|
||||
if handle == INVALID_HANDLE_VALUE:
|
||||
error = ctypes.GetLastError()
|
||||
if error == ERROR_FILE_NOT_FOUND:
|
||||
# No files, don't yield anything
|
||||
return
|
||||
raise win_error(error, path)
|
||||
|
||||
# Call FindNextFile in a loop, stopping when no more files
|
||||
try:
|
||||
while True:
|
||||
# Skip '.' and '..' (current and parent directory), but
|
||||
# otherwise yield (filename, stat_result) tuple
|
||||
name = data.cFileName
|
||||
if name not in ('.', '..'):
|
||||
if is_bytes:
|
||||
name = name.encode('mbcs', 'replace')
|
||||
yield Win32DirEntryPython(path, name, data)
|
||||
|
||||
data = wintypes.WIN32_FIND_DATAW()
|
||||
data_p = ctypes.byref(data)
|
||||
success = FindNextFile(handle, data_p)
|
||||
if not success:
|
||||
error = ctypes.GetLastError()
|
||||
if error == ERROR_NO_MORE_FILES:
|
||||
break
|
||||
raise win_error(error, path)
|
||||
finally:
|
||||
if not FindClose(handle):
|
||||
raise win_error(ctypes.GetLastError(), path)
|
||||
|
||||
if IS_PY3:
|
||||
def scandir_python(path=unicode('.')):
|
||||
if isinstance(path, bytes):
|
||||
raise TypeError("os.scandir() doesn't support bytes path on Windows, use Unicode instead")
|
||||
return _scandir_python(path)
|
||||
scandir_python.__doc__ = _scandir_python.__doc__
|
||||
else:
|
||||
scandir_python = _scandir_python
|
||||
|
||||
if _scandir is not None:
|
||||
scandir_c = _scandir.scandir
|
||||
DirEntry_c = _scandir.DirEntry
|
||||
|
||||
if _scandir is not None:
|
||||
scandir = scandir_c
|
||||
DirEntry = DirEntry_c
|
||||
elif ctypes is not None:
|
||||
scandir = scandir_python
|
||||
DirEntry = Win32DirEntryPython
|
||||
else:
|
||||
scandir = scandir_generic
|
||||
DirEntry = GenericDirEntry
|
||||
|
||||
|
||||
# Linux, OS X, and BSD implementation
|
||||
elif sys.platform.startswith(('linux', 'darwin', 'sunos5')) or 'bsd' in sys.platform:
|
||||
have_dirent_d_type = (sys.platform != 'sunos5')
|
||||
|
||||
if ctypes is not None and have_dirent_d_type:
|
||||
import ctypes.util
|
||||
|
||||
DIR_p = ctypes.c_void_p
|
||||
|
||||
# Rather annoying how the dirent struct is slightly different on each
|
||||
# platform. The only fields we care about are d_name and d_type.
|
||||
class Dirent(ctypes.Structure):
|
||||
if sys.platform.startswith('linux'):
|
||||
_fields_ = (
|
||||
('d_ino', ctypes.c_ulong),
|
||||
('d_off', ctypes.c_long),
|
||||
('d_reclen', ctypes.c_ushort),
|
||||
('d_type', ctypes.c_byte),
|
||||
('d_name', ctypes.c_char * 256),
|
||||
)
|
||||
elif 'openbsd' in sys.platform:
|
||||
_fields_ = (
|
||||
('d_ino', ctypes.c_uint64),
|
||||
('d_off', ctypes.c_uint64),
|
||||
('d_reclen', ctypes.c_uint16),
|
||||
('d_type', ctypes.c_uint8),
|
||||
('d_namlen', ctypes.c_uint8),
|
||||
('__d_padding', ctypes.c_uint8 * 4),
|
||||
('d_name', ctypes.c_char * 256),
|
||||
)
|
||||
else:
|
||||
_fields_ = (
|
||||
('d_ino', ctypes.c_uint32), # must be uint32, not ulong
|
||||
('d_reclen', ctypes.c_ushort),
|
||||
('d_type', ctypes.c_byte),
|
||||
('d_namlen', ctypes.c_byte),
|
||||
('d_name', ctypes.c_char * 256),
|
||||
)
|
||||
|
||||
DT_UNKNOWN = 0
|
||||
DT_DIR = 4
|
||||
DT_REG = 8
|
||||
DT_LNK = 10
|
||||
|
||||
Dirent_p = ctypes.POINTER(Dirent)
|
||||
Dirent_pp = ctypes.POINTER(Dirent_p)
|
||||
|
||||
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
|
||||
opendir = libc.opendir
|
||||
opendir.argtypes = [ctypes.c_char_p]
|
||||
opendir.restype = DIR_p
|
||||
|
||||
readdir_r = libc.readdir_r
|
||||
readdir_r.argtypes = [DIR_p, Dirent_p, Dirent_pp]
|
||||
readdir_r.restype = ctypes.c_int
|
||||
|
||||
closedir = libc.closedir
|
||||
closedir.argtypes = [DIR_p]
|
||||
closedir.restype = ctypes.c_int
|
||||
|
||||
file_system_encoding = sys.getfilesystemencoding()
|
||||
|
||||
class PosixDirEntry(object):
|
||||
__slots__ = ('name', '_d_type', '_stat', '_lstat', '_scandir_path', '_path', '_inode')
|
||||
|
||||
def __init__(self, scandir_path, name, d_type, inode):
|
||||
self._scandir_path = scandir_path
|
||||
self.name = name
|
||||
self._d_type = d_type
|
||||
self._inode = inode
|
||||
self._stat = None
|
||||
self._lstat = None
|
||||
self._path = None
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
if self._path is None:
|
||||
self._path = join(self._scandir_path, self.name)
|
||||
return self._path
|
||||
|
||||
def stat(self, follow_symlinks=True):
|
||||
if follow_symlinks:
|
||||
if self._stat is None:
|
||||
if self.is_symlink():
|
||||
self._stat = stat(self.path)
|
||||
else:
|
||||
if self._lstat is None:
|
||||
self._lstat = lstat(self.path)
|
||||
self._stat = self._lstat
|
||||
return self._stat
|
||||
else:
|
||||
if self._lstat is None:
|
||||
self._lstat = lstat(self.path)
|
||||
return self._lstat
|
||||
|
||||
def is_dir(self, follow_symlinks=True):
|
||||
if (self._d_type == DT_UNKNOWN or
|
||||
(follow_symlinks and self.is_symlink())):
|
||||
try:
|
||||
st = self.stat(follow_symlinks=follow_symlinks)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False
|
||||
return st.st_mode & 0o170000 == S_IFDIR
|
||||
else:
|
||||
return self._d_type == DT_DIR
|
||||
|
||||
def is_file(self, follow_symlinks=True):
|
||||
if (self._d_type == DT_UNKNOWN or
|
||||
(follow_symlinks and self.is_symlink())):
|
||||
try:
|
||||
st = self.stat(follow_symlinks=follow_symlinks)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False
|
||||
return st.st_mode & 0o170000 == S_IFREG
|
||||
else:
|
||||
return self._d_type == DT_REG
|
||||
|
||||
def is_symlink(self):
|
||||
if self._d_type == DT_UNKNOWN:
|
||||
try:
|
||||
st = self.stat(follow_symlinks=False)
|
||||
except OSError as e:
|
||||
if e.errno != ENOENT:
|
||||
raise
|
||||
return False
|
||||
return st.st_mode & 0o170000 == S_IFLNK
|
||||
else:
|
||||
return self._d_type == DT_LNK
|
||||
|
||||
def inode(self):
|
||||
return self._inode
|
||||
|
||||
def __str__(self):
|
||||
return '<{0}: {1!r}>'.format(self.__class__.__name__, self.name)
|
||||
|
||||
__repr__ = __str__
|
||||
|
||||
def posix_error(filename):
|
||||
errno = ctypes.get_errno()
|
||||
exc = OSError(errno, strerror(errno))
|
||||
exc.filename = filename
|
||||
return exc
|
||||
|
||||
def scandir_python(path=unicode('.')):
|
||||
"""Like os.listdir(), but yield DirEntry objects instead of returning
|
||||
a list of names.
|
||||
"""
|
||||
if isinstance(path, bytes):
|
||||
opendir_path = path
|
||||
is_bytes = True
|
||||
else:
|
||||
opendir_path = path.encode(file_system_encoding)
|
||||
is_bytes = False
|
||||
dir_p = opendir(opendir_path)
|
||||
if not dir_p:
|
||||
raise posix_error(path)
|
||||
try:
|
||||
result = Dirent_p()
|
||||
while True:
|
||||
entry = Dirent()
|
||||
if readdir_r(dir_p, entry, result):
|
||||
raise posix_error(path)
|
||||
if not result:
|
||||
break
|
||||
name = entry.d_name
|
||||
if name not in (b'.', b'..'):
|
||||
if not is_bytes:
|
||||
name = name.decode(file_system_encoding)
|
||||
yield PosixDirEntry(path, name, entry.d_type, entry.d_ino)
|
||||
finally:
|
||||
if closedir(dir_p):
|
||||
raise posix_error(path)
|
||||
|
||||
if _scandir is not None:
|
||||
scandir_c = _scandir.scandir
|
||||
DirEntry_c = _scandir.DirEntry
|
||||
|
||||
if _scandir is not None:
|
||||
scandir = scandir_c
|
||||
DirEntry = DirEntry_c
|
||||
elif ctypes is not None:
|
||||
scandir = scandir_python
|
||||
DirEntry = PosixDirEntry
|
||||
else:
|
||||
scandir = scandir_generic
|
||||
DirEntry = GenericDirEntry
|
||||
|
||||
|
||||
# Some other system -- no d_type or stat information
|
||||
else:
|
||||
scandir = scandir_generic
|
||||
DirEntry = GenericDirEntry
|
||||
|
||||
|
||||
def _walk(top, topdown=True, onerror=None, followlinks=False):
|
||||
"""Like Python 3.5's implementation of os.walk() -- faster than
|
||||
the pre-Python 3.5 version as it uses scandir() internally.
|
||||
"""
|
||||
dirs = []
|
||||
nondirs = []
|
||||
|
||||
# We may not have read permission for top, in which case we can't
|
||||
# get a list of the files the directory contains. os.walk
|
||||
# always suppressed the exception then, rather than blow up for a
|
||||
# minor reason when (say) a thousand readable directories are still
|
||||
# left to visit. That logic is copied here.
|
||||
try:
|
||||
scandir_it = scandir(top)
|
||||
except OSError as error:
|
||||
if onerror is not None:
|
||||
onerror(error)
|
||||
return
|
||||
|
||||
while True:
|
||||
try:
|
||||
try:
|
||||
entry = next(scandir_it)
|
||||
except StopIteration:
|
||||
break
|
||||
except OSError as error:
|
||||
if onerror is not None:
|
||||
onerror(error)
|
||||
return
|
||||
|
||||
try:
|
||||
is_dir = entry.is_dir()
|
||||
except OSError:
|
||||
# If is_dir() raises an OSError, consider that the entry is not
|
||||
# a directory, same behaviour than os.path.isdir().
|
||||
is_dir = False
|
||||
|
||||
if is_dir:
|
||||
dirs.append(entry.name)
|
||||
else:
|
||||
nondirs.append(entry.name)
|
||||
|
||||
if not topdown and is_dir:
|
||||
# Bottom-up: recurse into sub-directory, but exclude symlinks to
|
||||
# directories if followlinks is False
|
||||
if followlinks:
|
||||
walk_into = True
|
||||
else:
|
||||
try:
|
||||
is_symlink = entry.is_symlink()
|
||||
except OSError:
|
||||
# If is_symlink() raises an OSError, consider that the
|
||||
# entry is not a symbolic link, same behaviour than
|
||||
# os.path.islink().
|
||||
is_symlink = False
|
||||
walk_into = not is_symlink
|
||||
|
||||
if walk_into:
|
||||
for entry in walk(entry.path, topdown, onerror, followlinks):
|
||||
yield entry
|
||||
|
||||
# Yield before recursion if going top down
|
||||
if topdown:
|
||||
yield top, dirs, nondirs
|
||||
|
||||
# Recurse into sub-directories
|
||||
for name in dirs:
|
||||
new_path = join(top, name)
|
||||
# Issue #23605: os.path.islink() is used instead of caching
|
||||
# entry.is_symlink() result during the loop on os.scandir() because
|
||||
# the caller can replace the directory entry during the "yield"
|
||||
# above.
|
||||
if followlinks or not islink(new_path):
|
||||
for entry in walk(new_path, topdown, onerror, followlinks):
|
||||
yield entry
|
||||
else:
|
||||
# Yield after recursion if going bottom up
|
||||
yield top, dirs, nondirs
|
||||
|
||||
|
||||
if IS_PY3 or sys.platform != 'win32':
|
||||
walk = _walk
|
||||
else:
|
||||
# Fix for broken unicode handling on Windows on Python 2.x, see:
|
||||
# https://github.com/benhoyt/scandir/issues/54
|
||||
file_system_encoding = sys.getfilesystemencoding()
|
||||
|
||||
def walk(top, topdown=True, onerror=None, followlinks=False):
|
||||
if isinstance(top, bytes):
|
||||
top = top.decode(file_system_encoding)
|
||||
return _walk(top, topdown, onerror, followlinks)
|
4
third_party/python/scandir/setup.cfg
vendored
4
third_party/python/scandir/setup.cfg
vendored
@ -1,4 +0,0 @@
|
||||
[egg_info]
|
||||
tag_build =
|
||||
tag_date = 0
|
||||
|
80
third_party/python/scandir/setup.py
vendored
80
third_party/python/scandir/setup.py
vendored
@ -1,80 +0,0 @@
|
||||
"""Run "python setup.py install" to install scandir."""
|
||||
|
||||
try:
|
||||
from setuptools import setup, Extension
|
||||
from setuptools.command.build_ext import build_ext as base_build_ext
|
||||
except ImportError:
|
||||
import warnings
|
||||
import sys
|
||||
val = sys.exc_info()[1]
|
||||
|
||||
warnings.warn("import of setuptools failed %r" % val)
|
||||
from distutils.core import setup, Extension
|
||||
from distutils.command.build_ext import build_ext as base_build_ext
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import logging
|
||||
|
||||
# Get version without importing scandir because that will lock the
|
||||
# .pyd file (if scandir is already installed) so it can't be
|
||||
# overwritten during the install process
|
||||
with open(os.path.join(os.path.dirname(__file__), 'scandir.py')) as f:
|
||||
for line in f:
|
||||
match = re.match(r"__version__.*'([0-9.]+)'", line)
|
||||
if match:
|
||||
version = match.group(1)
|
||||
break
|
||||
else:
|
||||
raise Exception("Couldn't find version in setup.py")
|
||||
|
||||
with open('README.rst') as f:
|
||||
long_description = f.read()
|
||||
|
||||
|
||||
class BuildExt(base_build_ext):
|
||||
|
||||
# the extension is optional since in case of lack of c the api
|
||||
# there is a ctypes fallback and a slow python fallback
|
||||
|
||||
def build_extension(self, ext):
|
||||
try:
|
||||
base_build_ext.build_extension(self, ext)
|
||||
except Exception:
|
||||
exception = sys.exc_info()[0]
|
||||
logging.warn("building the %s failed with %s", ext.name, exception)
|
||||
|
||||
extension = Extension('_scandir', ['_scandir.c'], optional=True)
|
||||
|
||||
|
||||
setup(
|
||||
name='scandir',
|
||||
version=version,
|
||||
author='Ben Hoyt',
|
||||
author_email='benhoyt@gmail.com',
|
||||
url='https://github.com/benhoyt/scandir',
|
||||
license='New BSD License',
|
||||
description='scandir, a better directory iterator and faster os.walk()',
|
||||
long_description=long_description,
|
||||
py_modules=['scandir'],
|
||||
ext_modules=[extension],
|
||||
classifiers=[
|
||||
'Development Status :: 5 - Production/Stable',
|
||||
'Intended Audience :: Developers',
|
||||
'Operating System :: OS Independent',
|
||||
'License :: OSI Approved :: BSD License',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: System :: Filesystems',
|
||||
'Topic :: System :: Operating System',
|
||||
'Programming Language :: Python',
|
||||
'Programming Language :: Python :: 2',
|
||||
'Programming Language :: Python :: 2.6',
|
||||
'Programming Language :: Python :: 2.7',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Programming Language :: Python :: 3.4',
|
||||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Programming Language :: Python :: Implementation :: CPython',
|
||||
], cmdclass={'build_ext': BuildExt},
|
||||
)
|
25
third_party/python/scandir/test/run_tests.py
vendored
25
third_party/python/scandir/test/run_tests.py
vendored
@ -1,25 +0,0 @@
|
||||
"""Run all unit tests."""
|
||||
|
||||
import glob
|
||||
import os
|
||||
import sys
|
||||
|
||||
if sys.version_info[:2] < (2, 7):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
|
||||
def main():
|
||||
test_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
test_files = glob.glob(os.path.join(test_dir, 'test_*.py'))
|
||||
test_names = [os.path.basename(f)[:-3] for f in test_files]
|
||||
|
||||
sys.path.insert(0, os.path.join(test_dir, '..'))
|
||||
|
||||
suite = unittest.defaultTestLoader.loadTestsFromNames(test_names)
|
||||
result = unittest.TextTestRunner(verbosity=2).run(suite)
|
||||
sys.exit(1 if (result.errors or result.failures) else 0)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
320
third_party/python/scandir/test/test_scandir.py
vendored
320
third_party/python/scandir/test/test_scandir.py
vendored
@ -1,320 +0,0 @@
|
||||
"""Tests for scandir.scandir()."""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import time
|
||||
|
||||
if sys.version_info[:2] < (2, 7):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
try:
|
||||
import scandir
|
||||
has_scandir = True
|
||||
except ImportError:
|
||||
has_scandir = False
|
||||
|
||||
FILE_ATTRIBUTE_DIRECTORY = 16
|
||||
|
||||
TEST_PATH = os.path.abspath(os.path.join(os.path.dirname(__file__), 'testdir'))
|
||||
|
||||
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 sys.version_info >= (3, 3):
|
||||
# "target_is_directory" was only added in Python 3.3
|
||||
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(object):
|
||||
def setUp(self):
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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):
|
||||
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 sys.platform != 'win32' or not self.has_file_attributes:
|
||||
# st_file_attributes is Win32 specific (but can't use
|
||||
# unittest.skipUnless on Python 2.6)
|
||||
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):
|
||||
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 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):
|
||||
# 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):
|
||||
# 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):
|
||||
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)
|
213
third_party/python/scandir/test/test_walk.py
vendored
213
third_party/python/scandir/test/test_walk.py
vendored
@ -1,213 +0,0 @@
|
||||
"""Tests for scandir.walk(), copied from CPython's tests for os.walk()."""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
if sys.version_info[:2] < (2, 7):
|
||||
import unittest2 as unittest
|
||||
else:
|
||||
import unittest
|
||||
|
||||
import scandir
|
||||
|
||||
walk_func = scandir.walk
|
||||
|
||||
|
||||
class TestWalk(unittest.TestCase):
|
||||
testfn = os.path.join(os.path.dirname(__file__), 'temp')
|
||||
|
||||
def test_traversal(self):
|
||||
# Build:
|
||||
# TESTFN/
|
||||
# TEST1/ a file kid and two directory kids
|
||||
# tmp1
|
||||
# SUB1/ a file kid and a directory kid
|
||||
# tmp2
|
||||
# SUB11/ no kids
|
||||
# SUB2/ a file kid and a dirsymlink kid
|
||||
# tmp3
|
||||
# link/ a symlink to TESTFN.2
|
||||
# TEST2/
|
||||
# tmp4 a lone file
|
||||
walk_path = os.path.join(self.testfn, "TEST1")
|
||||
sub1_path = os.path.join(walk_path, "SUB1")
|
||||
sub11_path = os.path.join(sub1_path, "SUB11")
|
||||
sub2_path = os.path.join(walk_path, "SUB2")
|
||||
tmp1_path = os.path.join(walk_path, "tmp1")
|
||||
tmp2_path = os.path.join(sub1_path, "tmp2")
|
||||
tmp3_path = os.path.join(sub2_path, "tmp3")
|
||||
link_path = os.path.join(sub2_path, "link")
|
||||
t2_path = os.path.join(self.testfn, "TEST2")
|
||||
tmp4_path = os.path.join(self.testfn, "TEST2", "tmp4")
|
||||
|
||||
# Create stuff.
|
||||
os.makedirs(sub11_path)
|
||||
os.makedirs(sub2_path)
|
||||
os.makedirs(t2_path)
|
||||
for path in tmp1_path, tmp2_path, tmp3_path, tmp4_path:
|
||||
f = open(path, "w")
|
||||
f.write("I'm " + path + " and proud of it. Blame test_os.\n")
|
||||
f.close()
|
||||
has_symlink = hasattr(os, "symlink")
|
||||
if has_symlink:
|
||||
try:
|
||||
if sys.platform == 'win32' and sys.version_info >= (3, 2):
|
||||
# "target_is_directory" was only added in Python 3.2 (on Windows)
|
||||
os.symlink(os.path.abspath(t2_path), link_path, target_is_directory=True)
|
||||
else:
|
||||
os.symlink(os.path.abspath(t2_path), link_path)
|
||||
sub2_tree = (sub2_path, ["link"], ["tmp3"])
|
||||
except NotImplementedError:
|
||||
sub2_tree = (sub2_path, [], ["tmp3"])
|
||||
else:
|
||||
sub2_tree = (sub2_path, [], ["tmp3"])
|
||||
|
||||
# Walk top-down.
|
||||
all = list(walk_func(walk_path))
|
||||
self.assertEqual(len(all), 4)
|
||||
# We can't know which order SUB1 and SUB2 will appear in.
|
||||
# Not flipped: TESTFN, SUB1, SUB11, SUB2
|
||||
# flipped: TESTFN, SUB2, SUB1, SUB11
|
||||
flipped = all[0][1][0] != "SUB1"
|
||||
all[0][1].sort()
|
||||
self.assertEqual(all[0], (walk_path, ["SUB1", "SUB2"], ["tmp1"]))
|
||||
self.assertEqual(all[1 + flipped], (sub1_path, ["SUB11"], ["tmp2"]))
|
||||
self.assertEqual(all[2 + flipped], (sub11_path, [], []))
|
||||
self.assertEqual(all[3 - 2 * flipped], sub2_tree)
|
||||
|
||||
# Prune the search.
|
||||
all = []
|
||||
for root, dirs, files in walk_func(walk_path):
|
||||
all.append((root, dirs, files))
|
||||
# Don't descend into SUB1.
|
||||
if 'SUB1' in dirs:
|
||||
# Note that this also mutates the dirs we appended to all!
|
||||
dirs.remove('SUB1')
|
||||
self.assertEqual(len(all), 2)
|
||||
self.assertEqual(all[0], (walk_path, ["SUB2"], ["tmp1"]))
|
||||
self.assertEqual(all[1], sub2_tree)
|
||||
|
||||
# Walk bottom-up.
|
||||
all = list(walk_func(walk_path, topdown=False))
|
||||
self.assertEqual(len(all), 4)
|
||||
# We can't know which order SUB1 and SUB2 will appear in.
|
||||
# Not flipped: SUB11, SUB1, SUB2, TESTFN
|
||||
# flipped: SUB2, SUB11, SUB1, TESTFN
|
||||
flipped = all[3][1][0] != "SUB1"
|
||||
all[3][1].sort()
|
||||
self.assertEqual(all[3], (walk_path, ["SUB1", "SUB2"], ["tmp1"]))
|
||||
self.assertEqual(all[flipped], (sub11_path, [], []))
|
||||
self.assertEqual(all[flipped + 1], (sub1_path, ["SUB11"], ["tmp2"]))
|
||||
self.assertEqual(all[2 - 2 * flipped], sub2_tree)
|
||||
|
||||
if has_symlink:
|
||||
# Walk, following symlinks.
|
||||
for root, dirs, files in walk_func(walk_path, followlinks=True):
|
||||
if root == link_path:
|
||||
self.assertEqual(dirs, [])
|
||||
self.assertEqual(files, ["tmp4"])
|
||||
break
|
||||
else:
|
||||
self.fail("Didn't follow symlink with followlinks=True")
|
||||
|
||||
# Test creating a directory and adding it to dirnames
|
||||
sub3_path = os.path.join(walk_path, "SUB3")
|
||||
all = []
|
||||
for root, dirs, files in walk_func(walk_path):
|
||||
all.append((root, dirs, files))
|
||||
if 'SUB1' in dirs:
|
||||
os.makedirs(sub3_path)
|
||||
dirs.append('SUB3')
|
||||
all.sort()
|
||||
self.assertEqual(os.path.split(all[-1][0])[1], 'SUB3')
|
||||
|
||||
def tearDown(self):
|
||||
# Tear everything down. This is a decent use for bottom-up on
|
||||
# Windows, which doesn't have a recursive delete command. The
|
||||
# (not so) subtlety is that rmdir will fail unless the dir's
|
||||
# kids are removed first, so bottom up is essential.
|
||||
for root, dirs, files in os.walk(self.testfn, topdown=False):
|
||||
for name in files:
|
||||
os.remove(os.path.join(root, name))
|
||||
for name in dirs:
|
||||
dirname = os.path.join(root, name)
|
||||
if not os.path.islink(dirname):
|
||||
os.rmdir(dirname)
|
||||
else:
|
||||
os.remove(dirname)
|
||||
os.rmdir(self.testfn)
|
||||
|
||||
|
||||
class TestWalkSymlink(unittest.TestCase):
|
||||
temp_dir = os.path.join(os.path.dirname(__file__), 'temp')
|
||||
|
||||
def setUp(self):
|
||||
os.mkdir(self.temp_dir)
|
||||
self.dir_name = os.path.join(self.temp_dir, 'dir')
|
||||
os.mkdir(self.dir_name)
|
||||
open(os.path.join(self.dir_name, 'subfile'), 'w').close()
|
||||
self.file_name = os.path.join(self.temp_dir, 'file')
|
||||
open(self.file_name, 'w').close()
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.temp_dir)
|
||||
|
||||
def test_symlink_to_file(self):
|
||||
if not hasattr(os, 'symlink'):
|
||||
return
|
||||
|
||||
try:
|
||||
os.symlink(self.file_name, os.path.join(self.temp_dir,
|
||||
'link_to_file'))
|
||||
except NotImplementedError:
|
||||
# Windows versions before Vista don't support symbolic links
|
||||
return
|
||||
|
||||
output = sorted(walk_func(self.temp_dir))
|
||||
dirs = sorted(output[0][1])
|
||||
files = sorted(output[0][2])
|
||||
self.assertEqual(dirs, ['dir'])
|
||||
self.assertEqual(files, ['file', 'link_to_file'])
|
||||
|
||||
self.assertEqual(len(output), 2)
|
||||
self.assertEqual(output[1][1], [])
|
||||
self.assertEqual(output[1][2], ['subfile'])
|
||||
|
||||
def test_symlink_to_directory(self):
|
||||
if not hasattr(os, 'symlink'):
|
||||
return
|
||||
|
||||
link_name = os.path.join(self.temp_dir, 'link_to_dir')
|
||||
try:
|
||||
if sys.platform == 'win32' and sys.version_info >= (3, 2):
|
||||
# "target_is_directory" was only added in Python 3.2 (on Windows)
|
||||
os.symlink(self.dir_name, link_name, target_is_directory=True)
|
||||
else:
|
||||
os.symlink(self.dir_name, link_name)
|
||||
except NotImplementedError:
|
||||
# Windows versions before Vista don't support symbolic links
|
||||
return
|
||||
|
||||
output = sorted(walk_func(self.temp_dir))
|
||||
dirs = sorted(output[0][1])
|
||||
files = sorted(output[0][2])
|
||||
self.assertEqual(dirs, ['dir', 'link_to_dir'])
|
||||
self.assertEqual(files, ['file'])
|
||||
|
||||
self.assertEqual(len(output), 2)
|
||||
self.assertEqual(output[1][1], [])
|
||||
self.assertEqual(output[1][2], ['subfile'])
|
||||
|
||||
output = sorted(walk_func(self.temp_dir, followlinks=True))
|
||||
dirs = sorted(output[0][1])
|
||||
files = sorted(output[0][2])
|
||||
self.assertEqual(dirs, ['dir', 'link_to_dir'])
|
||||
self.assertEqual(files, ['file'])
|
||||
|
||||
self.assertEqual(len(output), 3)
|
||||
self.assertEqual(output[1][1], [])
|
||||
self.assertEqual(output[1][2], ['subfile'])
|
||||
self.assertEqual(os.path.basename(output[2][0]), 'link_to_dir')
|
||||
self.assertEqual(output[2][1], [])
|
||||
self.assertEqual(output[2][2], ['subfile'])
|
53
third_party/python/scandir/winreparse.h
vendored
53
third_party/python/scandir/winreparse.h
vendored
@ -1,53 +0,0 @@
|
||||
#ifndef Py_WINREPARSE_H
|
||||
#define Py_WINREPARSE_H
|
||||
|
||||
#ifdef MS_WINDOWS
|
||||
#include <Windows.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The following structure was copied from
|
||||
http://msdn.microsoft.com/en-us/library/ff552012.aspx as the required
|
||||
include doesn't seem to be present in the Windows SDK (at least as included
|
||||
with Visual Studio Express). */
|
||||
typedef struct _REPARSE_DATA_BUFFER {
|
||||
ULONG ReparseTag;
|
||||
USHORT ReparseDataLength;
|
||||
USHORT Reserved;
|
||||
union {
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
ULONG Flags;
|
||||
WCHAR PathBuffer[1];
|
||||
} SymbolicLinkReparseBuffer;
|
||||
|
||||
struct {
|
||||
USHORT SubstituteNameOffset;
|
||||
USHORT SubstituteNameLength;
|
||||
USHORT PrintNameOffset;
|
||||
USHORT PrintNameLength;
|
||||
WCHAR PathBuffer[1];
|
||||
} MountPointReparseBuffer;
|
||||
|
||||
struct {
|
||||
UCHAR DataBuffer[1];
|
||||
} GenericReparseBuffer;
|
||||
};
|
||||
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
||||
|
||||
#define REPARSE_DATA_BUFFER_HEADER_SIZE FIELD_OFFSET(REPARSE_DATA_BUFFER,\
|
||||
GenericReparseBuffer)
|
||||
#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE ( 16 * 1024 )
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* MS_WINDOWS */
|
||||
|
||||
#endif /* !Py_WINREPARSE_H */
|
@ -18,16 +18,8 @@ from mach.decorators import (
|
||||
Command,
|
||||
)
|
||||
|
||||
try:
|
||||
from os import scandir
|
||||
except ImportError:
|
||||
from scandir import scandir
|
||||
|
||||
|
||||
here = os.path.abspath(os.path.dirname(__file__))
|
||||
GLOBAL_EXCLUDES = [
|
||||
'tools/lint/test/files',
|
||||
]
|
||||
|
||||
|
||||
def setup_argument_parser():
|
||||
@ -35,13 +27,6 @@ def setup_argument_parser():
|
||||
return cli.MozlintParser()
|
||||
|
||||
|
||||
def get_global_excludes(topsrcdir):
|
||||
excludes = GLOBAL_EXCLUDES[:]
|
||||
excludes.extend([e.name for e in scandir(topsrcdir)
|
||||
if e.name.startswith('obj') and e.is_dir()])
|
||||
return excludes
|
||||
|
||||
|
||||
@CommandProvider
|
||||
class MachCommands(MachCommandBase):
|
||||
|
||||
@ -51,12 +36,11 @@ class MachCommands(MachCommandBase):
|
||||
parser=setup_argument_parser)
|
||||
def lint(self, *runargs, **lintargs):
|
||||
"""Run linters."""
|
||||
self._activate_virtualenv()
|
||||
from mozlint import cli
|
||||
|
||||
lintargs.setdefault('root', self.topsrcdir)
|
||||
lintargs['exclude'] = get_global_excludes(lintargs['root'])
|
||||
lintargs['exclude'] = ['obj*', 'tools/lint/test/files']
|
||||
cli.SEARCH_PATHS.append(here)
|
||||
self._activate_virtualenv()
|
||||
return cli.run(*runargs, **lintargs)
|
||||
|
||||
@Command('eslint', category='devenv',
|
||||
|
Loading…
Reference in New Issue
Block a user