0
# Distribution Classes
1
2
Distribution classes provide object-oriented access to package metadata, including abstract base classes and concrete implementations for different package formats. The Distribution system forms the core of importlib_metadata's architecture.
3
4
## Capabilities
5
6
### Abstract Distribution Base Class
7
8
The base Distribution class defines the interface for accessing package metadata and provides common functionality.
9
10
```python { .api }
11
class Distribution(metaclass=abc.ABCMeta):
12
"""
13
An abstract Python distribution package.
14
15
Custom providers may derive from this class and define the abstract methods
16
to provide a concrete implementation for their environment.
17
"""
18
19
@abc.abstractmethod
20
def read_text(self, filename) -> str | None:
21
"""
22
Attempt to load metadata file given by the name.
23
24
Parameters:
25
- filename: The name of the file in the distribution info
26
27
Returns:
28
str | None: The text if found, otherwise None
29
"""
30
31
@abc.abstractmethod
32
def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
33
"""
34
Given a path to a file in this distribution, return a SimplePath to it.
35
36
Parameters:
37
- path: Path to a file in this distribution
38
39
Returns:
40
SimplePath: Path object for the file
41
42
Raises:
43
NotImplementedError: If provider doesn't support file location
44
"""
45
46
@classmethod
47
def from_name(cls, name: str) -> Distribution:
48
"""
49
Return the Distribution for the given package name.
50
51
Parameters:
52
- name: The name of the distribution package to search for
53
54
Returns:
55
Distribution: The Distribution instance for the named package
56
57
Raises:
58
PackageNotFoundError: When the named package's distribution metadata cannot be found
59
ValueError: When an invalid value is supplied for name
60
"""
61
62
@classmethod
63
def discover(cls, *, context: DistributionFinder.Context | None = None, **kwargs) -> Iterable[Distribution]:
64
"""
65
Return an iterable of Distribution objects for all packages.
66
67
Pass a context or pass keyword arguments for constructing a context.
68
69
Parameters:
70
- context: A DistributionFinder.Context object (optional)
71
- **kwargs: Keyword arguments for constructing a context
72
73
Returns:
74
Iterable[Distribution]: Iterable of Distribution objects for packages matching the context
75
76
Raises:
77
ValueError: If both context and kwargs are provided
78
"""
79
80
@staticmethod
81
def at(path: str | os.PathLike[str]) -> Distribution:
82
"""
83
Return a Distribution for the indicated metadata path.
84
85
Parameters:
86
- path: A string or path-like object to the metadata directory
87
88
Returns:
89
Distribution: A concrete Distribution instance for the path
90
"""
91
92
@staticmethod
93
def _discover_resolvers():
94
"""
95
Search the meta_path for resolvers (MetadataPathFinders).
96
97
Returns:
98
Iterator: Iterator of find_distributions methods from MetaPathFinders
99
"""
100
101
@staticmethod
102
def _prefer_valid(dists: Iterable[Distribution]) -> Iterable[Distribution]:
103
"""
104
Prefer (move to the front) distributions that have metadata.
105
106
This optimization addresses cases where multiple distributions
107
may be found but some lack proper metadata.
108
109
Parameters:
110
- dists: Iterable of Distribution instances
111
112
Returns:
113
Iterable[Distribution]: Distributions with valid metadata prioritized
114
"""
115
```
116
117
### Distribution Properties
118
119
Access to distribution metadata through properties:
120
121
```python { .api }
122
class Distribution:
123
@property
124
def metadata(self) -> PackageMetadata | None:
125
"""
126
Return the parsed metadata for this Distribution.
127
128
The returned object will have keys that name the various bits of metadata
129
per the Core metadata specifications.
130
"""
131
132
@property
133
def name(self) -> str:
134
"""Return the 'Name' metadata for the distribution package."""
135
136
@property
137
def version(self) -> str:
138
"""Return the 'Version' metadata for the distribution package."""
139
140
@property
141
def entry_points(self) -> EntryPoints:
142
"""Return EntryPoints for this distribution."""
143
144
@property
145
def files(self) -> list[PackagePath] | None:
146
"""
147
Files in this distribution.
148
149
Returns:
150
list[PackagePath] | None: List of PackagePath for this distribution or None
151
if the metadata file that enumerates files is missing
152
"""
153
154
@property
155
def requires(self) -> list[str] | None:
156
"""Generated requirements specified for this Distribution."""
157
158
@property
159
def origin(self):
160
"""
161
Return the origin metadata from direct_url.json.
162
163
This provides information about how the package was installed,
164
including VCS information, local paths, or archive URLs.
165
166
Returns:
167
SimpleNamespace | None: Origin metadata object or None if not available
168
"""
169
170
@property
171
def _normalized_name(self):
172
"""
173
Return a normalized version of the name.
174
175
Used internally for consistent name comparisons and lookups.
176
177
Returns:
178
str: Normalized package name
179
"""
180
```
181
182
#### Usage Examples
183
184
```python
185
import importlib_metadata
186
187
# Get distribution and access properties
188
dist = importlib_metadata.Distribution.from_name('requests')
189
print(f"Name: {dist.name}")
190
print(f"Version: {dist.version}")
191
192
# Access metadata
193
metadata = dist.metadata
194
if metadata:
195
print(f"Summary: {metadata['Summary']}")
196
print(f"Author: {metadata['Author']}")
197
198
# Get entry points for this distribution
199
eps = dist.entry_points
200
console_scripts = eps.select(group='console_scripts')
201
for ep in console_scripts:
202
print(f"Script: {ep.name}")
203
204
# Get files
205
files = dist.files
206
if files:
207
print(f"Distribution has {len(files)} files")
208
```
209
210
### PathDistribution Implementation
211
212
Concrete implementation for filesystem-based distributions:
213
214
```python { .api }
215
class PathDistribution(Distribution):
216
"""Concrete Distribution implementation for filesystem paths."""
217
218
def __init__(self, path: SimplePath) -> None:
219
"""
220
Construct a distribution.
221
222
Parameters:
223
- path: SimplePath indicating the metadata directory
224
"""
225
226
def read_text(self, filename: str | os.PathLike[str]) -> str | None:
227
"""Read text from a file in the distribution metadata."""
228
229
def locate_file(self, path: str | os.PathLike[str]) -> SimplePath:
230
"""Locate a file relative to the distribution."""
231
```
232
233
#### Usage Examples
234
235
```python
236
import importlib_metadata
237
from pathlib import Path
238
239
# Create distribution from path
240
metadata_path = Path('/path/to/package-1.0.dist-info')
241
dist = importlib_metadata.PathDistribution(metadata_path)
242
243
# Use like any other distribution
244
print(f"Package: {dist.name}")
245
print(f"Version: {dist.version}")
246
```
247
248
### Distribution Discovery
249
250
The DistributionFinder system enables pluggable package discovery:
251
252
```python { .api }
253
class DistributionFinder(MetaPathFinder):
254
"""
255
A MetaPathFinder capable of discovering installed distributions.
256
257
Custom providers should implement this interface in order to supply metadata.
258
"""
259
260
class Context:
261
"""
262
Keyword arguments presented by the caller to distributions() or
263
Distribution.discover() to narrow the scope of a search for distributions.
264
"""
265
266
name = None # Specific name for which a distribution finder should match
267
268
def __init__(self, **kwargs):
269
"""
270
Initialize context with keyword arguments.
271
272
Parameters:
273
- **kwargs: Context parameters for filtering distributions
274
"""
275
276
@property
277
def path(self) -> list[str]:
278
"""
279
The sequence of directory paths that a distribution finder should search.
280
Typically refers to Python installed package paths such as "site-packages"
281
directories and defaults to sys.path.
282
283
Returns:
284
list[str]: List of directory paths to search
285
"""
286
287
@abc.abstractmethod
288
def find_distributions(self, context=Context()) -> Iterable[Distribution]:
289
"""
290
Find distributions.
291
292
Parameters:
293
- context: DistributionFinder.Context instance for filtering
294
295
Returns:
296
Iterable[Distribution]: Iterable of all Distribution instances capable of
297
loading the metadata for packages matching the context
298
"""
299
```
300
301
### MetadataPathFinder Implementation
302
303
Concrete implementation of DistributionFinder for filesystem-based package discovery:
304
305
```python { .api }
306
class MetadataPathFinder(DistributionFinder):
307
"""
308
A degenerate finder for distribution packages on the file system.
309
310
This finder supplies only a find_distributions() method for versions
311
of Python that do not have a PathFinder find_distributions().
312
"""
313
314
@classmethod
315
def find_distributions(cls, context=DistributionFinder.Context()) -> Iterable[PathDistribution]:
316
"""
317
Find distributions.
318
319
Return an iterable of all Distribution instances capable of
320
loading the metadata for packages matching context.name
321
(or all names if None indicated) along the paths in the list
322
of directories context.path.
323
324
Parameters:
325
- context: DistributionFinder.Context instance for filtering
326
327
Returns:
328
Iterable[PathDistribution]: PathDistribution instances for found packages
329
"""
330
331
@classmethod
332
def invalidate_caches(cls) -> None:
333
"""Invalidate internal caches used for distribution discovery."""
334
```
335
336
#### Usage Examples
337
338
```python
339
import importlib_metadata
340
341
# Discover all distributions
342
all_dists = list(importlib_metadata.Distribution.discover())
343
print(f"Found {len(all_dists)} distributions")
344
345
# Discover with context
346
context = importlib_metadata.DistributionFinder.Context(name='requests')
347
matching_dists = list(importlib_metadata.Distribution.discover(context=context))
348
349
# Discover with kwargs
350
specific_dists = list(importlib_metadata.Distribution.discover(name='requests'))
351
```
352
353
## Error Handling
354
355
Distribution classes raise specific exceptions for various error conditions:
356
357
```python
358
import importlib_metadata
359
360
try:
361
dist = importlib_metadata.Distribution.from_name('nonexistent')
362
except importlib_metadata.PackageNotFoundError:
363
print("Package not found")
364
365
try:
366
dist = importlib_metadata.Distribution.from_name('')
367
except ValueError:
368
print("Invalid distribution name")
369
```