Dan Willemsen | adad21e | 2022-03-25 17:22:05 -0700 | [diff] [blame] | 1 | ========================== |
| 2 | ``setuptools`` Quickstart |
| 3 | ========================== |
| 4 | |
| 5 | Installation |
| 6 | ============ |
| 7 | |
| 8 | To install the latest version of setuptools, use:: |
| 9 | |
| 10 | pip install --upgrade setuptools |
| 11 | |
| 12 | |
| 13 | Python packaging at a glance |
| 14 | ============================ |
| 15 | The landscape of Python packaging is shifting and ``Setuptools`` has evolved to |
| 16 | only provide backend support, no longer being the de-facto packaging tool in |
| 17 | the market. Every python package must provide a ``pyproject.toml`` and specify |
| 18 | the backend (build system) it wants to use. The distribution can then |
| 19 | be generated with whatever tool that provides a ``build sdist``-like |
| 20 | functionality. While this may appear cumbersome, given the added pieces, |
| 21 | it in fact tremendously enhances the portability of your package. The |
| 22 | change is driven under :pep:`PEP 517 <517#build-requirements>`. To learn more about Python packaging in general, |
| 23 | navigate to the :ref:`bottom <packaging-resources>` of this page. |
| 24 | |
| 25 | |
| 26 | Basic Use |
| 27 | ========= |
| 28 | For basic use of setuptools, you will need a ``pyproject.toml`` with the |
| 29 | exact following info, which declares you want to use ``setuptools`` to |
| 30 | package your project: |
| 31 | |
| 32 | .. code-block:: toml |
| 33 | |
| 34 | [build-system] |
| 35 | requires = ["setuptools"] |
| 36 | build-backend = "setuptools.build_meta" |
| 37 | |
| 38 | Then, you will need to specify your package information such as metadata, |
| 39 | contents, dependencies, etc. |
| 40 | |
| 41 | Setuptools currently supports configurations from either ``setup.cfg``, |
| 42 | ``setup.py`` or ``pyproject.toml`` [#experimental]_ files, however, configuring new |
| 43 | projects via ``setup.py`` is discouraged [#setup.py]_. |
| 44 | |
| 45 | The following example demonstrates a minimum configuration: |
| 46 | |
| 47 | .. tab:: setup.cfg |
| 48 | |
| 49 | .. code-block:: ini |
| 50 | |
| 51 | [metadata] |
| 52 | name = mypackage |
| 53 | version = 0.0.1 |
| 54 | |
| 55 | [options] |
| 56 | packages = mypackage |
| 57 | install_requires = |
| 58 | requests |
| 59 | importlib-metadata; python_version < "3.8" |
| 60 | |
| 61 | See :doc:`/userguide/declarative_config` for more information. |
| 62 | |
| 63 | .. tab:: setup.py [#setup.py]_ |
| 64 | |
| 65 | .. code-block:: python |
| 66 | |
| 67 | from setuptools import setup |
| 68 | |
| 69 | setup( |
| 70 | name='mypackage', |
| 71 | version='0.0.1', |
| 72 | packages=['mypackage'], |
| 73 | install_requires=[ |
| 74 | 'requests', |
| 75 | 'importlib-metadata; python_version == "3.8"', |
| 76 | ], |
| 77 | ) |
| 78 | |
| 79 | See :doc:`/references/keywords` for more information. |
| 80 | |
| 81 | .. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ |
| 82 | |
| 83 | .. code-block:: toml |
| 84 | |
| 85 | [project] |
| 86 | name = "mypackage" |
| 87 | version = "0.0.1" |
| 88 | dependencies = [ |
| 89 | "requests", |
| 90 | 'importlib-metadata; python_version<"3.8"', |
| 91 | ] |
| 92 | |
| 93 | See :doc:`/userguide/pyproject_config` for more information. |
| 94 | |
| 95 | This is what your project would look like:: |
| 96 | |
| 97 | ~/mypackage/ |
| 98 | pyproject.toml |
| 99 | setup.cfg # or setup.py |
| 100 | mypackage/__init__.py |
| 101 | |
| 102 | Then, you need a builder, such as :std:doc:`PyPA build <pypa-build:index>` |
| 103 | which you can obtain via ``pip install build``. After downloading it, invoke |
| 104 | the builder:: |
| 105 | |
| 106 | python -m build |
| 107 | |
| 108 | You now have your distribution ready (e.g. a ``tar.gz`` file and a ``.whl`` |
| 109 | file in the ``dist`` directory), which you can upload to PyPI! |
| 110 | |
| 111 | Of course, before you release your project to PyPI, you'll want to add a bit |
| 112 | more information to your setup script to help people find or learn about your |
| 113 | project. And maybe your project will have grown by then to include a few |
| 114 | dependencies, and perhaps some data files and scripts. In the next few sections, |
| 115 | we will walk through the additional but essential information you need |
| 116 | to specify to properly package your project. |
| 117 | |
| 118 | |
| 119 | Automatic package discovery |
| 120 | =========================== |
| 121 | For simple projects, it's usually easy enough to manually add packages to |
| 122 | the ``packages`` keyword in ``setup.cfg``. However, for very large projects, |
| 123 | it can be a big burden to keep the package list updated. |
| 124 | Therefore, ``setuptoops`` provides a convenient way to automatically list all |
| 125 | the packages in your project directory: |
| 126 | |
| 127 | .. tab:: setup.cfg |
| 128 | |
| 129 | .. code-block:: ini |
| 130 | |
| 131 | [options] |
| 132 | packages = find: # OR `find_namespaces:` if you want to use namespaces |
| 133 | |
| 134 | [options.packages.find] (always `find` even if `find_namespaces:` was used before) |
| 135 | # This section is optional |
| 136 | # Each entry in this section is optional, and if not specified, the default values are: |
| 137 | # `where=.`, `include=*` and `exclude=` (empty). |
| 138 | include=mypackage* |
| 139 | exclude=mypackage.tests* |
| 140 | |
| 141 | .. tab:: setup.py [#setup.py]_ |
| 142 | |
| 143 | .. code-block:: python |
| 144 | |
| 145 | from setuptools import find_packages # or find_namespace_packages |
| 146 | |
| 147 | setup( |
| 148 | # ... |
| 149 | packages=find_packages( |
| 150 | where='.', |
| 151 | include=['mypackage*'], # ["*"] by default |
| 152 | exclude=['mypackage.tests'], # empty by default |
| 153 | ), |
| 154 | # ... |
| 155 | ) |
| 156 | |
| 157 | .. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ |
| 158 | |
| 159 | .. code-block:: toml |
| 160 | |
| 161 | # ... |
| 162 | [tool.setuptools.packages] |
| 163 | find = {} # Scan the project directory with the default parameters |
| 164 | |
| 165 | # OR |
| 166 | [tool.setuptools.packages.find] |
| 167 | where = ["src"] # ["."] by default |
| 168 | include = ["mypackage*"] # ["*"] by default |
| 169 | exclude = ["mypackage.tests*"] # empty by default |
| 170 | namespaces = false # true by default |
| 171 | |
| 172 | When you pass the above information, alongside other necessary information, |
| 173 | ``setuptools`` walks through the directory specified in ``where`` (omitted |
| 174 | here as the package resides in the current directory) and filters the packages |
| 175 | it can find following the ``include`` (defaults to none), then removes |
| 176 | those that match the ``exclude`` and returns a list of Python packages. The above |
| 177 | setup also allows you to adopt a ``src/`` layout. For more details and advanced |
| 178 | use, go to :ref:`package_discovery`. |
| 179 | |
| 180 | .. tip:: |
| 181 | Starting with version 61.0.0, setuptools' automatic discovery capabilities |
| 182 | have been improved to detect popular project layouts (such as the |
| 183 | :ref:`flat-layout` and :ref:`src-layout`) without requiring any |
| 184 | special configuration. Check out our :ref:`reference docs <package_discovery>` |
| 185 | for more information, but please keep in mind that this functionality is |
| 186 | still considered **experimental** and might change (or even be removed) in |
| 187 | future releases. |
| 188 | |
| 189 | |
| 190 | Entry points and automatic script creation |
| 191 | =========================================== |
| 192 | Setuptools supports automatic creation of scripts upon installation, that runs |
| 193 | code within your package if you specify them as :doc:`entry points |
| 194 | <PyPUG:specifications/entry-points>`. |
| 195 | This is what allows you to run commands like ``pip install`` instead of having |
| 196 | to type ``python -m pip install``. |
| 197 | The following configuration examples show how to accomplish this: |
| 198 | |
| 199 | .. tab:: setup.cfg |
| 200 | |
| 201 | .. code-block:: ini |
| 202 | |
| 203 | [options.entry_points] |
| 204 | console_scripts = |
| 205 | cli-name = mypkg.mymodule:some_func |
| 206 | |
| 207 | .. tab:: setup.py [#setup.py]_ |
| 208 | |
| 209 | .. code-block:: python |
| 210 | |
| 211 | setup( |
| 212 | # ... |
| 213 | entry_points={ |
| 214 | 'console_scripts': [ |
| 215 | 'cli-name = mypkg.mymodule:some_func', |
| 216 | ] |
| 217 | } |
| 218 | ) |
| 219 | |
| 220 | .. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ |
| 221 | |
| 222 | .. code-block:: toml |
| 223 | |
| 224 | [project.scripts] |
| 225 | cli-name = mypkg.mymodule:some_func |
| 226 | |
| 227 | When this project is installed, a ``cli-name`` executable will be created. |
| 228 | ``cli-name`` will invoke the function ``some_func`` in the |
| 229 | ``mypkg/mymodule.py`` file when called by the user. |
| 230 | Note that you can also use the ``entry-points`` mechanism to advertise |
| 231 | components between installed packages and implement plugin systems. |
| 232 | For detailed usage, go to :doc:`entry_point`. |
| 233 | |
| 234 | |
| 235 | Dependency management |
| 236 | ===================== |
| 237 | Packages built with ``setuptools`` can specify dependencies to be automatically |
| 238 | installed when the package itself is installed. |
| 239 | The example below show how to configure this kind of dependencies: |
| 240 | |
| 241 | .. tab:: setup.cfg |
| 242 | |
| 243 | .. code-block:: ini |
| 244 | |
| 245 | [options] |
| 246 | install_requires = |
| 247 | docutils |
| 248 | requests <= 0.4 |
| 249 | |
| 250 | .. tab:: setup.py [#setup.py]_ |
| 251 | |
| 252 | .. code-block:: python |
| 253 | |
| 254 | setup( |
| 255 | # ... |
| 256 | install_requires=["docutils", "requests <= 0.4"], |
| 257 | # ... |
| 258 | ) |
| 259 | |
| 260 | .. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ |
| 261 | |
| 262 | .. code-block:: toml |
| 263 | |
| 264 | [project] |
| 265 | # ... |
| 266 | dependencies = [ |
| 267 | "docutils", |
| 268 | "requires <= 0.4", |
| 269 | ] |
| 270 | # ... |
| 271 | |
| 272 | Each dependency is represented by a string that can optionally contain version requirements |
| 273 | (e.g. one of the operators <, >, <=, >=, == or !=, followed by a version identifier), |
| 274 | and/or conditional environment markers, e.g. ``sys_platform == "win32"`` |
| 275 | (see :doc:`PyPUG:specifications/version-specifiers` for more information). |
| 276 | |
| 277 | When your project is installed, all of the dependencies not already installed |
| 278 | will be located (via PyPI), downloaded, built (if necessary), and installed. |
| 279 | This, of course, is a simplified scenario. You can also specify groups of |
| 280 | extra dependencies that are not strictly required by your package to work, but |
| 281 | that will provide additional functionalities. |
| 282 | For more advanced use, see :doc:`dependency_management`. |
| 283 | |
| 284 | |
| 285 | .. _Including Data Files: |
| 286 | |
| 287 | Including Data Files |
| 288 | ==================== |
| 289 | The distutils have traditionally allowed installation of "data files", which |
| 290 | are placed in a platform-specific location. Setuptools offers three ways to |
| 291 | specify data files to be included in your packages. For the simplest use, you |
| 292 | can simply use the ``include_package_data`` keyword: |
| 293 | |
| 294 | .. tab:: setup.cfg |
| 295 | |
| 296 | .. code-block:: ini |
| 297 | |
| 298 | [options] |
| 299 | include_package_data = True |
| 300 | |
| 301 | .. tab:: setup.py [#setup.py]_ |
| 302 | |
| 303 | .. code-block:: python |
| 304 | |
| 305 | setup( |
| 306 | # ... |
| 307 | include_package_data=True, |
| 308 | # ... |
| 309 | ) |
| 310 | |
| 311 | .. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ |
| 312 | |
| 313 | .. code-block:: toml |
| 314 | |
| 315 | [tool.setuptools] |
| 316 | include-package-data = true |
| 317 | # This is already the default behaviour if your are using |
| 318 | # pyproject.toml to configure your build. |
| 319 | # You can deactivate that with `include-package-data = false` |
| 320 | |
| 321 | This tells setuptools to install any data files it finds in your packages. |
| 322 | The data files must be specified via the distutils' |MANIFEST.in|_ file |
| 323 | or automatically added by a :ref:`Revision Control System plugin |
| 324 | <Adding Support for Revision Control Systems>`. |
| 325 | For more details, see :doc:`datafiles`. |
| 326 | |
| 327 | |
| 328 | Development mode |
| 329 | ================ |
| 330 | |
| 331 | ``setuptools`` allows you to install a package without copying any files |
| 332 | to your interpreter directory (e.g. the ``site-packages`` directory). |
| 333 | This allows you to modify your source code and have the changes take |
| 334 | effect without you having to rebuild and reinstall. |
| 335 | Here's how to do it:: |
| 336 | |
| 337 | pip install --editable . |
| 338 | |
| 339 | This creates a link file in your interpreter site package directory which |
| 340 | associate with your source code. For more information, see :doc:`development_mode`. |
| 341 | |
| 342 | .. tip:: |
| 343 | |
| 344 | Prior to :ref:`pip v21.1 <pip:v21-1>`, a ``setup.py`` script was |
| 345 | required to be compatible with development mode. With late |
| 346 | versions of pip, ``setup.cfg``-only projects may be installed in this mode. |
| 347 | |
| 348 | If you are experimenting with :doc:`configuration using <pyproject_config>`, |
| 349 | or have version of ``pip`` older than v21.1, you might need to keep a |
| 350 | ``setup.py`` file in file in your repository if you want to use editable |
| 351 | installs (for the time being). |
| 352 | |
| 353 | A simple script will suffice, for example: |
| 354 | |
| 355 | .. code-block:: python |
| 356 | |
| 357 | from setuptools import setup |
| 358 | |
| 359 | setup() |
| 360 | |
| 361 | You can still keep all the configuration in :doc:`setup.cfg </userguide/declarative_config>` |
| 362 | (or :doc:`pyproject.toml </userguide/pyproject_config>`). |
| 363 | |
| 364 | |
| 365 | Uploading your package to PyPI |
| 366 | ============================== |
| 367 | After generating the distribution files, the next step would be to upload your |
| 368 | distribution so others can use it. This functionality is provided by |
| 369 | :pypi:`twine` and is documented in the :doc:`Python packaging tutorial |
| 370 | <PyPUG:tutorials/packaging-projects>`. |
| 371 | |
| 372 | |
| 373 | Transitioning from ``setup.py`` to ``setup.cfg`` |
| 374 | ================================================ |
| 375 | To avoid executing arbitrary scripts and boilerplate code, we are transitioning |
| 376 | into a full-fledged ``setup.cfg`` to declare your package information instead |
| 377 | of running ``setup()``. This inevitably brings challenges due to a different |
| 378 | syntax. :doc:`Here </userguide/declarative_config>` we provide a quick guide to |
| 379 | understanding how ``setup.cfg`` is parsed by ``setuptools`` to ease the pain of |
| 380 | transition. |
| 381 | |
| 382 | .. _packaging-resources: |
| 383 | |
| 384 | Resources on Python packaging |
| 385 | ============================= |
| 386 | Packaging in Python can be hard and is constantly evolving. |
| 387 | `Python Packaging User Guide <https://packaging.python.org>`_ has tutorials and |
| 388 | up-to-date references that can help you when it is time to distribute your work. |
| 389 | |
| 390 | |
| 391 | .. |MANIFEST.in| replace:: ``MANIFEST.in`` |
| 392 | .. _MANIFEST.in: https://packaging.python.org/en/latest/guides/using-manifest-in/ |
| 393 | |
| 394 | |
| 395 | ---- |
| 396 | |
| 397 | .. rubric:: Notes |
| 398 | |
| 399 | .. [#setup.py] |
| 400 | The ``setup.py`` file should be used only when custom scripting during the |
| 401 | build is necessary. |
| 402 | Examples are kept in this document to help people interested in maintaining or |
| 403 | contributing to existing packages that use ``setup.py``. |
| 404 | Note that you can still keep most of configuration declarative in |
| 405 | :doc:`setup.cfg <declarative_config>` or :doc:`pyproject.toml |
| 406 | <pyproject_config>` and use ``setup.py`` only for the parts not |
| 407 | supported in those files (e.g. C extensions). |
| 408 | |
| 409 | .. [#experimental] |
| 410 | While the ``[build-system]`` table should always be specified in the |
| 411 | ``pyproject.toml`` file, support for adding package metadata and build configuration |
| 412 | options via the ``[project]`` and ``[tool.setuptools]`` tables is still |
| 413 | experimental and might change (or be completely removed) in future releases. |
| 414 | See :doc:`/userguide/pyproject_config`. |