packetary/packetary/objects/package_relation.py

155 lines
4.6 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2015 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import operator
class VersionRange(object):
"""Describes the range of versions.
Range of version is compare operation and edge.
the compare operation can be one of:
equal, greater, less, greater or equal, less or equal.
"""
def __init__(self, op=None, edge=None):
"""Initialises.
:param op: the name of operator to compare.
:param edge: the edge of versions.
"""
self.op = op
self.edge = edge
def __hash__(self):
return hash((self.op, self.edge))
def __eq__(self, other):
if not isinstance(other, VersionRange):
return False
return self.op == other.op and \
self.edge == other.edge
def __str__(self):
if self.edge is not None:
return "{0} {1}".format(self.op, self.edge)
return "any"
def __unicode__(self):
if self.edge is not None:
return u"{0} {1}".format(self.op, self.edge)
return u"any"
def has_intersection(self, other):
"""Checks that 2 ranges has intersection."""
if not isinstance(other, VersionRange):
raise TypeError(
"Unorderable type <type 'VersionRange'> and {0}"
.format(type(other))
)
if self.op is None or other.op is None:
return True
my_op = getattr(operator, self.op)
other_op = getattr(operator, other.op)
if self.op[0] == other.op[0]:
if self.op[0] == 'l':
if self.edge < other.edge:
return my_op(self.edge, other.edge)
return other_op(other.edge, self.edge)
elif self.op[0] == 'g':
if self.edge > other.edge:
return my_op(self.edge, other.edge)
return other_op(other.edge, self.edge)
if self.op == 'eq':
return other_op(self.edge, other.edge)
if other.op == 'eq':
return my_op(other.edge, self.edge)
return (
my_op(other.edge, self.edge) and
other_op(self.edge, other.edge)
)
class PackageRelation(object):
"""Describes the package`s relation.
Relation includes the name of required package
and range of versions that satisfies requirement.
"""
def __init__(self, name, version=None, alternative=None):
"""Initialises.
:param name: the name of required package
:param version: the version range of required package
:param alternative: the alternative relation
"""
self.name = name
self.version = VersionRange() if version is None else version
self.alternative = alternative
@classmethod
def from_args(cls, *args):
"""Construct relation from list of arguments.
:param args: the list of tuples(name, [version_op, version_edge])
"""
if len(args) == 0:
return None
head = args[0]
name = head[0]
version = VersionRange(*head[1:])
alternative = cls.from_args(*args[1:])
return cls(name, version, alternative)
def __iter__(self):
"""Iterates over alternatives."""
r = self
while r is not None:
yield r
r = r.alternative
def __hash__(self):
return hash((self.name, self.version))
def __eq__(self, other):
if not isinstance(other, PackageRelation):
return False
return self.name == other.name and \
self.version == other.version
def __str__(self):
if self.alternative is None:
return "{0} ({1})".format(self.name, self.version)
return "{0} ({1}) | {2}".format(
self.name, self.version, self.alternative
)
def __unicode__(self):
if self.alternative is None:
return u"{0} ({1})".format(self.name, self.version)
return u"{0} ({1}) | {2}".format(
self.name, self.version, self.alternative
)