示例#1
0
    def all_classifiers(self):
        classifiers = copy.copy(self.classifiers)

        # Automatically set python classifiers
        parser = VersionParser()
        if self.python_versions == '*':
            python_constraint = parser.parse_constraints('~2.7 || ^3.4')
        else:
            python_constraint = self.python_constraint

        for version in sorted(self.AVAILABLE_PYTHONS):
            if len(version) == 1:
                constraint = parser.parse_constraints(version + '.*')
            else:
                constraint = Constraint('=', version)

            if python_constraint.matches(constraint):
                classifiers.append(
                    'Programming Language :: Python :: {}'.format(version))

        # Automatically set license classifiers
        if self.license:
            classifiers.append(self.license.classifier)

        classifiers = set(classifiers)

        return sorted(classifiers)
示例#2
0
def test_format_python_constraint():
    parser = VersionParser()
    constraint = parser.parse_constraints('~2.7 || ^3.6')

    result = format_python_constraint(constraint)

    assert result == '>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*'
示例#3
0
    def find_packages(self,
                      name: str,
                      constraint: Union[Constraint, str, None] = None,
                      extras: Union[list, None] = None) -> List[Package]:
        """
        Find packages on the remote server.
        """
        packages = []

        if constraint is not None and not isinstance(constraint,
                                                     BaseConstraint):
            version_parser = VersionParser()
            constraint = version_parser.parse_constraints(constraint)

        info = self.get_package_info(name)

        versions = []

        for version, release in info['releases'].items():
            if (not constraint or
                (constraint and constraint.matches(Constraint('=', version)))):
                versions.append(version)

        for version in versions:
            packages.append(self.package(name, version, extras=extras))

        return packages
示例#4
0
文件: repository.py 项目: undu/poetry
    def find_packages(self, name, constraint=None,
                      extras=None,
                      allow_prereleases=False):
        name = name.lower()
        packages = []
        if extras is None:
            extras = []

        if constraint is None:
            constraint = '*'

        if not isinstance(constraint, BaseConstraint):
            parser = VersionParser()
            constraint = parser.parse_constraints(constraint)

        for package in self.packages:
            if name == package.name:
                pkg_constraint = Constraint('==', package.version)

                if constraint is None or constraint.matches(pkg_constraint):
                    for dep in package.requires:
                        for extra in extras:
                            if extra not in package.extras:
                                continue

                            reqs = package.extras[extra]
                            for req in reqs:
                                if req.name == dep.name:
                                    dep.activate()

                    packages.append(package)

        return packages
示例#5
0
    def find_packages(self, name, constraint=None, extras=None):
        packages = []

        if constraint is not None and not isinstance(constraint,
                                                     BaseConstraint):
            version_parser = VersionParser()
            constraint = version_parser.parse_constraints(constraint)

        key = name
        if constraint:
            key = f'{key}:{str(constraint)}'

        if self._cache.store('matches').has(key):
            versions = self._cache.store('matches').get(key)
        else:
            candidates = [str(c.version) for c in self._repository.find_all_candidates(name)]

            versions = []
            for version in candidates:
                if version in versions:
                    continue

                if (
                    not constraint
                    or (constraint and constraint.matches(Constraint('=', version)))
                ):
                    versions.append(version)

            self._cache.store('matches').put(key, versions, 5)

        for version in versions:
            packages.append(self.package(name, version, extras=extras))

        return packages
示例#6
0
    def find_packages(self, name, constraint=None, extras=None):
        name = name.lower()
        packages = []
        if extras is None:
            extras = []

        if not isinstance(constraint, BaseConstraint):
            parser = VersionParser()
            constraint = parser.parse_constraints(constraint)

        for package in self.packages:
            if name == package.name:
                pkg_constraint = Constraint('==', package.version)

                if constraint is None or constraint.matches(pkg_constraint):
                    for extra in extras:
                        if extra in package.extras:
                            for dep in package.extras[extra]:
                                dep.activate()

                            package.requires += package.extras[extra]

                    packages.append(package)

        return packages
示例#7
0
def format_python_constraint(constraint):
    """
    This helper will help in transforming
    disjunctive constraint into proper constraint.
    """
    if not isinstance(constraint, MultiConstraint):
        return str(constraint)

    has_disjunctive = False
    for c in constraint.constraints:
        if isinstance(c, MultiConstraint) and c.is_disjunctive():
            has_disjunctive = True
            break

    parser = VersionParser()
    formatted = []
    accepted = []
    if not constraint.is_disjunctive() and not has_disjunctive:
        return str(constraint)

    for version in PYTHON_VERSION:
        version_constraint = parser.parse_constraints(version)
        matches = constraint.matches(version_constraint)
        if not matches:
            formatted.append('!=' + version)
        else:
            accepted.append(version)

    # Checking lower bound
    low = accepted[0]

    formatted.insert(0, '>=' + '.'.join(low.split('.')[:2]))

    return ', '.join(formatted)
