Skip to content

Commit

Permalink
Massive API refactoring; see setuptools.txt changelog for details. Also,
Browse files Browse the repository at this point in the history
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
Show file tree
Hide file tree
Showing 10 changed files with 3,420 additions and 250 deletions.
16 changes: 16 additions & 0 deletions EasyInstall.txt
Expand Up @@ -623,6 +623,22 @@ Known Issues
Made ``easy-install.pth`` work in platform-specific alternate site
directories (e.g. ``~/Library/Python/2.x/site-packages``).

* If you manually delete the current version of a package, the next run of
EasyInstall against the target directory will now remove the stray entry
from the ``easy-install.pth``file.

* EasyInstall now recognizes URLs with a ``#egg=project_name`` fragment ID
as pointing to the named project's source checkout. Such URLs have a lower
match precedence than any other kind of distribution, so they'll only be
used if they have a higher version number than any other available
distribution. (Future versions may allow you to specify that you want to
use source checkouts instead of other kinds of distributions.) The ``#egg``
fragment can contain a version if it's formatted as ``#egg=proj-ver``,
where ``proj`` is the project name, and ``ver`` is the version number. You
*must* use the format for these values that the ``bdist_egg`` command uses;
i.e., all non-alphanumeric runs must be condensed to single underscore
characters.

0.5a12
* Fix ``python -m easy_install`` not working due to setuptools being installed
as a zipfile. Update safety scanner to check for modules that might be used
Expand Down
247 changes: 247 additions & 0 deletions api_tests.txt
@@ -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

0 comments on commit c721485

Please sign in to comment.