mirror of https://github.com/pypa/pip
Merge pull request #5542 from pradyunsg/dev-ops/deprecation-utilities
Introduce a helper function for deprecation
This commit is contained in:
commit
169ca95666
|
@ -211,6 +211,12 @@ document existing behavior with the intention of covering that behavior with
|
|||
the above deprecation process are always acceptable, and will be considered on
|
||||
their merits.
|
||||
|
||||
.. note::
|
||||
|
||||
pip has a helper function for making deprecation easier for pip maintainers.
|
||||
The supporting documentation can be found in the source code of
|
||||
``pip._internal.utils.deprecation.deprecated``. The function is not a part of
|
||||
pip's public API.
|
||||
|
||||
Release Process
|
||||
===============
|
||||
|
|
|
@ -9,7 +9,6 @@ import os
|
|||
import posixpath
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
|
||||
from pip._vendor import html5lib, requests, six
|
||||
|
@ -29,7 +28,7 @@ from pip._internal.exceptions import (
|
|||
)
|
||||
from pip._internal.models.index import PyPI
|
||||
from pip._internal.pep425tags import get_supported
|
||||
from pip._internal.utils.deprecation import RemovedInPip12Warning
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.logging import indent_log
|
||||
from pip._internal.utils.misc import (
|
||||
ARCHIVE_EXTENSIONS, SUPPORTED_EXTENSIONS, cached_property, normalize_path,
|
||||
|
@ -212,10 +211,12 @@ class PackageFinder(object):
|
|||
# # dependency_links value
|
||||
# # FIXME: also, we should track comes_from (i.e., use Link)
|
||||
if self.process_dependency_links:
|
||||
warnings.warn(
|
||||
deprecated(
|
||||
"Dependency Links processing has been deprecated and will be "
|
||||
"removed in a future release.",
|
||||
RemovedInPip12Warning,
|
||||
replacement=None,
|
||||
gone_in="18.2",
|
||||
issue=4187,
|
||||
)
|
||||
self.dependency_links.extend(links)
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@ import collections
|
|||
import logging
|
||||
import os
|
||||
import re
|
||||
import warnings
|
||||
|
||||
from pip._vendor import pkg_resources, six
|
||||
from pip._vendor.packaging.utils import canonicalize_name
|
||||
|
@ -13,7 +12,7 @@ from pip._vendor.pkg_resources import RequirementParseError
|
|||
from pip._internal.exceptions import InstallationError
|
||||
from pip._internal.req import InstallRequirement
|
||||
from pip._internal.req.req_file import COMMENT_RE
|
||||
from pip._internal.utils.deprecation import RemovedInPip12Warning
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.misc import (
|
||||
dist_is_editable, get_installed_distributions,
|
||||
)
|
||||
|
@ -216,10 +215,12 @@ class FrozenRequirement(object):
|
|||
'for this package:'
|
||||
)
|
||||
else:
|
||||
warnings.warn(
|
||||
deprecated(
|
||||
"SVN editable detection based on dependency links "
|
||||
"will be dropped in the future.",
|
||||
RemovedInPip12Warning,
|
||||
replacement=None,
|
||||
gone_in="18.2",
|
||||
issue=4187,
|
||||
)
|
||||
comments.append(
|
||||
'# Installing as editable to satisfy requirement %s:' %
|
||||
|
|
|
@ -8,7 +8,6 @@ import shutil
|
|||
import sys
|
||||
import sysconfig
|
||||
import traceback
|
||||
import warnings
|
||||
import zipfile
|
||||
from distutils.util import change_root
|
||||
from email.parser import FeedParser # type: ignore
|
||||
|
@ -33,7 +32,7 @@ from pip._internal.locations import (
|
|||
PIP_DELETE_MARKER_FILENAME, running_under_virtualenv,
|
||||
)
|
||||
from pip._internal.req.req_uninstall import UninstallPathSet
|
||||
from pip._internal.utils.deprecation import RemovedInPip12Warning
|
||||
from pip._internal.utils.deprecation import deprecated
|
||||
from pip._internal.utils.hashes import Hashes
|
||||
from pip._internal.utils.logging import indent_log
|
||||
from pip._internal.utils.misc import (
|
||||
|
@ -582,11 +581,13 @@ class InstallRequirement(object):
|
|||
|
||||
if requires is None:
|
||||
logging.warn(template, self, "it is missing.")
|
||||
warnings.warn(
|
||||
deprecated(
|
||||
"Future versions of pip may reject packages with "
|
||||
"pyproject.toml files that do not contain the [build-system]"
|
||||
"table and the requires key, as specified in PEP 518.",
|
||||
RemovedInPip12Warning,
|
||||
replacement=None,
|
||||
gone_in="18.2",
|
||||
issue=5416,
|
||||
)
|
||||
|
||||
# Currently, we're isolating the build based on the presence of the
|
||||
|
|
|
@ -6,58 +6,36 @@ from __future__ import absolute_import
|
|||
import logging
|
||||
import warnings
|
||||
|
||||
from pip._vendor.packaging.version import parse
|
||||
|
||||
from pip import __version__ as current_version
|
||||
from pip._internal.utils.typing import MYPY_CHECK_RUNNING
|
||||
|
||||
if MYPY_CHECK_RUNNING:
|
||||
from typing import Any # noqa: F401
|
||||
from typing import Any, Optional # noqa: F401
|
||||
|
||||
|
||||
class PipDeprecationWarning(Warning):
|
||||
pass
|
||||
|
||||
|
||||
class Pending(object):
|
||||
pass
|
||||
|
||||
|
||||
class RemovedInPip12Warning(PipDeprecationWarning, Pending):
|
||||
pass
|
||||
_original_showwarning = None # type: Any
|
||||
|
||||
|
||||
# Warnings <-> Logging Integration
|
||||
|
||||
|
||||
_warnings_showwarning = None # type: Any
|
||||
|
||||
|
||||
def _showwarning(message, category, filename, lineno, file=None, line=None):
|
||||
if file is not None:
|
||||
if _warnings_showwarning is not None:
|
||||
_warnings_showwarning(
|
||||
if _original_showwarning is not None:
|
||||
_original_showwarning(
|
||||
message, category, filename, lineno, file, line,
|
||||
)
|
||||
else:
|
||||
if issubclass(category, PipDeprecationWarning):
|
||||
elif issubclass(category, PipDeprecationWarning):
|
||||
# We use a specially named logger which will handle all of the
|
||||
# deprecation messages for pip.
|
||||
logger = logging.getLogger("pip._internal.deprecations")
|
||||
|
||||
# This is purposely using the % formatter here instead of letting
|
||||
# the logging module handle the interpolation. This is because we
|
||||
# want it to appear as if someone typed this entire message out.
|
||||
log_message = "DEPRECATION: %s" % message
|
||||
|
||||
# PipDeprecationWarnings that are Pending still have at least 2
|
||||
# versions to go until they are removed so they can just be
|
||||
# warnings. Otherwise, they will be removed in the very next
|
||||
# version of pip. We want these to be more obvious so we use the
|
||||
# ERROR logging level.
|
||||
if issubclass(category, Pending):
|
||||
logger.warning(log_message)
|
||||
logger.warning(message)
|
||||
else:
|
||||
logger.error(log_message)
|
||||
else:
|
||||
_warnings_showwarning(
|
||||
_original_showwarning(
|
||||
message, category, filename, lineno, file, line,
|
||||
)
|
||||
|
||||
|
@ -66,8 +44,46 @@ def install_warning_logger():
|
|||
# Enable our Deprecation Warnings
|
||||
warnings.simplefilter("default", PipDeprecationWarning, append=True)
|
||||
|
||||
global _warnings_showwarning
|
||||
global _original_showwarning
|
||||
|
||||
if _warnings_showwarning is None:
|
||||
_warnings_showwarning = warnings.showwarning
|
||||
if _original_showwarning is None:
|
||||
_original_showwarning = warnings.showwarning
|
||||
warnings.showwarning = _showwarning
|
||||
|
||||
|
||||
def deprecated(reason, replacement, gone_in, issue=None):
|
||||
# type: (str, Optional[str], Optional[str], Optional[int]) -> None
|
||||
"""Helper to deprecate existing functionality.
|
||||
|
||||
reason:
|
||||
Textual reason shown to the user about why this functionality has
|
||||
been deprecated.
|
||||
replacement:
|
||||
Textual suggestion shown to the user about what alternative
|
||||
functionality they can use.
|
||||
gone_in:
|
||||
The version of pip does this functionality should get removed in.
|
||||
Raises errors if pip's current version is greater than or equal to
|
||||
this.
|
||||
issue:
|
||||
Issue number on the tracker that would serve as a useful place for
|
||||
users to find related discussion and provide feedback.
|
||||
|
||||
Always pass replacement, gone_in and issue as keyword arguments for clarity
|
||||
at the call site.
|
||||
"""
|
||||
|
||||
# Construct a nice message.
|
||||
# This is purposely eagerly formatted as we want it to appear as if someone
|
||||
# typed this entire message out.
|
||||
message = "DEPRECATION: " + reason
|
||||
if replacement is not None:
|
||||
message += " A possible replacement is {}.".format(replacement)
|
||||
if issue is not None:
|
||||
url = "https://github.com/pypa/pip/issues/" + str(issue)
|
||||
message += " You can find discussion regarding this at {}.".format(url)
|
||||
|
||||
# Raise as an error if it has to be removed.
|
||||
if gone_in is not None and parse(current_version) >= parse(gone_in):
|
||||
raise PipDeprecationWarning(message)
|
||||
warnings.warn(message, category=PipDeprecationWarning, stacklevel=2)
|
||||
|
|
|
@ -1,21 +1,22 @@
|
|||
import textwrap
|
||||
|
||||
|
||||
def test_environ(script, tmpdir):
|
||||
"""$PYTHONWARNINGS was added in python2.7"""
|
||||
demo = tmpdir.join('warnings_demo.py')
|
||||
demo.write('''
|
||||
from pip._internal.utils import deprecation
|
||||
deprecation.install_warning_logger()
|
||||
demo.write(textwrap.dedent('''
|
||||
from logging import basicConfig
|
||||
from pip._internal.utils import deprecation
|
||||
|
||||
from logging import basicConfig
|
||||
basicConfig()
|
||||
deprecation.install_warning_logger()
|
||||
basicConfig()
|
||||
|
||||
from warnings import warn
|
||||
warn("deprecated!", deprecation.PipDeprecationWarning)
|
||||
''')
|
||||
deprecation.deprecated("deprecated!", replacement=None, gone_in=None)
|
||||
'''))
|
||||
|
||||
result = script.run('python', demo, expect_stderr=True)
|
||||
assert result.stderr == \
|
||||
'ERROR:pip._internal.deprecations:DEPRECATION: deprecated!\n'
|
||||
expected = 'WARNING:pip._internal.deprecations:DEPRECATION: deprecated!\n'
|
||||
assert result.stderr == expected
|
||||
|
||||
script.environ['PYTHONWARNINGS'] = 'ignore'
|
||||
result = script.run('python', demo)
|
||||
|
|
Loading…
Reference in New Issue