- python: *latest_py2
env: LANG=C
- stage: deploy (to PyPI for tagged commits)
+ if: tag IS present
python: *latest_py3
install: skip
script: skip
+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
-------
+.. 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
<https://packaging.python.org/installing/>`_ in the Python Packaging
User's Guide for instructions on installing, upgrading, and uninstalling
.. 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.
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
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.
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
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
.. 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
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.
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.
--------------------------------
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)
"""
Determine if given path appears to be an egg.
"""
- return (
- path.lower().endswith('.egg')
- )
+ return path.lower().endswith('.egg')
def _is_unpacked_egg(path):
[bumpversion]
-current_version = 36.4.0
+current_version = 36.5.0
commit = True
tag = True
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",
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,
))
import glob
import os
+import sys
from pytest import yield_fixture
from pytest_fixture_config import yield_requires_config
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}',