示例#8
0
    def _filter_operations(self,
                           ops,
                           repo
                           ):  # type: (List[Operation], Repository) -> None
        extra_packages = [p.name for p in
                          self._get_extra_packages(repo)]
        for op in ops:
            if isinstance(op, Update):
                package = op.target_package
            else:
                package = op.package

            if op.job_type == 'uninstall':
                continue

            parser = VersionParser()
            python = '.'.join([str(i) for i in self._venv.version_info[:3]])
            if 'python' in package.requirements:
                python_constraint = parser.parse_constraints(
                    package.requirements['python']
                )
                if not python_constraint.matches(Constraint('=', python)):
                    # Incompatible python versions
                    op.skip('Not needed for the current python version')
                    continue

            if not package.python_constraint.matches(Constraint('=', python)):
                op.skip('Not needed for the current python version')
                continue

            if 'platform' in package.requirements:
                platform_constraint = GenericConstraint.parse(
                    package.requirements['platform']
                )
                if not platform_constraint.matches(
                        GenericConstraint('=', sys.platform)
                ):
                    # Incompatible systems
                    op.skip('Not needed for the current platform')
                    continue

            if self._update:
                extras = {}
                for extra, deps in self._package.extras.items():
                    extras[extra] = [dep.name for dep in deps]
            else:
                extras = {}
                for extra, deps in self._locker.lock_data.get('extras', {}).items():
                    extras[extra] = [dep.lower() for dep in deps]

            # If a package is optional and not requested
            # in any extra we skip it
            if package.optional:
                if package.name not in extra_packages:
                    op.skip('Not required')

            # If the package is a dev package and dev packages
            # are not requests, we skip it
            if package.category == 'dev' and not self.is_dev_mode():
                op.skip('Dev dependencies not requested')
示例#9
0
    def classifiers(self):
        classifiers = []

        # Automatically set python classifiers
        parser = VersionParser()
        if self.python_versions == '*':
            python_constraint = parser.parse_constraints('~2.7 || ^3.4')
        else:
            python_constraint = self.python_constraint

        for version in sorted(self.AVAILABLE_PYTHONS):
            if len(version) == 1:
                constraint = parser.parse_constraints(version + '.*')
            else:
                constraint = Constraint('=', version)

            if python_constraint.matches(constraint):
                classifiers.append(
                    f'Programming Language :: Python :: {version}')

        return classifiers
示例#10
0
文件: resolve.py 项目: paralax/poetry
    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>)')
示例#11
0
    def find_packages(self,
                      name,                    # type: str
                      constraint=None,         # type: Union[Constraint, str, None]
                      extras=None,             # type: Union[list, None]
                      allow_prereleases=False  # type: bool
                      ):  # type: (...) -> List[Package]
        """
        Find packages on the remote server.
        """
        if constraint is not None and not isinstance(constraint, BaseConstraint):
            version_parser = VersionParser()
            constraint = version_parser.parse_constraints(constraint)

        info = self.get_package_info(name)

        packages = []

        for version, release in info['releases'].items():
            if not release:
                # Bad release
                self._log(
                    'No release information found for {}-{}, skipping'.format(
                        name, version
                    ),
                    level='debug'
                )
                continue

            package = Package(name, version)

            if package.is_prerelease() and not allow_prereleases:
                continue

            if (
                not constraint
                or (constraint and constraint.matches(Constraint('=', version)))
            ):
                if extras is not None:
                    package.requires_extras = extras

                packages.append(package)

        self._log(
            '{} packages found for {} {}'.format(
                len(packages), name, str(constraint)
            ),
            level='debug'
        )

        return packages
示例#12
0
    def get_classifers(self):
        classifiers = []

        # Automatically set python classifiers
        parser = VersionParser()
        if self._package.python_versions == '*':
            python_constraint = parser.parse_constraints('~2.7 || ^3.4')
        else:
            python_constraint = self._package.python_constraint

        for version in sorted(self.AVAILABLE_PYTHONS):
            if python_constraint.matches(Constraint('=', version)):
                classifiers.append(
                    'Programming Language :: Python :: {}'.format(version))

        return classifiers
