Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Massive API refactoring; see setuptools.txt changelog for details. Also,
add ``#egg=project-version`` link support, and docs on how to make your package available for EasyInstall to find. --HG-- branch : setuptools extra : convert_revision : svn%3A6015fed2-1504-0410-9fe1-9d1591cc4771/sandbox/trunk/setuptools%4041135
- Loading branch information
PJ Eby
committed
Jul 18, 2005
1 parent
61a0e71
commit c721485
Showing
10 changed files
with
3,420 additions
and
250 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,247 @@ | ||
Pluggable Distributions of Python Software | ||
========================================== | ||
|
||
Distributions | ||
------------- | ||
|
||
A "Distribution" is a collection of files that represent a "Release" of a | ||
"Project" as of a particular point in time, denoted by a | ||
"Version":: | ||
|
||
>>> import sys, pkg_resources | ||
>>> from pkg_resources import Distribution | ||
>>> Distribution(project_name="Foo", version="1.2") | ||
Foo 1.2 | ||
|
||
Distributions have a location, which can be a filename, URL, or really anything | ||
else you care to use:: | ||
|
||
>>> dist = Distribution( | ||
... location="http://example.com/something", | ||
... project_name="Bar", version="0.9" | ||
... ) | ||
|
||
>>> dist | ||
Bar 0.9 (http://example.com/something) | ||
|
||
|
||
Distributions have various introspectable attributes:: | ||
|
||
>>> dist.location | ||
'http://example.com/something' | ||
|
||
>>> dist.project_name | ||
'Bar' | ||
|
||
>>> dist.version | ||
'0.9' | ||
|
||
>>> dist.py_version == sys.version[:3] | ||
True | ||
|
||
>>> print dist.platform | ||
None | ||
|
||
Including various computed attributes:: | ||
|
||
>>> from pkg_resources import parse_version | ||
>>> dist.parsed_version == parse_version(dist.version) | ||
True | ||
|
||
>>> dist.key # case-insensitive form of the project name | ||
'bar' | ||
|
||
Distributions are compared (and hashed) by version first:: | ||
|
||
>>> Distribution(version='1.0') == Distribution(version='1.0') | ||
True | ||
>>> Distribution(version='1.0') == Distribution(version='1.1') | ||
False | ||
>>> Distribution(version='1.0') < Distribution(version='1.1') | ||
True | ||
|
||
but also by project name (case-insensitive), platform, Python version, | ||
location, etc.:: | ||
|
||
>>> Distribution(project_name="Foo",version="1.0") == \ | ||
... Distribution(project_name="Foo",version="1.0") | ||
True | ||
|
||
>>> Distribution(project_name="Foo",version="1.0") == \ | ||
... Distribution(project_name="foo",version="1.0") | ||
True | ||
|
||
>>> Distribution(project_name="Foo",version="1.0") == \ | ||
... Distribution(project_name="Foo",version="1.1") | ||
False | ||
|
||
>>> Distribution(project_name="Foo",py_version="2.3",version="1.0") == \ | ||
... Distribution(project_name="Foo",py_version="2.4",version="1.0") | ||
False | ||
|
||
>>> Distribution(location="spam",version="1.0") == \ | ||
... Distribution(location="spam",version="1.0") | ||
True | ||
|
||
>>> Distribution(location="spam",version="1.0") == \ | ||
... Distribution(location="baz",version="1.0") | ||
False | ||
|
||
|
||
|
||
Hash and compare distribution by prio/plat | ||
|
||
Get version from metadata | ||
provider capabilities | ||
egg_name() | ||
as_requirement() | ||
from_location, from_filename (w/path normalization) | ||
|
||
Releases may have zero or more "Requirements", which indicate | ||
what releases of another project the release requires in order to | ||
function. A Requirement names the other project, expresses some criteria | ||
as to what releases of that project are acceptable, and lists any "Extras" | ||
that the requiring release may need from that project. (An Extra is an | ||
optional feature of a Release, that can only be used if its additional | ||
Requirements are satisfied.) | ||
|
||
|
||
|
||
The Working Set | ||
--------------- | ||
|
||
A collection of active distributions is called a Working Set. Note that a | ||
Working Set can contain any importable distribution, not just pluggable ones. | ||
For example, the Python standard library is an importable distribution that | ||
will usually be part of the Working Set, even though it is not pluggable. | ||
Similarly, when you are doing development work on a project, the files you are | ||
editing are also a Distribution. (And, with a little attention to the | ||
directory names used, and including some additional metadata, such a | ||
"development distribution" can be made pluggable as well.) | ||
|
||
>>> from pkg_resources import WorkingSet | ||
|
||
A working set's entries are the sys.path entries that correspond to the active | ||
distributions. By default, the working set's entries are the items on | ||
``sys.path``:: | ||
|
||
>>> ws = WorkingSet() | ||
>>> ws.entries == sys.path | ||
True | ||
|
||
But you can also create an empty working set explicitly, and add distributions | ||
to it:: | ||
|
||
>>> ws = WorkingSet([]) | ||
>>> ws.add(dist) | ||
>>> ws.entries | ||
['http://example.com/something'] | ||
>>> dist in ws | ||
True | ||
>>> Distribution('foo') in ws | ||
False | ||
|
||
And you can iterate over its distributions:: | ||
|
||
>>> list(ws) | ||
[Bar 0.9 (http://example.com/something)] | ||
|
||
Adding the same distribution more than once is a no-op:: | ||
|
||
>>> ws.add(dist) | ||
>>> list(ws) | ||
[Bar 0.9 (http://example.com/something)] | ||
|
||
For that matter, adding multiple distributions for the same project also does | ||
nothing, because a working set can only hold one active distribution per | ||
project -- the first one added to it:: | ||
|
||
>>> ws.add( | ||
... Distribution( | ||
... 'http://example.com/something', project_name="Bar", | ||
... version="7.2" | ||
... ) | ||
... ) | ||
>>> list(ws) | ||
[Bar 0.9 (http://example.com/something)] | ||
|
||
You can append a path entry to a working set using ``add_entry()``:: | ||
|
||
>>> ws.entries | ||
['http://example.com/something'] | ||
>>> ws.add_entry(pkg_resources.__file__) | ||
>>> ws.entries | ||
['http://example.com/something', '...pkg_resources.py...'] | ||
|
||
Multiple additions result in multiple entries, even if the entry is already in | ||
the working set (because ``sys.path`` can contain the same entry more than | ||
once):: | ||
|
||
>>> ws.add_entry(pkg_resources.__file__) | ||
>>> ws.entries | ||
['...example.com...', '...pkg_resources...', '...pkg_resources...'] | ||
|
||
And you can specify the path entry a distribution was found under, using the | ||
optional second parameter to ``add()`` | ||
|
||
>>> ws.add(dist,"foo") | ||
>>> ws.add(dist,"bar") | ||
>>> ws.entries | ||
['http://example.com/something', ..., 'foo', 'bar'] | ||
|
||
But even if a distribution is found under multiple path entries, it still only | ||
shows up once when iterating the working set: | ||
|
||
>>> list(ws) | ||
[Bar 0.9 (http://example.com/something)] | ||
|
||
You can ask a WorkingSet to ``find()`` a distribution matching a requirement:: | ||
|
||
>>> from pkg_resources import Requirement | ||
>>> print ws.find(Requirement.parse("Foo==1.0")) # no match, return None | ||
None | ||
|
||
>>> ws.find(Requirement.parse("Bar==0.9")) # match, return distribution | ||
Bar 0.9 (http://example.com/something) | ||
|
||
Note that asking for a conflicting version of a distribution already in a | ||
working set triggers a ``pkg_resources.VersionConflict`` error: | ||
|
||
>>> ws.find(Requirement.parse("Bar==1.0")) # doctest: +NORMALIZE_WHITESPACE | ||
Traceback (most recent call last): | ||
... | ||
VersionConflict: (Bar 0.9 (http://example.com/something), | ||
Requirement.parse('Bar==1.0')) | ||
|
||
You can subscribe a callback function to receive notifications whenever a new | ||
distribution is added to a working set. The callback is immediately invoked | ||
once for each existing distribution in the working set, and then is called | ||
again for new distributions added thereafter:: | ||
|
||
>>> def added(dist): print "Added", dist | ||
>>> ws.subscribe(added) | ||
Added Bar 0.9 | ||
>>> foo12 = Distribution(project_name="Foo", version="1.2") | ||
>>> ws.add(foo12) | ||
Added Foo 1.2 | ||
|
||
Note, however, that only the first distribution added for a given project name | ||
will trigger a callback, even during the initial ``subscribe()`` callback:: | ||
|
||
>>> foo14 = Distribution(project_name="Foo", version="1.4") | ||
>>> ws.add(foo14) # no callback, because Foo 1.2 is already active | ||
|
||
>>> ws = WorkingSet([]) | ||
>>> ws.add(foo12) | ||
>>> ws.add(foo14) | ||
>>> ws.subscribe(added) | ||
Added Foo 1.2 | ||
|
||
And adding a callback more than once has no effect, either:: | ||
|
||
>>> ws.subscribe(added) # no callbacks | ||
|
||
# and no double-callbacks on subsequent additions, either | ||
>>> ws.add(Distribution(project_name="JustATest", version="0.99")) | ||
Added JustATest 0.99 | ||
|
Oops, something went wrong.