| | """ |
| | A module that implements tooling to enable easy warnings about deprecations. |
| | """ |
| |
|
| | import logging |
| | import warnings |
| | from typing import Any, Optional, TextIO, Type, Union |
| |
|
| | from pip._vendor.packaging.version import parse |
| |
|
| | from pip import __version__ as current_version |
| |
|
| | DEPRECATION_MSG_PREFIX = "DEPRECATION: " |
| |
|
| |
|
| | class PipDeprecationWarning(Warning): |
| | pass |
| |
|
| |
|
| | _original_showwarning: Any = None |
| |
|
| |
|
| | |
| | def _showwarning( |
| | message: Union[Warning, str], |
| | category: Type[Warning], |
| | filename: str, |
| | lineno: int, |
| | file: Optional[TextIO] = None, |
| | line: Optional[str] = None, |
| | ) -> None: |
| | if file is not None: |
| | if _original_showwarning is not None: |
| | _original_showwarning(message, category, filename, lineno, file, line) |
| | elif issubclass(category, PipDeprecationWarning): |
| | |
| | |
| | logger = logging.getLogger("pip._internal.deprecations") |
| | logger.warning(message) |
| | else: |
| | _original_showwarning(message, category, filename, lineno, file, line) |
| |
|
| |
|
| | def install_warning_logger() -> None: |
| | |
| | warnings.simplefilter("default", PipDeprecationWarning, append=True) |
| |
|
| | global _original_showwarning |
| |
|
| | if _original_showwarning is None: |
| | _original_showwarning = warnings.showwarning |
| | warnings.showwarning = _showwarning |
| |
|
| |
|
| | def deprecated( |
| | *, |
| | reason: str, |
| | replacement: Optional[str], |
| | gone_in: Optional[str], |
| | feature_flag: Optional[str] = None, |
| | issue: Optional[int] = None, |
| | ) -> None: |
| | """Helper to deprecate existing functionality. |
| | |
| | reason: |
| | Textual reason shown to the user about why this functionality has |
| | been deprecated. Should be a complete sentence. |
| | 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 an error if pip's current version is greater than or equal to |
| | this. |
| | feature_flag: |
| | Command-line flag of the form --use-feature={feature_flag} for testing |
| | upcoming functionality. |
| | issue: |
| | Issue number on the tracker that would serve as a useful place for |
| | users to find related discussion and provide feedback. |
| | """ |
| |
|
| | |
| | is_gone = gone_in is not None and parse(current_version) >= parse(gone_in) |
| |
|
| | message_parts = [ |
| | (reason, f"{DEPRECATION_MSG_PREFIX}{{}}"), |
| | ( |
| | gone_in, |
| | "pip {} will enforce this behaviour change." |
| | if not is_gone |
| | else "Since pip {}, this is no longer supported.", |
| | ), |
| | ( |
| | replacement, |
| | "A possible replacement is {}.", |
| | ), |
| | ( |
| | feature_flag, |
| | "You can use the flag --use-feature={} to test the upcoming behaviour." |
| | if not is_gone |
| | else None, |
| | ), |
| | ( |
| | issue, |
| | "Discussion can be found at https://github.com/pypa/pip/issues/{}", |
| | ), |
| | ] |
| |
|
| | message = " ".join( |
| | format_str.format(value) |
| | for value, format_str in message_parts |
| | if format_str is not None and value is not None |
| | ) |
| |
|
| | |
| | if is_gone: |
| | raise PipDeprecationWarning(message) |
| |
|
| | warnings.warn(message, category=PipDeprecationWarning, stacklevel=2) |
| |
|