示例#13
0
    def _filter_operations(self, ops: List[Operation]):
        for op in ops:
            if isinstance(op, Update):
                package = op.target_package
            else:
                package = op.package

            if op.job_type == 'uninstall':
                continue

            parser = VersionParser()
            python = '.'.join([str(i) for i in sys.version_info[:3]])
            if 'python' in package.requirements:
                python_constraint = parser.parse_constraints(
                    package.requirements['python'])
                if not python_constraint.matches(Constraint('=', python)):
                    # Incompatible python versions
                    op.skip('Not needed for the current python version')
                    continue

            if self._update:
                extras = {}
                for extra, deps in self._package.extras.items():
                    extras[extra] = [dep.name for dep in deps]
            else:
                extras = {}
                for extra, deps in self._locker.lock_data.get('extras', {}):
                    extras[extra] = [dep.lower() for dep in deps]

            # If a package is optional and not requested
            # in any extra we skip it
            if package.optional:
                drop = True
                for extra in self._extras:
                    if extra in extras and package.name in extras[extra]:
                        drop = False
                        continue

                if drop:
                    op.skip('Not required')
示例#14
0
class Dependency:
    def __init__(self,
                 name: str,
                 constraint: str,
                 optional: bool = False,
                 category: str = 'main',
                 allows_prereleases: bool = False):
        self._name = name.lower()
        self._pretty_name = name
        self._parser = VersionParser()

        try:
            self._constraint = self._parser.parse_constraints(constraint)
        except ValueError:
            self._constraint = self._parser.parse_constraints('*')

        self._pretty_constraint = constraint
        self._optional = optional
        self._category = category
        self._allows_prereleases = allows_prereleases

        self._python_versions = '*'
        self._python_constraint = self._parser.parse_constraints('*')
        self._platform = '*'
        self._platform_constraint = self._parser.parse_constraints('*')

        self._extras = []

    @property
    def name(self):
        return self._name

    @property
    def constraint(self):
        return self._constraint

    @property
    def pretty_constraint(self):
        return self._pretty_constraint

    @property
    def pretty_name(self):
        return self._pretty_name

    @property
    def category(self):
        return self._category

    @property
    def python_versions(self):
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value: str):
        self._python_versions = value
        self._python_constraint = self._parser.parse_constraints(value)

    @property
    def python_constraint(self):
        return self._python_constraint

    @property
    def platform(self) -> str:
        return self._platform

    @platform.setter
    def platform(self, value: str):
        self._platform = value

    @property
    def platform_constraint(self):
        return self._platform_constraint

    @property
    def extras(self) -> list:
        return self._extras

    def allows_prereleases(self):
        return self._allows_prereleases

    def is_optional(self):
        return self._optional

    def is_vcs(self):
        return False

    def accepts(self, package: 'poetry.packages.Package') -> bool:
        """
        Determines if the given package matches this dependency.
        """
        return (self._name == package.name
                and self._constraint.matches(Constraint('=', package.version))
                and (not package.is_prerelease() or self.allows_prereleases()))

    def to_pep_508(self) -> str:
        requirement = f'{self.pretty_name}'

        if isinstance(self.constraint, MultiConstraint):
            requirement += ' ({})'.format(','.join([
                str(c).replace(' ', '') for c in self.constraint.constraints
            ]))
        else:
            requirement += ' ({})'.format(
                str(self.constraint).replace(' ', ''))

        # Markers
        markers = []

        # Python marker
        if self.python_versions != '*':
            python_constraint = self.python_constraint

            markers.append(
                self._create_nested_marker('python_version',
                                           python_constraint))

        if markers:
            requirement += f'; {" and ".join(markers)}'

        return requirement

    def _create_nested_marker(self, name, constraint):
        if isinstance(constraint, MultiConstraint):
            parts = []
            for c in constraint.constraints:
                parts.append(self._create_nested_marker(name, c))

            glue = ' and '
            if constraint.is_disjunctive():
                parts = [f'({part})' for part in parts]
                glue = ' or '

            marker = glue.join(parts)
        else:
            marker = f'{name}{constraint.string_operator}"{constraint.version}"'

        return marker

    def activate(self):
        """
        Set the dependency as mandatory.
        """
        self._optional = False

    def __eq__(self, other):
        if not isinstance(other, Dependency):
            return NotImplemented

        return self._name == other.name and self._constraint == other.constraint

    def __hash__(self):
        return hash((self._name, self._pretty_constraint))

    def __str__(self):
        return f'{self._pretty_name} ({self._pretty_constraint})'

    def __repr__(self):
        return f'<Dependency {str(self)}>'
示例#15
0
    def solve(self, requested, fixed=None) -> List[Operation]:
        resolver = Resolver(Provider(self._package, self._pool), UI(self._io))

        base = None
        if fixed is not None:
            base = DependencyGraph()
            for fixed_req in fixed:
                base.add_vertex(fixed_req.name, fixed_req, True)

        try:
            graph = resolver.resolve(requested, base=base)
        except ResolverError as e:
            raise SolverProblemError(e)

        packages = [v.payload for v in graph.vertices.values()]

        # Setting info
        for vertex in graph.vertices.values():
            tags = self._get_tags_for_vertex(vertex, requested)
            if 'main' in tags['category']:
                vertex.payload.category = 'main'
            else:
                vertex.payload.category = 'dev'

            if not tags['optional']:
                vertex.payload.optional = False
            else:
                vertex.payload.optional = True

            # Finding the less restrictive requirements
            requirements = {}
            parser = VersionParser()
            for req_name, reqs in tags['requirements'].items():
                for req in reqs:
                    if req_name == 'python':
                        if 'python' not in requirements:
                            requirements['python'] = req
                            continue

                        previous = parser.parse_constraints(requirements['python'])
                        current = parser.parse_constraints(req)

                        if current.matches(previous):
                            requirements['python'] = req

                    if req_name == 'platform':
                        if 'platform' not in requirements:
                            requirements['platform'] = req
                            continue

            vertex.payload.requirements = requirements

        operations = []
        for package in packages:
            installed = False
            for pkg in self._locked.packages:
                if package.name == pkg.name:
                    installed = True
                    # Checking version
                    if package.version != pkg.version:
                        operations.append(Update(pkg, package))

                    break

            if not installed:
                operations.append(Install(package))

        # Checking for removals
        for pkg in self._locked.packages:
            remove = True
            for package in packages:
                if pkg.name == package.name:
                    remove = False
                    break

            if remove:
                operations.append(Uninstall(pkg))

        return list(reversed(operations))
示例#16
0
class Package(object):

    AVAILABLE_PYTHONS = {'2', '2.7', '3', '3.4', '3.5', '3.6', '3.7'}

    supported_link_types = {
        'require': {
            'description': 'requires',
            'method': 'requires'
        },
        'provide': {
            'description': 'provides',
            'method': 'provides'
        }
    }

    STABILITY_STABLE = 0
    STABILITY_RC = 5
    STABILITY_BETA = 10
    STABILITY_ALPHA = 15
    STABILITY_DEV = 20

    stabilities = {
        'stable': STABILITY_STABLE,
        'rc': STABILITY_RC,
        'beta': STABILITY_BETA,
        'alpha': STABILITY_ALPHA,
        'dev': STABILITY_DEV,
    }

    def __init__(self, name, version, pretty_version=None):
        """
        Creates a new in memory package.
        """
        self._pretty_name = name
        self._name = name.lower()

        self._version = str(parse_version(version))
        self._pretty_version = pretty_version or version

        self.description = ''

        self._stability = parse_stability(version)
        self._dev = self._stability == 'dev'

        self._authors = []

        self.homepage = None
        self.repository_url = None
        self.keywords = []
        self._license = None
        self.readme = None

        self.source_type = ''
        self.source_reference = ''
        self.source_url = ''

        self.requires = []
        self.dev_requires = []
        self.extras = {}

        self._parser = VersionParser()

        self.category = 'main'
        self.hashes = []
        self.optional = False

        # Requirements for making it mandatory
        self.requirements = {}

        self.build = None
        self.include = []
        self.exclude = []

        self.classifiers = []

        self._python_versions = '*'
        self._python_constraint = self._parser.parse_constraints('*')
        self._platform = '*'
        self._platform_constraint = EmptyConstraint()

        self.cwd = None

    @property
    def name(self):
        return self._name

    @property
    def pretty_name(self):
        return self._pretty_name

    @property
    def version(self):
        return self._version

    @property
    def pretty_version(self):
        return self._pretty_version

    @property
    def unique_name(self):
        return self.name + '-' + self._version

    @property
    def pretty_string(self):
        return self.pretty_name + ' ' + self.pretty_version

    @property
    def full_pretty_version(self):
        if not self._dev and self.source_type not in ['hg', 'git']:
            return self._pretty_version

        # if source reference is a sha1 hash -- truncate
        if len(self.source_reference) == 40:
            return '{} {}'.format(self._pretty_version,
                                  self.source_reference[0:7])

        return '{} {}'.format(self._pretty_version, self.source_reference)

    @property
    def authors(self):  # type: () -> list
        return self._authors

    @property
    def author_name(self):  # type: () -> str
        return self._get_author()['name']

    @property
    def author_email(self):  # type: () -> str
        return self._get_author()['email']

    def _get_author(self):  # type: () -> dict
        if not self._authors:
            return {'name': None, 'email': None}

        m = AUTHOR_REGEX.match(self._authors[0])

        name = m.group('name')
        email = m.group('email')

        return {'name': name, 'email': email}

    @property
    def python_versions(self):
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value):
        self._python_versions = value
        self._python_constraint = self._parser.parse_constraints(value)

    @property
    def python_constraint(self):
        return self._python_constraint

    @property
    def platform(self):  # type: () -> str
        return self._platform

    @platform.setter
    def platform(self, value):  # type: (str) -> None
        self._platform = value
        self._platform_constraint = GenericConstraint.parse(value)

    @property
    def platform_constraint(self):
        return self._platform_constraint

    @property
    def license(self):
        return self._license

    @license.setter
    def license(self, value):
        if value is None:
            self._license = value
        elif isinstance(value, License):
            self._license = value
        else:
            self._license = license_by_id(value)

    @property
    def all_classifiers(self):
        classifiers = copy.copy(self.classifiers)

        # Automatically set python classifiers
        parser = VersionParser()
        if self.python_versions == '*':
            python_constraint = parser.parse_constraints('~2.7 || ^3.4')
        else:
            python_constraint = self.python_constraint

        for version in sorted(self.AVAILABLE_PYTHONS):
            if len(version) == 1:
                constraint = parser.parse_constraints(version + '.*')
            else:
                constraint = Constraint('=', version)

            if python_constraint.matches(constraint):
                classifiers.append(
                    'Programming Language :: Python :: {}'.format(version))

        # Automatically set license classifiers
        if self.license:
            classifiers.append(self.license.classifier)

        classifiers = set(classifiers)

        return sorted(classifiers)

    def is_dev(self):
        return self._dev

    def is_prerelease(self):
        return self._stability != 'stable'

    def add_dependency(
        self,
        name,  # type: str
        constraint=None,  # type: Union[str, dict, None]
        category='main'  # type: str
    ):  # type: (...) -> Dependency
        if constraint is None:
            constraint = '*'

        if isinstance(constraint, dict):
            optional = constraint.get('optional', False)
            python_versions = constraint.get('python')
            platform = constraint.get('platform')
            allows_prereleases = constraint.get('allows-prereleases', False)

            if 'git' in constraint:
                # VCS dependency
                dependency = VCSDependency(
                    name,
                    'git',
                    constraint['git'],
                    branch=constraint.get('branch', None),
                    tag=constraint.get('tag', None),
                    rev=constraint.get('rev', None),
                    optional=optional,
                )

                if python_versions:
                    dependency.python_versions = python_versions

                if platform:
                    dependency.platform = platform
            elif 'file' in constraint:
                file_path = Path(constraint['file'])

                dependency = FileDependency(file_path, base=self.cwd)
            else:
                version = constraint['version']

                dependency = Dependency(name,
                                        version,
                                        optional=optional,
                                        category=category,
                                        allows_prereleases=allows_prereleases)

                if python_versions:
                    dependency.python_versions = python_versions

                if platform:
                    dependency.platform = platform

            if 'extras' in constraint:
                for extra in constraint['extras']:
                    dependency.extras.append(extra)
        else:
            dependency = Dependency(name, constraint, category=category)

        if category == 'dev':
            self.dev_requires.append(dependency)
        else:
            self.requires.append(dependency)

        return dependency

    def __hash__(self):
        return hash((self._name, self._version))

    def __eq__(self, other):
        if not isinstance(other, Package):
            return NotImplemented

        return self._name == other.name and self._version == other.version

    def __str__(self):
        return self.unique_name

    def __repr__(self):
        return '<Package {}>'.format(self.unique_name)
