blob: 2a8c39f92c423141ce370d4663dfba13d6cd58e0 [file] [log] [blame]
Éric Araujo3a9f58f2011-06-01 20:42:49 +02001:mod:`packaging.database` --- Database of installed distributions
2=================================================================
3
4.. module:: packaging.database
5 :synopsis: Functions to query and manipulate installed distributions.
6
7
8This module provides an implementation of :PEP:`376`. It was originally
9intended to land in :mod:`pkgutil`, but with the inclusion of Packaging in the
10standard library, it was thought best to include it in a submodule of
11:mod:`packaging`, leaving :mod:`pkgutil` to deal with imports.
12
13Installed Python distributions are represented by instances of
14:class:`Distribution`, or :class:`EggInfoDistribution` for legacy egg formats.
15Most functions also provide an extra argument ``use_egg_info`` to take legacy
16distributions into account.
17
18
19Classes 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
122Functions 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
169Utility 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
183For performance purposes, the list of distributions is being internally
184cached. Caching is enabled by default, but you can control it with these
185functions:
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
200Examples
201--------
202
203Print all information about a distribution
204^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
205
206Given a path to a ``.dist-info`` distribution, we shall print out all
207information 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
242If we save the script above as ``print_info.py``, we can use it to extract
243information 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
249we 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
302Find out obsoleted distributions
303^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
304
305Now, we take tackle a different problem, we are interested in finding out
306which 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
319This 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'