Skip to content

Commit

Permalink
Handle epochs
Browse files Browse the repository at this point in the history
The actual work is still delegated to parse_version, only the epoch
delimiter is switched from colon to exclamation.
  • Loading branch information
lubomir committed Sep 7, 2015
1 parent f6de78d commit 6f1ff52
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 22 deletions.
13 changes: 13 additions & 0 deletions pdc/apps/common/hacks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,15 @@
# Licensed under The MIT License (MIT)
# http://opensource.org/licenses/MIT
#
import re

from django.db import connection
from django.conf import settings
from django.core.exceptions import ValidationError
from rest_framework import serializers

from productmd import composeinfo, images, rpms
from pkg_resources import parse_version


def composeinfo_from_str(data):
Expand Down Expand Up @@ -113,3 +116,13 @@ def srpm_name_to_component_names(srpm_name):
return binding_models.ReleaseComponentSRPMNameMapping.get_component_names_by_srpm_name(srpm_name)
else:
return [srpm_name]


def parse_epoch_version(version):
"""
Wrapper around `pkg_resources.parse_version` that can handle epochs
delimited by colon as is customary for RPMs.
"""
if re.match(r'^\d+:', version):
version = re.sub(r'^(\d+):', r'\1!', version)
return parse_version(version)
18 changes: 10 additions & 8 deletions pdc/apps/package/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ def dependency_filter(type, queryset, value):
queryset = queryset.filter(dependency__name=groups['name'], dependency__type=type).distinct()
for dep in models.Dependency.objects.filter(type=type, name=groups['name']):

is_equal = dep.is_equal(groups['version']) if groups['version'] else False

if groups['op'] == '=':
if not dep.is_satisfied_by(groups['version']):
queryset = queryset.exclude(pk=dep.rpm_id)
Expand All @@ -32,31 +34,31 @@ def dependency_filter(type, queryset, value):
if dep.comparison == '>' or dep.comparison == '>=':
# Same direction, does not limit anything
pass
elif dep.comparison == '<' and (dep.is_lower(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '<' and (dep.is_lower(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '<=' and (dep.is_lower(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '<=' and (dep.is_lower(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '=' and (dep.is_lower(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '=' and (dep.is_lower(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)

# User requests everything depending on lesser than X
elif groups['op'] == '<':
if dep.comparison == '<' or dep.comparison == '<=':
# Same direction, does not limit anything
pass
elif dep.comparison == '>' and (dep.is_higher(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '>' and (dep.is_higher(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '>=' and (dep.is_higher(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '>=' and (dep.is_higher(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '=' and (dep.is_higher(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '=' and (dep.is_higher(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)

# User requests everything depending on at least X
elif groups['op'] == '>=':
if dep.comparison == '>' or dep.comparison == '>=':
# Same direction, does not limit anything
pass
elif dep.comparison == '<' and (dep.is_lower(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '<' and (dep.is_lower(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '<=' and dep.is_lower(groups['version']):
queryset = queryset.exclude(pk=dep.rpm_id)
Expand All @@ -68,7 +70,7 @@ def dependency_filter(type, queryset, value):
if dep.comparison == '<' or dep.comparison == '<=':
# Same direction, does not limit anything
pass
elif dep.comparison == '>' and (dep.is_higher(groups['version']) or dep.version == groups['version']):
elif dep.comparison == '>' and (dep.is_higher(groups['version']) or is_equal):
queryset = queryset.exclude(pk=dep.rpm_id)
elif dep.comparison == '>=' and dep.is_higher(groups['version']):
queryset = queryset.exclude(pk=dep.rpm_id)
Expand Down
32 changes: 18 additions & 14 deletions pdc/apps/package/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@
from django.core.exceptions import ValidationError
from django.forms.models import model_to_dict

from pkg_resources import parse_version
from kobo.rpmlib import parse_nvra

from pdc.apps.common.models import get_cached_id
from pdc.apps.common.validators import validate_md5, validate_sha1, validate_sha256
from pdc.apps.common.hacks import add_returning
from pdc.apps.common.hacks import add_returning, parse_epoch_version
from pdc.apps.common.constants import ARCH_SRC
from pdc.apps.release.models import Release

Expand Down Expand Up @@ -110,7 +109,7 @@ def bulk_insert(cursor, rpm_nevra, filename, srpm_nevra=None):

@property
def sort_key(self):
return (self.epoch, parse_version(self.version), parse_version(self.release))
return (self.epoch, parse_epoch_version(self.version), parse_epoch_version(self.release))

@property
def dependencies(self):
Expand Down Expand Up @@ -175,25 +174,30 @@ def clean(self):

def is_satisfied_by(self, other):
if not hasattr(self, '_version'):
self._version = parse_version(self.version)
self._version = parse_epoch_version(self.version)
funcs = {
'=': lambda x: x == self.version,
'<': lambda x: parse_version(x) < self._version,
'<=': lambda x: parse_version(x) <= self._version,
'>': lambda x: parse_version(x) > self._version,
'>=': lambda x: parse_version(x) >= self._version,
'=': lambda x: x == self._version,
'<': lambda x: x < self._version,
'<=': lambda x: x <= self._version,
'>': lambda x: x > self._version,
'>=': lambda x: x >= self._version,
}
return funcs[self.comparison](other)
return funcs[self.comparison](parse_epoch_version(other))

def is_equal(self, other):
if not hasattr(self, '_version'):
self._version = parse_epoch_version(self.version)
return self._version == parse_epoch_version(other)

def is_higher(self, other):
if not hasattr(self, '_version'):
self._version = parse_version(self.version)
return self._version > parse_version(other)
self._version = parse_epoch_version(self.version)
return self._version > parse_epoch_version(other)

def is_lower(self, other):
if not hasattr(self, '_version'):
self._version = parse_version(self.version)
return self._version < parse_version(other)
self._version = parse_epoch_version(self.version)
return self._version < parse_epoch_version(other)


class ImageFormat(models.Model):
Expand Down
60 changes: 60 additions & 0 deletions pdc/apps/package/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,66 @@ def test_filter_with_lesser_or_equal_version_obsoletes(self):
['test-{}'.format(i) for i in [0, 1, 2, 3, 4, 5, 6, 8, 9, 11, 13]])


class RPMDepsFilterWithEpochTestCase(APITestCase):
def setUp(self):
self.rpm = models.RPM.objects.create(name='test-pkg', epoch=0, version='1.0',
release='1', arch='x86_64', srpm_name='test-pkg',
srpm_nevra='test-pkg-1.0.1.x86_64',
filename='dummy')
self.rpm.dependency_set.create(name='pkg', version='3.0',
type=models.Dependency.REQUIRES, comparison='=')

def test_filter_with_same_epoch_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg=0:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_same_epoch_lesser(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg<0:4.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_same_epoch_greater(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg>0:2.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_same_epoch_lesser_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg<=0:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_same_epoch_greater_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg>=0:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_different_epoch_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg=1:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 0)

def test_filter_with_different_epoch_lesser(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg<1:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_different_epoch_greater(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg>1:2.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 0)

def test_filter_with_different_epoch_lesser_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg<=1:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 1)

def test_filter_with_different_epoch_greater_equal(self):
response = self.client.get(reverse('rpms-list'), {'requires': 'pkg>=1:3.0'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data.get('count'), 0)


class RPMDepsFilterRangeAPITestCase(APITestCase):
def setUp(self):
rpm = models.RPM.objects.create(name='test-pkg', epoch=0, version='1.0',
Expand Down

0 comments on commit 6f1ff52

Please sign in to comment.