示例#17
0
文件: add.py 项目: undu/poetry
    def handle(self):
        from poetry.installation import Installer
        from poetry.semver.version_parser import VersionParser

        packages = self.argument('name')
        is_dev = self.option('dev')

        if (self.option('git') or self.option('path') or self.option('extras')) and len(packages) > 1:
            raise ValueError(
                'You can only specify one package '
                'when using the --git or --path options'
            )

        if self.option('git') and self.option('path'):
            raise RuntimeError(
                '--git and --path cannot be used at the same time'
            )

        section = 'dependencies'
        if is_dev:
            section = 'dev-dependencies'

        original_content = self.poetry.file.read()
        content = self.poetry.file.read()
        poetry_content = content['tool']['poetry']

        for name in packages:
            for key in poetry_content[section]:
                if key.lower() == name.lower():
                    raise ValueError(
                        'Package {} is already present'.format(name)
                    )

        if self.option('git') or self.option('path'):
            requirements = {
                packages[0]: ''
            }
        else:
            requirements = self._determine_requirements(
                packages,
                allow_prereleases=self.option('allow-prereleases')
            )
            requirements = self._format_requirements(requirements)

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

        for name, constraint in requirements.items():
            constraint = {
                'version': constraint
            }

            if self.option('git'):
                del constraint['version']

                constraint['git'] = self.option('git')
            elif self.option('path'):
                del constraint['version']

                constraint['path'] = self.option('path')

            if self.option('optional'):
                constraint['optional'] = True

            if self.option('allow-prereleases'):
                constraint['allows-prereleases'] = True

            if self.option('extras'):
                constraint['extras'] = self.option('extras')

            if len(constraint) == 1 and 'version' in constraint:
                constraint = constraint['version']

            poetry_content[section][name] = constraint

        # Write new content
        self.poetry.file.write(content)

        # Cosmetic new line
        self.line('')

        # Update packages
        self.reset_poetry()

        installer = Installer(
            self.output,
            self.venv,
            self.poetry.package,
            self.poetry.locker,
            self.poetry.pool
        )

        installer.dry_run(self.option('dry-run'))
        installer.update(True)
        installer.whitelist(requirements)

        try:
            status = installer.run()
        except Exception:
            self.poetry.file.write(original_content)

            raise

        if status != 0 or self.option('dry-run'):
            # Revert changes
            if not self.option('dry-run'):
                self.error(
                    '\n'
                    'Addition failed, reverting pyproject.toml '
                    'to its original content.'
                )

            self.poetry.file.write(original_content)

        return status
