Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support Normalization of VersionRange #108

Merged
merged 31 commits into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
218c61a
Add support for NormalizedVersionRanges
keshav-space Mar 14, 2023
1a69d07
Test NormalizedVersionRanges
keshav-space Mar 15, 2023
3b3570c
Add ABOUT and LICENSE file for spans.py
keshav-space Mar 15, 2023
d88b80c
Fallback to builtin set when intbitset is not installed
keshav-space Apr 3, 2023
5ab9b3a
Added docs server script, dark mode & copybutton for docs
OmkarPh Oct 18, 2023
af7e542
Merge pull request #83 from OmkarPh/enhance/docs
AyanSinhaMahapatra Oct 18, 2023
0a9d983
Update CSS to widen page and handle mobile #84
johnmhoran Nov 21, 2023
4e36fc6
Delete theme_overrides_SUPERSEDED.css as no longer needed #84
johnmhoran Jan 16, 2024
7d74b8a
Fix top padding for rst content
AyanSinhaMahapatra Jan 18, 2024
0071028
Merge pull request #85 from nexB/84-widen-rtd-page
AyanSinhaMahapatra Jan 18, 2024
008d521
Update CI runners and python version
AyanSinhaMahapatra Feb 19, 2024
acf94b3
Merge pull request #87 from nexB/update-macos-runners
AyanSinhaMahapatra Feb 19, 2024
124da3d
Replace deprecated macos CI runners
AyanSinhaMahapatra Jul 1, 2024
be4e14d
Update minimum required python version to 3.8
AyanSinhaMahapatra Jul 1, 2024
5c3e935
Merge pull request #90 from nexB/update-ci-runners
keshav-space Jul 1, 2024
f0bac8c
Support both PURL and GitLab schema in from_gitlab_native
keshav-space Jul 19, 2024
629d03a
Use native impl for Maven and NuGet in from_gitlab_native
keshav-space Jul 19, 2024
1c70ea5
Use proper splitter for composer in from_gitlab_native
keshav-space Jul 19, 2024
d109a1b
Support splitting bracket notation ranges
keshav-space Jul 19, 2024
169a6a1
Add support for version range from snyk advisory
keshav-space Jul 22, 2024
e7d7c55
Add support for version range from discrete versions
keshav-space Jul 22, 2024
f8a6d70
Refactor NormalizedVersionRange
keshav-space Jul 23, 2024
d2904b0
Add multi vers test for normalized version range
keshav-space Jul 24, 2024
5a32eda
Fix the edge case resulting in incorrect `contains` resolution
keshav-space Jul 24, 2024
c833b97
Refactor VersionRange normalization without Span
keshav-space Jul 24, 2024
8f0d727
Add function to parse bracket notation constraints
keshav-space Jul 24, 2024
3d0de11
Use from_versions for getting vers from discrete versions
keshav-space Jul 24, 2024
fa7009a
Merge remote-tracking branch 'origin/main' into range_normalization
keshav-space Jul 24, 2024
fe35a34
Merge remote-tracking branch 'skeleton/main' into range_normalization
keshav-space Jul 24, 2024
594baf5
Set `shell` param to False while running code style tests
keshav-space Jul 24, 2024
b12572d
Use only macOS-14 image for macOS 14 CI
keshav-space Jul 24, 2024
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
136 changes: 136 additions & 0 deletions src/univers/normalized_range.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#
# Copyright (c) nexB Inc. and others.
# SPDX-License-Identifier: Apache-2.0
#
# Visit https://aboutcode.org and https://github.com/nexB/univers for support and download.


from typing import List

import attr

from univers.span import Span
from univers.version_constraint import VersionConstraint
from univers.version_range import RANGE_CLASS_BY_SCHEMES
from univers.version_range import VersionRange


keshav-space marked this conversation as resolved.
Show resolved Hide resolved
@attr.s(frozen=True, order=False, eq=True, hash=True)
class NormalizedVersionRange:
"""
A normalized_range is a summation of the largest contiguous version ranges.

For example, for an npm package with the version range "vers:npm/<=2.0.0|>=3.0.0|<3.1.0|4.0.0"
and available package versions ["1.0.0", "2.0.0", "3.0.0", "3.1.0", "4.0.0"], the normalized range
would be "vers:npm/>=1.0.0|<=3.0.0|4.0.0".
"""

normalized_range = attr.ib(type=VersionRange, default=None)

def __str__(self):
return str(self.normalized_range)

@classmethod
def from_vers(cls, vers_range: VersionRange, all_versions: List):
"""
Return NormalizedVersionRange computed from vers_range and all the available version of package.
"""
version_class = vers_range.version_class
versions = sorted([version_class(i.lstrip("vV")) for i in all_versions])
keshav-space marked this conversation as resolved.
Show resolved Hide resolved

bounded_span = None
total_span = None
for constraint in vers_range.constraints:
local_span = get_region(constraint=constraint, versions=versions)

if bounded_span and constraint.comparator in ("<", "<="):
local_span = bounded_span.intersection(local_span)
elif constraint.comparator in (">", ">="):
bounded_span = local_span
continue

total_span = local_span if not total_span else total_span.union(local_span)
bounded_span = None

# If '<' or '<=' is the last constraint.
if bounded_span:
total_span = bounded_span if not total_span else total_span.union(bounded_span)

normalized_version_range = get_version_range_from_span(
span=total_span, purl_type=vers_range.scheme, versions=versions
)
return cls(normalized_range=normalized_version_range)


def get_version_range_from_span(span: Span, purl_type: str, versions: List):
"""
Return VersionRange computed from the span and all the versions available for package.

For example::
>>> from univers.versions import SemverVersion as SV
>>> versions = [SV("1.0"), SV("1.1"), SV("1.2"), SV("1.3")]
>>> span = Span(0,1).union(Span(3))
>>> vr = get_version_range_from_span(span, "npm", versions)
>>> assert str(vr) == "vers:npm/>=1.0.0|<=1.1.0|1.3.0"
"""

version_range_class = RANGE_CLASS_BY_SCHEMES[purl_type]
version_constraints = []
spans = span.subspans()
for subspan in spans:
lower_bound = versions[subspan.start]
upper_bound = versions[subspan.end]
if lower_bound == upper_bound:
version_constraints.append(
VersionConstraint(
version=lower_bound,
)
)
continue
version_constraints.append(VersionConstraint(comparator=">=", version=lower_bound))
version_constraints.append(
VersionConstraint(
comparator="<=",
version=upper_bound,
)
)

return version_range_class(constraints=version_constraints)


def get_region(constraint: VersionConstraint, versions: List):
"""
Return a Span representing the region covered by the constraint on
the given universe of versions.

For example::
>>> from univers.versions import SemverVersion as SV
>>> versions = [SV("1.0"), SV("1.1"), SV("1.2"), SV("1.3")]
>>> constraint = VersionConstraint(comparator="<", version=SV("1.2"))
>>> get_region(constraint, versions)
Span(0, 1)
"""

try:
index = 0
if str(constraint.version) != "0":
index = versions.index(constraint.version)
except ValueError as err:
err.args = (f"'{constraint.version}' doesn't exist in the versions list.",)
raise

last_index = len(versions) - 1
comparator = constraint.comparator

if comparator == "<":
return Span(0, index - 1)
if comparator == ">":
return Span(index + 1, last_index)
if comparator == ">=":
return Span(index, last_index)
if comparator == "<=":
return Span(0, index)
if comparator == "=":
return Span(index)
if comparator == "!=":
return Span(0, last_index).difference(Span(index))
Loading
Loading