Skip to content

Commit

Permalink
Merge pull request #7919 from jasongrout/cmpsemver
Browse files Browse the repository at this point in the history
Support comparing semver ranges that have top-level `||` operators.
  • Loading branch information
Steven Silvester committed Feb 25, 2020
2 parents 3ba2e65 + 39f0969 commit 1263446
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 41 deletions.
104 changes: 64 additions & 40 deletions jupyterlab/commands.py
Expand Up @@ -8,6 +8,7 @@
import errno
import glob
import hashlib
import itertools
import json
import logging
import os
Expand Down Expand Up @@ -94,7 +95,6 @@ def wait(self):
cache = []
proc = self.proc
kill_event = self._kill_event
import itertools
spinner = itertools.cycle(['-', '\\', '|', '/'])
while proc.poll() is None:
sys.stdout.write(next(spinner)) # write the next character
Expand Down Expand Up @@ -1965,51 +1965,75 @@ def _compare_ranges(spec1, spec2, drop_prerelease1=False, drop_prerelease2=False
if not r1.range or not r2.range:
return

x1 = r1.set[0][0].semver
x2 = r1.set[0][-1].semver
y1 = r2.set[0][0].semver
y2 = r2.set[0][-1].semver
# Set return_value to a sentinel value
return_value = False

if x1.prerelease and drop_prerelease1:
x1 = x1.inc('patch')
# r1.set may be a list of ranges if the range involved an ||, so we need to test for overlaps between each pair.
for r1set, r2set in itertools.product(r1.set, r2.set):
x1 = r1set[0].semver
x2 = r1set[-1].semver
y1 = r2set[0].semver
y2 = r2set[-1].semver

if y1.prerelease and drop_prerelease2:
y1 = y1.inc('patch')
if x1.prerelease and drop_prerelease1:
x1 = x1.inc('patch')

o1 = r1.set[0][0].operator
o2 = r2.set[0][0].operator
if y1.prerelease and drop_prerelease2:
y1 = y1.inc('patch')

# We do not handle (<) specifiers.
if (o1.startswith('<') or o2.startswith('<')):
return
o1 = r1set[0].operator
o2 = r2set[0].operator

# Handle single value specifiers.
lx = lte if x1 == x2 else lt
ly = lte if y1 == y2 else lt
gx = gte if x1 == x2 else gt
gy = gte if x1 == x2 else gt
# We do not handle (<) specifiers.
if (o1.startswith('<') or o2.startswith('<')):
continue

# Handle unbounded (>) specifiers.
def noop(x, y, z):
return True
# Handle single value specifiers.
lx = lte if x1 == x2 else lt
ly = lte if y1 == y2 else lt
gx = gte if x1 == x2 else gt
gy = gte if x1 == x2 else gt

if x1 == x2 and o1.startswith('>'):
lx = noop
if y1 == y2 and o2.startswith('>'):
ly = noop

# Check for overlap.
if (gte(x1, y1, True) and ly(x1, y2, True) or
gy(x2, y1, True) and ly(x2, y2, True) or
gte(y1, x1, True) and lx(y1, x2, True) or
gx(y2, x1, True) and lx(y2, x2, True)
):
return 0
if gte(y1, x2, True):
return 1
if gte(x1, y2, True):
return -1
raise AssertionError('Unexpected case comparing version ranges')
# Handle unbounded (>) specifiers.
def noop(x, y, z):
return True

if x1 == x2 and o1.startswith('>'):
lx = noop
if y1 == y2 and o2.startswith('>'):
ly = noop

# Check for overlap.
if (gte(x1, y1, True) and ly(x1, y2, True) or
gy(x2, y1, True) and ly(x2, y2, True) or
gte(y1, x1, True) and lx(y1, x2, True) or
gx(y2, x1, True) and lx(y2, x2, True)
):
# if we ever find an overlap, we can return immediately
return 0

if gte(y1, x2, True):
if return_value is False:
# We can possibly return 1
return_value = 1
elif return_value == -1:
# conflicting information, so we must return None
return_value = None
continue

if gte(x1, y2, True):
if return_value is False:
return_value = -1
elif return_value == 1:
# conflicting information, so we must return None
return_value = None
continue

raise AssertionError('Unexpected case comparing version ranges')

if return_value is False:
return_value = None
return return_value


def _is_disabled(name, disabled=[]):
Expand Down Expand Up @@ -2051,7 +2075,7 @@ def _format_compatibility_errors(name, version, errors):


def _log_multiple_compat_errors(logger, errors_map):
"""Log compatability errors for multiple extensions at once"""
"""Log compatibility errors for multiple extensions at once"""

outdated = []
others = []
Expand Down
18 changes: 17 additions & 1 deletion jupyterlab/tests/test_jupyterlab.py
Expand Up @@ -28,7 +28,7 @@
install_extension, uninstall_extension, list_extensions,
build, link_package, unlink_package, build_check,
disable_extension, enable_extension, get_app_info,
check_extension, _test_overlap, update_extension,
check_extension, _test_overlap, _compare_ranges, update_extension,
AppOptions
)
from jupyterlab.coreconfig import CoreConfig, _get_default_core_data
Expand Down Expand Up @@ -613,6 +613,22 @@ def test_compatibility(self):
assert _test_overlap('*', '0.6') is None
assert _test_overlap('<0.6', '0.1') is None

assert _test_overlap('^1 || ^2', '^1')
assert _test_overlap('^1 || ^2', '^2')
assert _test_overlap('^1', '^1 || ^2')
assert _test_overlap('^2', '^1 || ^2')
assert _test_overlap('^1 || ^2', '^2 || ^3')
assert not _test_overlap('^1 || ^2', '^3 || ^4')
assert not _test_overlap('^2', '^1 || ^3')

def test_compare_ranges(self):
assert _compare_ranges('^1 || ^2', '^1') == 0
assert _compare_ranges('^1 || ^2', '^2 || ^3') == 0
assert _compare_ranges('^1 || ^2', '^3 || ^4') == 1
assert _compare_ranges('^3 || ^4', '^1 || ^2') == -1
assert _compare_ranges('^2 || ^3', '^1 || ^4') is None


def test_install_compatible(self):
core_data = _get_default_core_data()
current_app_dep = core_data['dependencies']['@jupyterlab/application']
Expand Down

0 comments on commit 1263446

Please sign in to comment.