{"id":16653401,"url":"https://github.com/sdispater/mixology","last_synced_at":"2025-10-07T08:32:17.154Z","repository":{"id":36466012,"uuid":"224886265","full_name":"sdispater/mixology","owner":"sdispater","description":"A generic dependency-resolution library written in pure Python","archived":false,"fork":false,"pushed_at":"2020-08-10T13:15:25.000Z","size":64,"stargazers_count":78,"open_issues_count":6,"forks_count":11,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-01-05T21:34:11.044Z","etag":null,"topics":["dependency-resolution","python"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sdispater.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-11-29T16:13:43.000Z","updated_at":"2024-11-28T16:34:33.000Z","dependencies_parsed_at":"2022-08-08T15:01:12.120Z","dependency_job_id":null,"html_url":"https://github.com/sdispater/mixology","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdispater%2Fmixology","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdispater%2Fmixology/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdispater%2Fmixology/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sdispater%2Fmixology/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sdispater","download_url":"https://codeload.github.com/sdispater/mixology/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":235607148,"owners_count":19017302,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dependency-resolution","python"],"created_at":"2024-10-12T09:44:16.764Z","updated_at":"2025-10-07T08:32:11.858Z","avatar_url":"https://github.com/sdispater.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mixology\n\nA generic dependency-resolution library written in pure Python.\nIt is based on the [PubGrub](https://github.com/dart-lang/pub/blob/master/doc/solver.md) algorithm.\n\n\n## Installation\n\nIf you are using [poetry](https://github.com/sdispater/poetry), it's as simple as:\n\n```bash\npoetry add mixology\n```\n\nIf not you can use `pip`:\n\n```bash\npip install mixology\n```\n\n## Usage\n\nMixology is a dependency resolution algorithm.\n\nIn order to start using Mixology you need to initialize a [`VersionSolver`](mixology/version_solver.py) instance\nwith a [`PackageSource`](mixology/package_source.py) which should be adapted to work with your system.\n\nThen, you need to call `VersionSolver.solve()` which will return a [result](mixology/result.py) with the list of decisions\nor raise a [`SolveFailure`](mixology/failure.py) which will give a detailed explanation of the reason why the resolution failed.\n\n## Example\n\nThis example is extracted from the test suite of Mixology\nand uses the [`poetry-semver`](https://github.com/python-poetry/semver) library.\n\nFirst we need to have our own `PackageSource` class which implements the required methods\nand a simple `Dependency` class. Packages will be represented by simple strings.\n\n```python\nfrom semver import Version\nfrom semver import VersionRange\nfrom semver import parse_constraint\n\nfrom mixology.constraint import Constraint\nfrom mixology.package_source import PackageSource as BasePackageSource\nfrom mixology.range import Range\nfrom mixology.union import Union\n\n\nclass Dependency:\n\n    def __init__(self, name, constraint):  # type: (str, str) -\u003e None\n        self.name = name\n        self.constraint = parse_constraint(constraint)\n        self.pretty_constraint = constraint\n\n    def __str__(self):  # type: () -\u003e str\n        return self.pretty_constraint\n\n\nclass PackageSource(BasePackageSource):\n\n    def __init__(self):  # type: () -\u003e None\n        self._root_version = Version.parse(\"0.0.0\")\n        self._root_dependencies = []\n        self._packages = {}\n\n        super(PackageSource, self).__init__()\n\n    @property\n    def root_version(self):\n        return self._root_version\n\n    def add(\n        self, name, version, deps=None\n    ):  # type: (str, str, Optional[Dict[str, str]]) -\u003e None\n        if deps is None:\n            deps = {}\n\n        version = Version.parse(version)\n        if name not in self._packages:\n            self._packages[name] = {}\n\n        if version in self._packages[name]:\n            raise ValueError(\"{} ({}) already exists\".format(name, version))\n\n        dependencies = []\n        for dep_name, spec in deps.items():\n            dependencies.append(Dependency(dep_name, spec))\n\n        self._packages[name][version] = dependencies\n\n    def root_dep(self, name, constraint):  # type: (str, str) -\u003e None\n        self._root_dependencies.append(Dependency(name, constraint))\n\n    def _versions_for(\n        self, package, constraint=None\n    ):  # type: (Hashable, Any) -\u003e List[Hashable]\n        if package not in self._packages:\n            return []\n\n        versions = []\n        for version in self._packages[package].keys():\n            if not constraint or constraint.allows_any(\n                Range(version, version, True, True)\n            ):\n                versions.append(version)\n\n        return sorted(versions, reverse=True)\n\n    def dependencies_for(self, package, version):  # type: (Hashable, Any) -\u003e List[Any]\n        if package == self.root:\n            return self._root_dependencies\n\n        return self._packages[package][version]\n\n    def convert_dependency(self, dependency):  # type: (Dependency) -\u003e Constraint\n        if isinstance(dependency.constraint, VersionRange):\n            constraint = Range(\n                dependency.constraint.min,\n                dependency.constraint.max,\n                dependency.constraint.include_min,\n                dependency.constraint.include_max,\n                dependency.pretty_constraint,\n            )\n        else:\n            # VersionUnion\n            ranges = [\n                Range(\n                    range.min,\n                    range.max,\n                    range.include_min,\n                    range.include_max,\n                    str(range),\n                )\n                for range in dependency.constraint.ranges\n            ]\n            constraint = Union.of(ranges)\n\n        return Constraint(dependency.name, constraint)\n```\n\nNow, we need to specify our root dependencies and the available packages.\n\n```python\nsource = PackageSource()\n\nsource.root_dep(\"a\", \"1.0.0\")\nsource.root_dep(\"b\", \"1.0.0\")\n\nsource.add(\"a\", \"1.0.0\", deps={\"shared\": \"\u003e=2.0.0 \u003c4.0.0\"})\nsource.add(\"b\", \"1.0.0\", deps={\"shared\": \"\u003e=3.0.0 \u003c5.0.0\"})\nsource.add(\"shared\", \"2.0.0\")\nsource.add(\"shared\", \"3.0.0\")\nsource.add(\"shared\", \"3.6.9\")\nsource.add(\"shared\", \"4.0.0\")\nsource.add(\"shared\", \"5.0.0\")\n```\n\nNow that everything is in place we can create a `VersionSolver` instance\nwith the newly created `PackageSource` and call `solve()` to retrieve a `SolverResult` instance.\n\n```python\nfrom mixology.version_solver import VersionSolver\n\nsolver = VersionSolver(source)\nresult = solver.solve()\nresult.decisions\n# {Package(\"_root_\"): \u003cVersion 0.0.0\u003e, 'b': \u003cVersion 1.0.0\u003e, 'a': \u003cVersion 1.0.0\u003e, 'shared': \u003cVersion 3.6.9\u003e}\nresult.attempted_solutions\n# 1\n```\n\n\n## Contributing\n\nTo work on the Mixology codebase, you'll want to fork the project, clone the fork locally\nand install the required dependencies via `poetry \u003chttps://poetry.eustace.io\u003e`_.\n\n```bash\ngit clone git@github.com:sdispater/mixology.git\npoetry install\n```\n\nThen, create your feature branch:\n\n```bash\ngit checkout -b my-new-feature\n```\n\nMake your modifications, add tests accordingly and execute the test suite:\n\n```bash\npoetry run pytest tests/\n```\n\nWhen you are ready, commit your changes:\n\n```bash\ngit commit -am 'Add new feature'\n```\n\npush your branch:\n\n```bash\ngit push origin my-new-feature\n```\n\nand finally create a pull request.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdispater%2Fmixology","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsdispater%2Fmixology","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsdispater%2Fmixology/lists"}