Commit df23966a authored by Julien Danjou's avatar Julien Danjou

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.
parent 60cfeeac
......@@ -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,7 +2014,6 @@ 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(
......@@ -2016,8 +2021,20 @@ def find_on_path(importer, path_item, only=False):
)
)
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(os.listdir(path_item))
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'):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment