From b0fba9e4dfa5ca73262e05e9f0cda3e0f06860dc Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Mon, 14 Jan 2019 10:34:58 +0900 Subject: [PATCH] Imported Upstream version 36.5.0 --- .travis.yml | 1 + CHANGES.rst | 10 ++ README.rst | 11 ++ docs/setuptools.txt | 92 ++++++++-------- pkg_resources/__init__.py | 158 +++++++++++++++++++--------- setup.cfg | 2 +- setup.py | 2 +- setuptools/package_index.py | 2 +- setuptools/tests/test_virtualenv.py | 4 + 9 files changed, 186 insertions(+), 96 deletions(-) diff --git a/.travis.yml b/.travis.yml index a5e08a2..60f5815 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,6 +18,7 @@ jobs: - python: *latest_py2 env: LANG=C - stage: deploy (to PyPI for tagged commits) + if: tag IS present python: *latest_py3 install: skip script: skip diff --git a/CHANGES.rst b/CHANGES.rst index 8130511..8217aab 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,13 @@ +v36.5.0 +------- + +* #170: When working with Mercurial checkouts, use Windows-friendly + syntax for suppressing output. + +* Inspired by #1134, performed substantial refactoring of + ``pkg_resources.find_on_path`` to facilitate an optimization + for paths with many non-version entries. + v36.4.0 ------- diff --git a/README.rst b/README.rst index 2f55bce..1c6ee60 100755 --- a/README.rst +++ b/README.rst @@ -1,6 +1,17 @@ +.. image:: https://img.shields.io/pypi/v/setuptools.svg + :target: https://pypi.org/project/setuptools + .. image:: https://readthedocs.org/projects/setuptools/badge/?version=latest :target: https://setuptools.readthedocs.io +.. image:: https://img.shields.io/travis/pypa/setuptools/master.svg?label=Linux%20build%20%40%20Travis%20CI + :target: http://travis-ci.org/pypa/setuptools + +.. image:: https://img.shields.io/appveyor/ci/jaraco/setuptools/master.svg?label=Windows%20build%20%40%20Appveyor + :target: https://ci.appveyor.com/project/jaraco/setuptools/branch/master + +.. image:: https://img.shields.io/pypi/pyversions/setuptools.svg + See the `Installation Instructions `_ in the Python Packaging User's Guide for instructions on installing, upgrading, and uninstalling diff --git a/docs/setuptools.txt b/docs/setuptools.txt index 531d727..f255cfd 100644 --- a/docs/setuptools.txt +++ b/docs/setuptools.txt @@ -2281,21 +2281,23 @@ Configuring setup() using setup.cfg files .. note:: New in 30.3.0 (8 Dec 2016). -.. important:: ``setup.py`` with ``setup()`` function call is still required even - if your configuration resides in ``setup.cfg``. +.. important:: + A ``setup.py`` file containing a ``setup()`` function call is still + required even if your configuration resides in ``setup.cfg``. -``Setuptools`` allows using configuration files (usually `setup.cfg`) -to define package’s metadata and other options which are normally supplied -to ``setup()`` function. +``Setuptools`` allows using configuration files (usually :file:`setup.cfg`) +to define a package’s metadata and other options that are normally supplied +to the ``setup()`` function. -This approach not only allows automation scenarios, but also reduces +This approach not only allows automation scenarios but also reduces boilerplate code in some cases. .. note:: - Implementation presents limited compatibility with distutils2-like - ``setup.cfg`` sections (used by ``pbr`` and ``d2to1`` packages). - Namely: only metadata related keys from ``metadata`` section are supported + This implementation has limited compatibility with the distutils2-like + ``setup.cfg`` sections used by the ``pbr`` and ``d2to1`` packages. + + Namely: only metadata-related keys from ``metadata`` section are supported (except for ``description-file``); keys from ``files``, ``entry_points`` and ``backwards_compat`` are not supported. @@ -2336,12 +2338,13 @@ boilerplate code in some cases. src.subpackage2 -Metadata and options could be set in sections with the same names. +Metadata and options are set in the config sections of the same name. -* Keys are the same as keyword arguments one provides to ``setup()`` function. +* Keys are the same as the keyword arguments one provides to the ``setup()`` + function. -* Complex values could be placed comma-separated or one per line - in *dangling* sections. The following are the same: +* Complex values can be written comma-separated or placed one per line + in *dangling* config values. The following are equivalent: .. code-block:: ini @@ -2353,10 +2356,11 @@ Metadata and options could be set in sections with the same names. one two -* In some cases complex values could be provided in subsections for clarity. +* In some cases, complex values can be provided in dedicated subsections for + clarity. -* Some keys allow ``file:``, ``attr:`` and ``find:`` directives to cover - common usecases. +* Some keys allow ``file:``, ``attr:``, and ``find:`` directives in order to + cover common usecases. * Unknown keys are ignored. @@ -2369,33 +2373,34 @@ Some values are treated as simple strings, some allow more logic. Type names used below: * ``str`` - simple string -* ``list-comma`` - dangling list or comma-separated values string -* ``list-semi`` - dangling list or semicolon-separated values string -* ``bool`` - ``True`` is 1, yes, true -* ``dict`` - list-comma where keys from values are separated by = -* ``section`` - values could be read from a dedicated (sub)section +* ``list-comma`` - dangling list or string of comma-separated values +* ``list-semi`` - dangling list or string of semicolon-separated values +* ``bool`` - ``True`` is 1, yes, true +* ``dict`` - list-comma where keys are separated from values by ``=`` +* ``section`` - values are read from a dedicated (sub)section Special directives: -* ``attr:`` - value could be read from module attribute -* ``file:`` - value could be read from a list of files and then concatenated +* ``attr:`` - Value is read from a module attribute. ``attr:`` supports + callables and iterables; unsupported types are cast using ``str()``. +* ``file:`` - Value is read from a list of files and then concatenated .. note:: - ``file:`` directive is sandboxed and won't reach anything outside - directory with ``setup.py``. + The ``file:`` directive is sandboxed and won't reach anything outside + the directory containing ``setup.py``. Metadata -------- .. note:: - Aliases given below are supported for compatibility reasons, - but not advised. + The aliases given below are supported for compatibility reasons, + but their use is not advised. ============================== ================= ===== -Key Aliases Accepted value type +Key Aliases Type ============================== ================= ===== name str version attr:, str @@ -2417,17 +2422,12 @@ requires list-comma obsoletes list-comma ============================== ================= ===== -.. note:: - - **version** - ``attr:`` supports callables; supports iterables; - unsupported types are casted using ``str()``. - Options ------- ======================= ===== -Key Accepted value type +Key Type ======================= ===== zip_safe bool setup_requires list-semi @@ -2454,10 +2454,10 @@ py_modules list-comma .. note:: - **packages** - ``find:`` directive can be further configured - in a dedicated subsection `options.packages.find`. This subsection - accepts the same keys as `setuptools.find` function: - `where`, `include`, `exclude`. + **packages** - The ``find:`` directive can be further configured + in a dedicated subsection ``options.packages.find``. This subsection + accepts the same keys as the `setuptools.find` function: + ``where``, ``include``, and ``exclude``. Configuration API @@ -2465,7 +2465,7 @@ Configuration API Some automation tools may wish to access data from a configuration file. -``Setuptools`` exposes ``read_configuration()`` function allowing +``Setuptools`` exposes a ``read_configuration()`` function for parsing ``metadata`` and ``options`` sections into a dictionary. @@ -2476,16 +2476,16 @@ parsing ``metadata`` and ``options`` sections into a dictionary. conf_dict = read_configuration('/home/user/dev/package/setup.cfg') -By default ``read_configuration()`` will read only file provided +By default, ``read_configuration()`` will read only the file provided in the first argument. To include values from other configuration files -which could be in various places set `find_others` function argument +which could be in various places, set the ``find_others`` keyword argument to ``True``. -If you have only a configuration file but not the whole package you can still -try to get data out of it with the help of `ignore_option_errors` function -argument. When it is set to ``True`` all options with errors possibly produced -by directives, such as ``attr:`` and others will be silently ignored. -As a consequence the resulting dictionary will include no such options. +If you have only a configuration file but not the whole package, you can still +try to get data out of it with the help of the ``ignore_option_errors`` keyword +argument. When it is set to ``True``, all options with errors possibly produced +by directives, such as ``attr:`` and others, will be silently ignored. +As a consequence, the resulting dictionary will include no such options. -------------------------------- diff --git a/pkg_resources/__init__.py b/pkg_resources/__init__.py index ce6053f..68349df 100644 --- a/pkg_resources/__init__.py +++ b/pkg_resources/__init__.py @@ -2028,51 +2028,117 @@ def find_on_path(importer, path_item, only=False): path_item, os.path.join(path_item, 'EGG-INFO') ) ) - 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 + return + + entries = safe_listdir(path_item) + + # for performance, before sorting by version, + # screen entries for only those that will yield + # distributions + filtered = ( + entry + for entry in entries + if dist_factory(path_item, entry, only) + ) + + # scan for .egg and .egg-info in directory + path_item_entries = _by_version_descending(filtered) + for entry in path_item_entries: + fullpath = os.path.join(path_item, entry) + factory = dist_factory(path_item, entry, only) + for dist in factory(fullpath): + yield dist + + +def dist_factory(path_item, entry, only): + """ + Return a dist_factory for a path_item and entry + """ + lower = entry.lower() + is_meta = any(map(lower.endswith, ('.egg-info', '.dist-info'))) + return ( + distributions_from_metadata + if is_meta else + find_distributions + if not only and _is_egg_path(entry) else + resolve_egg_link + if not only and lower.endswith('.egg-link') else + NoDists() + ) + + +class NoDists: + """ + >>> bool(NoDists()) + False + + >>> list(NoDists()('anything')) + [] + """ + def __bool__(self): + return False + if six.PY2: + __nonzero__ = __bool__ + + def __call__(self, fullpath): + return iter(()) + + +def safe_listdir(path): + """ + Attempt to list contents of path, but suppress some exceptions. + """ + try: + return os.listdir(path) + except (PermissionError, NotADirectoryError): + pass + except OSError as e: + # Ignore the directory if does not exist, not a directory or + # permission denied + ignorable = ( + e.errno in (errno.ENOTDIR, errno.EACCES, errno.ENOENT) + # Python 2 on Windows needs to be handled this way :( + or getattr(e, "winerror", None) == 267 + ) + if not ignorable: 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 + return () + + +def distributions_from_metadata(path): + root = os.path.dirname(path) + if os.path.isdir(path): + if len(os.listdir(path)) == 0: + # empty metadata dir; skip + return + metadata = PathMetadata(root, path) + else: + metadata = FileMetadata(path) + entry = os.path.basename(path) + yield Distribution.from_location( + root, entry, metadata, precedence=DEVELOP_DIST, + ) + + +def non_empty_lines(path): + """ + Yield non-empty lines from file at path + """ + return (line.rstrip() for line in open(path) if line.strip()) + + +def resolve_egg_link(path): + """ + Given a path to an .egg-link, resolve distributions + present in the referenced path. + """ + referenced_paths = non_empty_lines(path) + resolved_paths = ( + os.path.join(os.path.dirname(path), ref) + for ref in referenced_paths + ) + dist_groups = map(find_distributions, resolved_paths) + return next(dist_groups, ()) register_finder(pkgutil.ImpImporter, find_on_path) @@ -2250,9 +2316,7 @@ def _is_egg_path(path): """ Determine if given path appears to be an egg. """ - return ( - path.lower().endswith('.egg') - ) + return path.lower().endswith('.egg') def _is_unpacked_egg(path): diff --git a/setup.cfg b/setup.cfg index ea64989..e5ebf4b 100755 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 36.4.0 +current_version = 36.5.0 commit = True tag = True diff --git a/setup.py b/setup.py index eb939ff..ee968d9 100755 --- a/setup.py +++ b/setup.py @@ -89,7 +89,7 @@ def pypi_link(pkg_filename): setup_params = dict( name="setuptools", - version="36.4.0", + version="36.5.0", description="Easily download, build, install, upgrade, and uninstall " "Python packages", author="Python Packaging Authority", diff --git a/setuptools/package_index.py b/setuptools/package_index.py index 2acc817..a6363b1 100755 --- a/setuptools/package_index.py +++ b/setuptools/package_index.py @@ -893,7 +893,7 @@ class PackageIndex(Environment): if rev is not None: self.info("Updating to %s", rev) - os.system("(cd %s && hg up -C -r %s >&-)" % ( + os.system("(cd %s && hg up -C -r %s -q)" % ( filename, rev, )) diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py index 17b8793..9dbd3c8 100644 --- a/setuptools/tests/test_virtualenv.py +++ b/setuptools/tests/test_virtualenv.py @@ -1,5 +1,6 @@ import glob import os +import sys from pytest import yield_fixture from pytest_fixture_config import yield_requires_config @@ -39,6 +40,9 @@ def test_pip_upgrade_from_source(virtualenv): Check pip can upgrade setuptools from source. """ dist_dir = virtualenv.workspace + if sys.version_info < (2, 7): + # Python 2.6 support was dropped in wheel 0.30.0. + virtualenv.run('pip install -U "wheel<0.30.0"') # Generate source distribution / wheel. virtualenv.run(' && '.join(( 'cd {source}', -- 2.34.1