+v57.5.0
+-------
+
+
+Changes
+^^^^^^^
+* #2712: Added implicit globbing support for `[options.data_files]` values.
+
+Documentation changes
+^^^^^^^^^^^^^^^^^^^^^
+* #2737: fix various syntax and style errors in code snippets in docs
+
+
v57.4.0
-------
Metadata-Version: 2.1
Name: setuptools
-Version: 57.4.0
+Version: 57.5.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Home-page: https://github.com/pypa/setuptools
Author: Python Packaging Authority
.. note::
:pep:`517` doesn't support editable installs so this is currently
- incompatible with ``pip install -e .``, as :pep:`517` does not support editable installs.
+ incompatible with ``pip install -e .``.
This means that you can have a Python project with all build configuration
specified in ``setup.cfg``, without a ``setup.py`` file, if you **can rely
site.d/00_default.conf
host.d/00_default.conf
data = data/img/logo.png, data/svg/icon.svg
+ fonts = data/fonts/*.ttf, data/fonts/*.otf
Metadata and options are set in the config sections of the same name.
.. code-block:: python
setup(
- #...,
- install_requires = [
+ ...,
+ install_requires=[
'docutils',
- 'BazSpam ==1.1'
- ]
+ 'BazSpam ==1.1',
+ ],
)
.. code-block:: python
setup(
- #...
+ ...,
install_requires=[
- "enum34;python_version<'3.4'",]
+ "enum34;python_version<'3.4'",
+ ],
)
Similarly, if you also wish to declare ``pywin32`` with a minimal version of 1.0
.. code-block:: python
setup(
- #...
+ ...,
install_requires=[
"enum34;python_version<'3.4'",
- "pywin32 >= 1.0;platform_system=='Windows'"
- ]
+ "pywin32 >= 1.0;platform_system=='Windows'",
+ ],
)
The environmental markers that may be used for testing platform types are
.. code-block:: python
setup(
- #...
+ ...,
dependency_links=[
- "http://peak.telecommunity.com/snapshots/"
+ "http://peak.telecommunity.com/snapshots/",
],
)
setup(
name="Project-A",
- #...
+ ...,
extras_require={
- "PDF": ["ReportLab>=1.2", "RXP"],
- }
+ "PDF": ["ReportLab>=1.2", "RXP"],
+ },
)
The name ``PDF`` is an arbitrary identifier of such a list of dependencies, to
.. code-block:: python
setup(
- name = "Project-A"
- #...,
+ name="Project-A",
+ ...,
entry_points={
"console_scripts": [
"rst2pdf = project_a.tools.pdfgen [PDF]",
"rst2html = project_a.tools.htmlgen",
],
- }
+ },
)
This syntax indicates that the entry point (in this case a console script)
setup(
name="Project-B",
install_requires=["Project-A[PDF]"],
- ...
+ ...,
)
This will cause ReportLab to be installed along with project A, if project B is
.. code-block:: python
from . import hello_world
+
if __name__ == '__main__':
hello_world()
For example, to find the console script entry points from the example above:
-.. code-block:: python
+.. code-block:: pycon
>>> from importlib import metadata
>>> eps = metadata.entry_points()['console_scripts']
Then, a different project wishing to load 'my.plugins' plugins could run
the following routine to load (and invoke) such plugins:
-.. code-block:: python
+.. code-block:: pycon
>>> from importlib import metadata
>>> eps = metadata.entry_points()['my.plugins']
>>> for ep in eps:
... plugin = ep.load()
... plugin()
+ ...
The project soliciting the entry points needs not to have any dependency
or prior knowledge about the libraries implementing the entry points, and
.. code-block:: python
def find_files_for_foobar(dirname):
- # loop to yield paths that start with `dirname`
+ ... # loop to yield paths that start with `dirname`
And you would register it in a setup script using something like this::
.. code-block:: python
setup(
- #...
- packages = ['mypkg1', 'mypkg2']
+ # ...
+ packages=['mypkg1', 'mypkg2']
)
This can get tiresome reallly quickly. To speed things up, we introduce two
.. code-block:: python
from setuptools import find_packages
- #or
+
+ # or
from setuptools import find_namespace_packages
.. code-block:: python
setup(
- #...
- packages = find_packages(
- where = 'src',
- include = ['pkg*',],
- exclude = ['additional',]
+ # ...
+ packages=find_packages(
+ where='src',
+ include=['pkg*'],
+ exclude=['additional'],
),
- package_dir = {"":"src"}
- #...
+ package_dir={"": "src"}
+ # ...
)
namespace package called ``timmins`` will be created automatically for you when
you invoke the import mechanism, allowing you to accomplish the following
-.. code-block:: python
+.. code-block:: pycon
>>> import timmins.foo
>>> import timmins.bar
setup(
# ...
- namespace_packages = ['timmins']
+ namespace_packages=['timmins']
)
And your directory should look like this
[metadata]
-license_files =
- LICENSE
name = setuptools
-version = 57.4.0
+version = 57.5.0
author = Python Packaging Authority
author_email = distutils-sig@python.org
description = Easily download, build, install, upgrade, and uninstall Python packages
pytest >= 4.6
pytest-checkdocs >= 2.4
pytest-flake8
- pytest-black >= 0.3.7; python_implementation != "PyPy" and python_version < "3.10"
+ pytest-black >= 0.3.7; \
+ python_implementation != "PyPy"
pytest-cov
- pytest-mypy; python_implementation != "PyPy" and python_version < "3.10"
+ pytest-mypy; \
+ python_implementation != "PyPy"
pytest-enabler >= 1.0.1
mock
Metadata-Version: 2.1
Name: setuptools
-Version: 57.4.0
+Version: 57.5.0
Summary: Easily download, build, install, upgrade, and uninstall Python packages
Home-page: https://github.com/pypa/setuptools
Author: Python Packaging Authority
sphinx
jaraco.path>=3.2.0
-[testing:platform_python_implementation != "PyPy" and python_version < "3.10"]
+[testing:platform_python_implementation != "PyPy"]
pytest-black>=0.3.7
pytest-mypy
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
-__ https://setuptools.readthedocs.io/en/latest/easy_install.html
+__ https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
"""
For information on other options, you may wish to consult the
documentation at:
- https://setuptools.readthedocs.io/en/latest/easy_install.html
+ https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html
Please make the appropriate changes for your system and try again.
""").lstrip() # noqa
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
- https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
+ https://setuptools.readthedocs.io/en/latest/deprecated/easy_install.html#custom-installation-locations
Please make the appropriate changes for your system and try again.
from collections import defaultdict
from functools import partial
from functools import wraps
+from glob import iglob
import contextlib
from distutils.errors import DistutilsOptionError, DistutilsFileError
return [chunk.strip() for chunk in value if chunk.strip()]
@classmethod
+ def _parse_list_glob(cls, value, separator=','):
+ """Equivalent to _parse_list() but expands any glob patterns using glob().
+
+ However, unlike with glob() calls, the results remain relative paths.
+
+ :param value:
+ :param separator: List items separator character.
+ :rtype: list
+ """
+ glob_characters = ('*', '?', '[', ']', '{', '}')
+ values = cls._parse_list(value, separator=separator)
+ expanded_values = []
+ for value in values:
+
+ # Has globby characters?
+ if any(char in value for char in glob_characters):
+ # then expand the glob pattern while keeping paths *relative*:
+ expanded_values.extend(sorted(
+ os.path.relpath(path, os.getcwd())
+ for path in iglob(os.path.abspath(value))))
+
+ else:
+ # take the value as-is:
+ expanded_values.append(value)
+
+ return expanded_values
+
+ @classmethod
def _parse_dict(cls, value):
"""Represents value as a dict.
:param dict section_options:
"""
- parsed = self._parse_section_to_dict(section_options, self._parse_list)
+ parsed = self._parse_section_to_dict(section_options, self._parse_list_glob)
self['data_files'] = [(k, v) for k, v in parsed.items()]
]
assert sorted(dist.data_files) == sorted(expected)
+ def test_data_files_globby(self, tmpdir):
+ fake_env(
+ tmpdir,
+ '[options.data_files]\n'
+ 'cfg =\n'
+ ' a/b.conf\n'
+ ' c/d.conf\n'
+ 'data = *.dat\n'
+ 'icons = \n'
+ ' *.ico\n'
+ 'audio = \n'
+ ' *.wav\n'
+ ' sounds.db\n'
+ )
+
+ # Create dummy files for glob()'s sake:
+ tmpdir.join('a.dat').write('')
+ tmpdir.join('b.dat').write('')
+ tmpdir.join('c.dat').write('')
+ tmpdir.join('a.ico').write('')
+ tmpdir.join('b.ico').write('')
+ tmpdir.join('c.ico').write('')
+ tmpdir.join('beep.wav').write('')
+ tmpdir.join('boop.wav').write('')
+ tmpdir.join('sounds.db').write('')
+
+ with get_dist(tmpdir) as dist:
+ expected = [
+ ('cfg', ['a/b.conf', 'c/d.conf']),
+ ('data', ['a.dat', 'b.dat', 'c.dat']),
+ ('icons', ['a.ico', 'b.ico', 'c.ico']),
+ ('audio', ['beep.wav', 'boop.wav', 'sounds.db']),
+ ]
+ assert sorted(dist.data_files) == sorted(expected)
+
def test_python_requires_simple(self, tmpdir):
fake_env(
tmpdir,
[testenv]
deps =
+ # workaround for sphinx-doc/sphinx#9562
+ # TODO: remove after Sphinx>4.1.2 is available.
+ sphinx@git+https://github.com/sphinx-doc/sphinx; python_version>="3.10"
commands =
pytest {posargs}
usedevelop = True