Source code for thoth.python.packages

#!/usr/bin/env python3
# thoth-python
# Copyright(C) 2018, 2019, 2020 Fridolin Pokorny
#
# This program is free software: you can redistribute it and / or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

"""Representation of development and default packages as stated in Pipfile and Pipfile.lock."""

import typing
import logging

import attr

from .package_version import PackageVersion
from .exceptions import InternalError
from .exceptions import PackageVersionAlreadyPresentError

_LOGGER = logging.getLogger(__name__)


[docs]@attr.s(slots=True) class Packages: """Encapsulate logic on package manipulation.""" develop = attr.ib(type=bool) packages = attr.ib(type=dict)
[docs] def is_develop(self): """Check if packages provided by this instance are development packages.""" return self.develop
[docs] def is_default(self): """Check if packages provided by this instance are dependencies of application packages.""" return not self.develop
[docs] def to_pipfile(self) -> dict: """Convert packages representation as seen in Pipfile file.""" _LOGGER.debug("Generating Pipfile entry for packages (develop: %s)", self.develop) result = {} for package_name, package_version in self.packages.items(): result.update(package_version.to_pipfile()) return result
[docs] def to_pipfile_lock(self) -> dict: """Convert packages representation as seen in Pipfile.lock file.""" _LOGGER.debug("Generating Pipfile.lock entry for packages (develop: %s)", self.develop) result = {} for package_name, package_version in self.packages.items(): result.update(package_version.to_pipfile_lock()) return result
[docs] @classmethod def from_package_versions(cls, package_versions: typing.List[PackageVersion], develop: bool): """Create Packages instance from a list of packages in specific versions.""" if not package_versions: return cls(develop=develop, packages={}) package_version_map = {} for package_version in package_versions: if package_version.name in package_version_map: raise InternalError(f"Atempt adding multiple packages with same name to Packages: {package_version!r}") package_version_map[package_version.name] = package_version if develop != package_version.develop: raise InternalError( "Not all packages provided to construct Packages instance have the same develop flag set" ) return cls(develop=develop, packages=package_version_map)
[docs] @classmethod def from_pipfile(cls, packages, develop, meta): """Parse Pipfile entry stating list of packages used.""" _LOGGER.debug("Parsing Pipfile entry for %s packages", "develop" if develop else "default") package_version = {} for package_name, package_info in packages.items(): package_version[package_name] = PackageVersion.from_pipfile_entry(package_name, package_info, develop, meta) return cls(develop=develop, packages=package_version)
[docs] @classmethod def from_pipfile_lock(cls, packages, develop, meta): """Parse Pipfile.lock entry stating list of packages used.""" _LOGGER.debug("Parsing Pipfile.lock entry for %s packages", "develop" if develop else "default") package_version = {} for package_name, package_info in packages.items(): package_version[package_name] = PackageVersion.from_pipfile_lock_entry( package_name, package_info, develop, meta ) return cls(develop=develop, packages=package_version)
def __iter__(self): """Iterate over packages encapsulated by this wrapper.""" yield from self.packages.values()
[docs] def get(self, package_name: str) -> typing.Optional[PackageVersion]: """Get package by its name.""" return self.packages.get(package_name)
def __setitem__(self, package_name: str, package_version: PackageVersion) -> None: """Set the given package to a value.""" self.packages[package_name] = package_version def __getitem__(self, item): """Get the given package from section.""" return self.packages[item]
[docs] def add_package_version(self, package_version: PackageVersion, *, force: bool = False): """Add the given package version to package list.""" if (package_version.develop and not self.develop) or (not package_version.develop and self.develop): raise InternalError(f"Adding package {package_version!r} to package listing without proper develop flag") if package_version.name in self.packages and not force: raise PackageVersionAlreadyPresentError( f"Adding package {package_version!r} to packages, but this package is already present there" ) self.packages[package_version.name] = package_version