Examples

This section highlights the main APIs through some simple examples.

Package search/listing

Most search, listing operations in enstaller are done through Repository instances, which are containers of package metadata. For example, to list every egg installed in sys.prefix:

from enstaller import Repository

repository = Repository._from_prefixes([sys.prefix])
for package in repository.iter_packages():
    print("{0}-{1}".format(package.name, package.version))

one can also list the most recent version for each package:

for package in repository.iter_most_recent_packages():
    print("{0}-{1}".format(package.name, package.version))

Repository instances are “dumb” containers, and don’t handle network connections, authentication, etc... A simple way to create a “real” repository is to start from a set of eggs:

from enstaller import Repository, RepositoryPackageMetadata

repository = Repository()
for path in glob.glob("*.egg"):
    package = RepositoryPackageMetadata.from_egg(path)
    repository.add_package(package)

We will later look into creating repositories from old-style repositories (as created by epd-repo) or brood repositories. The simplicity of repositories allows loose-coupling between operations on a repository and the package metadata origin.

Connecting and authenticating

Http connections are handled through Session objects. To start a session, one may simply do:

from enstaller import Configuration, Session

configuration = Configuration(auth=("username", "password"))

session = Session.authenticated_from_configuration(configuration)

Session are thin wrappers around requests’ Session. Its main features over requests’ Session are etag handling, file:// uri handling, pluggable authentication method as well as integration with Configuration instances for settings (proxy, etc...).

In addition to head/get/post methods, Session instances have a slighly higher-level download method, which enables streaming and raises an exception if an HTTP error occurs, and is robust against stalled/cancelled downloads:

# target is the path for the created file. Will not exist if download fails
# (including cancelled by e.g. `Ctr+C`).
target = session.download(some_url)

Delayed authenticated sessions

If one needs to authenticate the session later than creation time, e.g. if the auth information is set up in the configuration, that’s possible as follows:

from enstaller import Configuration, Session

configuration = Configuration()
session = Session.from_configuration(configuration)

# Prompt the user for authentication, etc...
...

configuration.update(auth=("username", "password"))
session.authenticate(configuration.auth)

Creating remote repositories

To create repositories from our legacy index.json formats, one can use the repository_factory method from enstaller.legacy_stores:

from enstaller import Configuration, Session
from enstaller.legacy_stores import repository_factory

config = Configuration._from_legacy_locations()

session = Session.from_configuration(config)
session.authenticate(config.auth)

remote_repository = repository_factory(session, config.indices)

# Same, with etag-based caching
with session.etag():
    remote_repository = repository_factory(session, config.indices)

Note

this works for both use_webservice enabled and disabled:

  • when enabled, config.indices returns a one item-list of (index, store) pair corresponding to the canopy-style index, whereas
  • when disabled, config.indices returns a list of pairs (index, store), one pair per entry in IndexedRepos.

Solving dependencies

The dependency solver has a simple API to resolve dependencies:

from enstaller.solver import Request, Requirement, Solver

# represents the set of packages available
remote_repository = Repository(...)
# represents the set of packages currently installed
installed_repository = Repository(...)

solver = Solver(remote_repository, installed_repository)

request = Request()
request.install(Requirement.from_anything("numpy"))
request.install(Requirement.from_anything("ipython"))

# actions are (opcode, egg) pairs
# WARNING: this is likely to change
actions = solver.resolve(request)

Note

actions returned by the solver are only of the install/remove type, fetching is handled outside the solver.

Executor