:mod:`pkgutil` --- Package utilities ==================================== .. module:: pkgutil :synopsis: Utilities to support packages. .. TODO Follow the reST conventions used in the stdlib This module provides functions to manipulate packages, as well as the necessary functions to provide support for the "Importer Protocol" as described in :PEP:`302` and for working with the database of installed Python distributions which is specified in :PEP:`376`. In addition to the functions required in :PEP:`376`, back support for older ``.egg`` and ``.egg-info`` distributions is provided as well. These distributions are represented by the class :class:`~distutils2._backport.pkgutil.EggInfoDistribution` and most functions provide an extra argument ``use_egg_info`` which indicates if they should consider these old styled distributions. This document details first the functions and classes available and then presents several use cases. .. function:: extend_path(path, name) Extend the search path for the modules which comprise a package. Intended use is to place the following code in a package's :file:`__init__.py`:: from pkgutil import extend_path __path__ = extend_path(__path__, __name__) This will add to the package's ``__path__`` all subdirectories of directories on ``sys.path`` named after the package. This is useful if one wants to distribute different parts of a single logical package as multiple directories. It also looks for :file:`\*.pkg` files beginning where ``*`` matches the *name* argument. This feature is similar to :file:`\*.pth` files (see the :mod:`site` module for more information), except that it doesn't special-case lines starting with ``import``. A :file:`\*.pkg` file is trusted at face value: apart from checking for duplicates, all entries found in a :file:`\*.pkg` file are added to the path, regardless of whether they exist on the filesystem. (This is a feature.) If the input path is not a list (as is the case for frozen packages) it is returned unchanged. The input path is not modified; an extended copy is returned. Items are only appended to the copy at the end. It is assumed that ``sys.path`` is a sequence. Items of ``sys.path`` that are not strings referring to existing directories are ignored. Unicode items on ``sys.path`` that cause errors when used as filenames may cause this function to raise an exception (in line with :func:`os.path.isdir` behavior). .. function:: get_data(package, resource) Get a resource from a package. This is a wrapper for the :pep:`302` loader :func:`get_data` API. The package argument should be the name of a package, in standard module format (foo.bar). The resource argument should be in the form of a relative filename, using ``/`` as the path separator. The parent directory name ``..`` is not allowed, and nor is a rooted name (starting with a ``/``). The function returns a binary string that is the contents of the specified resource. For packages located in the filesystem, which have already been imported, this is the rough equivalent of:: d = os.path.dirname(sys.modules[package].__file__) data = open(os.path.join(d, resource), 'rb').read() If the package cannot be located or loaded, or it uses a :pep:`302` loader which does not support :func:`get_data`, then None is returned. API Reference ============= .. automodule:: distutils2._backport.pkgutil :members: Caching +++++++ For performance purposes, the list of distributions is being internally cached. It is enabled by default, but you can turn it off or clear it using :func:`~distutils2._backport.pkgutil.enable_cache`, :func:`~distutils2._backport.pkgutil.disable_cache` and :func:`~distutils2._backport.pkgutil.clear_cache`. Example Usage ============= Print All Information About a Distribution ++++++++++++++++++++++++++++++++++++++++++ Given a path to a ``.dist-info`` distribution, we shall print out all information that can be obtained using functions provided in this module:: from distutils2._backport import pkgutil import sys path = raw_input() # read the path from the keyboard # first create the Distribution instance try: dist = pkgutil.Distribution(path) except IOError: print('No such distribution') sys.exit(1) print('Information about %s' % dist.name) print('Files') print('=====') for (path, md5, size) in dist.get_installed_files(): print('* Path: %s' % path) print(' Hash %s, Size: %s bytes' % (md5, size)) print('Metadata') print('========') for key, value in dist.metadata.items(): print('%20s: %s' % (key, value)) print('Extra') print('=====') if dist.requested: print('* It was installed by user request') else: print('* It was installed as a dependency') If we save the script above as ``print_info.py`` and we are intested in the distribution located at ``/home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9`` then by typing in the console: .. code-block:: bash $ echo /home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info | python print_info.py we get the following output: .. code-block:: none Information about choxie Files ===== * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/truffles.py Hash 5e052db6a478d06bad9ae033e6bc08af, Size: 111 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py Hash ac56bf496d8d1d26f866235b95f31030, Size: 214 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py Hash 416aab08dfa846f473129e89a7625bbc, Size: 25 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA Hash 696a209967fef3c8b8f5a7bb10386385, Size: 225 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes * Path: ../home/josip/dev/distutils2/src/distutils2/_backport/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD Hash None, Size: None bytes Metadata ======== Metadata-Version: 1.2 Name: choxie Version: 2.0.0.9 Platform: [] Supported-Platform: UNKNOWN Summary: Chocolate with a kick! Description: UNKNOWN Keywords: [] Home-page: UNKNOWN Author: UNKNOWN Author-email: UNKNOWN Maintainer: UNKNOWN Maintainer-email: UNKNOWN License: UNKNOWN Classifier: [] Download-URL: UNKNOWN Obsoletes-Dist: ['truffles (<=0.8,>=0.5)', 'truffles (<=0.9,>=0.6)'] Project-URL: [] Provides-Dist: ['truffles (1.0)'] Requires-Dist: ['towel-stuff (0.1)'] Requires-Python: UNKNOWN Requires-External: [] Extra ===== * It was installed as a dependency Find Out Obsoleted Distributions ++++++++++++++++++++++++++++++++ Now, we take tackle a different problem, we are interested in finding out which distributions have been obsoleted. This can be easily done as follows:: from distutils2._backport import pkgutil # iterate over all distributions in the system for dist in pkgutil.get_distributions(): name = dist.name version = dist.metadata['Version'] # find out which distributions obsolete this name/version combination for obsoleted_by in pkgutil.obsoletes_distribution(name, version): print('%s(%s) is obsoleted by %s' % (name, version, obsoleted_by.name)) This is how the output might look like: .. code-block:: none strawberry(0.6) is obsoleted by choxie grammar(1.0a4) is obsoleted by towel-stuff