Source code for thoth.python.constraints
#!/usr/bin/env python3
# thoth-python
# Copyright(C) 2021 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/>.
"""Implementation of an abstraction keeping constraints."""
import logging
from typing import Any
from typing import Dict
from typing import List
import attr
from .package_version import PackageVersion
from .helpers import parse_requirements
from .helpers import parse_requirements_str
from .exceptions import ConstraintsError
_LOGGER = logging.getLogger(__name__)
[docs]@attr.s(slots=True)
class Constraints:
"""Constraints manipulation."""
package_versions = attr.ib(type=Dict[str, PackageVersion], kw_only=True, default=attr.Factory(dict))
[docs] @classmethod
def from_file(cls, constraints_file: str) -> "Constraints":
"""Load constraints from a file."""
try:
_, package_versions = parse_requirements(constraints_file)
except Exception as exc:
raise ConstraintsError(f"Failed to parse constraints: {str(exc)}") from exc
return cls.from_package_versions(package_versions)
[docs] @classmethod
def from_string(cls, content: str) -> "Constraints":
"""Load constraints from a string."""
try:
_, package_versrsions = parse_requirements_str(content)
except Exception as exc:
raise ConstraintsError(f"Failed to parse constraints: {str(exc)}") from exc
return cls.from_package_versions(package_versrsions)
[docs] @classmethod
def from_dict(cls, dict_: List[Dict[str, Any]]) -> "Constraints":
"""Instantiate constraints from a dictionary representation."""
package_versions = []
for item in dict_:
name = item.get("name")
if not name:
raise ConstraintsError(f"Name of a package not provided in constraints entry: {str(item)}")
unknown = set(item.keys()) - {"name", "version", "markers"}
if unknown:
raise ConstraintsError(
f"Unknown entries in the constraint serialized representation: {', '.join(unknown)}"
)
package_version = PackageVersion(
name=name,
version=item.get("version"),
markers=item.get("markers"),
develop=False,
index=None,
hashes=[],
extras=[],
)
package_versions.append(package_version)
return cls.from_package_versions(package_versions)
[docs] @classmethod
def from_package_versions(cls, package_versions: List[PackageVersion]) -> "Constraints":
"""Instantiate constraints from package versions, perform checks to verify correct instance."""
package_versions_dict = {}
for pv in package_versions:
if pv.name in package_versions_dict:
raise ConstraintsError(f"Multiple constraints found for package {pv.name!r}")
if pv.extras:
raise ConstraintsError(
f"Specifying extras in constraints is not supported for {pv.name!r}: {pv.extras!r}"
)
if pv.hashes:
raise ConstraintsError(f"Specifying hashes in constraints is not supported for {pv.name}")
package_versions_dict[pv.name] = pv
return cls(package_versions=package_versions_dict)
[docs] def to_dict(self) -> List[Dict[str, Any]]:
"""Serialize constraint instance to a dictionary representation."""
result = []
for package_version in self.package_versions.values():
# Use only constraint relevant fields for package version abstraction.
result.append(
{
"name": package_version.name,
"version": package_version.version,
"markers": package_version.markers,
}
)
return result