Commit 6d8381aa authored by Jason R. Coombs's avatar Jason R. Coombs Committed by GitHub

Merge pull request #1135 from jd/less-stat

pkg_resources: do not call stat() and access()
parents 10bcb50a 679ead9c
...@@ -16,8 +16,11 @@ v36.4.0 ...@@ -16,8 +16,11 @@ v36.4.0
distributions (e.g. when using ``setup_requires`` with a conflicting distributions (e.g. when using ``setup_requires`` with a conflicting
transitive dependency, fix #1124). transitive dependency, fix #1124).
* #1133: Improved handling of README files extensions and added * #1133: Improved handling of README files extensions and added
Markdown to the list of searched READMES. Markdown to the list of searched READMES.
* #1135: Improve performance of pkg_resources import by not invoking
``access`` or ``stat`` and using ``os.listdir`` instead.
v36.3.0 v36.3.0
------- -------
......
...@@ -34,6 +34,7 @@ import platform ...@@ -34,6 +34,7 @@ import platform
import collections import collections
import plistlib import plistlib
import email.parser import email.parser
import errno
import tempfile import tempfile
import textwrap import textwrap
import itertools import itertools
...@@ -80,6 +81,11 @@ __import__('pkg_resources.extern.packaging.markers') ...@@ -80,6 +81,11 @@ __import__('pkg_resources.extern.packaging.markers')
if (3, 0) < sys.version_info < (3, 3): if (3, 0) < sys.version_info < (3, 3):
raise RuntimeError("Python 3.3 or later is required") 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 # declare some globals that will be defined later to
# satisfy the linters. # satisfy the linters.
require = None require = None
...@@ -2016,46 +2022,57 @@ def find_on_path(importer, path_item, only=False): ...@@ -2016,46 +2022,57 @@ def find_on_path(importer, path_item, only=False):
"""Yield distributions accessible on a sys.path directory""" """Yield distributions accessible on a sys.path directory"""
path_item = _normalize_cached(path_item) 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):
if _is_unpacked_egg(path_item): yield Distribution.from_filename(
yield Distribution.from_filename( path_item, metadata=PathMetadata(
path_item, metadata=PathMetadata( path_item, os.path.join(path_item, 'EGG-INFO')
path_item, os.path.join(path_item, 'EGG-INFO')
)
) )
else: )
# scan for .egg and .egg-info in directory else:
path_item_entries = _by_version_descending(os.listdir(path_item)) try:
for entry in path_item_entries: entries = os.listdir(path_item)
lower = entry.lower() except (PermissionError, NotADirectoryError):
if lower.endswith('.egg-info') or lower.endswith('.dist-info'): return
fullpath = os.path.join(path_item, entry) except OSError as e:
if os.path.isdir(fullpath): # Ignore the directory if does not exist, not a directory or we
# egg-info directory, allow getting metadata # don't have permissions
if len(os.listdir(fullpath)) == 0: if (e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT)
# Empty egg directory, skip. # Python 2 on Windows needs to be handled this way :(
continue or hasattr(e, "winerror") and e.winerror == 267):
metadata = PathMetadata(path_item, fullpath) return
else: raise
metadata = FileMetadata(fullpath) # scan for .egg and .egg-info in directory
yield Distribution.from_location( path_item_entries = _by_version_descending(entries)
path_item, entry, metadata, precedence=DEVELOP_DIST for entry in path_item_entries:
) lower = entry.lower()
elif not only and _is_egg_path(entry): if lower.endswith('.egg-info') or lower.endswith('.dist-info'):
dists = find_distributions(os.path.join(path_item, entry)) fullpath = os.path.join(path_item, entry)
for dist in dists: if os.path.isdir(fullpath):
yield dist # egg-info directory, allow getting metadata
elif not only and lower.endswith('.egg-link'): if len(os.listdir(fullpath)) == 0:
with open(os.path.join(path_item, entry)) as entry_file: # Empty egg directory, skip.
entry_lines = entry_file.readlines() continue
for line in entry_lines: metadata = PathMetadata(path_item, fullpath)
if not line.strip(): else:
continue metadata = FileMetadata(fullpath)
path = os.path.join(path_item, line.rstrip()) yield Distribution.from_location(
dists = find_distributions(path) path_item, entry, metadata, precedence=DEVELOP_DIST
for item in dists: )
yield item elif not only and _is_egg_path(entry):
break 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) register_finder(pkgutil.ImpImporter, find_on_path)
......
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