diff --git a/resolver/mindeps/__init__.py b/resolver/mindeps/__init__.py index 310c5f0..627f399 100644 --- a/resolver/mindeps/__init__.py +++ b/resolver/mindeps/__init__.py @@ -1,15 +1,6 @@ # SPDX-License-Identifier: MIT -import operator +from resolver.mindeps.__main__ import entrypoint, get_min_deps # noqa: F401 -from typing import Iterable, Sequence -import resolver - - -class MinimumDependencyProvider(resolver.Provider): - def sort_candidates( - self, - candidates: Iterable[resolver.Candidate], - ) -> Sequence[resolver.Candidate]: - return sorted(candidates, key=operator.attrgetter('version'), reverse=False) +__all__ = ('entrypoint', 'get_min_deps') diff --git a/resolver/mindeps/__main__.py b/resolver/mindeps/__main__.py index 98d2c37..8e94e81 100644 --- a/resolver/mindeps/__main__.py +++ b/resolver/mindeps/__main__.py @@ -1,12 +1,13 @@ # SPDX-License-Identifier: MIT import argparse +import operator import os import os.path import pathlib import tempfile -from typing import Iterable, Sequence, Set +from typing import Dict, Iterable, List, Optional, Sequence, Set import packaging.markers import packaging.requirements @@ -91,39 +92,43 @@ def main_parser() -> argparse.ArgumentParser: return parser -def task() -> None: # noqa: C901 - parser = main_parser() - args = parser.parse_args() +class MinimumDependencyProvider(resolver.Provider): + def sort_candidates( + self, + candidates: Iterable[resolver.Candidate], + ) -> Sequence[resolver.Candidate]: + return sorted(candidates, key=operator.attrgetter('version'), reverse=False) - if args.requirements: - for bad_arg in _MARKER_KEYS + ('extras',): - if bad_arg in args: - resolver.__main__._error(f'Option --{bad_arg} not supported when specifying bare requirements') - reporter = resolver.__main__.VerboseReporter if args.verbose else resolvelib.BaseReporter +def get_min_deps( + requirements: List[str], + reporter: Optional[resolvelib.BaseReporter] = None, + extras: Optional[Set[str]] = None, + markers: Optional[Dict[str, str]] = None +) -> Dict[str, str]: + reporter = reporter or resolvelib.BaseReporter package_resolver = resolvelib.Resolver( - resolver.mindeps.MinimumDependencyProvider( + MinimumDependencyProvider( '/tmp/resolver-cache' if os.name == 'posix' else None ), reporter(), ) requirements: Iterable[packaging.requirements.Requirement] = map( - packaging.requirements.Requirement, - args.requirements or _project_requirements(), + packaging.requirements.Requirement, requirements ) - extras = set(vars(args).get('extras', {})) | {''} - if any(arg in _MARKER_KEYS for arg in vars(args)): + extras = extras.copy() | {''} if extras else {''} + if markers is not None and any(marker in _MARKER_KEYS for marker in markers): marker_env = { - k: v for k, v in vars(args).items() + k: v for k, v in markers.items() if k in _MARKER_KEYS } else: marker_env = packaging.markers.default_environment() resolver_requirements: Set[packaging.requirements.Requirement] = set() - for requirement in requirements: + for requirement in list(requirements): for extra in extras: if not requirement.marker: resolver_requirements.add(requirement) @@ -133,13 +138,34 @@ def task() -> None: # noqa: C901 result = package_resolver.resolve(resolver_requirements) - if args.verbose: - print('\n--- Solution ---') - pinned = { candidate.name: candidate.version for candidate in result.mapping.values() } + + return pinned + + +def task() -> None: # noqa: C901 + parser = main_parser() + args = parser.parse_args() + + if args.requirements: + for bad_arg in _MARKER_KEYS + ('extras',): + if bad_arg in args: + resolver.__main__._error(f'Option --{bad_arg} not supported when specifying bare requirements') + + reporter = resolver.__main__.VerboseReporter if args.verbose else resolvelib.BaseReporter + + if args.verbose: + print('\n--- Solution ---') + + pinned = get_min_deps( + args.requirements or _project_requirements(), + reporter=reporter, + extras=set(vars(args).get('extras', {})), + markers=vars(args) + ) for name, version in pinned.items(): print(f'{name}=={str(version)}')