summaryrefslogtreecommitdiff
path: root/pkg_resources
diff options
context:
space:
mode:
authorJulien Danjou <julien@danjou.info>2017-08-25 11:32:36 +0200
committerJulien Danjou <julien@danjou.info>2017-08-28 11:39:13 +0200
commitdf23966ac5bae1028a37af53c644b9e0839ac93b (patch)
treeac08f04537db73789aba80a845dfc9e5faf541b3 /pkg_resources
parent60cfeeacd34da6eb3881808f556ae4b194b60c73 (diff)
downloadpython-setuptools-git-df23966ac5bae1028a37af53c644b9e0839ac93b.tar.gz
pkg_resources: do not call stat() and access()
The current code in find_on_path is doing a lot of stat() calls which are actually useless and prone to race conditions. As described in Python documentation (https://docs.python.org/3/library/os.html#os.access), os.access must not be used before opening a file. Same goes for a directory. This patch removes those checks by handling exceptions correctly when using os.listdir() instead, which improves pkg_resources import time.
Diffstat (limited to 'pkg_resources')
-rw-r--r--pkg_resources/__init__.py95
1 files changed, 56 insertions, 39 deletions
diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py
index f13a69b3..277ee938 100644
--- a/pkg_resources/__init__.py
+++ b/pkg_resources/__init__.py
@@ -34,6 +34,7 @@ import platform
import collections
import plistlib
import email.parser
+import errno
import tempfile
import textwrap
import itertools
@@ -80,6 +81,11 @@ __import__('pkg_resources.extern.packaging.markers')
if (3, 0) < sys.version_info < (3, 3):
raise RuntimeError("Python 3.3 or later is required")
+if six.PY2:
+ # Those builtin exceptions are only defined in Python 3
+ PermissionError = None
+ NotADirectoryError = None
+
# declare some globals that will be defined later to
# satisfy the linters.
require = None
@@ -2008,46 +2014,57 @@ def find_on_path(importer, path_item, only=False):
"""Yield distributions accessible on a sys.path directory"""
path_item = _normalize_cached(path_item)
- if os.path.isdir(path_item) and os.access(path_item, os.R_OK):
- if _is_unpacked_egg(path_item):
- yield Distribution.from_filename(
- path_item, metadata=PathMetadata(
- path_item, os.path.join(path_item, 'EGG-INFO')
- )
+ if _is_unpacked_egg(path_item):
+ yield Distribution.from_filename(
+ path_item, metadata=PathMetadata(
+ path_item, os.path.join(path_item, 'EGG-INFO')
)
- else:
- # scan for .egg and .egg-info in directory
- path_item_entries = _by_version_descending(os.listdir(path_item))
- for entry in path_item_entries:
- lower = entry.lower()
- if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
- fullpath = os.path.join(path_item, entry)
- if os.path.isdir(fullpath):
- # egg-info directory, allow getting metadata
- if len(os.listdir(fullpath)) == 0:
- # Empty egg directory, skip.
- continue
- metadata = PathMetadata(path_item, fullpath)
- else:
- metadata = FileMetadata(fullpath)
- yield Distribution.from_location(
- path_item, entry, metadata, precedence=DEVELOP_DIST
- )
- elif not only and _is_egg_path(entry):
- dists = find_distributions(os.path.join(path_item, entry))
- for dist in dists:
- yield dist
- elif not only and lower.endswith('.egg-link'):
- with open(os.path.join(path_item, entry)) as entry_file:
- entry_lines = entry_file.readlines()
- for line in entry_lines:
- if not line.strip():
- continue
- path = os.path.join(path_item, line.rstrip())
- dists = find_distributions(path)
- for item in dists:
- yield item
- break
+ )
+ else:
+ try:
+ entries = os.listdir(path_item)
+ except (PermissionError, NotADirectoryError):
+ return
+ except OSError as e:
+ # Ignore the directory if does not exist, not a directory or we
+ # don't have permissions
+ if (e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)
+ # Python 2 on Windows needs to be handled this way :(
+ or hasattr(e, "winerror") and e.winerror == 267):
+ return
+ raise
+ # scan for .egg and .egg-info in directory
+ path_item_entries = _by_version_descending(entries)
+ for entry in path_item_entries:
+ lower = entry.lower()
+ if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
+ fullpath = os.path.join(path_item, entry)
+ if os.path.isdir(fullpath):
+ # egg-info directory, allow getting metadata
+ if len(os.listdir(fullpath)) == 0:
+ # Empty egg directory, skip.
+ continue
+ metadata = PathMetadata(path_item, fullpath)
+ else:
+ metadata = FileMetadata(fullpath)
+ yield Distribution.from_location(
+ path_item, entry, metadata, precedence=DEVELOP_DIST
+ )
+ elif not only and _is_egg_path(entry):
+ dists = find_distributions(os.path.join(path_item, entry))
+ for dist in dists:
+ yield dist
+ elif not only and lower.endswith('.egg-link'):
+ with open(os.path.join(path_item, entry)) as entry_file:
+ entry_lines = entry_file.readlines()
+ for line in entry_lines:
+ if not line.strip():
+ continue
+ path = os.path.join(path_item, line.rstrip())
+ dists = find_distributions(path)
+ for item in dists:
+ yield item
+ break
register_finder(pkgutil.ImpImporter, find_on_path)