resolvelib

1.2.1 · active · verified Sun Apr 05

Resolvelib is a Python library that provides a generic dependency resolution algorithm. It allows you to resolve abstract dependencies into concrete ones by implementing a custom 'Provider' interface that defines how packages, requirements, and candidates interact. The current version is 1.2.1, and it maintains an active release cadence with several updates per year, indicating ongoing development and support.

Warnings

Install

Imports

Quickstart

This quickstart demonstrates the core usage of `resolvelib`. It involves defining custom `Package` and `Requirement` objects, then implementing a `Provider` class to teach the resolver how to find candidates, handle dependencies, and determine preferences. Finally, a `Resolver` instance is created and used to find a consistent set of packages based on the initial requirements.

import resolvelib
from resolvelib.reporters import BaseReporter
import os

# Define simple data structures for our 'packages' and 'requirements'
# In a real scenario, these would be richer objects (e.g., Package(name, version), Requirement(package_name, specifier))
class MyPackage:
    def __init__(self, name, version):
        self.name = name
        self.version = version

    def __repr__(self):
        return f'{self.name}=={self.version}'

    def __hash__(self):
        return hash((self.name, self.version))

    def __eq__(self, other):
        return isinstance(other, MyPackage) and self.name == other.name and self.version == other.version

class MyRequirement:
    def __init__(self, name, specifier):
        self.name = name
        self.specifier = specifier # e.g., '>=1.0', '<2.0'

    def __repr__(self):
        return f'{self.name}{self.specifier}'

    def __hash__(self):
        return hash((self.name, self.specifier))

    def __eq__(self, other):
        return isinstance(other, MyRequirement) and self.name == other.name and self.specifier == other.specifier

# Implement the Provider interface
class MyProvider:
    def get_base_requirement(self, identifier):
        # For this simple example, we assume identifier is the package name
        # and we don't have a 'base' requirement beyond the initial ones.
        return None

    def identify(self, requirement_or_candidate):
        return requirement_or_candidate.name

    def get_preference(self, identifier, resolutions, candidates, information):
        # Prefer higher versions
        return len(candidates) + 1 # Dummy preference, real logic would sort candidates

    def get_dependencies(self, candidate):
        # Define dependencies for candidates
        if candidate.name == 'A' and candidate.version == '1.0':
            return [MyRequirement('B', '>=1.0')]
        if candidate.name == 'B' and candidate.version == '1.0':
            return [MyRequirement('C', '>=1.0')]
        return []

    def get_candidates(self, requirement):
        # Return available candidates for a given requirement
        if requirement.name == 'A':
            yield MyPackage('A', '1.0')
            yield MyPackage('A', '2.0')
        elif requirement.name == 'B':
            yield MyPackage('B', '1.0')
            yield MyPackage('B', '1.1')
        elif requirement.name == 'C':
            yield MyPackage('C', '1.0')
            yield MyPackage('C', '1.2')
        else:
            return []

    def is_satisfied_by(self, requirement, candidate):
        # In a real scenario, this would check if candidate.version satisfies requirement.specifier
        # For simplicity, we assume any candidate with the correct name satisfies a basic requirement
        return requirement.name == candidate.name

# Create an instance of the provider and reporter
provider = MyProvider()
reporter = BaseReporter()

# Create the resolver
resolver = resolvelib.Resolver(provider, reporter)

# Define the initial requirements
requirements = [MyRequirement('A', '>=1.0')]

# Kick off the resolution process
try:
    result = resolver.resolve(requirements)
    print("Resolution successful:")
    for candidate in result.graph.iter_network_linear():
        if isinstance(candidate, MyPackage):
            print(f"  - {candidate}")
except resolvelib.ResolutionImpossible as e:
    print(f"Resolution failed: {e}")

# Example of getting an auth key, though not directly used by resolvelib
api_key = os.environ.get('RESOLVELIB_API_KEY', 'your_default_or_mock_key')
if api_key == 'your_default_or_mock_key':
    print("\nNote: For real-world use with external registries, an API key might be passed via Provider.")
else:
    print(f"\nUsing API Key (first 5 chars): {api_key[:5]}...")

view raw JSON →