15

When I try to import pip package and use pip.get_installed_distributions(), console is printing error:

 AttributeError: 'module' object has no attribute 'get_installed_distributions'

Are there any solutions which exclude downgrading pip?

3 Answers3

22

Update

With Python 3.8, the standard library has got a way of querying the environment for installed distributions and their metadata: importlib.metadata. For older Python versions, there's a backport importlib_metadata:

$ pip install importlib-metadata

It is thus advisable to use it (or the backport) instead of relying on pip's internals.

Importing with backwards compatibility:

import sys

if sys.version_info >= (3, 8):
    from importlib import metadata as importlib_metadata
else:
    import importlib_metadata

Usage examples:

Get names, versions and licenses (check out more available metadata keys in core metadata spec) of all installed distributions:

dists = importlib_metadata.distributions()
for dist in dists:
    name = dist.metadata["Name"]
    version = dist.version
    license = dist.metadata["License"]
    print(f'found distribution {name}=={version}')

Querying single distribution by name:

wheel = importlib_metadata.distribution('wheel') 
print(wheel.metadata["Name"], 'installed')

Original answer:

The function was moved to the pip._internal subpackage. Import example with backwards compatibility:

try:
    from pip._internal.utils.misc import get_installed_distributions
except ImportError:  # pip<10
    from pip import get_installed_distributions
hoefling
  • 59,418
  • 12
  • 147
  • 194
9

@hoefling It is not recommended and is bad practice to import items from pip._internal pip has warned against this and preceding the release of pip 10 they made an announcement regarding this too.

A good alternative would be to use setuptools pkg_resources instead. From there you can use pkg_resources.working_set. See the comment from @pradyunsg here.

import pkg_resources

dists = [d for d in pkg_resources.working_set]
# You can filter and use information from the installed distributions.
wim
  • 338,267
  • 99
  • 616
  • 750
Mmelcor
  • 103
  • 6
  • While I understand your intentions, I highly discourage the ignorant and sudden decision regarding the API move. A lot of package developers (including myself) relied on public API like stuff in `pip.pep425tags` for years. Now all of the sudden, all of it is moved and lots of projects are broken, and all that is left are some arrogant commentaries about _people do actually use bad examples because they are the first thing they find that works_ (from the link you've posted). – hoefling Jul 10 '18 at 16:19
  • 2
    So while your answer is correct, the _real_ bad practice here is what the pip devs did. They should leave the old modules in place with the import of moved modules and issue a `DeprecationWarning` for at least a version - like this being done in lots of good projects out there, Django being a good example. Instead, now we're forced to look for replacement code. Many thanks to the `setuptools` devs that they adopted the `pep425tags` module, but now I have to work around for the support of old versions of `setuptools` where the module is not available yet. – hoefling Jul 10 '18 at 16:22
  • Another thing - I don't get a notification when you mention my nickname in the answer; I think this works only in comments for user's answer or where the user commented previously. – hoefling Jul 10 '18 at 16:24
3

Adding to @Mmelcor answer, the items returned in the list comprehension is a PathMetadata object, something like:

[wrapt 1.10.11 (/Users/<username>/path/venv/lib/python3.6/site-packages),
 widgetsnbextension 3.2.1 (/Users/<username>/path/venv/lib/python3.6/site-packages),....]

You may need to get the string representation before filtering:

import pkg_resources
dists = [str(d) for d in pkg_resources.working_set]
print(dists)

Result:

['wrapt 1.10.11',
 'widgetsnbextension 3.2.1',...]
sedeh
  • 7,083
  • 6
  • 48
  • 65