示例#18
0
class Package:

    supported_link_types = {
        'require': {
            'description': 'requires',
            'method': 'requires'
        },
        'provide': {
            'description': 'provides',
            'method': 'provides'
        }
    }

    STABILITY_STABLE = 0
    STABILITY_RC = 5
    STABILITY_BETA = 10
    STABILITY_ALPHA = 15
    STABILITY_DEV = 20

    stabilities = {
        'stable': STABILITY_STABLE,
        'rc': STABILITY_RC,
        'beta': STABILITY_BETA,
        'alpha': STABILITY_ALPHA,
        'dev': STABILITY_DEV,
    }

    def __init__(self, name, version, pretty_version=None):
        """
        Creates a new in memory package.
        """
        self._pretty_name = name
        self._name = name.lower()

        self._version = version
        self._pretty_version = pretty_version or version

        self.description = ''

        self._stability = parse_stability(version)
        self._dev = self._stability == 'dev'

        self._authors = []

        self.homepage = None
        self.repository_url = None
        self.keywords = []
        self.license = None
        self.readme = ''

        self.source_type = ''
        self.source_reference = ''
        self.source_url = ''

        self.requires = []
        self.dev_requires = []
        self.extras = {}

        self._parser = VersionParser()

        self.category = 'main'
        self.hashes = []
        self.optional = False

        # Requirements for making it mandatory
        self.requirements = {}

        self._python_versions = '*'
        self._python_constraint = self._parser.parse_constraints('*')
        self._platform = '*'
        self._platform_constraint = self._parser.parse_constraints('*')

    @property
    def name(self):
        return self._name

    @property
    def pretty_name(self):
        return self._pretty_name

    @property
    def version(self):
        return self._version

    @property
    def pretty_version(self):
        return self._pretty_version

    @property
    def unique_name(self):
        return self.name + '-' + self._version

    @property
    def pretty_string(self):
        return self.pretty_name + ' ' + self.pretty_version

    @property
    def full_pretty_version(self):
        if not self._dev and self.source_type not in ['hg', 'git']:
            return self._pretty_version

        # if source reference is a sha1 hash -- truncate
        if len(self.source_reference) == 40:
            return '{} {}'.format(self._pretty_version,
                                  self.source_reference[0:7])

        return '{} {}'.format(self._pretty_version, self.source_reference)

    @property
    def authors(self) -> list:
        return self._authors

    @property
    def python_versions(self):
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value: str):
        self._python_versions = value
        self._python_constraint = self._parser.parse_constraints(value)

    @property
    def python_constraint(self):
        return self._python_constraint

    @property
    def platform(self) -> str:
        return self._platform

    @platform.setter
    def platform(self, value: str):
        self._platform = value
        self._platform_constraint = self._parser.parse_constraints(value)

    @property
    def platform_constraint(self):
        return self._platform_constraint

    def is_dev(self):
        return self._dev

    def is_prerelease(self):
        return self._stability != 'stable'

    def add_dependency(self,
                       name: str,
                       constraint: Union[str, dict, None] = None,
                       category: str = 'main') -> Dependency:
        if constraint is None:
            constraint = '*'

        if isinstance(constraint, dict):
            if 'git' in constraint:
                # VCS dependency
                optional = constraint.get('optional', False)
                python_versions = constraint.get('python')
                platform = constraint.get('platform')

                dependency = VCSDependency(
                    name,
                    'git',
                    constraint['git'],
                    branch=constraint.get('branch', None),
                    tag=constraint.get('tag', None),
                    rev=constraint.get('rev', None),
                    optional=optional,
                )

                if python_versions:
                    dependency.python_versions = python_versions

                if platform:
                    dependency.platform = platform
            else:
                version = constraint['version']
                optional = constraint.get('optional', False)
                allows_prereleases = constraint.get('allows_prereleases',
                                                    False)
                python_versions = constraint.get('python')
                platform = constraint.get('platform')

                dependency = Dependency(name,
                                        version,
                                        optional=optional,
                                        category=category,
                                        allows_prereleases=allows_prereleases)

                if python_versions:
                    dependency.python_versions = python_versions

                if platform:
                    dependency.platform = platform

            if 'extras' in constraint:
                for extra in constraint['extras']:
                    dependency.extras.append(extra)
        else:
            dependency = Dependency(name, constraint, category=category)

        if category == 'dev':
            self.dev_requires.append(dependency)
        else:
            self.requires.append(dependency)

        return dependency

    def __hash__(self):
        return hash((self._name, self._version))

    def __eq__(self, other):
        if not isinstance(other, Package):
            return NotImplemented

        return self._name == other.name and self._version == other.version

    def __str__(self):
        return self.unique_name

    def __repr__(self):
        return '<Package {}>'.format(self.unique_name)
示例#19
0
文件: solver.py 项目: blueyed/poetry
    def _get_tags_for_vertex(self, vertex, requested):
        category = 'dev'
        optional = True
        python_version = None
        platform = None

        if not vertex.incoming_edges:
            # Original dependency
            for req in requested:
                if vertex.payload.name == req.name:
                    category = req.category
                    optional = req.is_optional()

                    python_version = str(req.python_constraint)

                    platform = str(req.platform_constraint)

                    break

            return category, optional, python_version, platform

        parser = VersionParser()
        python_versions = []
        platforms = []
        for edge in vertex.incoming_edges:
            python_version = None
            platform = None
            for req in edge.origin.payload.requires:
                if req.name == vertex.payload.name:
                    python_version = req.python_versions
                    platform = req.platform

                    break

            (top_category,
             top_optional,
             top_python_version,
             top_platform) = self._get_tags_for_vertex(
                edge.origin, requested
            )

            if top_category == 'main':
                category = top_category

            optional = optional and top_optional

            # Take the most restrictive constraints
            if top_python_version is not None:
                if python_version is not None:
                    previous = parser.parse_constraints(python_version)
                    current = parser.parse_constraints(top_python_version)

                    if top_python_version != '*' and previous.matches(current):
                        python_versions.append(top_python_version)
                    else:
                        python_versions.append(python_version)
                else:
                    python_versions.append(top_python_version)
            elif python_version is not None:
                python_versions.append(python_version)

            if top_platform is not None:
                if platform is not None:
                    previous = GenericConstraint.parse(platform)
                    current = GenericConstraint.parse(top_platform)

                    if top_platform != '*' and previous.matches(current):
                        platforms.append(top_platform)
                    else:
                        platforms.append(platform)
                else:
                    platforms.append(top_platform)
            elif platform is not None:
                platforms.append(platform)

        if not python_versions:
            python_version = None
        else:
            # Find the least restrictive constraint
            python_version = python_versions[0]
            previous = parser.parse_constraints(python_version)
            for constraint in python_versions[1:]:
                current = parser.parse_constraints(constraint)

                if python_version == '*':
                    continue
                elif constraint == '*':
                    python_version = constraint
                elif current.matches(previous):
                    python_version = constraint

        if not platforms:
            platform = None
        else:
            platform = platforms[0]
            previous = GenericConstraint.parse(platform)
            for constraint in platforms[1:]:
                current = GenericConstraint.parse(constraint)

                if platform == '*':
                    continue
                elif constraint == '*':
                    platform = constraint
                elif current.matches(previous):
                    platform = constraint

        return category, optional, python_version, platform
