Unverified Commit 03ade36c authored by Sébastien Eustace's avatar Sébastien Eustace
Browse files

Add the debug:resolve command

parent b5e3d51d
Loading
Loading
Loading
Loading
+1 −0
Changes for CHANGELOG.md: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@

- Added support for virtualenv autogeneration (Python 3.6+ only).
- Added the `run` command to execute commands inside the created virtualenvs.
- Added the `debug:resolve` command to debug dependency resolution.

### Fixed

+10 −1
Changes for poetry/console/application.py: 10 added lines, 1 removed line.
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ from .commands import RunCommand
from .commands import ShowCommand
from .commands import UpdateCommand

from .commands.debug import DebugResolveCommand


class Application(BaseApplication):

@@ -78,7 +80,7 @@ class Application(BaseApplication):
    def get_default_commands(self) -> list:
        commands = super(Application, self).get_default_commands()

        return commands + [
        commands += [
            AboutCommand(),
            AddCommand(),
            BuildCommand(),
@@ -92,3 +94,10 @@ class Application(BaseApplication):
            ShowCommand(),
            UpdateCommand(),
        ]

        # Debug commands
        commands += [
            DebugResolveCommand(),
        ]

        return commands
+1 −0
Changes for poetry/console/commands/debug/__init__.py: 1 added line, 0 removed lines.
Original line number Diff line number Diff line
from .resolve import DebugResolveCommand
+100 −0
Changes for poetry/console/commands/debug/resolve.py: 100 added lines, 0 removed lines.
Original line number Diff line number Diff line
import re

from typing import List

from poetry.packages import Dependency
from poetry.puzzle import Solver
from poetry.repositories.repository import Repository
from poetry.semver.version_parser import VersionParser

from ..command import Command


class DebugResolveCommand(Command):
    """
    Debugs dependency resolution.

    debug:resolve
        { package?* : packages to resolve. }
    """

    def handle(self):
        packages = self.argument('package')

        if not packages:
            package = self.poetry.package
            dependencies = package.requires + package.dev_requires
        else:
            requirements = self._determine_requirements(packages)
            requirements = self._format_requirements(requirements)

            # validate requirements format
            parser = VersionParser()
            for constraint in requirements.values():
                parser.parse_constraints(constraint)

            dependencies = []
            for name, constraint in requirements.items():
                dependencies.append(
                    Dependency(name, constraint)
                )

        solver = Solver(
            self.poetry.package,
            self.poetry.pool,
            Repository(),
            self.output
        )

        ops = solver.solve(dependencies)

        self.line('')
        self.line('Resolution results:')
        self.line('')

        for op in ops:
            package = op.package
            self.line(f'  - <info>{package.name}</info> '
                      f'(<comment>{package.version}</comment>)')

    def _determine_requirements(self, requires: List[str]) -> List[str]:
        if not requires:
            return []

        requires = self._parse_name_version_pairs(requires)
        result = []
        for requirement in requires:
            if 'version' not in requirement:
                requirement['version'] = '*'

            result.append(f'{requirement["name"]} {requirement["version"]}')

        return result

    def _parse_name_version_pairs(self, pairs: list) -> list:
        result = []

        for i in range(len(pairs)):
            pair = re.sub('^([^=: ]+)[=: ](.*)$', '\\1 \\2', pairs[i].strip())
            pair = pair.strip()

            if ' ' in pair:
                name, version = pair.split(' ', 2)
                result.append({
                    'name': name,
                    'version': version
                })
            else:
                result.append({
                    'name': pair
                })

        return result

    def _format_requirements(self, requirements: List[str]) -> dict:
        requires = {}
        requirements = self._parse_name_version_pairs(requirements)
        for requirement in requirements:
            requires[requirement['name']] = requirement['version']

        return requires