jupyter-builder 0.1.0a3__py3-none-any.whl → 0.1.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -79,14 +79,10 @@ def _compare_ranges(spec1, spec2, drop_prerelease1=False, drop_prerelease2=False
79
79
 
80
80
  # Check for overlap.
81
81
  if (
82
- gte(x1, y1, True)
83
- and ly(x1, y2, True)
84
- or gy(x2, y1, True)
85
- and ly(x2, y2, True)
86
- or gte(y1, x1, True)
87
- and lx(y1, x2, True)
88
- or gx(y2, x1, True)
89
- and lx(y2, x2, True)
82
+ (gte(x1, y1, True) and ly(x1, y2, True))
83
+ or (gy(x2, y1, True) and ly(x2, y2, True))
84
+ or (gte(y1, x1, True) and lx(y1, x2, True))
85
+ or (gx(y2, x1, True) and lx(y2, x2, True))
90
86
  ):
91
87
  # if we ever find an overlap, we can return immediately
92
88
  return 0
@@ -32,10 +32,9 @@ try:
32
32
  except ImportError:
33
33
  from tomli import load
34
34
 
35
+ from .commands import _test_overlap
35
36
  from .core_path import default_core_path
36
37
 
37
- # from .commands import _test_overlap TO BE DONE -----------------------------
38
-
39
38
  DEPRECATED_ARGUMENT = object()
40
39
 
41
40
  HERE = osp.abspath(osp.dirname(__file__))
@@ -201,9 +200,7 @@ def build_labextension( # noqa: PLR0913
201
200
  path, logger=None, development=False, static_url=None, source_map=False, core_path=None
202
201
  ):
203
202
  """Build a labextension in the given path"""
204
-
205
203
  core_path = default_core_path() if core_path is None else str(Path(core_path).resolve())
206
-
207
204
  ext_path = str(Path(path).resolve())
208
205
 
209
206
  if logger:
@@ -293,23 +290,22 @@ def _ensure_builder(ext_path, core_path):
293
290
  raise ValueError(msg)
294
291
  target = osp.dirname(target)
295
292
 
296
- # IGNORING Test Overlap ---------------------------------
297
-
298
- # overlap = _test_overlap(
299
- # dep_version1, dep_version2, drop_prerelease1=True, drop_prerelease2=True
300
- # )
301
- # if not overlap:
302
- # with open(
303
- # osp.join(target, "node_modules", "@jupyterlab", "builder", "package.json")
304
- # ) as fid:
305
- # dep_version2 = json.load(fid).get("version")
306
- # overlap = _test_overlap(
307
- # dep_version1, dep_version2, drop_prerelease1=True, drop_prerelease2=True
308
- # )
309
-
310
- # if not overlap:
311
- # msg = f"Extensions require a devDependency on @jupyterlab/builder@{dep_version1}, you have a dependency on {dep_version2}" # noqa: E501
312
- # raise ValueError(msg)
293
+ # Check for compatible versions
294
+ overlap = _test_overlap(
295
+ dep_version1, dep_version2, drop_prerelease1=True, drop_prerelease2=True
296
+ )
297
+ if not overlap:
298
+ with open(
299
+ osp.join(target, "node_modules", "@jupyterlab", "builder", "package.json")
300
+ ) as fid:
301
+ dep_version2 = json.load(fid).get("version")
302
+ overlap = _test_overlap(
303
+ dep_version1, dep_version2, drop_prerelease1=True, drop_prerelease2=True
304
+ )
305
+
306
+ if not overlap:
307
+ msg = f"Extensions require a devDependency on @jupyterlab/builder@{dep_version1}, you have a dependency on {dep_version2}" # noqa: E501
308
+ raise ValueError(msg)
313
309
 
314
310
  return osp.join(
315
311
  target, "node_modules", "@jupyterlab", "builder", "lib", "build-labextension.js"
@@ -32,6 +32,7 @@ import re
32
32
 
33
33
  logger = logging.getLogger(__name__)
34
34
 
35
+
35
36
  SEMVER_SPEC_VERSION = "2.0.0"
36
37
 
37
38
  string_type = str
@@ -549,8 +550,7 @@ class SemVer:
549
550
  else:
550
551
  self.prerelease = [identifier, 0]
551
552
  else:
552
- msg = f"invalid increment argument: {release}"
553
- raise ValueError(msg)
553
+ raise ValueError(f"invalid increment argument: {release}")
554
554
  self.format()
555
555
  self.raw = self.version
556
556
  return self
@@ -639,9 +639,581 @@ def lt(a, b, loose):
639
639
  return compare(a, b, loose) < 0
640
640
 
641
641
 
642
+ def eq(a, b, loose):
643
+ return compare(a, b, loose) == 0
644
+
645
+
646
+ def neq(a, b, loose):
647
+ return compare(a, b, loose) != 0
648
+
649
+
642
650
  def gte(a, b, loose):
643
651
  return compare(a, b, loose) >= 0
644
652
 
645
653
 
646
654
  def lte(a, b, loose):
647
655
  return compare(a, b, loose) <= 0
656
+
657
+
658
+ def cmp(a, op, b, loose): # noqa PLR0911
659
+ logger.debug("cmp: %s", op)
660
+ if op == "===":
661
+ return a == b
662
+ elif op == "!==":
663
+ return a != b
664
+ elif op == "" or op == "=" or op == "==":
665
+ return eq(a, b, loose)
666
+ elif op == "!=":
667
+ return neq(a, b, loose)
668
+ elif op == ">":
669
+ return gt(a, b, loose)
670
+ elif op == ">=":
671
+ return gte(a, b, loose)
672
+ elif op == "<":
673
+ return lt(a, b, loose)
674
+ elif op == "<=":
675
+ return lte(a, b, loose)
676
+ else:
677
+ raise ValueError(f"Invalid operator: {op}")
678
+
679
+
680
+ def comparator(comp, loose):
681
+ if isinstance(comp, Comparator):
682
+ if comp.loose == loose:
683
+ return comp
684
+ else:
685
+ comp = comp.value
686
+
687
+ # if (!(this instanceof Comparator))
688
+ # return new Comparator(comp, loose)
689
+ return Comparator(comp, loose)
690
+
691
+
692
+ make_comparator = comparator
693
+
694
+ ANY = object()
695
+
696
+
697
+ class Comparator:
698
+ semver = None
699
+
700
+ def __init__(self, comp, loose):
701
+ logger.debug("comparator: %s %s", comp, loose)
702
+ self.loose = loose
703
+ self.parse(comp)
704
+
705
+ if self.semver == ANY:
706
+ self.value = ""
707
+ else:
708
+ self.value = self.operator + self.semver.version
709
+
710
+ def parse(self, comp):
711
+ r = regexp[COMPARATORLOOSE] if self.loose else regexp[COMPARATOR]
712
+ logger.debug("parse comp=%s", comp)
713
+ m = r.search(comp)
714
+
715
+ if m is None:
716
+ raise ValueError(f"Invalid comparator: {comp}")
717
+
718
+ self.operator = m.group(1)
719
+ # if it literally is just '>' or '' then allow anything.
720
+ if m.group(2) is None:
721
+ self.semver = ANY
722
+ else:
723
+ self.semver = semver(m.group(2), self.loose)
724
+
725
+ def __repr__(self):
726
+ return f'<SemVer Comparator "{self}">'
727
+
728
+ def __str__(self):
729
+ return self.value
730
+
731
+ def test(self, version):
732
+ logger.debug("Comparator, test %s, %s", version, self.loose)
733
+ if self.semver == ANY:
734
+ return True
735
+ else:
736
+ return cmp(version, self.operator, self.semver, self.loose)
737
+
738
+
739
+ def make_range(range_, loose):
740
+ if isinstance(range_, Range) and range_.loose == loose:
741
+ return range_
742
+
743
+ # if (!(this instanceof Range))
744
+ # return new Range(range, loose);
745
+ return Range(range_, loose)
746
+
747
+
748
+ class Range:
749
+ def __init__(self, range_, loose):
750
+ self.loose = loose
751
+ # First, split based on boolean or ||
752
+ self.raw = range_
753
+ xs = [self.parse_range(r.strip()) for r in re.split(r"\s*\|\|\s*", range_)]
754
+ self.set = [r for r in xs if r]
755
+
756
+ if not len(self.set):
757
+ raise ValueError(f"Invalid SemVer Range: {range_}")
758
+
759
+ self.format()
760
+
761
+ def __repr__(self):
762
+ return f'<SemVer Range "{self.range}">'
763
+
764
+ def format(self):
765
+ self.range = "||".join(
766
+ [" ".join(c.value for c in comps).strip() for comps in self.set]
767
+ ).strip()
768
+ logger.debug("Range format %s", self.range)
769
+ return self.range
770
+
771
+ def __str__(self):
772
+ return self.range
773
+
774
+ def parse_range(self, range_):
775
+ loose = self.loose
776
+ logger.debug("range %s %s", range_, loose)
777
+ # `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
778
+ hr = regexp[HYPHENRANGELOOSE] if loose else regexp[HYPHENRANGE]
779
+
780
+ range_ = hr.sub(
781
+ hyphen_replace,
782
+ range_,
783
+ )
784
+ logger.debug("hyphen replace %s", range_)
785
+
786
+ # `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
787
+ range_ = regexp[COMPARATORTRIM].sub(comparatorTrimReplace, range_)
788
+ logger.debug("comparator trim %s, %s", range_, regexp[COMPARATORTRIM])
789
+
790
+ # `~ 1.2.3` => `~1.2.3`
791
+ range_ = regexp[TILDETRIM].sub(tildeTrimReplace, range_)
792
+
793
+ # `^ 1.2.3` => `^1.2.3`
794
+ range_ = regexp[CARETTRIM].sub(caretTrimReplace, range_)
795
+
796
+ # normalize spaces
797
+ range_ = " ".join(re.split(r"\s+", range_))
798
+
799
+ # At this point, the range is completely trimmed and
800
+ # ready to be split into comparators.
801
+ comp_re = regexp[COMPARATORLOOSE] if loose else regexp[COMPARATOR]
802
+ set_ = re.split(
803
+ r"\s+", " ".join([parse_comparator(comp, loose) for comp in range_.split(" ")])
804
+ )
805
+ if self.loose:
806
+ # in loose mode, throw out any that are not valid comparators
807
+ set_ = [comp for comp in set_ if comp_re.search(comp)]
808
+ set_ = [make_comparator(comp, loose) for comp in set_]
809
+ return set_
810
+
811
+ def test(self, version):
812
+ if not version: # xxx
813
+ return False
814
+
815
+ if isinstance(version, string_type):
816
+ version = make_semver(version, loose=self.loose)
817
+
818
+ return any(test_set(e, version) for e in self.set)
819
+
820
+
821
+ # Mostly just for testing and legacy API reasons
822
+ def to_comparators(range_, loose):
823
+ return [
824
+ " ".join([c.value for c in comp]).strip().split(" ")
825
+ for comp in make_range(range_, loose).set
826
+ ]
827
+
828
+
829
+ # comprised of xranges, tildes, stars, and gtlt's at this point.
830
+ # already replaced the hyphen ranges
831
+ # turn into a set of JUST comparators.
832
+
833
+
834
+ def parse_comparator(comp, loose):
835
+ logger.debug("comp %s", comp)
836
+ comp = replace_carets(comp, loose)
837
+ logger.debug("caret %s", comp)
838
+ comp = replace_tildes(comp, loose)
839
+ logger.debug("tildes %s", comp)
840
+ comp = replace_xranges(comp, loose)
841
+ logger.debug("xrange %s", comp)
842
+ comp = replace_stars(comp, loose)
843
+ logger.debug("stars %s", comp)
844
+ return comp
845
+
846
+
847
+ def is_x(id_):
848
+ return id_ is None or id_ == "" or id_.lower() == "x" or id_ == "*"
849
+
850
+
851
+ # ~, ~> --> * (any, kinda silly)
852
+ # ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
853
+ # ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
854
+ # ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
855
+ # ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
856
+ # ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
857
+
858
+
859
+ def replace_tildes(comp, loose):
860
+ return " ".join([replace_tilde(c, loose) for c in re.split(r"\s+", comp.strip())])
861
+
862
+
863
+ def replace_tilde(comp, loose):
864
+ r = regexp[TILDELOOSE] if loose else regexp[TILDE]
865
+
866
+ def repl(mob):
867
+ _ = mob.group(0)
868
+ M, m, p, pr, _ = mob.groups()
869
+ logger.debug("tilde %s %s %s %s %s %s", comp, _, M, m, p, pr)
870
+ if is_x(M):
871
+ ret = ""
872
+ elif is_x(m):
873
+ ret = ">=" + M + ".0.0 <" + str(int(M) + 1) + ".0.0"
874
+ elif is_x(p):
875
+ # ~1.2 == >=1.2.0 <1.3.0
876
+ ret = ">=" + M + "." + m + ".0 <" + M + "." + str(int(m) + 1) + ".0"
877
+ elif pr:
878
+ logger.debug("replaceTilde pr %s", pr)
879
+ if pr[0] != "-":
880
+ pr = "-" + pr
881
+ ret = ">=" + M + "." + m + "." + p + pr + " <" + M + "." + str(int(m) + 1) + ".0"
882
+ else:
883
+ # ~1.2.3 == >=1.2.3 <1.3.0
884
+ ret = ">=" + M + "." + m + "." + p + " <" + M + "." + str(int(m) + 1) + ".0"
885
+ logger.debug("tilde return, %s", ret)
886
+ return ret
887
+
888
+ return r.sub(repl, comp)
889
+
890
+
891
+ # ^ --> * (any, kinda silly)
892
+ # ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
893
+ # ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
894
+ # ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
895
+ # ^1.2.3 --> >=1.2.3 <2.0.0
896
+ # ^1.2.0 --> >=1.2.0 <2.0.0
897
+ def replace_carets(comp, loose):
898
+ return " ".join([replace_caret(c, loose) for c in re.split(r"\s+", comp.strip())])
899
+
900
+
901
+ def replace_caret(comp, loose):
902
+ r = regexp[CARETLOOSE] if loose else regexp[CARET]
903
+
904
+ def repl(mob):
905
+ m0 = mob.group(0)
906
+ M, m, p, pr, _ = mob.groups()
907
+ logger.debug("caret %s %s %s %s %s %s", comp, m0, M, m, p, pr)
908
+
909
+ if is_x(M):
910
+ ret = ""
911
+ elif is_x(m):
912
+ ret = ">=" + M + ".0.0 <" + str(int(M) + 1) + ".0.0"
913
+ elif is_x(p):
914
+ if M == "0":
915
+ ret = ">=" + M + "." + m + ".0 <" + M + "." + str(int(m) + 1) + ".0"
916
+ else:
917
+ ret = ">=" + M + "." + m + ".0 <" + str(int(M) + 1) + ".0.0"
918
+ elif pr:
919
+ logger.debug("replaceCaret pr %s", pr)
920
+ if pr[0] != "-":
921
+ pr = "-" + pr
922
+ if M == "0":
923
+ if m == "0":
924
+ ret = (
925
+ ">="
926
+ + M
927
+ + "."
928
+ + m
929
+ + "."
930
+ + (p or "")
931
+ + pr
932
+ + " <"
933
+ + M
934
+ + "."
935
+ + m
936
+ + "."
937
+ + str(int(p or 0) + 1)
938
+ )
939
+ else:
940
+ ret = (
941
+ ">="
942
+ + M
943
+ + "."
944
+ + m
945
+ + "."
946
+ + (p or "")
947
+ + pr
948
+ + " <"
949
+ + M
950
+ + "."
951
+ + str(int(m) + 1)
952
+ + ".0"
953
+ )
954
+ else:
955
+ ret = ">=" + M + "." + m + "." + (p or "") + pr + " <" + str(int(M) + 1) + ".0.0"
956
+ else:
957
+ if M == "0":
958
+ if m == "0":
959
+ ret = (
960
+ ">="
961
+ + M
962
+ + "."
963
+ + m
964
+ + "."
965
+ + (p or "")
966
+ + " <"
967
+ + M
968
+ + "."
969
+ + m
970
+ + "."
971
+ + str(int(p or 0) + 1)
972
+ )
973
+ else:
974
+ ret = (
975
+ ">="
976
+ + M
977
+ + "."
978
+ + m
979
+ + "."
980
+ + (p or "")
981
+ + " <"
982
+ + M
983
+ + "."
984
+ + str(int(m) + 1)
985
+ + ".0"
986
+ )
987
+ else:
988
+ ret = ">=" + M + "." + m + "." + (p or "") + " <" + str(int(M) + 1) + ".0.0"
989
+ logger.debug("caret return %s", ret)
990
+ return ret
991
+
992
+ return r.sub(repl, comp)
993
+
994
+
995
+ def replace_xranges(comp, loose):
996
+ logger.debug("replaceXRanges %s %s", comp, loose)
997
+ return " ".join([replace_xrange(c, loose) for c in re.split(r"\s+", comp.strip())])
998
+
999
+
1000
+ def replace_xrange(comp, loose):
1001
+ comp = comp.strip()
1002
+ r = regexp[XRANGELOOSE] if loose else regexp[XRANGE]
1003
+
1004
+ def repl(mob):
1005
+ ret = mob.group(0)
1006
+ gtlt, M, m, p, pr, _ = mob.groups()
1007
+
1008
+ logger.debug("xrange %s %s %s %s %s %s %s", comp, ret, gtlt, M, m, p, pr)
1009
+
1010
+ xM = is_x(M)
1011
+ xm = xM or is_x(m)
1012
+ xp = xm or is_x(p)
1013
+ any_x = xp
1014
+
1015
+ if gtlt == "=" and any_x:
1016
+ gtlt = ""
1017
+
1018
+ logger.debug("xrange gtlt=%s any_x=%s", gtlt, any_x)
1019
+ if xM:
1020
+ if gtlt == ">" or gtlt == "<": # noqa SIM108
1021
+ # nothing is allowed
1022
+ ret = "<0.0.0"
1023
+ else:
1024
+ ret = "*"
1025
+ elif gtlt and any_x:
1026
+ # replace X with 0, and then append the -0 min-prerelease
1027
+ if xm:
1028
+ m = 0
1029
+ if xp:
1030
+ p = 0
1031
+
1032
+ if gtlt == ">":
1033
+ # >1 => >=2.0.0
1034
+ # >1.2 => >=1.3.0
1035
+ # >1.2.3 => >= 1.2.4
1036
+ gtlt = ">="
1037
+ if xm:
1038
+ M = int(M) + 1
1039
+ m = 0
1040
+ p = 0
1041
+ elif xp:
1042
+ m = int(m) + 1
1043
+ p = 0
1044
+ elif gtlt == "<=":
1045
+ # <=0.7.x is actually <0.8.0, since any 0.7.x should
1046
+ # pass. Similarly, <=7.x is actually <8.0.0, etc.
1047
+ gtlt = "<"
1048
+ if xm:
1049
+ M = int(M) + 1
1050
+ else:
1051
+ m = int(m) + 1
1052
+
1053
+ ret = gtlt + str(M) + "." + str(m) + "." + str(p)
1054
+ elif xm:
1055
+ ret = ">=" + M + ".0.0 <" + str(int(M) + 1) + ".0.0"
1056
+ elif xp:
1057
+ ret = ">=" + M + "." + m + ".0 <" + M + "." + str(int(m) + 1) + ".0"
1058
+ logger.debug("xRange return %s", ret)
1059
+
1060
+ return ret
1061
+
1062
+ return r.sub(repl, comp)
1063
+
1064
+
1065
+ # Because * is AND-ed with everything else in the comparator,
1066
+ # and '' means "any version", just remove the *s entirely.
1067
+ def replace_stars(comp, loose):
1068
+ logger.debug("replaceStars %s %s", comp, loose)
1069
+ # Looseness is ignored here. star is always as loose as it gets!
1070
+ return regexp[STAR].sub("", comp.strip())
1071
+
1072
+
1073
+ # This function is passed to string.replace(re[HYPHENRANGE])
1074
+ # M, m, patch, prerelease, build
1075
+ # 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
1076
+ # 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
1077
+ # 1.2 - 3.4 => >=1.2.0 <3.5.0
1078
+ def hyphen_replace(mob):
1079
+ from_, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb = mob.groups()
1080
+ if is_x(fM):
1081
+ from_ = ""
1082
+ elif is_x(fm):
1083
+ from_ = ">=" + fM + ".0.0"
1084
+ elif is_x(fp):
1085
+ from_ = ">=" + fM + "." + fm + ".0"
1086
+ else:
1087
+ from_ = ">=" + from_
1088
+
1089
+ if is_x(tM):
1090
+ to = ""
1091
+ elif is_x(tm):
1092
+ to = "<" + str(int(tM) + 1) + ".0.0"
1093
+ elif is_x(tp):
1094
+ to = "<" + tM + "." + str(int(tm) + 1) + ".0"
1095
+ elif tpr:
1096
+ to = "<=" + tM + "." + tm + "." + tp + "-" + tpr
1097
+ else:
1098
+ to = "<=" + to
1099
+ return (from_ + " " + to).strip()
1100
+
1101
+
1102
+ def test_set(set_, version):
1103
+ for e in set_:
1104
+ if not e.test(version):
1105
+ return False
1106
+ if len(version.prerelease) > 0:
1107
+ # Find the set of versions that are allowed to have prereleases
1108
+ # For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
1109
+ # That should allow `1.2.3-pr.2` to pass.
1110
+ # However, `1.2.4-alpha.notready` should NOT be allowed,
1111
+ # even though it's within the range set by the comparators.
1112
+ for e in set_:
1113
+ if e.semver == ANY:
1114
+ continue
1115
+ if len(e.semver.prerelease) > 0:
1116
+ allowed = e.semver
1117
+ if (
1118
+ allowed.major == version.major
1119
+ and allowed.minor == version.minor
1120
+ and allowed.patch == version.patch
1121
+ ):
1122
+ return True
1123
+ # Version has a -pre, but it's not one of the ones we like.
1124
+ return False
1125
+ return True
1126
+
1127
+
1128
+ def satisfies(version, range_, loose=False):
1129
+ try:
1130
+ range_ = make_range(range_, loose)
1131
+ except Exception:
1132
+ return False
1133
+ return range_.test(version)
1134
+
1135
+
1136
+ def max_satisfying(versions, range_, loose=False):
1137
+ try:
1138
+ range_ob = make_range(range_, loose=loose)
1139
+ except Exception:
1140
+ return None
1141
+ max_ = None
1142
+ max_sv = None
1143
+ for v in versions:
1144
+ if range_ob.test(v): # noqa # satisfies(v, range_, loose=loose)
1145
+ if max_ is None or max_sv.compare(v) == -1: # compare(max, v, true)
1146
+ max_ = v
1147
+ max_sv = make_semver(max_, loose=loose)
1148
+ return max_
1149
+
1150
+
1151
+ def valid_range(range_, loose):
1152
+ try:
1153
+ # Return '*' instead of '' so that truthiness works.
1154
+ # This will throw if it's invalid anyway
1155
+ return make_range(range_, loose).range or "*"
1156
+ except Exception:
1157
+ return None
1158
+
1159
+
1160
+ # Determine if version is less than all the versions possible in the range
1161
+ def ltr(version, range_, loose):
1162
+ return outside(version, range_, "<", loose)
1163
+
1164
+
1165
+ # Determine if version is greater than all the versions possible in the range.
1166
+ def rtr(version, range_, loose):
1167
+ return outside(version, range_, ">", loose)
1168
+
1169
+
1170
+ def outside(version, range_, hilo, loose):
1171
+ version = make_semver(version, loose)
1172
+ range_ = make_range(range_, loose)
1173
+
1174
+ if hilo == ">":
1175
+ gtfn = gt
1176
+ ltefn = lte
1177
+ ltfn = lt
1178
+ comp = ">"
1179
+ ecomp = ">="
1180
+ elif hilo == "<":
1181
+ gtfn = lt
1182
+ ltefn = gte
1183
+ ltfn = gt
1184
+ comp = "<"
1185
+ ecomp = "<="
1186
+ else:
1187
+ raise ValueError("Must provide a hilo val of '<' or '>'")
1188
+
1189
+ # If it satisfies the range it is not outside
1190
+ if satisfies(version, range_, loose):
1191
+ return False
1192
+
1193
+ # From now on, variable terms are as if we're in "gtr" mode.
1194
+ # but note that everything is flipped for the "ltr" function.
1195
+ for comparators in range_.set:
1196
+ high = None
1197
+ low = None
1198
+
1199
+ for comparator in comparators:
1200
+ high = high or comparator
1201
+ low = low or comparator
1202
+
1203
+ if gtfn(comparator.semver, high.semver, loose):
1204
+ high = comparator
1205
+ elif ltfn(comparator.semver, low.semver, loose):
1206
+ low = comparator
1207
+
1208
+ # If the edge version comparator has a operator then our version
1209
+ # isn't outside it
1210
+ if high.operator == comp or high.operator == ecomp:
1211
+ return False
1212
+
1213
+ # If the lowest version comparator has an operator and our version
1214
+ # is less than it then it isn't higher than the range
1215
+ if (not low.operator or low.operator == comp) and ltefn(version, low.semver): # noqa SIM114
1216
+ return False
1217
+ elif low.operator == ecomp and ltfn(version, low.semver):
1218
+ return False
1219
+ return True