예제 #1
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 = '{}:{}'.format(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(Package(name, version, extras=extras))

        return packages
예제 #2
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))
예제 #3
0
 def __init__(self, pool, parser=VersionParser()):
     self._pool = pool
     self._parser = parser
예제 #4
0
def parser():
    return VersionParser()
예제 #5
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)
예제 #6
0
    def increment_version(self, version, rule):
        from poetry.semver.version_parser import VersionParser

        parser = VersionParser()
        version_regex = (
            'v?(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:\.(\d+))?{}(?:\+[^\s]+)?'
        ).format(parser._modifier_regex)

        m = re.match(version_regex, version)
        if not m:
            raise ValueError(
                'The project\'s version doesn\'t seem to follow semver')

        if m.group(3):
            index = 2
        elif m.group(2):
            index = 1
        else:
            index = 0

        matches = m.groups()[:index + 1]
        base = '.'.join(matches)
        extra_matches = list(g or '' for g in m.groups()[4:])
        extras = version[len('.'.join(matches)):]
        increment = 1
        is_prerelease = (extra_matches[0] or extra_matches[1]) != ''
        bump_prerelease = rule in {
            'premajor', 'preminor', 'prepatch', 'prerelease'
        }
        position = -1

        if rule in {'major', 'premajor'}:
            if m.group(1) != '0' or m.group(2) != '0' or not is_prerelease:
                position = 0
        elif rule in {'minor', 'preminor'}:
            if m.group(2) != '0' or not is_prerelease:
                position = 1
        elif rule in {'patch', 'prepatch'}:
            if not is_prerelease:
                position = 2
        elif rule == 'prerelease' and not is_prerelease:
            position = 2

        if position != -1:
            extra_matches[0] = None

            base = parser._manipulate_version_string(matches,
                                                     position,
                                                     increment=increment)

        if bump_prerelease:
            # We bump the prerelease part of the version
            sep = ''
            if not extra_matches[0]:
                extra_matches[0] = 'alpha'
                extra_matches[1] = '.0'
                sep = '-'
            else:
                if extras.startswith(('.', '_', '-')):
                    sep = extras[0]

                prerelease = extra_matches[1]
                if not prerelease:
                    prerelease = '.1'

                psep = ''
                if prerelease.startswith(('.', '-')):
                    psep = prerelease[0]
                    prerelease = prerelease[1:]

                new_prerelease = str(int(prerelease) + 1)
                extra_matches[1] = '{}{}'.format(psep, new_prerelease)

            extras = '{}{}{}{}'.format(sep, extra_matches[0], extra_matches[1],
                                       extra_matches[2])
        else:
            extras = ''

        return '.'.join(base.split('.')[:max(index, position) + 1]) + extras
예제 #7
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)}>'
예제 #8
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
예제 #9
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))
예제 #10
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
예제 #11
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)
예제 #12
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