How to filter `PipDeprecationWarning`

Hi :wave:

I’m having a problem with pip. In particular its deprecation warning. So when installing a package that uses the legacy setup.py install method, pip emits a PipDeprecationWarning. So far, so sensible.

Now I usually run Python with -Werror in my CI so that I notice when new warnings (e.g., deprecations) pop up. I then decide if I fix the underlying reason or mute the warning. With pip’s warning, the muting is near impossible. Reason: with there being no supported pip API, you cannot use the more advance warning filters import warnings; warnings.filterwarnings(...), but have to rely on the interpreter config (-W flag or PYTHONWARNINGS env variable). The interpreter config, however, is rather limited in that the message filter cannot be a regex and cannot contain a : which this pip warning does right at the start: DEPRECATION: ... is being installed using the legacy [...]. On top, the warning class lives in an internal module pip._internal.utils.deprecation.PipDeprecationWarning so filtering for the class is by Python convention a no-no.

Weirdly enough, and this might be a separate issue, it doesn’t even work if you try.

python3.11 -W 'error::pip._internal.utils.deprecation.PipDeprecationWarning' -m pip install --no-cache-dir --force-reinstall MarkupSafe==2.1.1

(using MarkupSafe as an example using the legacy installation here) reports

Invalid -W option ignored: invalid module name: 'pip._internal.utils.deprecation'

Any tips on how I can filter out this particular warning without stopping to raise on other warning would be highly appreciated.

Can you run pip with the --use-pep517 option to force the non-legacy build mechanism? The intention is for that option to become the default in the future, so if a package won’t build/install with that option there’s a problem.

1 Like

Thanks for the suggestions. As expected, it gets rid of the deprecation warning and I’ll probably go down that route for now. But it still leaves users that can’t or don’t want to with this warning that effectively cannot be silenced in isolation. I think this should be fixed.

The exact exception string and full traceback would be useful in
determining exactly how and whether it can be filtered. I run into a
lot of similar struggles setting PYTHONWARNINGS=error globally in my
tox.ini and have encountered a variety of things in my projects’
dependencies which I can’t easily filter for a multitude of reasons
not limited to inability to escape some symbols they’ve embedded in
their exception messages and the lack of basic pattern recognition
features for matching messages, but also non-public exception
classes which derive directly from overly-general ones like Warning.
When you get multiple situations like that, your pool of options
becomes rather grim (like ignoring all warnings in a specific
module).

There have been a few proposals for making PYTHONWARNINGS a little
more flexible which have gone approximately nowhere, unfortunately.
Occasionally I have luck proposing a PR to some dependency cleaning
up their warnings to the point that they can be filtered more
precisely, or better still, helping whatever other dependency is
triggering the warning to fix their future bugs.

1 Like

I feel you on this one. Maybe the best solution would be to increase the filtering flexibility of the interpreter config…

Here is the full traceback

