Éric Araujo | 3a9f58f | 2011-06-01 20:42:49 +0200 | [diff] [blame^] | 1 | :mod:`packaging.database` --- Database of installed distributions |
| 2 | ================================================================= |
| 3 | |
| 4 | .. module:: packaging.database |
| 5 | :synopsis: Functions to query and manipulate installed distributions. |
| 6 | |
| 7 | |
| 8 | This module provides an implementation of :PEP:`376`. It was originally |
| 9 | intended to land in :mod:`pkgutil`, but with the inclusion of Packaging in the |
| 10 | standard library, it was thought best to include it in a submodule of |
| 11 | :mod:`packaging`, leaving :mod:`pkgutil` to deal with imports. |
| 12 | |
| 13 | Installed Python distributions are represented by instances of |
| 14 | :class:`Distribution`, or :class:`EggInfoDistribution` for legacy egg formats. |
| 15 | Most functions also provide an extra argument ``use_egg_info`` to take legacy |
| 16 | distributions into account. |
| 17 | |
| 18 | |
| 19 | Classes representing installed distributions |
| 20 | -------------------------------------------- |
| 21 | |
| 22 | .. class:: Distribution(path) |
| 23 | |
| 24 | Class representing an installed distribution. It is different from |
| 25 | :class:`packaging.dist.Distribution` which holds the list of files, the |
| 26 | metadata and options during the run of a Packaging command. |
| 27 | |
| 28 | Instantiate with the *path* to a ``.dist-info`` directory. Instances can be |
| 29 | compared and sorted. Other available methods are: |
| 30 | |
| 31 | .. XXX describe how comparison works |
| 32 | |
| 33 | .. method:: get_distinfo_file(path, binary=False) |
| 34 | |
| 35 | Return a read-only file object for a file located at |
| 36 | :file:`{project-version}.dist-info/path}`. *path* should be a |
| 37 | ``'/'``-separated path relative to the ``.dist-info`` directory or an |
| 38 | absolute path; if it is an absolute path and doesn't start with the path |
| 39 | to the :file:`.dist-info` directory, a :class:`PackagingError` is raised. |
| 40 | |
| 41 | If *binary* is ``True``, the file is opened in binary mode. |
| 42 | |
| 43 | .. method:: get_resource_path(relative_path) |
| 44 | |
| 45 | .. TODO |
| 46 | |
| 47 | .. method:: list_distinfo_files(local=False) |
| 48 | |
| 49 | Return an iterator over all files located in the :file:`.dist-info` |
| 50 | directory. If *local* is ``True``, each returned path is transformed into |
| 51 | a local absolute path, otherwise the raw value found in the :file:`RECORD` |
| 52 | file is returned. |
| 53 | |
| 54 | .. method:: list_installed_files(local=False) |
| 55 | |
| 56 | Iterate over the files installed with the distribution and registered in |
| 57 | the :file:`RECORD` file and yield a tuple ``(path, md5, size)`` for each |
| 58 | line. If *local* is ``True``, the returned path is transformed into a |
| 59 | local absolute path, otherwise the raw value is returned. |
| 60 | |
| 61 | A local absolute path is an absolute path in which occurrences of ``'/'`` |
| 62 | have been replaced by :data:`os.sep`. |
| 63 | |
| 64 | .. method:: uses(path) |
| 65 | |
| 66 | Check whether *path* was installed by this distribution (i.e. if the path |
| 67 | is present in the :file:`RECORD` file). *path* can be a local absolute |
| 68 | path or a relative ``'/'``-separated path. Returns a boolean. |
| 69 | |
| 70 | Available attributes: |
| 71 | |
| 72 | .. attribute:: metadata |
| 73 | |
| 74 | Instance of :class:`packaging.metadata.Metadata` filled with the contents |
| 75 | of the :file:`{project-version}.dist-info/METADATA` file. |
| 76 | |
| 77 | .. attribute:: name |
| 78 | |
| 79 | Shortcut for ``metadata['Name']``. |
| 80 | |
| 81 | .. attribute:: version |
| 82 | |
| 83 | Shortcut for ``metadata['Version']``. |
| 84 | |
| 85 | .. attribute:: requested |
| 86 | |
| 87 | Boolean indicating whether this distribution was requested by the user of |
| 88 | automatically installed as a dependency. |
| 89 | |
| 90 | |
| 91 | .. class:: EggInfoDistribution(path) |
| 92 | |
| 93 | Class representing a legacy distribution. It is compatible with distutils' |
| 94 | and setuptools' :file:`.egg-info` and :file:`.egg` files and directories. |
| 95 | |
| 96 | .. FIXME should be named EggDistribution |
| 97 | |
| 98 | Instantiate with the *path* to an egg file or directory. Instances can be |
| 99 | compared and sorted. Other available methods are: |
| 100 | |
| 101 | .. method:: list_installed_files(local=False) |
| 102 | |
| 103 | .. method:: uses(path) |
| 104 | |
| 105 | Available attributes: |
| 106 | |
| 107 | .. attribute:: metadata |
| 108 | |
| 109 | Instance of :class:`packaging.metadata.Metadata` filled with the contents |
| 110 | of the :file:`{project-version}.egg-info/PKG-INFO` or |
| 111 | :file:`{project-version}.egg` file. |
| 112 | |
| 113 | .. attribute:: name |
| 114 | |
| 115 | Shortcut for ``metadata['Name']``. |
| 116 | |
| 117 | .. attribute:: version |
| 118 | |
| 119 | Shortcut for ``metadata['Version']``. |
| 120 | |
| 121 | |
| 122 | Functions to work with the database |
| 123 | ----------------------------------- |
| 124 | |
| 125 | .. function:: get_distribution(name, use_egg_info=False, paths=None) |
| 126 | |
| 127 | Return an instance of :class:`Distribution` or :class:`EggInfoDistribution` |
| 128 | for the first installed distribution matching *name*. Egg distributions are |
| 129 | considered only if *use_egg_info* is true; if both a dist-info and an egg |
| 130 | file are found, the dist-info prevails. The directories to be searched are |
| 131 | given in *paths*, which defaults to :data:`sys.path`. Return ``None`` if no |
| 132 | matching distribution is found. |
| 133 | |
| 134 | .. FIXME param should be named use_egg |
| 135 | |
| 136 | |
| 137 | .. function:: get_distributions(use_egg_info=False, paths=None) |
| 138 | |
| 139 | Return an iterator of :class:`Distribution` instances for all installed |
| 140 | distributions found in *paths* (defaults to :data:`sys.path`). If |
| 141 | *use_egg_info* is true, also return instances of :class:`EggInfoDistribution` |
| 142 | for legacy distributions found. |
| 143 | |
| 144 | |
| 145 | .. function:: get_file_users(path) |
| 146 | |
| 147 | Return an iterator over all distributions using *path*, a local absolute path |
| 148 | or a relative ``'/'``-separated path. |
| 149 | |
| 150 | .. XXX does this work with prefixes or full file path only? |
| 151 | |
| 152 | |
| 153 | .. function:: obsoletes_distribution(name, version=None, use_egg_info=False) |
| 154 | |
| 155 | Return an iterator over all distributions that declare they obsolete *name*. |
| 156 | *version* is an optional argument to match only specific releases (see |
| 157 | :mod:`packaging.version`). If *use_egg_info* is true, legacy egg |
| 158 | distributions will be considered as well. |
| 159 | |
| 160 | |
| 161 | .. function:: provides_distribution(name, version=None, use_egg_info=False) |
| 162 | |
| 163 | Return an iterator over all distributions that declare they provide *name*. |
| 164 | *version* is an optional argument to match only specific releases (see |
| 165 | :mod:`packaging.version`). If *use_egg_info* is true, legacy egg |
| 166 | distributions will be considered as well. |
| 167 | |
| 168 | |
| 169 | Utility functions |
| 170 | ----------------- |
| 171 | |
| 172 | .. function:: distinfo_dirname(name, version) |
| 173 | |
| 174 | Escape *name* and *version* into a filename-safe form and return the |
| 175 | directory name built from them, for example |
| 176 | :file:`{safename}-{safeversion}.dist-info.` In *name*, runs of |
| 177 | non-alphanumeric characters are replaced with one ``'_'``; in *version*, |
| 178 | spaces become dots, and runs of other non-alphanumeric characters (except |
| 179 | dots) a replaced by one ``'-'``. |
| 180 | |
| 181 | .. XXX wth spaces in version numbers? |
| 182 | |
| 183 | For performance purposes, the list of distributions is being internally |
| 184 | cached. Caching is enabled by default, but you can control it with these |
| 185 | functions: |
| 186 | |
| 187 | .. function:: clear_cache() |
| 188 | |
| 189 | Clear the cache. |
| 190 | |
| 191 | .. function:: disable_cache() |
| 192 | |
| 193 | Disable the cache, without clearing it. |
| 194 | |
| 195 | .. function:: enable_cache() |
| 196 | |
| 197 | Enable the internal cache, without clearing it. |
| 198 | |
| 199 | |
| 200 | Examples |
| 201 | -------- |
| 202 | |
| 203 | Print all information about a distribution |
| 204 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 205 | |
| 206 | Given a path to a ``.dist-info`` distribution, we shall print out all |
| 207 | information that can be obtained using functions provided in this module:: |
| 208 | |
| 209 | import sys |
| 210 | import packaging.database |
| 211 | |
| 212 | path = input() |
| 213 | # first create the Distribution instance |
| 214 | try: |
| 215 | dist = packaging.database.Distribution(path) |
| 216 | except IOError: |
| 217 | sys.exit('No such distribution') |
| 218 | |
| 219 | print('Information about %r' % dist.name) |
| 220 | print() |
| 221 | |
| 222 | print('Files') |
| 223 | print('=====') |
| 224 | for path, md5, size in dist.list_installed_files(): |
| 225 | print('* Path: %s' % path) |
| 226 | print(' Hash %s, Size: %s bytes' % (md5, size)) |
| 227 | print() |
| 228 | |
| 229 | print('Metadata') |
| 230 | print('========') |
| 231 | for key, value in dist.metadata.items(): |
| 232 | print('%20s: %s' % (key, value)) |
| 233 | print() |
| 234 | |
| 235 | print('Extra') |
| 236 | print('=====') |
| 237 | if dist.requested: |
| 238 | print('* It was installed by user request') |
| 239 | else: |
| 240 | print('* It was installed as a dependency') |
| 241 | |
| 242 | If we save the script above as ``print_info.py``, we can use it to extract |
| 243 | information from a :file:`.dist-info` directory. By typing in the console: |
| 244 | |
| 245 | .. code-block:: sh |
| 246 | |
| 247 | $ echo /tmp/choxie/choxie-2.0.0.9.dist-info | python3 print_info.py |
| 248 | |
| 249 | we get the following output: |
| 250 | |
| 251 | .. code-block:: none |
| 252 | |
| 253 | Information about 'choxie' |
| 254 | |
| 255 | Files |
| 256 | ===== |
| 257 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/truffles.py |
| 258 | Hash 5e052db6a478d06bad9ae033e6bc08af, Size: 111 bytes |
| 259 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/chocolate.py |
| 260 | Hash ac56bf496d8d1d26f866235b95f31030, Size: 214 bytes |
| 261 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9/choxie/__init__.py |
| 262 | Hash 416aab08dfa846f473129e89a7625bbc, Size: 25 bytes |
| 263 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/INSTALLER |
| 264 | Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes |
| 265 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/METADATA |
| 266 | Hash 696a209967fef3c8b8f5a7bb10386385, Size: 225 bytes |
| 267 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/REQUESTED |
| 268 | Hash d41d8cd98f00b204e9800998ecf8427e, Size: 0 bytes |
| 269 | * Path: ../tmp/distutils2/tests/fake_dists/choxie-2.0.0.9.dist-info/RECORD |
| 270 | Hash None, Size: None bytes |
| 271 | |
| 272 | Metadata |
| 273 | ======== |
| 274 | Metadata-Version: 1.2 |
| 275 | Name: choxie |
| 276 | Version: 2.0.0.9 |
| 277 | Platform: [] |
| 278 | Supported-Platform: UNKNOWN |
| 279 | Summary: Chocolate with a kick! |
| 280 | Description: UNKNOWN |
| 281 | Keywords: [] |
| 282 | Home-page: UNKNOWN |
| 283 | Author: UNKNOWN |
| 284 | Author-email: UNKNOWN |
| 285 | Maintainer: UNKNOWN |
| 286 | Maintainer-email: UNKNOWN |
| 287 | License: UNKNOWN |
| 288 | Classifier: [] |
| 289 | Download-URL: UNKNOWN |
| 290 | Obsoletes-Dist: ['truffles (<=0.8,>=0.5)', 'truffles (<=0.9,>=0.6)'] |
| 291 | Project-URL: [] |
| 292 | Provides-Dist: ['truffles (1.0)'] |
| 293 | Requires-Dist: ['towel-stuff (0.1)'] |
| 294 | Requires-Python: UNKNOWN |
| 295 | Requires-External: [] |
| 296 | |
| 297 | Extra |
| 298 | ===== |
| 299 | * It was installed as a dependency |
| 300 | |
| 301 | |
| 302 | Find out obsoleted distributions |
| 303 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
| 304 | |
| 305 | Now, we take tackle a different problem, we are interested in finding out |
| 306 | which distributions have been obsoleted. This can be easily done as follows:: |
| 307 | |
| 308 | import packaging.database |
| 309 | |
| 310 | # iterate over all distributions in the system |
| 311 | for dist in packaging.database.get_distributions(): |
| 312 | name, version = dist.name, dist.version |
| 313 | # find out which distributions obsolete this name/version combination |
| 314 | replacements = packaging.database.obsoletes_distribution(name, version) |
| 315 | if replacements: |
| 316 | print('%r %s is obsoleted by' % (name, version), |
| 317 | ', '.join(repr(r.name) for r in replacements)) |
| 318 | |
| 319 | This is how the output might look like: |
| 320 | |
| 321 | .. code-block:: none |
| 322 | |
| 323 | 'strawberry' 0.6 is obsoleted by 'choxie' |
| 324 | 'grammar' 1.0a4 is obsoleted by 'towel-stuff' |