Skip to main content

A generic dependency-resolution algorithm written in pure Python.

Project description


A generic dependency-resolution algorithm written in pure Python.


If you are using poetry, it's as simple as:

poetry add mixology

If not you can use pip:

pip install mixology


Mixology is a dependency resolution algorithm. It is heavily inspired by Molinillo in Ruby.

In order to start using mixology you need to initialize a Resolver instance with a SpecificationProvider and a UI which should be adapted to work with your system.

Then, you need to call Resolver.resolve() with a list of user-requested dependencies and an optional "locking" DependencyGraph.

Specification provider

The SpecificationProvider class forms the basis for the key integration point for a client library with Molinillo.

Its methods convert the client's domain-specific model objects into concepts the resolver understands:

  • Nested dependencies
  • Names
  • Requirement satisfaction
  • Finding specifications (known internally as possibilities)
  • Sorting dependencies (for the sake of reasonable resolver performance)

The base class looks like this:

class SpecificationProvider(object):
    Provides information about specifications and dependencies to the resolver,
    allowing the Resolver class to remain generic while still providing power
    and flexibility.

    This contract contains the methods that users of mixology must implement
    using knowledge of their own model classes.

    def name_for_explicit_dependency_source(self):  # type: () -> str
        return 'user-specified dependency'

    def name_for_locking_dependency_source(self):  # type: () -> str
        return 'Lockfile'

    def search_for(self, dependency):  # type: (Any) -> List[Any]
        Search for the specifications that match the given dependency.

        The specifications in the returned list will be considered in reverse
        order, so the latest version ought to be last.
        return []

    def dependencies_for(self, specification):  # type: (Any) -> List[Any]
        Returns the dependencies of specification.
        return []

    def is_requirement_satisfied_by(self,
                                    requirement,  # type: Any
                                    activated,    # type: DependencyGraph
                                    spec          # type: Any
                                    ):  # type: (...) -> Any
        Determines whether the given requirement is satisfied by the given
        spec, in the context of the current activated dependency graph.
        return True

    def name_for(self, dependency):  # type: (Any) -> str
        Returns the name for the given dependency.
        return str(dependency)

    def sort_dependencies(self,
                          dependencies,  # type: List[Any]
                          activated,     # type: DependencyGraph
                          conflicts      # type: Dict[str, List[Conflict]]
                          ):  # type: (...) -> List[Any]
        Sort dependencies so that the ones
        that are easiest to resolve are first.

        Easiest to resolve is (usually) defined by:
            1) Is this dependency already activated?
            2) How relaxed are the requirements?
            3) Are there any conflicts for this dependency?
            4) How many possibilities are there to satisfy this dependency?
        return sorted(
            key=lambda dep: (
                activated.vertex_named(self.name_for(dep)).payload is None,
                conflicts.get(self.name_for(dep) is None)

    def allow_missing(self, dependency):  # type: (Any) -> bool
        Returns whether this dependency, which has no possible matching
        specifications, can safely be ignored.
        return False


The UI class helps give feedback on and debug the depency resolution process.

You can subclass it to customize it to your needs.

The base class looks like this:

class UI(object):

    def __init__(self, debug=False):
        self._debug = debug

    def output(self):
        return sys.stdout

    def progress_rate(self):  # type: () -> float
        return 0.33

    def is_debugging(self):  # type: () -> bool
        return self._debug

    def indicate_progress(self):  # type: () -> None

    def before_resolution(self):  # type: () -> None
        self.output.write('Resolving dependencies...\n')

    def after_resolution(self):  # type: () -> None

    def debug(self, message, depth):  # type: (...) -> None
        if self.is_debugging():
            debug_info = str(message)
            debug_info = '\n'.join([
                ':{}: {}'.format(str(depth).rjust(4), s)
                for s in debug_info.split('\n')
            ]) + '\n'



To work on the Mixology codebase, you'll want to fork the project, clone the fork locally and install the required depedendencies via poetry <>_.

git clone
poetry install

Then, create your feature branch:

git checkout -b my-new-feature

Make your modifications, add tests accordingly and execute the test suite:

poetry run pytest tests/

When you are ready commit your changes:

git commit -am 'Add new feature'

push your branch:

git push origin my-new-feature

and finally create a pull request.

Project details

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Files for mixology, version 0.1.0
Filename, size File type Python version Upload date Hashes
Filename, size mixology-0.1.0-py2.py3-none-any.whl (72.1 kB) File type Wheel Python version py2.py3 Upload date Hashes View
Filename, size mixology-0.1.0.tar.gz (17.4 kB) File type Source Python version None Upload date Hashes View

Supported by

Pingdom Pingdom Monitoring Google Google Object Storage and Download Analytics Sentry Sentry Error logging AWS AWS Cloud computing DataDog DataDog Monitoring Fastly Fastly CDN DigiCert DigiCert EV certificate StatusPage StatusPage Status page