444 lines
12 KiB
ReStructuredText
444 lines
12 KiB
ReStructuredText
=====================================
|
|
Dependencies Management in Setuptools
|
|
=====================================
|
|
|
|
There are three types of dependency styles offered by setuptools:
|
|
1) build system requirement, 2) required dependency and 3) optional
|
|
dependency.
|
|
|
|
.. Note::
|
|
Packages that are added to dependency can be optionally specified with the
|
|
version by following `PEP 440 <https://www.python.org/dev/peps/pep-0440/>`_
|
|
|
|
|
|
Build system requirement
|
|
========================
|
|
|
|
Package requirement
|
|
-------------------
|
|
After organizing all the scripts and files and getting ready for packaging,
|
|
there needs to be a way to tell Python what programs it needs to actually
|
|
do the packaging (in our case, ``setuptools`` of course). Usually,
|
|
you also need the ``wheel`` package as well since it is recommended that you
|
|
upload a ``.whl`` file to PyPI alongside your ``.tar.gz`` file. Unlike the
|
|
other two types of dependency keyword, this one is specified in your
|
|
``pyproject.toml`` file (if you have forgot what this is, go to
|
|
:doc:`quickstart` or (WIP)):
|
|
|
|
.. code-block:: ini
|
|
|
|
[build-system]
|
|
requires = ["setuptools"]
|
|
#...
|
|
|
|
.. note::
|
|
This used to be accomplished with the ``setup_requires`` keyword but is
|
|
now considered deprecated in favor of the PEP 517 style described above.
|
|
To peek into how this legacy keyword is used, consult our :doc:`guide on
|
|
deprecated practice (WIP) <../deprecated/index>`
|
|
|
|
|
|
.. _Declaring Dependencies:
|
|
|
|
Declaring required dependency
|
|
=============================
|
|
This is where a package declares its core dependencies, without which it won't
|
|
be able to run. ``setuptools`` support automatically download and install
|
|
these dependencies when the package is installed. Although there is more
|
|
finesse to it, let's start with a simple example.
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[options]
|
|
#...
|
|
install_requires =
|
|
docutils
|
|
BazSpam ==1.1
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
...,
|
|
install_requires=[
|
|
'docutils',
|
|
'BazSpam ==1.1',
|
|
],
|
|
)
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
[project]
|
|
# ...
|
|
dependencies = [
|
|
"docutils",
|
|
"BazSpam == 1.1",
|
|
]
|
|
# ...
|
|
|
|
|
|
When your project is installed (e.g. using pip), all of the dependencies not
|
|
already installed will be located (via PyPI), downloaded, built (if necessary),
|
|
and installed and 2) Any scripts in your project will be installed with wrappers
|
|
that verify the availability of the specified dependencies at runtime.
|
|
|
|
|
|
Platform specific dependencies
|
|
------------------------------
|
|
Setuptools offer the capability to evaluate certain conditions before blindly
|
|
installing everything listed in ``install_requires``. This is great for platform
|
|
specific dependencies. For example, the ``enum`` package was added in Python
|
|
3.4, therefore, package that depends on it can elect to install it only when
|
|
the Python version is older than 3.4. To accomplish this
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[options]
|
|
#...
|
|
install_requires =
|
|
enum34;python_version<'3.4'
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
...,
|
|
install_requires=[
|
|
"enum34;python_version<'3.4'",
|
|
],
|
|
)
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
[project]
|
|
# ...
|
|
dependencies = [
|
|
"enum34; python_version<'3.4'",
|
|
]
|
|
# ...
|
|
|
|
Similarly, if you also wish to declare ``pywin32`` with a minimal version of 1.0
|
|
and only install it if the user is using a Windows operating system:
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[options]
|
|
#...
|
|
install_requires =
|
|
enum34;python_version<'3.4'
|
|
pywin32 >= 1.0;platform_system=='Windows'
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
...,
|
|
install_requires=[
|
|
"enum34;python_version<'3.4'",
|
|
"pywin32 >= 1.0;platform_system=='Windows'",
|
|
],
|
|
)
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
[project]
|
|
# ...
|
|
dependencies = [
|
|
"enum34; python_version<'3.4'",
|
|
"pywin32 >= 1.0; platform_system=='Windows'",
|
|
]
|
|
# ...
|
|
|
|
The environmental markers that may be used for testing platform types are
|
|
detailed in `PEP 508 <https://www.python.org/dev/peps/pep-0508/>`_.
|
|
|
|
|
|
Dependencies that aren't in PyPI
|
|
--------------------------------
|
|
.. warning::
|
|
Dependency links support has been dropped by pip starting with version
|
|
19.0 (released 2019-01-22).
|
|
|
|
If your project depends on packages that don't exist on PyPI, you may still be
|
|
able to depend on them, as long as they are available for download as:
|
|
|
|
- an egg, in the standard distutils ``sdist`` format,
|
|
- a single ``.py`` file, or
|
|
- a VCS repository (Subversion, Mercurial, or Git).
|
|
|
|
You just need to add some URLs to the ``dependency_links`` argument to
|
|
``setup()``.
|
|
|
|
The URLs must be either:
|
|
|
|
1. direct download URLs,
|
|
2. the URLs of web pages that contain direct download links, or
|
|
3. the repository's URL
|
|
|
|
In general, it's better to link to web pages, because it is usually less
|
|
complex to update a web page than to release a new version of your project.
|
|
You can also use a SourceForge ``showfiles.php`` link in the case where a
|
|
package you depend on is distributed via SourceForge.
|
|
|
|
If you depend on a package that's distributed as a single ``.py`` file, you
|
|
must include an ``"#egg=project-version"`` suffix to the URL, to give a project
|
|
name and version number. (Be sure to escape any dashes in the name or version
|
|
by replacing them with underscores.) EasyInstall will recognize this suffix
|
|
and automatically create a trivial ``setup.py`` to wrap the single ``.py`` file
|
|
as an egg.
|
|
|
|
In the case of a VCS checkout, you should also append ``#egg=project-version``
|
|
in order to identify for what package that checkout should be used. You can
|
|
append ``@REV`` to the URL's path (before the fragment) to specify a revision.
|
|
Additionally, you can also force the VCS being used by prepending the URL with
|
|
a certain prefix. Currently available are:
|
|
|
|
- ``svn+URL`` for Subversion,
|
|
- ``git+URL`` for Git, and
|
|
- ``hg+URL`` for Mercurial
|
|
|
|
A more complete example would be:
|
|
|
|
``vcs+proto://host/path@revision#egg=project-version``
|
|
|
|
Be careful with the version. It should match the one inside the project files.
|
|
If you want to disregard the version, you have to omit it both in the
|
|
``requires`` and in the URL's fragment.
|
|
|
|
This will do a checkout (or a clone, in Git and Mercurial parlance) to a
|
|
temporary folder and run ``setup.py bdist_egg``.
|
|
|
|
The ``dependency_links`` option takes the form of a list of URL strings. For
|
|
example, this will cause a search of the specified page for eggs or source
|
|
distributions, if the package's dependencies aren't already installed:
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[options]
|
|
#...
|
|
dependency_links = http://peak.telecommunity.com/snapshots/
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
...,
|
|
dependency_links=[
|
|
"http://peak.telecommunity.com/snapshots/",
|
|
],
|
|
)
|
|
|
|
|
|
Optional dependencies
|
|
=====================
|
|
Setuptools allows you to declare dependencies that only get installed under
|
|
specific circumstances. These dependencies are specified with ``extras_require``
|
|
keyword and are only installed if another package depends on it (either
|
|
directly or indirectly) This makes it convenient to declare dependencies for
|
|
ancillary functions such as "tests" and "docs".
|
|
|
|
.. note::
|
|
``tests_require`` is now deprecated
|
|
|
|
For example, Package-A offers optional PDF support and requires two other
|
|
dependencies for it to work:
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[metadata]
|
|
name = Package-A
|
|
|
|
[options.extras_require]
|
|
PDF = ReportLab>=1.2; RXP
|
|
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
name="Project-A",
|
|
...,
|
|
extras_require={
|
|
"PDF": ["ReportLab>=1.2", "RXP"],
|
|
},
|
|
)
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
# ...
|
|
[project.optional-dependencies]
|
|
PDF = ["ReportLab>=1.2", "RXP"]
|
|
|
|
The name ``PDF`` is an arbitrary identifier of such a list of dependencies, to
|
|
which other components can refer and have them installed.
|
|
|
|
A use case for this approach is that other package can use this "extra" for their
|
|
own dependencies. For example, if "Project-B" needs "project A" with PDF support
|
|
installed, it might declare the dependency like this:
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[metadata]
|
|
name = Project-B
|
|
#...
|
|
|
|
[options]
|
|
#...
|
|
install_requires =
|
|
Project-A[PDF]
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
name="Project-B",
|
|
install_requires=["Project-A[PDF]"],
|
|
...,
|
|
)
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
[project]
|
|
name = "Project-B"
|
|
# ...
|
|
dependencies = [
|
|
"Project-A[PDF]"
|
|
]
|
|
|
|
This will cause ReportLab to be installed along with project A, if project B is
|
|
installed -- even if project A was already installed. In this way, a project
|
|
can encapsulate groups of optional "downstream dependencies" under a feature
|
|
name, so that packages that depend on it don't have to know what the downstream
|
|
dependencies are. If a later version of Project A builds in PDF support and
|
|
no longer needs ReportLab, or if it ends up needing other dependencies besides
|
|
ReportLab in order to provide PDF support, Project B's setup information does
|
|
not need to change, but the right packages will still be installed if needed.
|
|
|
|
.. note::
|
|
Best practice: if a project ends up no longer needing any other packages to
|
|
support a feature, it should keep an empty requirements list for that feature
|
|
in its ``extras_require`` argument, so that packages depending on that feature
|
|
don't break (due to an invalid feature name).
|
|
|
|
Historically ``setuptools`` also used to support extra dependencies in console
|
|
scripts, for example:
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[metadata]
|
|
name = Project A
|
|
#...
|
|
|
|
[options]
|
|
#...
|
|
entry_points=
|
|
[console_scripts]
|
|
rst2pdf = project_a.tools.pdfgen [PDF]
|
|
rst2html = project_a.tools.htmlgen
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
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)
|
|
is only valid when the PDF extra is installed. It is up to the installer
|
|
to determine how to handle the situation where PDF was not indicated
|
|
(e.g. omit the console script, provide a warning when attempting to load
|
|
the entry point, assume the extras are present and let the implementation
|
|
fail later).
|
|
|
|
.. warning::
|
|
``pip`` and other tools might not support this use case for extra
|
|
dependencies, therefore this practice is considered **deprecated**.
|
|
See :doc:`PyPUG:specifications/entry-points`.
|
|
|
|
|
|
Python requirement
|
|
==================
|
|
In some cases, you might need to specify the minimum required python version.
|
|
This can be configured as shown in the example below.
|
|
|
|
.. tab:: setup.cfg
|
|
|
|
.. code-block:: ini
|
|
|
|
[metadata]
|
|
name = Project-B
|
|
#...
|
|
|
|
[options]
|
|
#...
|
|
python_requires = >=3.6
|
|
|
|
.. tab:: setup.py
|
|
|
|
.. code-block:: python
|
|
|
|
setup(
|
|
name="Project-B",
|
|
python_requires=">=3.6",
|
|
...,
|
|
)
|
|
|
|
|
|
.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_
|
|
|
|
.. code-block:: toml
|
|
|
|
[project]
|
|
name = "Project-B"
|
|
requires-python = ">=3.6"
|
|
# ...
|
|
|
|
----
|
|
|
|
.. rubric:: Notes
|
|
|
|
.. [#experimental]
|
|
While the ``[build-system]`` table should always be specified in the
|
|
``pyproject.toml`` file, support for adding package metadata and build configuration
|
|
options via the ``[project]`` and ``[tool.setuptools]`` tables is still
|
|
experimental and might change (or be completely removed) in future releases.
|
|
See :doc:`/userguide/pyproject_config`.
|