diff options
Diffstat (limited to 'docs/userguide/dependency_management.rst')
-rw-r--r-- | docs/userguide/dependency_management.rst | 443 |
1 files changed, 443 insertions, 0 deletions
diff --git a/docs/userguide/dependency_management.rst b/docs/userguide/dependency_management.rst new file mode 100644 index 0000000..279f794 --- /dev/null +++ b/docs/userguide/dependency_management.rst @@ -0,0 +1,443 @@ +===================================== +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`. |