From 0c273c80551f0cfaf68667a5466e8605fc2bd476 Mon Sep 17 00:00:00 2001 From: DongHun Kwak Date: Mon, 18 Jul 2022 15:39:12 +0900 Subject: [PATCH] Imported Upstream version 62.5.0 --- CHANGES.rst | 35 ++++ PKG-INFO | 2 +- _distutils_hack/__init__.py | 47 ++++- docs/conf.py | 17 +- docs/{userguide => deprecated}/commands.rst | 56 ++++++ docs/deprecated/distutils-legacy.rst | 2 +- docs/deprecated/index.rst | 2 +- docs/deprecated/running_commands.rst | 23 --- docs/userguide/datafiles.rst | 19 +- docs/userguide/dependency_management.rst | 132 ++++++------- docs/userguide/distribution.rst | 268 +++++++++++--------------- docs/userguide/entry_point.rst | 73 ++++---- docs/userguide/ext_modules.rst | 150 +++++++++++++++ docs/userguide/index.rst | 2 +- docs/userguide/miscellaneous.rst | 57 +++++- docs/userguide/package_discovery.rst | 20 +- docs/userguide/pyproject_config.rst | 15 +- docs/userguide/quickstart.rst | 280 ++++++++++++++++------------ pytest.ini | 2 +- setup.cfg | 2 +- setuptools.egg-info/PKG-INFO | 2 +- setuptools.egg-info/SOURCES.txt | 4 +- setuptools/config/_apply_pyprojecttoml.py | 4 +- setuptools/config/expand.py | 2 + setuptools/config/pyprojecttoml.py | 20 +- setuptools/config/setupcfg.py | 6 +- setuptools/extension.py | 87 ++++++++- setuptools/logging.py | 2 +- setuptools/tests/test_easy_install.py | 2 +- 29 files changed, 844 insertions(+), 489 deletions(-) rename docs/{userguide => deprecated}/commands.rst (92%) delete mode 100644 docs/deprecated/running_commands.rst create mode 100644 docs/userguide/ext_modules.rst diff --git a/CHANGES.rst b/CHANGES.rst index 61e7965..c192f16 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,38 @@ +v62.5.0 +------- + + +Changes +^^^^^^^ +* #3347: Changed warnings and documentation notes about *experimental* aspect of ``pyproject.toml`` configuration: + now ``[pyproject]`` is a fully supported configuration interface, but the ``[tool.setuptools]`` table + and sub-tables are still considered to be in **beta** stage. +* #3383: In _distutils_hack, suppress/undo the use of local distutils when select tests are imported in CPython. + +Documentation changes +^^^^^^^^^^^^^^^^^^^^^ +* #3368: Added documentation page about extension modules -- by :user:`mkoeppe` +* #3371: Moved documentation from ``/userguide/commands`` to ``/depracted/commands``. + This change was motived by the fact that running ``python setup.py`` directly is + considered a deprecated practice. +* #3372: Consolidated sections about ``sdist`` contents and ``MANIFEST.in`` into a single page. + + Added a simple ``MANIFEST.in`` example. +* #3373: Moved remarks about using :pypi:`Cython` to the newly created page for + extension modules. +* #3374: Added clarification that using ``python setup.py egg_info`` commands to + manage project versions is only supported in a *transitional* basis, and + that eventually ``egg_info`` will be deprecated. + + Reorganized sections with tips for managing versions. +* #3378: Updated ``Quickstart`` docs to make it easier to follow for beginners. + +Misc +^^^^ +* #3385: Modules used to parse and evaluate configuration from ``pyproject.toml`` files are + intended for internal use only and that not part of the public API. + + v62.4.0 ------- diff --git a/PKG-INFO b/PKG-INFO index 372fb81..55af556 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: setuptools -Version: 62.4.0 +Version: 62.5.0 Summary: Easily download, build, install, upgrade, and uninstall Python packages Home-page: https://github.com/pypa/setuptools Author: Python Packaging Authority diff --git a/_distutils_hack/__init__.py b/_distutils_hack/__init__.py index 605a6ed..f987a53 100644 --- a/_distutils_hack/__init__.py +++ b/_distutils_hack/__init__.py @@ -14,22 +14,26 @@ def warn_distutils_present(): # https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250 return import warnings + warnings.warn( "Distutils was imported before Setuptools, but importing Setuptools " "also replaces the `distutils` module in `sys.modules`. This may lead " "to undesirable behaviors or errors. To avoid these issues, avoid " "using distutils directly, ensure that setuptools is installed in the " "traditional way (e.g. not an editable install), and/or make sure " - "that setuptools is always imported before distutils.") + "that setuptools is always imported before distutils." + ) def clear_distutils(): if 'distutils' not in sys.modules: return import warnings + warnings.warn("Setuptools is replacing distutils.") mods = [ - name for name in sys.modules + name + for name in sys.modules if name == "distutils" or name.startswith("distutils.") ] for name in mods: @@ -46,6 +50,7 @@ def enabled(): def ensure_local_distutils(): import importlib + clear_distutils() # With the DistutilsMetaFinder in place, @@ -82,7 +87,9 @@ class _TrivialRe: class DistutilsMetaFinder: def find_spec(self, fullname, path, target=None): - if path is not None: + # optimization: only consider top level modules and those + # found in the CPython test suite. + if path is not None and not fullname.startswith('test.'): return method_name = 'spec_for_{fullname}'.format(**locals()) @@ -111,7 +118,6 @@ class DistutilsMetaFinder: return class DistutilsLoader(importlib.abc.Loader): - def create_module(self, spec): mod.__name__ = 'distutils' return mod @@ -147,9 +153,9 @@ class DistutilsMetaFinder: Detect if pip is being imported in a build script. Ref #2355. """ import traceback + return any( - cls.frame_file_is_setup(frame) - for frame, line in traceback.walk_stack(None) + cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None) ) @staticmethod @@ -160,6 +166,35 @@ class DistutilsMetaFinder: # some frames may not have __file__ (#2940) return frame.f_globals.get('__file__', '').endswith('setup.py') + def spec_for_sensitive_tests(self): + """ + Ensure stdlib distutils when running select tests under CPython. + + python/cpython#91169 + """ + clear_distutils() + self.spec_for_distutils = lambda: None + + sensitive_tests = ( + [ + 'test.test_distutils', + 'test.test_peg_generator', + 'test.test_importlib', + ] + if sys.version_info < (3, 10) + else [ + 'test.test_distutils', + ] + ) + + +for name in DistutilsMetaFinder.sensitive_tests: + setattr( + DistutilsMetaFinder, + f'spec_for_{name}', + DistutilsMetaFinder.spec_for_sensitive_tests, + ) + DISTUTILS_FINDER = DistutilsMetaFinder() diff --git a/docs/conf.py b/docs/conf.py index 159eedc..b7d0538 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -92,7 +92,14 @@ intersphinx_mapping = { } intersphinx_mapping.update({ - 'pypa-build': ('https://pypa-build.readthedocs.io/en/latest/', None) + 'pip': ('https://pip.pypa.io/en/latest', None), + 'build': ('https://pypa-build.readthedocs.io/en/latest', None), + 'PyPUG': ('https://packaging.python.org/en/latest/', None), + 'packaging': ('https://packaging.pypa.io/en/latest/', None), + 'twine': ('https://twine.readthedocs.io/en/stable/', None), + 'importlib-resources': ( + 'https://importlib-resources.readthedocs.io/en/latest', None + ), }) # Add support for linking usernames @@ -133,6 +140,7 @@ html_theme_options = { extensions += ['sphinx_reredirects'] redirects = { "userguide/keywords": "/deprecated/changed_keywords.html", + "userguide/commands": "/deprecated/commands.html", } # Add support for inline tabs @@ -211,10 +219,3 @@ favicons = [ }, # rel="apple-touch-icon" does not support SVG yet ] - -intersphinx_mapping['pip'] = 'https://pip.pypa.io/en/latest', None -intersphinx_mapping['PyPUG'] = ('https://packaging.python.org/en/latest/', None) -intersphinx_mapping['packaging'] = ('https://packaging.pypa.io/en/latest/', None) -intersphinx_mapping['importlib-resources'] = ( - 'https://importlib-resources.readthedocs.io/en/latest', None -) diff --git a/docs/userguide/commands.rst b/docs/deprecated/commands.rst similarity index 92% rename from docs/userguide/commands.rst rename to docs/deprecated/commands.rst index e632e55..ebd0687 100644 --- a/docs/userguide/commands.rst +++ b/docs/deprecated/commands.rst @@ -1,3 +1,59 @@ +=============================== +Running ``setuptools`` commands +=============================== + +Historically, ``setuptools`` allowed running commands via a ``setup.py`` script +at the root of a Python project, as indicated in the examples below:: + + python setup.py --help + python setup.py --help-commands + python setup.py --version + python setup.py sdist + python setup.py bdist_wheel + +You could also run commands in other circumstances: + +* ``setuptools`` projects without ``setup.py`` (e.g., ``setup.cfg``-only):: + + python -c "import setuptools; setup()" --help + +* ``distutils`` projects (with a ``setup.py`` importing ``distutils``):: + + python -c "import setuptools; with open('setup.py') as f: exec(compile(f.read(), 'setup.py', 'exec'))" develop + +That is, you can simply list the normal setup commands and options following the quoted part. + +.. warning:: + While it is perfectly fine that users write ``setup.py`` files to configure + a package build (e.g. to specify binary extensions or customize commands), + on recent versions of ``setuptools``, running ``python setup.py`` directly + as a script is considered **deprecated**. This also means that users should + avoid running commands directly via ``python setup.py ``. + + If you want to create :term:`sdist ` or :term:`wheel` + distributions the recommendation is to use the command line tool provided by :pypi:`build`:: + + pip install build # needs to be installed first + + python -m build # builds both sdist and wheel + python -m build --sdist + python -m build --wheel + + Build will automatically download ``setuptools`` and build the package in an + isolated environment. You can also specify specific versions of + ``setuptools``, by setting the :doc:`build requirements in pyproject.toml + `. + + If you want to install a package, you can use :pypi:`pip` or :pypi:`installer`:: + + pip install /path/to/wheel/file.whl + pip install /path/to/sdist/file.tar.gz + pip install . # replacement for python setup.py install + pip install --editable . # replacement for python setup.py develop + + pip install installer # needs to be installed first + python -m installer /path/to/wheel/file.whl + ----------------- Command Reference ----------------- diff --git a/docs/deprecated/distutils-legacy.rst b/docs/deprecated/distutils-legacy.rst index e73cdff..e106ce9 100644 --- a/docs/deprecated/distutils-legacy.rst +++ b/docs/deprecated/distutils-legacy.rst @@ -3,7 +3,7 @@ Porting from Distutils Setuptools and the PyPA have a `stated goal `_ to make Setuptools the reference API for distutils. -Since the 60.0.0 release, Setuptools includes a local, vendored copy of distutils (from late copies of CPython) that is enabled by default. To disable the use of this copy of distutils when invoking setuptools, set the enviroment variable: +Since the 60.0.0 release, Setuptools includes a local, vendored copy of distutils (from late copies of CPython) that is enabled by default. To disable the use of this copy of distutils when invoking setuptools, set the environment variable: SETUPTOOLS_USE_DISTUTILS=stdlib diff --git a/docs/deprecated/index.rst b/docs/deprecated/index.rst index ea9069e..0ea66cf 100644 --- a/docs/deprecated/index.rst +++ b/docs/deprecated/index.rst @@ -22,4 +22,4 @@ objectives. distutils/index distutils-legacy functionalities - running_commands + commands diff --git a/docs/deprecated/running_commands.rst b/docs/deprecated/running_commands.rst deleted file mode 100644 index 8d4ca93..0000000 --- a/docs/deprecated/running_commands.rst +++ /dev/null @@ -1,23 +0,0 @@ -Running ``setuptools`` commands -=============================== - -Historically, ``setuptools`` allowed running commands via a ``setup.py`` script -at the root of a Python project, as indicated in the examples below:: - - python setup.py --help - python setup.py --help-commands - python setup.py --version - python setup.py sdist - python setup.py bdist_wheel - -You could also run commands in other circumstances: - -* ``setuptools`` projects without ``setup.py`` (e.g., ``setup.cfg``-only):: - - python -c "import setuptools; setup()" --help - -* ``distutils`` projects (with a ``setup.py`` importing ``distutils``):: - - python -c "import setuptools; with open('setup.py') as f: exec(compile(f.read(), 'setup.py', 'exec'))" develop - -That is, you can simply list the normal setup commands and options following the quoted part. diff --git a/docs/userguide/datafiles.rst b/docs/userguide/datafiles.rst index 3a2ffbd..44ff742 100644 --- a/docs/userguide/datafiles.rst +++ b/docs/userguide/datafiles.rst @@ -56,7 +56,7 @@ and you supply this configuration: include_package_data=True ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -137,7 +137,7 @@ data files: package_data={"mypkg": ["*.txt", "*.rst"]} ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -210,7 +210,7 @@ use the ``package_data`` option, the following configuration will work: package_data={"": ["*.txt"], "mypkg1": ["data1.rst"]}, ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -288,7 +288,7 @@ use the ``exclude_package_data`` option: exclude_package_data={"mypkg": [".gitattributes"]}, ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -365,7 +365,7 @@ the configuration might look like this: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -412,7 +412,7 @@ scanning of namespace packages in the ``src`` directory and the rest is handled include_package_data=True, ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -539,10 +539,9 @@ run time be included **inside the package**. ---- -.. [#experimental] - Support for specifying package metadata and build configuration options via - ``pyproject.toml`` is experimental and might change - in the future. See :doc:`/userguide/pyproject_config`. +.. [#beta] + Support for adding build configuration options via the ``[tool.setuptools]`` + table in the ``pyproject.toml`` file. See :doc:`/userguide/pyproject_config`. .. [#system-dirs] These locations can be discovered with the help of third-party libraries such as :pypi:`platformdirs`. diff --git a/docs/userguide/dependency_management.rst b/docs/userguide/dependency_management.rst index c7f1e05..56fbd0b 100644 --- a/docs/userguide/dependency_management.rst +++ b/docs/userguide/dependency_management.rst @@ -15,6 +15,8 @@ dependency. :pep:`direct URLs <440#direct-references>`. +.. _build-requires: + Build system requirement ======================== @@ -24,7 +26,7 @@ do the packaging (in our case, ``setuptools`` of course). This needs to be specified in your ``pyproject.toml`` file (if you have forgot what this is, go to :doc:`/userguide/quickstart` or :doc:`/build_meta`): -.. code-block:: ini +.. code-block:: toml [build-system] requires = ["setuptools"] @@ -51,6 +53,18 @@ be able to run. ``setuptools`` supports automatically downloading and installing these dependencies when the package is installed. Although there is more finesse to it, let's start with a simple example. +.. tab:: pyproject.toml + + .. code-block:: toml + + [project] + # ... + dependencies = [ + "docutils", + "BazSpam == 1.1", + ] + # ... + .. tab:: setup.cfg .. code-block:: ini @@ -73,18 +87,6 @@ finesse to it, let's start with a simple example. ], ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - # ... - dependencies = [ - "docutils", - "BazSpam == 1.1", - ] - # ... - When your project is installed (e.g., using :pypi:`pip`), all of the dependencies not already installed will be located (via `PyPI`_), downloaded, built (if necessary), @@ -102,6 +104,17 @@ 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:: pyproject.toml + + .. code-block:: toml + + [project] + # ... + dependencies = [ + "enum34; python_version<'3.4'", + ] + # ... + .. tab:: setup.cfg .. code-block:: ini @@ -122,7 +135,10 @@ the Python version is older than 3.4. To accomplish this ], ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +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:: pyproject.toml .. code-block:: toml @@ -130,12 +146,10 @@ the Python version is older than 3.4. To accomplish this # ... dependencies = [ "enum34; python_version<'3.4'", + "pywin32 >= 1.0; platform_system=='Windows'", ] # ... -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 @@ -158,18 +172,6 @@ and only install it if the user is using a Windows operating system: ], ) -.. 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`. @@ -188,6 +190,16 @@ set of extra functionalities. For example, let's consider a ``Package-A`` that offers optional PDF support and requires two other dependencies for it to work: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project] + name = "Package-A" + # ... + [project.optional-dependencies] + PDF = ["ReportLab>=1.2", "RXP"] + .. tab:: setup.cfg .. code-block:: ini @@ -213,16 +225,6 @@ optional PDF support and requires two other dependencies for it to work: }, ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - name = "Package-A" - # ... - [project.optional-dependencies] - PDF = ["ReportLab>=1.2", "RXP"] - .. sidebar:: .. tip:: @@ -236,6 +238,17 @@ A use case for this approach is that other package can use this "extra" for thei own dependencies. For example, if ``Package-B`` needs ``Package-B`` with PDF support installed, it might declare the dependency like this: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project] + name = "Package-B" + # ... + dependencies = [ + "Package-A[PDF]" + ] + .. tab:: setup.cfg .. code-block:: ini @@ -259,17 +272,6 @@ installed, it might declare the dependency like this: ..., ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - name = "Package-B" - # ... - dependencies = [ - "Package-A[PDF]" - ] - This will cause ``ReportLab`` to be installed along with ``Package-A``, if ``Package-B`` is installed -- even if ``Package-A`` was already installed. In this way, a project can encapsulate groups of optional "downstream dependencies" under a feature @@ -336,6 +338,15 @@ 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:: pyproject.toml + + .. code-block:: toml + + [project] + name = "Package-B" + requires-python = ">=3.6" + # ... + .. tab:: setup.cfg .. code-block:: ini @@ -359,25 +370,4 @@ This can be configured as shown in the example below. ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - name = "Package-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 in future releases. - See :doc:`/userguide/pyproject_config`. - - .. _PyPI: https://pypi.org diff --git a/docs/userguide/distribution.rst b/docs/userguide/distribution.rst index db0f1a5..be95a88 100644 --- a/docs/userguide/distribution.rst +++ b/docs/userguide/distribution.rst @@ -1,169 +1,10 @@ -Tagging and "Daily Build" or "Snapshot" Releases ------------------------------------------------- - -When a set of related projects are under development, it may be important to -track finer-grained version increments than you would normally use for e.g. -"stable" releases. While stable releases might be measured in dotted numbers -with alpha/beta/etc. status codes, development versions of a project often -need to be tracked by revision or build number or even build date. This is -especially true when projects in development need to refer to one another, and -therefore may literally need an up-to-the-minute version of something! - -To support these scenarios, ``setuptools`` allows you to "tag" your source and -egg distributions by adding one or more of the following to the project's -"official" version identifier: - -* A manually-specified pre-release tag, such as "build" or "dev", or a - manually-specified post-release tag, such as a build or revision number - (``--tag-build=STRING, -bSTRING``) - -* An 8-character representation of the build date (``--tag-date, -d``), as - a postrelease tag - -You can add these tags by adding ``egg_info`` and the desired options to -the command line ahead of the ``sdist`` or ``bdist`` commands that you want -to generate a daily build or snapshot for. See the section below on the -:ref:`egg_info ` command for more details. - -(Also, before you release your project, be sure to see the section on -:ref:`Specifying Your Project's Version` for more information about how pre- and -post-release tags affect how version numbers are interpreted. This is -important in order to make sure that dependency processing tools will know -which versions of your project are newer than others.) - -Finally, if you are creating builds frequently, and either building them in a -downloadable location or are copying them to a distribution server, you should -probably also check out the :ref:`rotate ` command, which lets you automatically -delete all but the N most-recently-modified distributions matching a glob -pattern. So, you can use a command line like:: - - setup.py egg_info -rbDEV bdist_egg rotate -m.egg -k3 - -to build an egg whose version info includes "DEV-rNNNN" (where NNNN is the -most recent Subversion revision that affected the source tree), and then -delete any egg files from the distribution directory except for the three -that were built most recently. - -If you have to manage automated builds for multiple packages, each with -different tagging and rotation policies, you may also want to check out the -:ref:`alias ` command, which would let each package define an alias like ``daily`` -that would perform the necessary tag, build, and rotate commands. Then, a -simpler script or cron job could just run ``setup.py daily`` in each project -directory. (And, you could also define sitewide or per-user default versions -of the ``daily`` alias, so that projects that didn't define their own would -use the appropriate defaults.) - -Generating Source Distributions -------------------------------- - -``setuptools`` enhances the distutils' default algorithm for source file -selection with pluggable endpoints for looking up files to include. If you are -using a revision control system, and your source distributions only need to -include files that you're tracking in revision control, use a corresponding -plugin instead of writing a ``MANIFEST.in`` file. See the section below on -:ref:`Adding Support for Revision Control Systems` for information on plugins. - -If you need to include automatically generated files, or files that are kept in -an unsupported revision control system, you'll need to create a ``MANIFEST.in`` -file to specify any files that the default file location algorithm doesn't -catch. See the distutils documentation for more information on the format of -the ``MANIFEST.in`` file. - -But, be sure to ignore any part of the distutils documentation that deals with -``MANIFEST`` or how it's generated from ``MANIFEST.in``; setuptools shields you -from these issues and doesn't work the same way in any case. Unlike the -distutils, setuptools regenerates the source distribution manifest file -every time you build a source distribution, and it builds it inside the -project's ``.egg-info`` directory, out of the way of your main project -directory. You therefore need not worry about whether it is up-to-date or not. - -Indeed, because setuptools' approach to determining the contents of a source -distribution is so much simpler, its ``sdist`` command omits nearly all of -the options that the distutils' more complex ``sdist`` process requires. For -all practical purposes, you'll probably use only the ``--formats`` option, if -you use any option at all. - - -Making "Official" (Non-Snapshot) Releases -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -When you make an official release, creating source or binary distributions, -you will need to override the tag settings from ``setup.cfg``, so that you -don't end up registering versions like ``foobar-0.7a1.dev-r34832``. This is -easy to do if you are developing on the trunk and using tags or branches for -your releases - just make the change to ``setup.cfg`` after branching or -tagging the release, so the trunk will still produce development snapshots. - -Alternately, if you are not branching for releases, you can override the -default version options on the command line, using something like:: - - setup.py egg_info -Db "" sdist bdist_egg - -The first part of this command (``egg_info -Db ""``) will override the -configured tag information, before creating source and binary eggs. Thus, these -commands will use the plain version from your ``setup.py``, without adding the -build designation string. - -Of course, if you will be doing this a lot, you may wish to create a personal -alias for this operation, e.g.:: - - setup.py alias -u release egg_info -Db "" - -You can then use it like this:: - - setup.py release sdist bdist_egg - -Or of course you can create more elaborate aliases that do all of the above. -See the sections below on the :ref:`egg_info ` and -:ref:`alias ` commands for more ideas. - -Distributing Extensions compiled with Cython --------------------------------------------- - -``setuptools`` will detect at build time whether Cython is installed or not. -If Cython is not found ``setuptools`` will ignore pyx files. - -To ensure Cython is available, include Cython in the build-requires section -of your pyproject.toml:: - - [build-system] - requires=[..., "cython"] - -Built with pip 10 or later, that declaration is sufficient to include Cython -in the build. For broader compatibility, declare the dependency in your -setup-requires of setup.cfg:: - - [options] - setup_requires = - ... - cython - -As long as Cython is present in the build environment, ``setuptools`` includes -transparent support for building Cython extensions, as -long as extensions are defined using ``setuptools.Extension``. - -If you follow these rules, you can safely list ``.pyx`` files as the source -of your ``Extension`` objects in the setup script. If it is, then ``setuptools`` -will use it. - -Of course, for this to work, your source distributions must include the C -code generated by Cython, as well as your original ``.pyx`` files. This means -that you will probably want to include current ``.c`` files in your revision -control system, rebuilding them whenever you check changes in for the ``.pyx`` -source files. This will ensure that people tracking your project in a revision -control system will be able to build it even if they don't have Cython -installed, and that your source releases will be similarly usable with or -without Cython. - - .. _Specifying Your Project's Version: Specifying Your Project's Version ---------------------------------- +================================= Setuptools can work well with most versioning schemes. Over the years, -setuptools has tried to closely follow the -`PEP 440 `_ scheme, but it +setuptools has tried to closely follow the :pep:`440` scheme, but it also supports legacy versions. There are, however, a few special things to watch out for, in order to ensure that setuptools and other tools can always tell what version of your package is newer than another @@ -245,7 +86,106 @@ but here are a few tips that will keep you out of trouble in the corner cases: Once you've decided on a version numbering scheme for your project, you can have setuptools automatically tag your in-development releases with various -pre- or post-release tags. See the following sections for more details: +pre- or post-release tags. See the following section for more details. + + +Tagging and "Daily Build" or "Snapshot" Releases +------------------------------------------------ + +.. warning:: + Please note that running ``python setup.py ...`` directly is no longer + considered a good practice and that in the future the commands ``egg_info`` + and ``rotate`` will be deprecated. + + As a result, the instructions and information presented in this section + should be considered **transitional** while setuptools don't provide a + mechanism for tagging releases. + + Meanwhile, if you can also consider using :pypi:`setuptools-scm` to achieve + similar objectives. + + +When a set of related projects are under development, it may be important to +track finer-grained version increments than you would normally use for e.g. +"stable" releases. While stable releases might be measured in dotted numbers +with alpha/beta/etc. status codes, development versions of a project often +need to be tracked by revision or build number or even build date. This is +especially true when projects in development need to refer to one another, and +therefore may literally need an up-to-the-minute version of something! + +To support these scenarios, ``setuptools`` allows you to "tag" your source and +egg distributions by adding one or more of the following to the project's +"official" version identifier: + +* A manually-specified pre-release tag, such as "build" or "dev", or a + manually-specified post-release tag, such as a build or revision number + (``--tag-build=STRING, -bSTRING``) + +* An 8-character representation of the build date (``--tag-date, -d``), as + a postrelease tag + +You can add these tags by adding ``egg_info`` and the desired options to +the command line ahead of the ``sdist`` or ``bdist`` commands that you want +to generate a daily build or snapshot for. See the section below on the +:ref:`egg_info ` command for more details. + +(Also, before you release your project, be sure to see the section on +:ref:`Specifying Your Project's Version` for more information about how pre- and +post-release tags affect how version numbers are interpreted. This is +important in order to make sure that dependency processing tools will know +which versions of your project are newer than others). + +Finally, if you are creating builds frequently, and either building them in a +downloadable location or are copying them to a distribution server, you should +probably also check out the :ref:`rotate ` command, which lets you automatically +delete all but the N most-recently-modified distributions matching a glob +pattern. So, you can use a command line like:: -* `Tagging and "Daily Build" or "Snapshot" Releases`_ -* The :ref:`egg_info ` command + setup.py egg_info -rbDEV bdist_egg rotate -m.egg -k3 + +to build an egg whose version info includes "DEV-rNNNN" (where NNNN is the +most recent Subversion revision that affected the source tree), and then +delete any egg files from the distribution directory except for the three +that were built most recently. + +If you have to manage automated builds for multiple packages, each with +different tagging and rotation policies, you may also want to check out the +:ref:`alias ` command, which would let each package define an alias like ``daily`` +that would perform the necessary tag, build, and rotate commands. Then, a +simpler script or cron job could just run ``setup.py daily`` in each project +directory. (And, you could also define sitewide or per-user default versions +of the ``daily`` alias, so that projects that didn't define their own would +use the appropriate defaults.) + +Making "Official" (Non-Snapshot) Releases +----------------------------------------- + +When you make an official release, creating source or binary distributions, +you will need to override the tag settings from ``setup.cfg``, so that you +don't end up registering versions like ``foobar-0.7a1.dev-r34832``. This is +easy to do if you are developing on the trunk and using tags or branches for +your releases - just make the change to ``setup.cfg`` after branching or +tagging the release, so the trunk will still produce development snapshots. + +Alternately, if you are not branching for releases, you can override the +default version options on the command line, using something like:: + + setup.py egg_info -Db "" sdist bdist_egg + +The first part of this command (``egg_info -Db ""``) will override the +configured tag information, before creating source and binary eggs. Thus, these +commands will use the plain version from your ``setup.py``, without adding the +build designation string. + +Of course, if you will be doing this a lot, you may wish to create a personal +alias for this operation, e.g.:: + + setup.py alias -u release egg_info -Db "" + +You can then use it like this:: + + setup.py release sdist bdist_egg + +Or of course you can create more elaborate aliases that do all of the above. +See the sections below on the :ref:`egg_info ` and +:ref:`alias ` commands for more ideas. diff --git a/docs/userguide/entry_point.rst b/docs/userguide/entry_point.rst index b7dd7aa..eff20cf 100644 --- a/docs/userguide/entry_point.rst +++ b/docs/userguide/entry_point.rst @@ -29,7 +29,7 @@ First consider an example without entry points. Imagine a package defined thus:: project_root_directory - ├── setup.py # and/or setup.cfg, pyproject.toml + ├── pyproject.toml # and/or setup.cfg, setup.py └── src └── timmins ├── __init__.py @@ -69,6 +69,13 @@ In the above example, to create a command ``hello-world`` that invokes ``timmins.hello_world``, add a console script entry point to your configuration: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project.scripts] + hello-world = "timmins:hello_world" + .. tab:: setup.cfg .. code-block:: ini @@ -92,13 +99,6 @@ configuration: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project.scripts] - hello-world = "timmins:hello_world" - After installing the package, a user may invoke that function by simply calling ``hello-world`` on the command line: @@ -139,6 +139,13 @@ with an ``__init__.py`` file containing the following: Then, we can add a GUI script entry point: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project.gui-scripts] + hello-world = "timmins:hello_world" + .. tab:: setup.cfg .. code-block:: ini @@ -150,7 +157,7 @@ Then, we can add a GUI script entry point: .. tab:: setup.py .. code-block:: python - + from setuptools import setup setup( @@ -162,13 +169,6 @@ Then, we can add a GUI script entry point: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project.gui-scripts] - hello-world = "timmins:hello_world" - .. note:: To be able to import ``PySimpleGUI``, you need to add ``pysimplegui`` to your package dependencies. See :doc:`/userguide/dependency_management` for more information. @@ -236,7 +236,7 @@ corresponding to plugins. Say we have a package ``timmins`` with the following directory structure:: timmins - ├── setup.py # and/or setup.cfg, pyproject.toml + ├── pyproject.toml # and/or setup.cfg, setup.py └── src └── timmins └── __init__.py @@ -328,7 +328,7 @@ which implements the entry point ``timmins.display``. Let us name this plugin ``timmins-plugin-fancy``, and set it up with the following directory structure:: timmins-plugin-fancy - ├── setup.py # and/or setup.cfg, pyproject.toml + ├── pyproject.toml # and/or setup.cfg, setup.py └── src └── timmins_plugin_fancy └── __init__.py @@ -345,6 +345,14 @@ This is the ``display()``-like function that we are looking to supply to the ``timmins`` package. We can do that by adding the following in the configuration of ``timmins-plugin-fancy``: +.. tab:: pyproject.toml + + .. code-block:: toml + + # Note the quotes around timmins.display in order to escape the dot . + [project.entry-points."timmins.display"] + excl = "timmins_plugin_fancy:excl_display" + .. tab:: setup.cfg .. code-block:: ini @@ -368,14 +376,6 @@ of ``timmins-plugin-fancy``: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - # Note the quotes around timmins.display in order to escape the dot . - [project.entry-points."timmins.display"] - excl = "timmins_plugin_fancy:excl_display" - Basically, this configuration states that we are a supplying an entry point under the group ``timmins.display``. The entry point is named ``excl`` and it refers to the function ``excl_display`` defined by the package ``timmins-plugin-fancy``. @@ -416,6 +416,14 @@ functions, as follows: The configuration of ``timmins-plugin-fancy`` would then change to: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project.entry-points."timmins.display"] + excl = "timmins_plugin_fancy:excl_display" + lined = "timmins_plugin_fancy:lined_display" + .. tab:: setup.cfg .. code-block:: ini @@ -441,14 +449,6 @@ The configuration of ``timmins-plugin-fancy`` would then change to: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project.entry-points."timmins.display"] - excl = "timmins_plugin_fancy:excl_display" - lined = "timmins_plugin_fancy:lined_display" - On the ``timmins`` side, we can also use a different strategy of loading entry points. For example, we can search for a specific display style: @@ -562,11 +562,6 @@ class or module. ---- -.. [#experimental] - Support for specifying package metadata and build configuration options via - ``pyproject.toml`` is experimental and might change - in the future. See :doc:`/userguide/pyproject_config`. - .. [#use_for_scripts] Reference: https://packaging.python.org/en/latest/specifications/entry-points/#use-for-scripts diff --git a/docs/userguide/ext_modules.rst b/docs/userguide/ext_modules.rst new file mode 100644 index 0000000..0467f4e --- /dev/null +++ b/docs/userguide/ext_modules.rst @@ -0,0 +1,150 @@ +========================== +Building Extension Modules +========================== + +Setuptools can build C/C++ extension modules. The keyword argument +``ext_modules`` of ``setup()`` should be a list of instances of the +:class:`setuptools.Extension` class. + + +For example, let's consider a simple project with only one extension module:: + + + ├── pyproject.toml + └── foo.c + +and all project metadata configuration in the ``pyproject.toml`` file: + +.. code-block:: toml + + # pyproject.toml + [build-system] + requires = ["setuptools"] + build-backend = "setuptools.build_meta" + + [project] + name = "mylib-foo" # as it would appear on PyPI + version = "0.42" + +To instruct setuptools to compile the ``foo.c`` file into the extension module +``mylib.foo``, we need to add a ``setup.py`` file similar to the following: + +.. code-block:: python + + from setuptools import Extension, setup + + setup( + ext_modules=[ + Extension( + name="mylib.foo", # as it would be imported + # may include packages/namespaces separated by `.` + + sources=["foo.c"], # all sources are compiled into a single binary file + ), + ] + ) + +.. seealso:: + You can find more information on the `Python docs about C/C++ extensions`_. + Alternatively, you might also be interested in learn about `Cython`_. + + If you plan to distribute a package that uses extensions across multiple + platforms, :pypi:`cibuildwheel` can also be helpful. + +.. important:: + All files used to compile your extension need to be available on the system + when building the package, so please make sure to include some documentation + on how developers interested in building your package from source + can obtain operating system level dependencies + (e.g. compilers and external binary libraries/artifacts). + + You will also need to make sure that all auxiliary files that are contained + inside your :term:`project` (e.g. C headers authored by you or your team) + are configured to be included in your :term:`sdist `. + Please have a look on our section on :ref:`Controlling files in the distribution`. + + +Compiler and linker options +=========================== + +The command ``build_ext`` builds C/C++ extension modules. It creates +a command line for running the compiler and linker by combining +compiler and linker options from various sources: + +.. Reference: `test_customize_compiler` in distutils/tests/test_sysconfig.py + +* the ``sysconfig`` variables ``CC``, ``CXX``, ``CCSHARED``, + ``LDSHARED``, and ``CFLAGS``, +* the environment variables ``CC``, ``CPP``, + ``CXX``, ``LDSHARED`` and ``LDFLAGS``, + ``CFLAGS``, ``CPPFLAGS``, ``LDFLAGS``, +* the ``Extension`` attributes ``include_dirs``, + ``library_dirs``, ``extra_compile_args``, ``extra_link_args``, + ``runtime_library_dirs``. + +.. Ignoring AR, ARFLAGS, RANLIB here because they are used by the (obsolete?) build_clib, not build_ext. + +The resulting command line is then processed by the compiler and linker. +According to the GCC manual sections on `directory options`_ and +`environment variables`_, the C/C++ compiler searches for files named in +``#include `` directives in the following order: + +* first, in directories given by ``-I`` options (in left-to-right order), +* then, in directories given by the environment variable ``CPATH`` (in left-to-right order), +* then, in directories given by ``-isystem`` options (in left-to-right order), +* then, in directories given by the environment variable ``C_INCLUDE_PATH`` (for C) and ``CPLUS_INCLUDE_PATH`` (for C++), +* then, in standard system directories, +* finally, in directories given by ``-idirafter`` options (in left-to-right order). + +The linker searches for libraries in the following order: + +* first, in directories given by ``-L`` options (in left-to-right order), +* then, in directories given by the environment variable ``LIBRARY_PATH`` (in left-to-right order). + + +Distributing Extensions compiled with Cython +============================================ + +When your :pypi:`Cython` extension modules *are declared using the* +:class:`setuptools.Extension` *class*, ``setuptools`` will detect at build time +whether Cython is installed or not. + +If Cython is present, then ``setuptools`` will use it to build the ``.pyx`` files. +Otherwise, ``setuptools`` will try to find and compile the equivalent ``.c`` files +(instead of ``.pyx``). These files can be generated using the +`cython command line tool`_. + +You can ensure that Cython is always automatically installed into the build +environment by including it as a :ref:`build dependency ` in +your ``pyproject.toml``: + +.. code-block:: toml + + [build-system] + requires = [..., "cython"] + +Alternatively, you can include the ``.c`` code that is pre-compiled by Cython +into your source distribution, alongside the original ``.pyx`` files (this +might save a few seconds when building from an ``sdist``). +To improve version compatibility, you probably also want to include current +``.c`` files in your :wiki:`revision control system`, and rebuild them whenever +you check changes in for the ``.pyx`` source files. +This will ensure that people tracking your project will be able to build it +without installing Cython, and that there will be no variation due to small +differences in the generate C files. +Please checkout our docs on :ref:`controlling files in the distribution` for +more information. + +---- + +Extension API Reference +======================= + +.. autoclass:: setuptools.Extension + + +.. _Python docs about C/C++ extensions: https://docs.python.org/3/extending/extending.html +.. _Cython: https://cython.readthedocs.io/en/stable/index.html +.. _directory options: https://gcc.gnu.org/onlinedocs/gcc/Directory-Options.html +.. _environment variables: https://gcc.gnu.org/onlinedocs/gcc/Environment-Variables.html> +.. _cython command line tool: https://cython.readthedocs.io/en/stable/src/userguide/source_files_and_compilation.html diff --git a/docs/userguide/index.rst b/docs/userguide/index.rst index d5d150a..c928f02 100644 --- a/docs/userguide/index.rst +++ b/docs/userguide/index.rst @@ -28,13 +28,13 @@ Contents package_discovery entry_point dependency_management + ext_modules datafiles development_mode distribution extension declarative_config pyproject_config - commands miscellaneous --- diff --git a/docs/userguide/miscellaneous.rst b/docs/userguide/miscellaneous.rst index 776f12f..19908e0 100644 --- a/docs/userguide/miscellaneous.rst +++ b/docs/userguide/miscellaneous.rst @@ -5,30 +5,66 @@ Controlling files in the distribution For the most common use cases, ``setuptools`` will automatically find out which files are necessary for distributing the package. -This includes all :term:`pure Python modules ` in the +These include all :term:`pure Python modules ` in the ``py_modules`` or ``packages`` configuration, and the C sources (but not C -headers) listed as part of extensions when creating a :term:`Source -Distribution (or "sdist")`. +headers) listed as part of extensions when creating a :term:`source +distribution (or "sdist")`. However, when building more complex packages (e.g. packages that include non-Python files, or that need to use custom C headers), you might find that not all files present in your project folder are included in package :term:`distribution archive `. -In these situations you can use a ``setuptools`` -:ref:`plugin `, -such as :pypi:`setuptools-scm` or :pypi:`setuptools-svn` to automatically -include all files tracked by your Revision Control System into the ``sdist``. +If you are using a :wiki:`Revision Control System`, such as git_ or mercurial_, +and your source distributions only need to include files that you're +tracking in revision control, you can use a ``setuptools`` :ref:`plugin `, such as :pypi:`setuptools-scm` or +:pypi:`setuptools-svn` to automatically include all tracked files into the ``sdist``. .. _Using MANIFEST.in: -Alternatively, if you need finer control, you can add a ``MANIFEST.in`` file at -the root of your project. +Alternatively, if you need finer control over the files (e.g. you don't want to +distribute :wiki:`CI/CD`-related files) or you need automatically generated files, +you can add a ``MANIFEST.in`` file at the root of your project, +to specify any files that the default file location algorithm doesn't catch. + This file contains instructions that tell ``setuptools`` which files exactly should be part of the ``sdist`` (or not). A comprehensive guide to ``MANIFEST.in`` syntax is available at the :doc:`PyPA's Packaging User Guide `. +.. attention:: + Please note that ``setuptools`` supports the ``MANIFEST.in``, + and not ``MANIFEST`` (no extension). Any documentation, tutorial or example + that recommends using ``MANIFEST`` (no extension) is likely outdated. + +.. tip:: + The ``MANIFEST.in`` file contains commands that allow you to discover and + manipulate lists of files. There are many commands that can be used with + different objectives, but you should try to not make your ``MANIFEST.in`` + file too fine grained. + + A good idea is to start with a ``graft`` command (to add all + files inside a set of directories) and then fine tune the file selection + by removing the excess or adding isolated files. + +An example of ``MANIFEST.in`` for a simple project that organized according to a +:ref:`src-layout` is: + +.. code-block:: bash + + # MANIFEST.in -- just for illustration + graft src + graft tests + graft docs + # `-> adds all files inside a directory + + include tox.ini + # `-> matches file paths relative to the root of the project + + global-exclude *~ *.py[cod] *.so + # `-> matches file names (regardless of directory) + Once the correct files are present in the ``sdist``, they can then be used by binary extensions during the build process, or included in the final :term:`wheel ` [#build-process]_ if you configure ``setuptools`` with @@ -59,3 +95,6 @@ binary extensions during the build process, or included in the final and is ready to be unpacked into a running installation of Python or :term:`Virtual Environment`. Therefore it only contains items that are required during runtime. + +.. _git: https://git-scm.com +.. _mercurial: https://www.mercurial-scm.org diff --git a/docs/userguide/package_discovery.rst b/docs/userguide/package_discovery.rst index 4391aa1..5732b6b 100644 --- a/docs/userguide/package_discovery.rst +++ b/docs/userguide/package_discovery.rst @@ -40,7 +40,7 @@ Normally, you would specify the packages to be included manually in the followin packages=['mypkg', 'mypkg.subpkg1', 'mypkg.subpkg2'] ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -93,7 +93,7 @@ exactly to the directory structure, you also need to configure ``package_dir``: # ... ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -128,7 +128,7 @@ the following sections. Automatic discovery =================== -.. warning:: Automatic discovery is an **experimental** feature and might change +.. warning:: Automatic discovery is an **beta** feature and might change (or be completely removed) in the future. See :ref:`custom-discovery` for a stable way of configuring ``setuptools``. @@ -276,7 +276,7 @@ the provided tools for package discovery: # or from setuptools import find_namespace_packages -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -349,7 +349,7 @@ in ``src`` that start with the name ``pkg`` and not ``additional``: ``pkg.namespace`` is ignored by ``find_packages()`` (see ``find_namespace_packages()`` below). -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -458,7 +458,7 @@ distribution, then you will need to specify: On the other hand, ``find_namespace_packages()`` will scan all directories. -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ +.. tab:: pyproject.toml (**BETA**) [#beta]_ .. code-block:: toml @@ -585,10 +585,10 @@ The project layout remains the same and ``setup.cfg`` remains the same. ---- -.. [#experimental] - Support for specifying package metadata and build configuration options via - ``pyproject.toml`` is experimental and might change - in the future. See :doc:`/userguide/pyproject_config`. +.. [#beta] + Support for adding build configuration options via the ``[tool.setuptools]`` + table in the ``pyproject.toml`` file is still in **beta** stage. + See :doc:`/userguide/pyproject_config`. .. [#layout1] https://blog.ionelmc.ro/2014/05/25/python-packaging/#the-structure .. [#layout2] https://blog.ionelmc.ro/2017/09/25/rehashing-the-src-layout/ diff --git a/docs/userguide/pyproject_config.rst b/docs/userguide/pyproject_config.rst index 8558f5d..2b0f9cb 100644 --- a/docs/userguide/pyproject_config.rst +++ b/docs/userguide/pyproject_config.rst @@ -4,13 +4,7 @@ Configuring setuptools using ``pyproject.toml`` files ----------------------------------------------------- -.. note:: New in 61.0.0 (**experimental**) - -.. warning:: - Support for declaring :doc:`project metadata - ` or configuring - ``setuptools`` via ``pyproject.toml`` files is still experimental and might - change in future releases. +.. note:: New in 61.0.0 .. important:: For the time being, ``pip`` still might require a ``setup.py`` file @@ -75,6 +69,11 @@ The ``project`` table contains metadata fields as described by Setuptools-specific configuration ================================= +.. warning:: + Support for declaring configurations not standardized by :pep:`621` + (i.e. the ``[tool.setuptools]`` table), + is still in **beta** stage and might change in future releases. + While the standard ``project`` table in the ``pyproject.toml`` file covers most of the metadata used during the packaging process, there are still some ``setuptools``-specific configurations that can be set by users that require @@ -100,7 +99,7 @@ Key Value Type (TOML) Notes ``exclude-package-data`` table/inline-table ``license-files`` array of glob patterns **Provisional** - likely to change with :pep:`639` (by default: ``['LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*']``) -``data-files`` table/inline-table **Deprecated** - check :doc:`/userguide/datafiles` +``data-files`` table/inline-table **Discouraged** - check :doc:`/userguide/datafiles` ``script-files`` array **Deprecated** - equivalent to the ``script`` keyword in ``setup.py`` (should be avoided in favour of ``project.scripts``) ``provides`` array **Ignored by pip** diff --git a/docs/userguide/quickstart.rst b/docs/userguide/quickstart.rst index 0b75947..6c39c35 100644 --- a/docs/userguide/quickstart.rst +++ b/docs/userguide/quickstart.rst @@ -5,29 +5,47 @@ Quickstart Installation ============ -To install the latest version of setuptools, use:: +You can install the latest version of ``setuptools`` using :pypi:`pip`:: pip install --upgrade setuptools +Most of the times, however, you don't have to... -Python packaging at a glance -============================ -The landscape of Python packaging is shifting and ``Setuptools`` has evolved to -only provide backend support, no longer being the de-facto packaging tool in -the market. Every python package must provide a ``pyproject.toml`` and specify +Instead, when creating new Python packages, it is recommended to use +a command line tool called :pypi:`build`. This tool will automatically download +``setuptools`` and any other build-time dependencies that your project might +have. You just need to specify them in a ``pyproject.toml`` file at the root of +your package, as indicated in the :ref:`following section `. + +.. _install-build: + +You can also :doc:`install build ` using :pypi:`pip`:: + + pip install --upgrade build + +This will allow you to run the command: ``python -m build``. + +.. important:: + Please note that some operating systems might be equipped with + the ``python3`` and ``pip3`` commands instead of ``python`` and ``pip`` + (but they should be equivalent). + If you don't have ``pip`` or ``pip3`` available in your system, please + check out :doc:`pip installation docs `. + + +Every python package must provide a ``pyproject.toml`` and specify the backend (build system) it wants to use. The distribution can then be generated with whatever tool that provides a ``build sdist``-like -functionality. While this may appear cumbersome, given the added pieces, -it in fact tremendously enhances the portability of your package. The -change is driven under :pep:`PEP 517 <517#build-requirements>`. To learn more about Python packaging in general, -navigate to the :ref:`bottom ` of this page. +functionality. + +.. _basic-use: Basic Use ========= -For basic use of setuptools, you will need a ``pyproject.toml`` with the -exact following info, which declares you want to use ``setuptools`` to -package your project: + +When creating a Python package, you must provide a ``pyproject.toml`` file +containing a ``build-system`` section similar to the example below: .. code-block:: toml @@ -35,14 +53,32 @@ package your project: requires = ["setuptools"] build-backend = "setuptools.build_meta" -Then, you will need to specify your package information such as metadata, -contents, dependencies, etc. +This section declares what are your build system dependencies, and which +library will be used to actually do the packaging. + +In addition to specifying a build system, you also will need to add +some package information such as metadata, contents, dependencies, etc. +This can be done in the same ``pyproject.toml`` [#beta]_ file, +or in a separated one: ``setup.cfg`` or ``setup.py`` (please note however +that configuring new projects via ``setup.py`` is discouraged [#setup.py]_). -Setuptools currently supports configurations from either ``setup.cfg``, -``setup.py`` or ``pyproject.toml`` [#experimental]_ files, however, configuring new -projects via ``setup.py`` is discouraged [#setup.py]_. +The following example demonstrates a minimum configuration +(which assumes the project depends on :pypi:`requests` and +:pypi:`importlib-metadata` to be able to run): -The following example demonstrates a minimum configuration: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project] + name = "mypackage" + version = "0.0.1" + dependencies = [ + "requests", + 'importlib-metadata; python_version<"3.8"', + ] + + See :doc:`/userguide/pyproject_config` for more information. .. tab:: setup.cfg @@ -53,7 +89,6 @@ The following example demonstrates a minimum configuration: version = 0.0.1 [options] - packages = mypackage install_requires = requests importlib-metadata; python_version < "3.8" @@ -69,7 +104,6 @@ The following example demonstrates a minimum configuration: setup( name='mypackage', version='0.0.1', - packages=['mypackage'], install_requires=[ 'requests', 'importlib-metadata; python_version == "3.8"', @@ -78,51 +112,74 @@ The following example demonstrates a minimum configuration: See :doc:`/references/keywords` for more information. -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - name = "mypackage" - version = "0.0.1" - dependencies = [ - "requests", - 'importlib-metadata; python_version<"3.8"', - ] - - See :doc:`/userguide/pyproject_config` for more information. - -This is what your project would look like:: +Finally, you will need to organize your Python code to make it ready for +distributing into something that looks like the following +(optional files marked with ``#``):: - ~/mypackage/ - pyproject.toml - setup.cfg # or setup.py - mypackage/__init__.py + mypackage + ├── pyproject.toml + | # setup.cfg or setup.py (depending on the confuguration method) + | # README.rst or README.md (a nice description of your package) + | # LICENCE (properly chosen license information, e.g. MIT, BSD-3, GPL-3, MPL-2, etc...) + └── mypackage + ├── __init__.py + └── ... (other Python files) -Then, you need a builder, such as :std:doc:`PyPA build ` -which you can obtain via ``pip install build``. After downloading it, invoke -the builder:: +With :ref:`build installed in you system `, you can then run:: python -m build -You now have your distribution ready (e.g. a ``tar.gz`` file and a ``.whl`` -file in the ``dist`` directory), which you can upload to PyPI! +You now have your distribution ready (e.g. a ``tar.gz`` file and a ``.whl`` file +in the ``dist`` directory), which you can :doc:`upload ` to PyPI_! -Of course, before you release your project to PyPI, you'll want to add a bit -more information to your setup script to help people find or learn about your -project. And maybe your project will have grown by then to include a few +Of course, before you release your project to PyPI_, you'll want to add a bit +more information to help people find or learn about your project. +And maybe your project will have grown by then to include a few dependencies, and perhaps some data files and scripts. In the next few sections, we will walk through the additional but essential information you need to specify to properly package your project. -Automatic package discovery -=========================== -For simple projects, it's usually easy enough to manually add packages to -the ``packages`` keyword in ``setup.cfg``. However, for very large projects, -it can be a big burden to keep the package list updated. -Therefore, ``setuptools`` provides a convenient way to automatically list all -the packages in your project directory: +.. + TODO: A previous generation of this document included a section called + "Python packaging at a glance". This is a nice title, but the content + removed because it assumed the reader had familiarity with the history of + setuptools and PEP 517. We should take advantage of this nice title and add + this section back, but use it to explain important concepts of the + ecosystem, such as "sdist", "wheel", "index". It would also be nice if we + could have a diagram for that (explaining for example that "wheels" are + built from "sdists" not the source tree). + + +Overview +======== + +Package discovery +----------------- +For projects that follow a simple directory structure, ``setuptools`` should be +able to automatically detect all :term:`packages ` and +:term:`namespaces `. However, complex projects might include +additional folders and supporting files that not necessarily should be +distributed (or that can confuse ``setuptools`` auto discovery algorithm). + +Therefore, ``setuptools`` provides a convenient way to customize +which packages should be distributed and in which directory they should be +found, as shown in the example below: + +.. tab:: pyproject.toml (**BETA**) [#beta]_ + + .. code-block:: toml + + # ... + [tool.setuptools.packages] + find = {} # Scan the project directory with the default parameters + + # OR + [tool.setuptools.packages.find] + where = ["src"] # ["."] by default + include = ["mypackage*"] # ["*"] by default + exclude = ["mypackage.tests*"] # empty by default + namespaces = false # true by default .. tab:: setup.cfg @@ -154,28 +211,13 @@ the packages in your project directory: # ... ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - # ... - [tool.setuptools.packages] - find = {} # Scan the project directory with the default parameters - - # OR - [tool.setuptools.packages.find] - where = ["src"] # ["."] by default - include = ["mypackage*"] # ["*"] by default - exclude = ["mypackage.tests*"] # empty by default - namespaces = false # true by default - When you pass the above information, alongside other necessary information, ``setuptools`` walks through the directory specified in ``where`` (omitted here as the package resides in the current directory) and filters the packages -it can find following the ``include`` (defaults to none), then removes -those that match the ``exclude`` and returns a list of Python packages. The above -setup also allows you to adopt a ``src/`` layout. For more details and advanced -use, go to :ref:`package_discovery`. +it can find following the ``include`` patterns (defaults to ``*``), then it removes +those that match the ``exclude`` patterns and returns a list of Python packages. + +For more details and advanced use, go to :ref:`package_discovery`. .. tip:: Starting with version 61.0.0, setuptools' automatic discovery capabilities @@ -183,19 +225,28 @@ use, go to :ref:`package_discovery`. :ref:`flat-layout` and :ref:`src-layout`) without requiring any special configuration. Check out our :ref:`reference docs ` for more information, but please keep in mind that this functionality is - still considered **experimental** and might change (or even be removed) in - future releases. + still considered **beta** and might change in future releases. Entry points and automatic script creation -=========================================== -Setuptools supports automatic creation of scripts upon installation, that runs +------------------------------------------- +Setuptools supports automatic creation of scripts upon installation, that run code within your package if you specify them as :doc:`entry points `. -This is what allows you to run commands like ``pip install`` instead of having +An example of how this feature can be used in ``pip``: +it allows you to run commands like ``pip install`` instead of having to type ``python -m pip install``. + The following configuration examples show how to accomplish this: + +.. tab:: pyproject.toml + + .. code-block:: toml + + [project.scripts] + cli-name = "mypkg.mymodule:some_func" + .. tab:: setup.cfg .. code-block:: ini @@ -217,13 +268,6 @@ The following configuration examples show how to accomplish this: } ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project.scripts] - cli-name = "mypkg.mymodule:some_func" - When this project is installed, a ``cli-name`` executable will be created. ``cli-name`` will invoke the function ``some_func`` in the ``mypkg/mymodule.py`` file when called by the user. @@ -233,11 +277,23 @@ For detailed usage, go to :doc:`entry_point`. Dependency management -===================== +--------------------- Packages built with ``setuptools`` can specify dependencies to be automatically installed when the package itself is installed. The example below show how to configure this kind of dependencies: +.. tab:: pyproject.toml + + .. code-block:: toml + + [project] + # ... + dependencies = [ + "docutils", + "requires <= 0.4", + ] + # ... + .. tab:: setup.cfg .. code-block:: ini @@ -257,18 +313,6 @@ The example below show how to configure this kind of dependencies: # ... ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [project] - # ... - dependencies = [ - "docutils", - "requires <= 0.4", - ] - # ... - Each dependency is represented by a string that can optionally contain version requirements (e.g. one of the operators <, >, <=, >=, == or !=, followed by a version identifier), and/or conditional environment markers, e.g. ``sys_platform == "win32"`` @@ -285,10 +329,20 @@ For more advanced use, see :doc:`dependency_management`. .. _Including Data Files: Including Data Files -==================== +-------------------- Setuptools offers three ways to specify data files to be included in your packages. For the simplest use, you can simply use the ``include_package_data`` keyword: +.. tab:: pyproject.toml (**BETA**) [#beta]_ + + .. code-block:: toml + + [tool.setuptools] + include-package-data = true + # This is already the default behaviour if your are using + # pyproject.toml to configure your build. + # You can deactivate that with `include-package-data = false` + .. tab:: setup.cfg .. code-block:: ini @@ -306,16 +360,6 @@ For the simplest use, you can simply use the ``include_package_data`` keyword: # ... ) -.. tab:: pyproject.toml (**EXPERIMENTAL**) [#experimental]_ - - .. code-block:: toml - - [tool.setuptools] - include-package-data = true - # This is already the default behaviour if your are using - # pyproject.toml to configure your build. - # You can deactivate that with `include-package-data = false` - This tells setuptools to install any data files it finds in your packages. The data files must be specified via the |MANIFEST.in|_ file or automatically added by a :ref:`Revision Control System plugin @@ -324,7 +368,7 @@ For more details, see :doc:`datafiles`. Development mode -================ +---------------- ``setuptools`` allows you to install a package without copying any files to your interpreter directory (e.g. the ``site-packages`` directory). @@ -361,7 +405,7 @@ associate with your source code. For more information, see :doc:`development_mod Uploading your package to PyPI -============================== +------------------------------ After generating the distribution files, the next step would be to upload your distribution so others can use it. This functionality is provided by :pypi:`twine` and is documented in the :doc:`Python packaging tutorial @@ -369,7 +413,7 @@ distribution so others can use it. This functionality is provided by Transitioning from ``setup.py`` to ``setup.cfg`` -================================================ +------------------------------------------------ To avoid executing arbitrary scripts and boilerplate code, we are transitioning into a full-fledged ``setup.cfg`` to declare your package information instead of running ``setup()``. This inevitably brings challenges due to a different @@ -404,9 +448,9 @@ up-to-date references that can help you when it is time to distribute your work. ` and use ``setup.py`` only for the parts not supported in those files (e.g. C extensions). -.. [#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 in future releases. +.. [#beta] + Support for adding build configuration options via the ``[tool.setuptools]`` + table in the ``pyproject.toml`` file is still in **beta** stage. See :doc:`/userguide/pyproject_config`. + +.. _PyPI: https://pypi.org diff --git a/pytest.ini b/pytest.ini index 14c7e94..7c86396 100644 --- a/pytest.ini +++ b/pytest.ini @@ -59,4 +59,4 @@ filterwarnings= ignore:Distutils was imported before setuptools ignore:Setuptools is replacing distutils - ignore:Support for project metadata in .pyproject.toml. is still experimental + ignore:Support for .* in .pyproject.toml. is still .beta. diff --git a/setup.cfg b/setup.cfg index 63b86a4..da05cdf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = setuptools -version = 62.4.0 +version = 62.5.0 author = Python Packaging Authority author_email = distutils-sig@python.org description = Easily download, build, install, upgrade, and uninstall Python packages diff --git a/setuptools.egg-info/PKG-INFO b/setuptools.egg-info/PKG-INFO index 372fb81..55af556 100644 --- a/setuptools.egg-info/PKG-INFO +++ b/setuptools.egg-info/PKG-INFO @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: setuptools -Version: 62.4.0 +Version: 62.5.0 Summary: Easily download, build, install, upgrade, and uninstall Python packages Home-page: https://github.com/pypa/setuptools Author: Python Packaging Authority diff --git a/setuptools.egg-info/SOURCES.txt b/setuptools.egg-info/SOURCES.txt index ae08751..843e825 100644 --- a/setuptools.egg-info/SOURCES.txt +++ b/setuptools.egg-info/SOURCES.txt @@ -24,6 +24,7 @@ docs/python 2 sunset.rst docs/roadmap.rst docs/setuptools.rst docs/deprecated/changed_keywords.rst +docs/deprecated/commands.rst docs/deprecated/dependency_links.rst docs/deprecated/distutils-legacy.rst docs/deprecated/easy_install.rst @@ -31,7 +32,6 @@ docs/deprecated/functionalities.rst docs/deprecated/index.rst docs/deprecated/python_eggs.rst docs/deprecated/resource_extraction.rst -docs/deprecated/running_commands.rst docs/deprecated/zip_safe.rst docs/deprecated/distutils/_setuptools_disclaimer.rst docs/deprecated/distutils/apiref.rst @@ -50,13 +50,13 @@ docs/development/developer-guide.rst docs/development/index.rst docs/development/releases.rst docs/references/keywords.rst -docs/userguide/commands.rst docs/userguide/datafiles.rst docs/userguide/declarative_config.rst docs/userguide/dependency_management.rst docs/userguide/development_mode.rst docs/userguide/distribution.rst docs/userguide/entry_point.rst +docs/userguide/ext_modules.rst docs/userguide/extension.rst docs/userguide/index.rst docs/userguide/miscellaneous.rst diff --git a/setuptools/config/_apply_pyprojecttoml.py b/setuptools/config/_apply_pyprojecttoml.py index 3bf8cc2..8af5561 100644 --- a/setuptools/config/_apply_pyprojecttoml.py +++ b/setuptools/config/_apply_pyprojecttoml.py @@ -4,6 +4,8 @@ metadata objects. The distribution and metadata objects are modeled after (an old version of) core metadata, therefore configs in the format specified for ``pyproject.toml`` need to be processed before being applied. + +**PRIVATE MODULE**: API reserved for setuptools internal usage only. """ import logging import os @@ -354,7 +356,7 @@ class _WouldIgnoreField(UserWarning): `{field} = {value!r}` - According to the spec (see the link bellow), however, setuptools CANNOT + According to the spec (see the link below), however, setuptools CANNOT consider this value unless {field!r} is listed as `dynamic`. https://packaging.python.org/en/latest/specifications/declaring-project-metadata/ diff --git a/setuptools/config/expand.py b/setuptools/config/expand.py index da55d4e..be987df 100644 --- a/setuptools/config/expand.py +++ b/setuptools/config/expand.py @@ -14,6 +14,8 @@ We can split the process of interpreting configuration files into 2 steps: This module focus on the second step, and therefore allow sharing the expansion functions among several configuration file formats. + +**PRIVATE MODULE**: API reserved for setuptools internal usage only. """ import ast import importlib diff --git a/setuptools/config/pyprojecttoml.py b/setuptools/config/pyprojecttoml.py index 976eb06..7587607 100644 --- a/setuptools/config/pyprojecttoml.py +++ b/setuptools/config/pyprojecttoml.py @@ -1,4 +1,8 @@ -"""Load setuptools configuration from ``pyproject.toml`` files""" +""" +Load setuptools configuration from ``pyproject.toml`` files. + +**PRIVATE MODULE**: API reserved for setuptools internal usage only. +""" import logging import os import warnings @@ -94,12 +98,10 @@ def read_configuration( if not asdict or not (project_table or setuptools_table): return {} # User is not using pyproject to configure setuptools - # TODO: Remove the following once the feature stabilizes: - msg = ( - "Support for project metadata in `pyproject.toml` is still experimental " - "and may be removed (or change) in future releases." - ) - warnings.warn(msg, _ExperimentalProjectMetadata) + if setuptools_table: + # TODO: Remove the following once the feature stabilizes: + msg = "Support for `[tool.setuptools]` in `pyproject.toml` is still *beta*." + warnings.warn(msg, _BetaConfiguration) # There is an overall sense in the community that making include_package_data=True # the default would be an improvement. @@ -413,8 +415,8 @@ class _EnsurePackagesDiscovered(_expand.EnsurePackagesDiscovered): return super().__exit__(exc_type, exc_value, traceback) -class _ExperimentalProjectMetadata(UserWarning): - """Explicitly inform users that `pyproject.toml` configuration is experimental""" +class _BetaConfiguration(UserWarning): + """Explicitly inform users that some `pyproject.toml` configuration is *beta*""" class _InvalidFile(UserWarning): diff --git a/setuptools/config/setupcfg.py b/setuptools/config/setupcfg.py index b2d5c34..6b874d1 100644 --- a/setuptools/config/setupcfg.py +++ b/setuptools/config/setupcfg.py @@ -1,4 +1,8 @@ -"""Load setuptools configuration from ``setup.cfg`` files""" +""" +Load setuptools configuration from ``setup.cfg`` files. + +**API will be made private in the future** +""" import os import warnings diff --git a/setuptools/extension.py b/setuptools/extension.py index f696c9c..64baf11 100644 --- a/setuptools/extension.py +++ b/setuptools/extension.py @@ -28,7 +28,92 @@ _Extension = get_unpatched(distutils.core.Extension) class Extension(_Extension): - """Extension that uses '.c' files in place of '.pyx' files""" + """ + Describes a single extension module. + + This means that all source files will be compiled into a single binary file + ``.`` (with ```` derived from ``name`` and + ```` defined by one of the values in + ``importlib.machinery.EXTENSION_SUFFIXES``). + + In the case ``.pyx`` files are passed as ``sources and`` ``Cython`` is **not** + installed in the build environment, ``setuptools`` may also try to look for the + equivalent ``.cpp`` or ``.c`` files. + + :arg str name: + the full name of the extension, including any packages -- ie. + *not* a filename or pathname, but Python dotted name + + :arg list[str] sources: + list of source filenames, relative to the distribution root + (where the setup script lives), in Unix form (slash-separated) + for portability. Source files may be C, C++, SWIG (.i), + platform-specific resource files, or whatever else is recognized + by the "build_ext" command as source for a Python extension. + + :keyword list[str] include_dirs: + list of directories to search for C/C++ header files (in Unix + form for portability) + + :keyword list[tuple[str, str|None]] define_macros: + list of macros to define; each macro is defined using a 2-tuple: + the first item corresponding to the name of the macro and the second + item either a string with its value or None to + define it without a particular value (equivalent of "#define + FOO" in source or -DFOO on Unix C compiler command line) + + :keyword list[str] undef_macros: + list of macros to undefine explicitly + + :keyword list[str] library_dirs: + list of directories to search for C/C++ libraries at link time + + :keyword list[str] libraries: + list of library names (not filenames or paths) to link against + + :keyword list[str] runtime_library_dirs: + list of directories to search for C/C++ libraries at run time + (for shared extensions, this is when the extension is loaded) + + :keyword list[str] extra_objects: + list of extra files to link with (eg. object files not implied + by 'sources', static library that must be explicitly specified, + binary resource files, etc.) + + :keyword list[str] extra_compile_args: + any extra platform- and compiler-specific information to use + when compiling the source files in 'sources'. For platforms and + compilers where "command line" makes sense, this is typically a + list of command-line arguments, but for other platforms it could + be anything. + + :keyword list[str] extra_link_args: + any extra platform- and compiler-specific information to use + when linking object files together to create the extension (or + to create a new static Python interpreter). Similar + interpretation as for 'extra_compile_args'. + + :keyword list[str] export_symbols: + list of symbols to be exported from a shared extension. Not + used on all platforms, and not generally necessary for Python + extensions, which typically export exactly one symbol: "init" + + extension_name. + + :keyword list[str] swig_opts: + any extra options to pass to SWIG if a source file has the .i + extension. + + :keyword list[str] depends: + list of files that the extension depends on + + :keyword str language: + extension language (i.e. "c", "c++", "objc"). Will be detected + from the source extensions if not provided. + + :keyword bool optional: + specifies that a build failure in the extension should not abort the + build process, but simply not install the failing extension. + """ def __init__(self, name, sources, *args, **kw): # The *args is needed for compatibility as calls may use positional diff --git a/setuptools/logging.py b/setuptools/logging.py index 15b5761..5d41c98 100644 --- a/setuptools/logging.py +++ b/setuptools/logging.py @@ -12,7 +12,7 @@ def configure(): """ Configure logging to emit warning and above to stderr and everything else to stdout. This behavior is provided - for compatibilty with distutils.log but may change in + for compatibility with distutils.log but may change in the future. """ err_handler = logging.StreamHandler() diff --git a/setuptools/tests/test_easy_install.py b/setuptools/tests/test_easy_install.py index 73a8dff..246d634 100644 --- a/setuptools/tests/test_easy_install.py +++ b/setuptools/tests/test_easy_install.py @@ -1179,7 +1179,7 @@ def test_editable_user_and_build_isolation(setup_context, monkeypatch, tmp_path) # == Arrange == # Pretend that build isolation was enabled - # e.g pip sets the environment varible PYTHONNOUSERSITE=1 + # e.g pip sets the environment variable PYTHONNOUSERSITE=1 monkeypatch.setattr('site.ENABLE_USER_SITE', False) # Patching $HOME for 2 reasons: -- 2.7.4