diff options
author | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-04-20 14:24:08 +0100 |
---|---|---|
committer | Anderson Bravalheri <andersonbravalheri@gmail.com> | 2023-04-20 14:24:08 +0100 |
commit | 73b5c62939cd2ef039da9d5f784083fa956ef4a1 (patch) | |
tree | c24848a7a4c705362a9c076f2c7b16aec8d752c7 /setuptools/command | |
parent | 000efbfae1e79d0a9fa9b16b55c4f5a2e90a64dd (diff) | |
parent | 54da8b6d69b7333424eff305218a10d9605a7e36 (diff) | |
download | python-setuptools-git-73b5c62939cd2ef039da9d5f784083fa956ef4a1.tar.gz |
Overhaul for better visibility of warnings (#3849)
Diffstat (limited to 'setuptools/command')
-rw-r--r-- | setuptools/command/bdist_rpm.py | 15 | ||||
-rw-r--r-- | setuptools/command/build.py | 19 | ||||
-rw-r--r-- | setuptools/command/build_py.py | 72 | ||||
-rw-r--r-- | setuptools/command/dist_info.py | 7 | ||||
-rw-r--r-- | setuptools/command/easy_install.py | 33 | ||||
-rw-r--r-- | setuptools/command/editable_wheel.py | 131 | ||||
-rw-r--r-- | setuptools/command/egg_info.py | 52 | ||||
-rw-r--r-- | setuptools/command/install.py | 22 | ||||
-rw-r--r-- | setuptools/command/upload_docs.py | 15 |
9 files changed, 210 insertions, 156 deletions
diff --git a/setuptools/command/bdist_rpm.py b/setuptools/command/bdist_rpm.py index 98bf5dea..047a6d08 100644 --- a/setuptools/command/bdist_rpm.py +++ b/setuptools/command/bdist_rpm.py @@ -1,7 +1,6 @@ import distutils.command.bdist_rpm as orig -import warnings -from setuptools import SetuptoolsDeprecationWarning +from ..warnings import SetuptoolsDeprecationWarning class bdist_rpm(orig.bdist_rpm): @@ -14,10 +13,14 @@ class bdist_rpm(orig.bdist_rpm): """ def run(self): - warnings.warn( - "bdist_rpm is deprecated and will be removed in a future " - "version. Use bdist_wheel (wheel packages) instead.", - SetuptoolsDeprecationWarning, + SetuptoolsDeprecationWarning.emit( + "Deprecated command", + """ + bdist_rpm is deprecated and will be removed in a future version. + Use bdist_wheel (wheel packages) instead. + """, + see_url="https://github.com/pypa/setuptools/issues/1988", + due_date=(2023, 10, 30) # Deprecation introduced in 22 Oct 2021. ) # ensure distro name is up-to-date diff --git a/setuptools/command/build.py b/setuptools/command/build.py index fa3c99ef..0f1d688e 100644 --- a/setuptools/command/build.py +++ b/setuptools/command/build.py @@ -1,9 +1,8 @@ import sys -import warnings from typing import TYPE_CHECKING, List, Dict from distutils.command.build import build as _build -from setuptools import SetuptoolsDeprecationWarning +from ..warnings import SetuptoolsDeprecationWarning if sys.version_info >= (3, 8): from typing import Protocol @@ -23,12 +22,16 @@ class build(_build): def get_sub_commands(self): subcommands = {cmd[0] for cmd in _build.sub_commands} if subcommands - _ORIGINAL_SUBCOMMANDS: - msg = """ - It seems that you are using `distutils.command.build` to add - new subcommands. Using `distutils` directly is considered deprecated, - please use `setuptools.command.build`. - """ - warnings.warn(msg, SetuptoolsDeprecationWarning) + SetuptoolsDeprecationWarning.emit( + "Direct usage of `distutils` commands", + """ + It seems that you are using `distutils.command.build` to add + new subcommands. Using `distutils` directly is considered deprecated, + please use `setuptools.command.build`. + """, + due_date=(2023, 12, 13), # Warning introduced in 13 Jun 2022. + see_url="https://peps.python.org/pep-0632/", + ) self.sub_commands = _build.sub_commands return super().get_sub_commands() diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index ec062742..f094496e 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -9,12 +9,11 @@ import io import distutils.errors import itertools import stat -import warnings from pathlib import Path from typing import Dict, Iterable, Iterator, List, Optional, Tuple -from setuptools._deprecation_warning import SetuptoolsDeprecationWarning -from setuptools.extern.more_itertools import unique_everseen +from ..extern.more_itertools import unique_everseen +from ..warnings import SetuptoolsDeprecationWarning def make_writable(target): @@ -325,28 +324,48 @@ def assert_relative(path): class _IncludePackageDataAbuse: """Inform users that package or module is included as 'data file'""" - MESSAGE = """\ - Installing {importable!r} as data is deprecated, please list it in `packages`. - !!\n\n - ############################ - # Package would be ignored # - ############################ - Python recognizes {importable!r} as an importable package, - but it is not listed in the `packages` configuration of setuptools. - - {importable!r} has been automatically added to the distribution only - because it may contain data files, but this behavior is likely to change - in future versions of setuptools (and therefore is considered deprecated). - - Please make sure that {importable!r} is included as a package by using - the `packages` configuration field or the proper discovery methods - (for example by using `find_namespace_packages(...)`/`find_namespace:` - instead of `find_packages(...)`/`find:`). - - You can read more about "package discovery" and "data files" on setuptools - documentation page. - \n\n!! - """ + class _Warning(SetuptoolsDeprecationWarning): + _SUMMARY = """ + Package {importable!r} is absent from the `packages` configuration. + """ + + _DETAILS = """ + ############################ + # Package would be ignored # + ############################ + Python recognizes {importable!r} as an importable package[^1], + but it is absent from setuptools' `packages` configuration. + + This leads to an ambiguous overall configuration. If you want to distribute this + package, please make sure that {importable!r} is explicitly added + to the `packages` configuration field. + + Alternatively, you can also rely on setuptools' discovery methods + (for example by using `find_namespace_packages(...)`/`find_namespace:` + instead of `find_packages(...)`/`find:`). + + You can read more about "package discovery" on setuptools documentation page: + + - https://setuptools.pypa.io/en/latest/userguide/package_discovery.html + + If you don't want {importable!r} to be distributed and are + already explicitly excluding {importable!r} via + `find_namespace_packages(...)/find_namespace` or `find_packages(...)/find`, + you can try to use `exclude_package_data`, or `include-package-data=False` in + combination with a more fine grained `package-data` configuration. + + You can read more about "package data files" on setuptools documentation page: + + - https://setuptools.pypa.io/en/latest/userguide/datafiles.html + + + [^1]: For Python, any directory (with suitable naming) can be imported, + even if it does not contain any `.py` files. + On the other hand, currently there is no concept of package data + directory, all directories are treated like packages. + """ + # _DUE_DATE: still not defined as this is particularly controversial. + # Warning initially introduced in May 2022. See issue #3340 for discussion. def __init__(self): self._already_warned = set() @@ -363,6 +382,5 @@ class _IncludePackageDataAbuse: def warn(self, importable): if importable not in self._already_warned: - msg = textwrap.dedent(self.MESSAGE).format(importable=importable) - warnings.warn(msg, SetuptoolsDeprecationWarning, stacklevel=2) + self._Warning.emit(importable=importable) self._already_warned.add(importable) diff --git a/setuptools/command/dist_info.py b/setuptools/command/dist_info.py index 40fdfd0a..99d3976d 100644 --- a/setuptools/command/dist_info.py +++ b/setuptools/command/dist_info.py @@ -6,14 +6,13 @@ As defined in the wheel specification import os import shutil import sys -import warnings from contextlib import contextmanager from distutils import log from distutils.core import Command from pathlib import Path from .. import _normalization -from .._deprecation_warning import SetuptoolsDeprecationWarning +from ..warnings import SetuptoolsDeprecationWarning class dist_info(Command): @@ -51,7 +50,9 @@ class dist_info(Command): def finalize_options(self): if self.egg_base: msg = "--egg-base is deprecated for dist_info command. Use --output-dir." - warnings.warn(msg, SetuptoolsDeprecationWarning) + SetuptoolsDeprecationWarning.emit(msg, due_date=(2023, 9, 26)) + # This command is internal to setuptools, therefore it should be safe + # to remove the deprecated support soon. self.output_dir = self.egg_base or self.output_dir dist = self.distribution diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py index 3185ee1d..29e424a8 100644 --- a/setuptools/command/easy_install.py +++ b/setuptools/command/easy_install.py @@ -44,8 +44,6 @@ import sysconfig from sysconfig import get_path -from setuptools import SetuptoolsDeprecationWarning - from setuptools import Command from setuptools.sandbox import run_setup from setuptools.command import setopt @@ -54,6 +52,7 @@ from setuptools.package_index import ( PackageIndex, parse_requirement_arg, URL_SCHEME, ) from setuptools.command import bdist_egg, egg_info +from setuptools.warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning from setuptools.wheel import Wheel from pkg_resources import ( normalize_path, resource_string, @@ -142,11 +141,7 @@ class easy_install(Command): create_index = PackageIndex def initialize_options(self): - warnings.warn( - "easy_install command is deprecated. " - "Use build and pip and other standards-based tools.", - EasyInstallDeprecationWarning, - ) + EasyInstallDeprecationWarning.emit() # the --user option seems to be an opt-in one, # so the default should be False. @@ -2095,7 +2090,8 @@ class ScriptWriter: @classmethod def get_script_args(cls, dist, executable=None, wininst=False): # for backward compatibility - warnings.warn("Use get_args", EasyInstallDeprecationWarning) + EasyInstallDeprecationWarning.emit("Use get_args", due_date=(2023, 6, 1)) + # This is a direct API call, it should be safe to remove soon. writer = (WindowsScriptWriter if wininst else ScriptWriter).best() header = cls.get_script_header("", executable, wininst) return writer.get_args(dist, header) @@ -2103,8 +2099,8 @@ class ScriptWriter: @classmethod def get_script_header(cls, script_text, executable=None, wininst=False): # for backward compatibility - warnings.warn( - "Use get_header", EasyInstallDeprecationWarning, stacklevel=2) + EasyInstallDeprecationWarning.emit("Use get_header", due_date=(2023, 6, 1)) + # This is a direct API call, it should be safe to remove soon. if wininst: executable = "python.exe" return cls.get_header(script_text, executable) @@ -2139,7 +2135,8 @@ class ScriptWriter: @classmethod def get_writer(cls, force_windows): # for backward compatibility - warnings.warn("Use best", EasyInstallDeprecationWarning) + EasyInstallDeprecationWarning.emit("Use best", due_date=(2023, 6, 1)) + # This is a direct API call, it should be safe to remove soon. return WindowsScriptWriter.best() if force_windows else cls.best() @classmethod @@ -2171,7 +2168,8 @@ class WindowsScriptWriter(ScriptWriter): @classmethod def get_writer(cls): # for backward compatibility - warnings.warn("Use best", EasyInstallDeprecationWarning) + EasyInstallDeprecationWarning.emit("Use best", due_date=(2023, 6, 1)) + # This is a direct API call, it should be safe to remove soon. return cls.best() @classmethod @@ -2196,7 +2194,7 @@ class WindowsScriptWriter(ScriptWriter): "{ext} not listed in PATHEXT; scripts will not be " "recognized as executables." ).format(**locals()) - warnings.warn(msg, UserWarning) + SetuptoolsWarning.emit(msg) old = ['.pya', '.py', '-script.py', '.pyc', '.pyo', '.pyw', '.exe'] old.remove(ext) header = cls._adjust_header(type_, header) @@ -2308,6 +2306,11 @@ def only_strs(values): class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning): + _SUMMARY = "easy_install command is deprecated." + _DETAILS = """ + Please avoid running ``setup.py`` and ``easy_install``. + Instead, use pypa/build, pypa/installer, pypa/build or + other standards-based tools. """ - Warning for EasyInstall deprecations, bypassing suppression. - """ + _SEE_URL = "https://github.com/pypa/setuptools/issues/917" + # _DUE_DATE not defined yet diff --git a/setuptools/command/editable_wheel.py b/setuptools/command/editable_wheel.py index 6fddf03d..ffcc2cc0 100644 --- a/setuptools/command/editable_wheel.py +++ b/setuptools/command/editable_wheel.py @@ -15,7 +15,6 @@ import os import shutil import sys import traceback -import warnings from contextlib import suppress from enum import Enum from inspect import cleandoc @@ -37,7 +36,6 @@ from typing import ( from .. import ( Command, - SetuptoolsDeprecationWarning, _normalization, _path, errors, @@ -45,6 +43,11 @@ from .. import ( ) from ..discovery import find_package_path from ..dist import Distribution +from ..warnings import ( + InformationOnly, + SetuptoolsDeprecationWarning, + SetuptoolsWarning, +) from .build_py import build_py as build_py_cls if TYPE_CHECKING: @@ -84,16 +87,21 @@ class _EditableMode(Enum): raise errors.OptionError(f"Invalid editable mode: {mode!r}. Try: 'strict'.") if _mode == "COMPAT": - msg = """ - The 'compat' editable mode is transitional and will be removed - in future versions of `setuptools`. - Please adapt your code accordingly to use either the 'strict' or the - 'lenient' modes. - - For more information, please check: - https://setuptools.pypa.io/en/latest/userguide/development_mode.html - """ - warnings.warn(msg, SetuptoolsDeprecationWarning) + SetuptoolsDeprecationWarning.emit( + "Compat editable installs", + """ + The 'compat' editable mode is transitional and will be removed + in future versions of `setuptools`. + Please adapt your code accordingly to use either the 'strict' or the + 'lenient' modes. + """, + see_docs="userguide/development_mode.html", + # TODO: define due_date + # There is a series of shortcomings with the available editable install + # methods, and they are very controversial. This is something that still + # needs work. + # Moreover, `pip` is still hiding this warning, so users are not aware. + ) return _EditableMode[_mode] @@ -148,7 +156,7 @@ class editable_wheel(Command): except Exception: traceback.print_exc() project = self.distribution.name or self.distribution.get_name() - _DebuggingTips.warn(project) + _DebuggingTips.emit(project=project) raise def _ensure_dist_info(self): @@ -289,21 +297,29 @@ class editable_wheel(Command): try: return self.run_command(cmd_name) except Exception: - msg = f"""{traceback.format_exc()}\n - If you are seeing this warning it is very likely that a setuptools - plugin or customization overrides the `{cmd_name}` command, without - taking into consideration how editable installs run build steps - starting from v64.0.0. - - Plugin authors and developers relying on custom build steps are encouraged - to update their `{cmd_name}` implementation considering the information in - https://setuptools.pypa.io/en/latest/userguide/extension.html - about editable installs. - - For the time being `setuptools` will silence this error and ignore - the faulty command, but this behaviour will change in future versions.\n - """ - warnings.warn(msg, SetuptoolsDeprecationWarning, stacklevel=2) + SetuptoolsDeprecationWarning.emit( + "Customization incompatible with editable install", + f""" + {traceback.format_exc()} + + If you are seeing this warning it is very likely that a setuptools + plugin or customization overrides the `{cmd_name}` command, without + taking into consideration how editable installs run build steps + starting from setuptools v64.0.0. + + Plugin authors and developers relying on custom build steps are + encouraged to update their `{cmd_name}` implementation considering the + information about editable installs in + https://setuptools.pypa.io/en/latest/userguide/extension.html. + + For the time being `setuptools` will silence this error and ignore + the faulty command, but this behaviour will change in future versions. + """, + # TODO: define due_date + # There is a series of shortcomings with the available editable install + # methods, and they are very controversial. This is something that still + # needs work. + ) def _create_wheel_file(self, bdist_wheel): from wheel.wheelfile import WheelFile @@ -468,7 +484,7 @@ class _LinkTree(_StaticPth): Please be careful to not remove this directory, otherwise you might not be able to import/use your package. """ - warnings.warn(msg, InformationOnly) + InformationOnly.emit("Editable installation.", msg) class _TopLevelFinder: @@ -505,7 +521,7 @@ class _TopLevelFinder: Please be careful with folders in your working directory with the same name as your package as they may take precedence during imports. """ - warnings.warn(msg, InformationOnly) + InformationOnly.emit("Editable installation.", msg) def _can_symlink_files(base_dir: Path) -> bool: @@ -811,46 +827,31 @@ def _finder_template( return _FINDER_TEMPLATE.format(name=name, mapping=mapping, namespaces=namespaces) -class InformationOnly(UserWarning): - """Currently there is no clear way of displaying messages to the users - that use the setuptools backend directly via ``pip``. - The only thing that might work is a warning, although it is not the - most appropriate tool for the job... - """ - - class LinksNotSupported(errors.FileError): """File system does not seem to support either symlinks or hard links.""" -class _DebuggingTips(InformationOnly): - @classmethod - def warn(cls, project: str): - msg = f"""An error happened while installing {project!r} in editable mode. - - ************************************************************************ - The following steps are recommended to help debugging this problem: +class _DebuggingTips(SetuptoolsWarning): + _SUMMARY = "Problem in editable installation." + _DETAILS = """ + An error happened while installing `{project}` in editable mode. - - Try to install the project normally, without using the editable mode. - Does the error still persists? - (If it does, try fixing the problem before attempting the editable mode). - - If you are using binary extensions, make sure you have all OS-level - dependencies installed (e.g. compilers, toolchains, binary libraries, ...). - - Try the latest version of setuptools (maybe the error was already fixed). - - If you (or your project dependencies) are using any setuptools extension - or customization, make sure they support the editable mode. + The following steps are recommended to help debug this problem: - After following the steps above, if the problem still persist and - you think this is related to how setuptools handles editable installations, - please submit a reproducible example - (see https://stackoverflow.com/help/minimal-reproducible-example) to: + - Try to install the project normally, without using the editable mode. + Does the error still persist? + (If it does, try fixing the problem before attempting the editable mode). + - If you are using binary extensions, make sure you have all OS-level + dependencies installed (e.g. compilers, toolchains, binary libraries, ...). + - Try the latest version of setuptools (maybe the error was already fixed). + - If you (or your project dependencies) are using any setuptools extension + or customization, make sure they support the editable mode. - https://github.com/pypa/setuptools/issues + After following the steps above, if the problem still persists and + you think this is related to how setuptools handles editable installations, + please submit a reproducible example + (see https://stackoverflow.com/help/minimal-reproducible-example) to: - More information about editable installs can be found in the docs: - - https://setuptools.pypa.io/en/latest/userguide/development_mode.html - ************************************************************************ - """ - # We cannot use `add_notes` since pip hides PEP 678 notes - warnings.warn(msg, cls, stacklevel=2) + https://github.com/pypa/setuptools/issues + """ + _SEE_DOCS = "userguide/development_mode.html" diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py index e40df9bb..f5163ae7 100644 --- a/setuptools/command/egg_info.py +++ b/setuptools/command/egg_info.py @@ -13,7 +13,6 @@ import os import re import sys import io -import warnings import time import collections @@ -30,7 +29,7 @@ from setuptools.glob import glob from setuptools.extern import packaging from setuptools.extern.jaraco.text import yield_lines -from setuptools import SetuptoolsDeprecationWarning +from ..warnings import SetuptoolsDeprecationWarning PY_MAJOR = '{}.{}'.format(*sys.version_info) @@ -331,12 +330,16 @@ class egg_info(InfoCommon, Command): if self.egg_base != os.curdir: bei = os.path.join(self.egg_base, bei) if os.path.exists(bei): - log.warn( - "-" * 78 + '\n' - "Note: Your current .egg-info directory has a '-' in its name;" - '\nthis will not work correctly with "setup.py develop".\n\n' - 'Please rename %s to %s to correct this problem.\n' + '-' * 78, - bei, self.egg_info + EggInfoDeprecationWarning.emit( + "Invalid egg-info directory name.", + f""" + Your current .egg-info directory has a '-' in its name; + this will not work correctly with setuptools commands. + + Please rename {bei!r} to {self.egg_info!r} to correct this problem. + """, + due_date=(2023, 6, 1), + # Old warning, introduced in 2005, might be safe to remove soon ) self.broken_egg_info = self.egg_info self.egg_info = bei # make it work for now @@ -658,11 +661,14 @@ class manifest_maker(sdist): if hasattr(build_py, 'get_data_files_without_manifest'): return build_py.get_data_files_without_manifest() - warnings.warn( - "Custom 'build_py' does not implement " - "'get_data_files_without_manifest'.\nPlease extend command classes" - " from setuptools instead of distutils.", - SetuptoolsDeprecationWarning + SetuptoolsDeprecationWarning.emit( + "`build_py` command does not inherit from setuptools' `build_py`.", + """ + Custom 'build_py' does not implement 'get_data_files_without_manifest'. + Please extend command classes from setuptools instead of distutils. + """, + see_url="https://peps.python.org/pep-0632/", + # due_date not defined yet, old projects might still do it? ) return build_py.get_data_files() @@ -701,9 +707,15 @@ def write_pkg_info(cmd, basename, filename): def warn_depends_obsolete(cmd, basename, filename): if os.path.exists(filename): - log.warn( - "WARNING: 'depends.txt' is not used by setuptools 0.6!\n" - "Use the install_requires/extras_require setup() args instead." + EggInfoDeprecationWarning.emit( + "Deprecated config.", + """ + 'depends.txt' is not used by setuptools >= 0.6! + Configure your dependencies via `setup.cfg` or `pyproject.toml` instead. + """, + see_docs="userguide/declarative_config.html", + due_date=(2023, 6, 1), + # Old warning, introduced in 2005, it might be safe to remove soon. ) @@ -766,8 +778,12 @@ def get_pkg_info_revision(): Get a -r### off of PKG-INFO Version in case this is an sdist of a subversion revision. """ - warnings.warn( - "get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning) + EggInfoDeprecationWarning.emit( + "Deprecated API call", + "get_pkg_info_revision is deprecated.", + due_date=(2023, 6, 1), + # Warning introduced in 11 Dec 2015, should be safe to remove + ) if os.path.exists('PKG-INFO'): with io.open('PKG-INFO') as f: for line in f: diff --git a/setuptools/command/install.py b/setuptools/command/install.py index 55fdb124..dec4e320 100644 --- a/setuptools/command/install.py +++ b/setuptools/command/install.py @@ -1,11 +1,11 @@ from distutils.errors import DistutilsArgError import inspect import glob -import warnings import platform import distutils.command.install as orig import setuptools +from ..warnings import SetuptoolsDeprecationWarning, SetuptoolsWarning # Prior to numpy 1.9, NumPy relies on the '_install' name, so provide it for # now. See https://github.com/pypa/setuptools/issues/199/ @@ -30,11 +30,17 @@ class install(orig.install): _nc = dict(new_commands) def initialize_options(self): - - warnings.warn( - "setup.py install is deprecated. " - "Use build and pip and other standards-based tools.", - setuptools.SetuptoolsDeprecationWarning, + SetuptoolsDeprecationWarning.emit( + "setup.py install is deprecated.", + """ + Please avoid running ``setup.py`` directly. + Instead, use pypa/build, pypa/installer, pypa/build or + other standards-based tools. + """, + see_url="https://blog.ganssle.io/articles/2021/10/setup-py-deprecated.html", + # TODO: Document how to bootstrap setuptools without install + # (e.g. by unziping the wheel file) + # and then add a due_date to this warning. ) orig.install.initialize_options(self) @@ -86,10 +92,10 @@ class install(orig.install): """ if run_frame is None: msg = "Call stack not available. bdist_* commands may fail." - warnings.warn(msg) + SetuptoolsWarning.emit(msg) if platform.python_implementation() == 'IronPython': msg = "For best results, pass -X:Frames to enable call stack." - warnings.warn(msg) + SetuptoolsWarning.emit(msg) return True frames = inspect.getouterframes(run_frame) diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py index 63eb28c7..077c9d2f 100644 --- a/setuptools/command/upload_docs.py +++ b/setuptools/command/upload_docs.py @@ -16,10 +16,9 @@ import itertools import functools import http.client import urllib.parse -import warnings from .._importlib import metadata -from .. import SetuptoolsDeprecationWarning +from ..warnings import SetuptoolsDeprecationWarning from .upload import upload @@ -91,10 +90,14 @@ class upload_docs(upload): zip_file.close() def run(self): - warnings.warn( - "upload_docs is deprecated and will be removed in a future " - "version. Use tools like httpie or curl instead.", - SetuptoolsDeprecationWarning, + SetuptoolsDeprecationWarning.emit( + "Deprecated command", + """ + upload_docs is deprecated and will be removed in a future version. + Instead, use tools like devpi and Read the Docs; or lower level tools like + httpie and curl to interact directly with your hosting service API. + """, + due_date=(2023, 9, 26), # warning introduced in 27 Jul 2022 ) # Run sub commands |