summaryrefslogtreecommitdiff
path: root/docs/userguide/dependency_management.rst
blob: a26ab6c3b078ba2e8d045fbd4c058edd7ac60c9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
=====================================
Dependencies Management in Setuptools
=====================================

There are three types of dependency styles offered by setuptools:
1) build system requirement, 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/>`_


.. contents::

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 need to actually
do the packgaging (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
:ref:`quickstart` or (WIP)):

.. code-block:: ini

    [build-system]
    requires = ["setuptools", "wheel"]
    #...

.. 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 :ref:`guide on
    deprecated practice (WIP)`



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
finess to it, let's start with a simple example.

.. code-block:: ini

    [options]
    #...
    install_requires =
        docutils
        BazSpam ==1.1

.. code-block:: python

    setup(
        #...,
        install_requires = [
            '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

.. code-block:: ini

    [options]
    #...
    install_requires =
        enum34;python_version<'3.4'

.. code-block:: python

    setup(
        #...
        install_requires=[
            "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:

.. code-block:: ini

    [options]
    #...
    install_requires =
        enum34;python_version<'3.4'
        pywin32 >= 1.0;platform_system=='Windows'

.. code-block:: python

    setup(
        #...
        install_requires=[
            "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:

.. code-block:: ini

    [options]
    #...
    dependency_links = http://peak.telecommunity.com/snapshots/

.. 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:

.. code-block:: ini

    [metadata]
    name = Package-A

    [options.extras_require]
    PDF = ReportLab>=1.2; RXP


.. code-block:: python

    setup(
        name="Project-A",
        #...
        extras_require={
            "PDF":  ["ReportLab>=1.2", "RXP"],
        }
    )

The name ``PDF`` is an arbitary identifier of such a list of dependencies, to
which other components can refer and have them installed. There are two common
use cases.

First is the console_scripts entry point:

.. code-block:: ini

    [metadata]
    name = Project A
    #...

    [options]
    #...
    entry_points=
        [console_scripts]
        rst2pdf = project_a.tools.pdfgen [PDF]
        rst2html = project_a.tools.htmlgen

.. code-block:: python

    setup(
        name = "Project-A"
        #...,
        entry_points={
            "console_scripts": [
                "rst2pdf = project_a.tools.pdfgen [PDF]",
                "rst2html = project_a.tools.htmlgen",
            ],
        }
    )

When the script ``rst2pdf`` is run, it will trigger the installation of
the two dependencies ``PDF`` maps to.

The second use case 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::

.. code-block:: ini

    [metadata]
    name = Project-B
    #...

    [options]
    #...
    install_requires =
        Project-A[PDF]

.. code-block:: python

    setup(
        name="Project-B",
        install_requires=["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 not 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).


Python requirement
==================
In some cases, you might need to specify the minimum required python version.
This is handled with the ``python_requires`` keyword supplied to ``setup.cfg``
or ``setup.py``.

Example WIP