示例#20
0
class Dependency(object):

    def __init__(self,
                 name,                     # type: str
                 constraint,               # type: str
                 optional=False,           # type: bool
                 category='main',          # type: str
                 allows_prereleases=False  # type: bool
                 ):
        self._name = canonicalize_name(name)
        self._pretty_name = name
        self._parser = VersionParser()

        try:
            if not isinstance(constraint, BaseConstraint):
                self._constraint = self._parser.parse_constraints(constraint)
            else:
                self._constraint = constraint
        except ValueError:
            self._constraint = self._parser.parse_constraints('*')

        self._pretty_constraint = constraint
        self._optional = optional
        self._category = category
        self._allows_prereleases = allows_prereleases

        self._python_versions = '*'
        self._python_constraint = self._parser.parse_constraints('*')
        self._platform = '*'
        self._platform_constraint = EmptyConstraint()

        self._extras = []
        self._in_extras = []

    @property
    def name(self):
        return self._name

    @property
    def constraint(self):
        return self._constraint
    
    @property
    def pretty_constraint(self):
        return self._pretty_constraint

    @property
    def pretty_name(self):
        return self._pretty_name

    @property
    def category(self):
        return self._category

    @property
    def python_versions(self):
        return self._python_versions

    @python_versions.setter
    def python_versions(self, value):
        self._python_versions = value
        self._python_constraint = self._parser.parse_constraints(value)

    @property
    def python_constraint(self):
        return self._python_constraint

    @property
    def platform(self):
        return self._platform

    @platform.setter
    def platform(self, value):
        self._platform = value
        self._platform_constraint = GenericConstraint.parse(value)

    @property
    def platform_constraint(self):
        return self._platform_constraint

    @property
    def extras(self):  # type: () -> list
        return self._extras

    @property
    def in_extras(self):  # type: () -> list
        return self._in_extras

    def allows_prereleases(self):
        return self._allows_prereleases

    def is_optional(self):
        return self._optional

    def is_vcs(self):
        return False

    def is_file(self):
        return False

    def is_directory(self):
        return False

    def accepts(self, package):  # type: (poetry.packages.Package) -> bool
        """
        Determines if the given package matches this dependency.
        """
        return (
            self._name == package.name
            and self._constraint.matches(Constraint('=', package.version))
            and (not package.is_prerelease() or self.allows_prereleases())
        )

    def to_pep_508(self, with_extras=True):  # type: (bool) -> str
        requirement = self.pretty_name

        if self.extras:
            requirement += '[{}]'.format(','.join(self.extras))

        if isinstance(self.constraint, MultiConstraint):
            requirement += ' ({})'.format(','.join(
                [str(c).replace(' ', '') for c in self.constraint.constraints]
            ))
        elif str(self.constraint) != '*':
            requirement += ' ({})'.format(str(self.constraint).replace(' ', ''))

        # Markers
        markers = []

        # Python marker
        if self.python_versions != '*':
            python_constraint = self.python_constraint

            markers.append(
                self._create_nested_marker('python_version', python_constraint)
            )

        in_extras = ' || '.join(self._in_extras)
        if in_extras and with_extras:
            markers.append(
                self._create_nested_marker(
                    'extra', GenericConstraint.parse(in_extras)
                )
            )

        if markers:
            if len(markers) > 1:
                markers = ['({})'.format(m) for m in markers]
                requirement += '; {}'.format(' and '.join(markers))
            else:
                requirement += '; {}'.format(markers[0])

        return requirement

    def _create_nested_marker(self, name, constraint):
        if isinstance(constraint, MultiConstraint):
            parts = []
            for c in constraint.constraints:
                multi = False
                if isinstance(c, MultiConstraint):
                    multi = True

                parts.append((multi, self._create_nested_marker(name, c)))

            glue = ' and '
            if constraint.is_disjunctive():
                parts = [
                    '({})'.format(part[1]) if part[0] else part[1]
                    for part in parts
                ]
                glue = ' or '
            else:
                parts = [part[1] for part in parts]

            marker = glue.join(parts)
        else:
            marker = '{} {} "{}"'.format(
                name, constraint.string_operator, constraint.version
            )

        return marker

    def activate(self):
        """
        Set the dependency as mandatory.
        """
        self._optional = False

    def deactivate(self):
        """
        Set the dependency as optional.
        """
        self._optional = True

    def __eq__(self, other):
        if not isinstance(other, Dependency):
            return NotImplemented

        return self._name == other.name and self._constraint == other.constraint

    def __hash__(self):
        return hash((self._name, self._pretty_constraint))

    def __str__(self):
        return '{} ({})'.format(
            self._pretty_name, self._pretty_constraint
        )

    def __repr__(self):
        return '<{} {}>'.format(self.__class__.__name__, str(self))
示例#21
0
    def handle(self):
        packages = self.argument('name')
        is_dev = self.option('dev')

        section = 'dependencies'
        if is_dev:
            section = 'dev-dependencies'

        original_content = self.poetry.file.read()
        content = self.poetry.file.read()
        poetry_content = content['tool']['poetry']

        for name in packages:
            for key in poetry_content[section]:
                if key.lower() == name.lower():
                    raise ValueError(f'Package {name} is already present')

        requirements = self._determine_requirements(packages)
        requirements = self._format_requirements(requirements)

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

        for name, constraint in requirements.items():
            if self.option('optional'):
                constraint = {'version': constraint, 'optional': True}

            poetry_content[section][name] = constraint

        # Write new content
        self.poetry.file.write(content)

        # Cosmetic new line
        self.line('')

        # Update packages
        self.reset_poetry()

        installer = Installer(self.output, self.venv, self.poetry.package,
                              self.poetry.locker, self.poetry.pool)

        installer.dry_run(self.option('dry-run'))
        installer.update(True)
        installer.whitelist(requirements)

        try:
            status = installer.run()
        except Exception:
            self.poetry.file.write(original_content)

            raise

        if status != 0 or self.option('dry-run'):
            # Revert changes
            if not self.option('dry-run'):
                self.error('\n'
                           'Addition failed, reverting pyproject.toml '
                           'to its original content.')

            self.poetry.file.write(original_content)

        return status