Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions src/test/unit/web_interface/test_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from zlib import compress

import pytest
from semver import Version

import web_interface.filter as flt
from test.common_helper import create_test_file_object
Expand Down Expand Up @@ -578,3 +579,45 @@ def test_sort_dict_list_by_key(input_, expected_result):
)
def test_sort_ip_list(input_, expected_result):
assert flt.sort_ip_list(input_) == expected_result


@pytest.mark.parametrize(
('version', 'other_version', 'expected_result', 'forgiving'),
[
('1.2.3', '1.2.3', True, False),
('1.2.3', '1.2.4', True, False),
('1.2.3', '1.3.4', True, False),
('1.2.3', '2.3.4', False, False),
# if both versions are not semver compatible, they are only considered compatible if equal
('1.2', '1.2', True, False),
('1.2', '1.3', False, False),
# with forgiving=True versions not strictly compatible with semver should still work
('1.2', '1.2.3', True, True),
('1.2', '1.2.3', False, False),
# below 1.0.0 the versions are considered incompatible
('0.1.2', '0.2.3', False, False),
# the semver Version class should also work
(Version(1, 2, 3), '1.2.4', True, False),
('1.2.3', Version(1, 2, 4), True, False),
(Version(1, 2, 3), Version(1, 2, 4), True, False),
],
)
def test_version_is_compatible(version, other_version, expected_result, forgiving):
assert flt.version_is_compatible(version, other_version, forgiving=forgiving) == expected_result


def test_version_is_compatible_error():
with pytest.raises(ValueError, match='not a valid version'):
assert flt.version_is_compatible('1.2.3', 'a.b.c')


@pytest.mark.parametrize(
('input_', 'expected_result'),
[
(None, None),
('foobar', 'foobar'),
({'foo': 'bar'}, '{\n "foo": "bar"\n}'),
],
)
def test_render_query_title(input_, expected_result):
assert flt.render_query_title(input_) == expected_result
51 changes: 22 additions & 29 deletions src/web_interface/filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@
from re import Match
from string import ascii_letters
from time import localtime, strftime, struct_time, time
from typing import TYPE_CHECKING, Any, Iterable, Union
from typing import TYPE_CHECKING, Any, Iterable

import packaging.version
import semver
from common_helper_files import human_readable_file_size
from flask import render_template
from semver import Version

from helperFunctions.compare_sets import remove_duplicates_from_list
from helperFunctions.data_conversion import make_unicode_string
Expand Down Expand Up @@ -550,55 +551,47 @@ def get_searchable_crypto_block(crypto_material: str) -> str:
return sorted(blocks, key=len, reverse=True)[0]


def version_is_compatible(
version: Union[str, semver.Version],
other: Union[str, semver.Version],
forgiving: bool = False,
) -> bool:
"""A warpper around ``semver.Version.is_compatible`` that allows non semver versions.
If :paramref:`forgiving` is True non semver versions will try to be coerced to semver versions.
def version_is_compatible(version: str | Version, other: str | Version, forgiving: bool = False) -> bool:
"""A wrapper around ``semver.Version.is_compatible`` that allows non semver versions.
If :paramref:`forgiving` is True, non semver versions will try to be coerced to semver versions.
If this does not succeed or :paramref:`forgiving` is False then any semver version will
be considered incompatible to any other non semver version.
So for example '1.1.0' would not be compatible '1.2' if forgiving is False.
Otherwise it would be coerced from '1.2' to '1.2.0'.
Otherwise, it would be coerced from '1.2' to '1.2.0'.

If both versions are not semver they are only compatible if they are equal.

:param version: The version to check compatiblity for.
:param version: The version to check compatibility for.
:param other: The version to compare to.
:param forgiving: Whether the version check is forgiving with versions that are not compatible to semver..

:return: If :paramref:`version` is compatible with :paramref:`other`.

:raises ValueError: If both versions are neither semver nor ``packaging.version.Version`` versions.
"""
version_is_semver = True
try:
if isinstance(version, str):
version = semver.Version.parse(version)
except ValueError:
version_is_semver = forgiving
version = _coerce_version(version)

other_is_semver = True
try:
if isinstance(other, str):
other = semver.Version.parse(other)
except ValueError:
other_is_semver = forgiving
other = _coerce_version(other)
version, version_is_semver = _convert_to_semver(version, forgiving)
other, other_is_semver = _convert_to_semver(other, forgiving)
except packaging.version.InvalidVersion as invalid_version:
raise ValueError(f'Either {version} or {other} is not a valid version') from invalid_version

if version_is_semver ^ other_is_semver:
return False

if not version_is_semver and not other_is_semver:
try:
return packaging.version.Version(version) == packaging.version.Version(other)
except packaging.version.InvalidVersion as invalid_version:
raise ValueError from invalid_version
return version == other

return version.is_compatible(other)


def _convert_to_semver(version: str | Version, forgiving: bool) -> tuple[Version, bool]:
try:
if isinstance(version, str):
version = Version.parse(version)
return version, True
except ValueError:
return _coerce_version(version), forgiving


def _coerce_version(version: str) -> semver.Version:
coerced = packaging.version.Version(version)
return semver.Version(
Expand Down