ERROR: Exception:
Traceback (most recent call last):
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/cli/base_command.py", line 160, in exc_logging_wrapper
    status = run_func(*args)
             ^^^^^^^^^^^^^^^
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/cli/req_command.py", line 247, in wrapper
    return func(self, options, args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/commands/install.py", line 494, in run
    installed = install_given_reqs(
                ^^^^^^^^^^^^^^^^^^^
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/req/__init__.py", line 73, in install_given_reqs
    requirement.install(
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/req/req_install.py", line 820, in install
    self.legacy_install_reason.emit_deprecation(self.name)
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/utils/deprecation.py", line 143, in emit_deprecation
    deprecated(
  File "****/venv_test/lib/python3.11/site-packages/pip/_internal/utils/deprecation.py", line 120, in deprecated
    warnings.warn(message, category=PipDeprecationWarning, stacklevel=2)
pip._internal.utils.deprecation.PipDeprecationWarning: DEPRECATION: MarkupSafe is being installed using the legacy 'setup.py install' method, because it does not have a 'pyproject.toml' and the 'wheel' package is not installed. pip 23.1 will enforce this behaviour change. A possible replacement is to enable the '--use-pep517' option. Discussion can be found at https://github.com/pypa/pip/issues/8559

Yeah, that’s a perfect example of the sorts of things I’ve never
been able to work out how to precisely filter with a PYTHONWARNINGS
ignore directive. In particular if the
pip._internal.utils.deprecation.PipDeprecationWarning isn’t exposed
in a way that it can be used directly in the category field, since
the message contains commas and colons which are delimiters for
PYTHONWARNINGS (it lacks any way to escape those that I’ve ever been
able to find documented), I’d probably end up with something vague
like ignore::warning:pip._internal.req which, frankly, is not great.
You could try ignore::PipDeprecationWarning:pip._internal.req or
ignore::pip._internal.utils.deprecation.PipDeprecationWarning:pip._internal.req
but I doubt you’ll have luck there.

I’ve been desperate enough to look at the code in warnings.py and initconfig.c. Turns out there’s not just no documented way, but there is also de-facto no way to escape such things.

how do you end up with that module path?

Agreed, I should have s/documented// there as I too went digging in
the source at one point and came to the same conclusion. The pull
request(s) I’m remembering were for adding such functionality. The
documentation also used to be a bit wrong about things, but
thankfully that got fixed relatively recently. The last related PR
I’m remembering which ultimately went nowhere was
bpo-34624: Allow regex for module passed via -W or PYTHONWARNINGS by coldfix · Pull Request #9358 · python/cpython · GitHub (for regex matching of
the messages).

As for figuring out which module to match, I’ve never been 100%
clear on how to go about it. I usually try to spot where in the
traceback the likely caller is which ultimately raises the warning,
but sometimes I find I need to wind forward or backward a few steps
to get the correct one. Trial and error. It’s entirely likely I’m
just not fully understanding the underlying mechanism for that field
in the ignore syntax.

Hmm… this is probably caused by the discrepancy in CPython’s warnings modules handling of -W/PYTHONWARNINGS vs warnings.filterwarnings; and me not realising that this was a thing. :slight_smile:

From https://docs.python.org/3/library/warnings.html:

  • module is a string containing a regular expression that the start of the fully qualified module name must match, case-sensitively. In -W and PYTHONWARNINGS, module is a literal string that the fully qualified module name must be equal to (case-sensitively), ignoring any whitespace at the start or end of module.
python -W "error:DEPRECATION::pip" -m pip ...

doesn’t error out (all deprecation warnings coming out of pip have the DEPRECATION prefix).

Besides this though, pip doesn’t expect that it’d be executed with -W – erroring out on these warnings can result in the environment being left in an inconsistent/broken state. pip’s codebase has defensive coding in place around a lot of I/O operations, but it does not expect presenting a warning to stdout/stderr to resulting in errors.

2 Likes

Right, and that makes it very hairy to attempt to catch build-time
deprecation warnings and the like in a dependency chain. Basically I
set PYTHONWARNINGS=error in a global setenv in my tox.ini and then
repeatedly blow away my pip cache and rerun all my tests against
each maintained interpreter version, playing whack-a-mole with
ignore stanzas for the package installation errors which result and
documenting each in turn, until I get to a clean result. It often
involves peeling back the layers of the various tools involved in
order to discover that, say, pip or virtualenv has vendored a
particular version of some lib which relies on deprecated calls
elsewhere, and then tracking that until the lib itself is fixed,
released, synced in the project vendoring it, and released there.

TBH, I’m OK with moving pip away from warnings.warn for its own deprecation warnings, with a flag to suppress specific warnings.

If someone else thinks this is useful and wants to file an issue to track adding this, please do mention this thread in pip’s issue tracker.

While it might help a little, pip is just one place this happens.
For example I end up having to squash similar warnings raised by
SetupTools and Cython which appear at build/install time, and when
the warning is coming from something forked to a child process for
a build backend which buries its actual traceback in a separate
log (if it even preserves it at all), or pip sees the failure as a
signal that it needs to quietly try different package versions ad
nauseum until it finds one old enough that it doesn’t trip the
warning condition because it pre-dates introduction of the problem
transitive dep entirely, it can be pretty hard to narrow down.

Right, a discussion about changing how warnings work in CPython’s standard library can happen in a different category – the Packaging category isn’t the right place to have that discussion IMO. :slight_smile:

FWIW, this doesn’t happen since 22.0.

Agreed, that’s basically the point I was trying to make. This is a
symptom of warning handling in Python as a whole, and not something
which can be fixed from pip’s end. The additional layers and
indirection involved in package management do make the problem a bit
more painful to work around, but ultimately that’s down to the
complexity of the challenges package management solves and not
necessarily something pip is “doing wrong.”

Agreed. User configuration of the warnings module for applications that use it is a topic for one of the core python categories, not the packaging category.

There are benefits and downsides to pip using stdlib warnings. I tend to disagree with @pradyunsg - I feel that using the stdlib feature is an overall benefit. But that doesn’t mean that there aren’t problems, just that I’d rather see the underlying issues dealt with, rather than every application having to work out their own fixes.

Also, I’ll reiterate, if you run pip with warnings treated as errors, you do so at your own risk. Pip isn’t intended to be run like that, and we’re not a library so it’s not as if you’re running any code other than pip in the process[1].


  1. Yeah, build backend code, I know. But my point stands. ↩︎