jupyter-builder 0.1.0a2__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.
@@ -27,6 +27,330 @@
27
27
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28
28
  # SOFTWARE.
29
29
 
30
+ import logging
31
+ import re
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ SEMVER_SPEC_VERSION = "2.0.0"
37
+
38
+ string_type = str
39
+
40
+
41
+ class _R:
42
+ def __init__(self, i):
43
+ self.i = i
44
+
45
+ def __call__(self):
46
+ v = self.i
47
+ self.i += 1
48
+ return v
49
+
50
+ def value(self):
51
+ return self.i
52
+
53
+
54
+ class Extendlist(list):
55
+ def __setitem__(self, i, v):
56
+ try:
57
+ list.__setitem__(self, i, v)
58
+ except IndexError:
59
+ if len(self) == i:
60
+ self.append(v)
61
+ else:
62
+ raise
63
+
64
+
65
+ def list_get(xs, i):
66
+ try:
67
+ return xs[i]
68
+ except IndexError:
69
+ return None
70
+
71
+
72
+ R = _R(0)
73
+ src = Extendlist()
74
+ regexp = {}
75
+
76
+ # The following Regular Expressions can be used for tokenizing,
77
+ # validating, and parsing SemVer version strings.
78
+
79
+ # ## Numeric Identifier
80
+ # A single `0`, or a non-zero digit followed by zero or more digits.
81
+
82
+ NUMERICIDENTIFIER = R()
83
+ src[NUMERICIDENTIFIER] = "0|[1-9]\\d*"
84
+
85
+ NUMERICIDENTIFIERLOOSE = R()
86
+ src[NUMERICIDENTIFIERLOOSE] = "[0-9]+"
87
+
88
+
89
+ # ## Non-numeric Identifier
90
+ # Zero or more digits, followed by a letter or hyphen, and then zero or
91
+ # more letters, digits, or hyphens.
92
+
93
+ NONNUMERICIDENTIFIER = R()
94
+ src[NONNUMERICIDENTIFIER] = "\\d*[a-zA-Z-][a-zA-Z0-9-]*"
95
+
96
+ # ## Main Version
97
+ # Three dot-separated numeric identifiers.
98
+
99
+ MAINVERSION = R()
100
+ src[MAINVERSION] = (
101
+ "("
102
+ + src[NUMERICIDENTIFIER]
103
+ + ")\\."
104
+ + "("
105
+ + src[NUMERICIDENTIFIER]
106
+ + ")\\."
107
+ + "("
108
+ + src[NUMERICIDENTIFIER]
109
+ + ")"
110
+ )
111
+
112
+ MAINVERSIONLOOSE = R()
113
+ src[MAINVERSIONLOOSE] = (
114
+ "("
115
+ + src[NUMERICIDENTIFIERLOOSE]
116
+ + ")\\."
117
+ + "("
118
+ + src[NUMERICIDENTIFIERLOOSE]
119
+ + ")\\."
120
+ + "("
121
+ + src[NUMERICIDENTIFIERLOOSE]
122
+ + ")"
123
+ )
124
+
125
+
126
+ # ## Pre-release Version Identifier
127
+ # A numeric identifier, or a non-numeric identifier.
128
+
129
+ PRERELEASEIDENTIFIER = R()
130
+ src[PRERELEASEIDENTIFIER] = "(?:" + src[NUMERICIDENTIFIER] + "|" + src[NONNUMERICIDENTIFIER] + ")"
131
+
132
+ PRERELEASEIDENTIFIERLOOSE = R()
133
+ src[PRERELEASEIDENTIFIERLOOSE] = (
134
+ "(?:" + src[NUMERICIDENTIFIERLOOSE] + "|" + src[NONNUMERICIDENTIFIER] + ")"
135
+ )
136
+
137
+
138
+ # ## Pre-release Version
139
+ # Hyphen, followed by one or more dot-separated pre-release version
140
+ # identifiers.
141
+
142
+ PRERELEASE = R()
143
+ src[PRERELEASE] = (
144
+ "(?:-(" + src[PRERELEASEIDENTIFIER] + "(?:\\." + src[PRERELEASEIDENTIFIER] + ")*))"
145
+ )
146
+
147
+ PRERELEASELOOSE = R()
148
+ src[PRERELEASELOOSE] = (
149
+ "(?:-?(" + src[PRERELEASEIDENTIFIERLOOSE] + "(?:\\." + src[PRERELEASEIDENTIFIERLOOSE] + ")*))"
150
+ )
151
+
152
+ # ## Build Metadata Identifier
153
+ # Any combination of digits, letters, or hyphens.
154
+
155
+ BUILDIDENTIFIER = R()
156
+ src[BUILDIDENTIFIER] = "[0-9A-Za-z-]+"
157
+
158
+ # ## Build Metadata
159
+ # Plus sign, followed by one or more period-separated build metadata
160
+ # identifiers.
161
+
162
+ BUILD = R()
163
+ src[BUILD] = "(?:\\+(" + src[BUILDIDENTIFIER] + "(?:\\." + src[BUILDIDENTIFIER] + ")*))"
164
+
165
+ # ## Full Version String
166
+ # A main version, followed optionally by a pre-release version and
167
+ # build metadata.
168
+
169
+ # Note that the only major, minor, patch, and pre-release sections of
170
+ # the version string are capturing groups. The build metadata is not a
171
+ # capturing group, because it should not ever be used in version
172
+ # comparison.
173
+
174
+ FULL = R()
175
+ FULLPLAIN = "v?" + src[MAINVERSION] + src[PRERELEASE] + "?" + src[BUILD] + "?"
176
+
177
+ src[FULL] = "^" + FULLPLAIN + "$"
178
+
179
+ # like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
180
+ # also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
181
+ # common in the npm registry.
182
+ LOOSEPLAIN = "[v=\\s]*" + src[MAINVERSIONLOOSE] + src[PRERELEASELOOSE] + "?" + src[BUILD] + "?"
183
+
184
+ LOOSE = R()
185
+ src[LOOSE] = "^" + LOOSEPLAIN + "$"
186
+
187
+ GTLT = R()
188
+ src[GTLT] = "((?:<|>)?=?)"
189
+
190
+ # Something like "2.*" or "1.2.x".
191
+ # Note that "x.x" is a valid xRange identifier, meaning "any version"
192
+ # Only the first item is strictly required.
193
+ XRANGEIDENTIFIERLOOSE = R()
194
+ src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + "|x|X|\\*"
195
+ XRANGEIDENTIFIER = R()
196
+ src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + "|x|X|\\*"
197
+
198
+ XRANGEPLAIN = R()
199
+ src[XRANGEPLAIN] = (
200
+ "[v=\\s]*("
201
+ + src[XRANGEIDENTIFIER]
202
+ + ")"
203
+ + "(?:\\.("
204
+ + src[XRANGEIDENTIFIER]
205
+ + ")"
206
+ + "(?:\\.("
207
+ + src[XRANGEIDENTIFIER]
208
+ + ")"
209
+ + "(?:"
210
+ + src[PRERELEASE]
211
+ + ")?"
212
+ + src[BUILD]
213
+ + "?"
214
+ + ")?)?"
215
+ )
216
+
217
+ XRANGEPLAINLOOSE = R()
218
+ src[XRANGEPLAINLOOSE] = (
219
+ "[v=\\s]*("
220
+ + src[XRANGEIDENTIFIERLOOSE]
221
+ + ")"
222
+ + "(?:\\.("
223
+ + src[XRANGEIDENTIFIERLOOSE]
224
+ + ")"
225
+ + "(?:\\.("
226
+ + src[XRANGEIDENTIFIERLOOSE]
227
+ + ")"
228
+ + "(?:"
229
+ + src[PRERELEASELOOSE]
230
+ + ")?"
231
+ + src[BUILD]
232
+ + "?"
233
+ + ")?)?"
234
+ )
235
+
236
+ XRANGE = R()
237
+ src[XRANGE] = "^" + src[GTLT] + "\\s*" + src[XRANGEPLAIN] + "$"
238
+ XRANGELOOSE = R()
239
+ src[XRANGELOOSE] = "^" + src[GTLT] + "\\s*" + src[XRANGEPLAINLOOSE] + "$"
240
+
241
+ # Tilde ranges.
242
+ # Meaning is "reasonably at or greater than"
243
+ LONETILDE = R()
244
+ src[LONETILDE] = "(?:~>?)"
245
+
246
+ TILDETRIM = R()
247
+ src[TILDETRIM] = "(\\s*)" + src[LONETILDE] + "\\s+"
248
+ regexp[TILDETRIM] = re.compile(src[TILDETRIM], re.M)
249
+ tildeTrimReplace = r"\1~"
250
+
251
+ TILDE = R()
252
+ src[TILDE] = "^" + src[LONETILDE] + src[XRANGEPLAIN] + "$"
253
+ TILDELOOSE = R()
254
+ src[TILDELOOSE] = "^" + src[LONETILDE] + src[XRANGEPLAINLOOSE] + "$"
255
+
256
+ # Caret ranges.
257
+ # Meaning is "at least and backwards compatible with"
258
+ LONECARET = R()
259
+ src[LONECARET] = "(?:\\^)"
260
+
261
+ CARETTRIM = R()
262
+ src[CARETTRIM] = "(\\s*)" + src[LONECARET] + "\\s+"
263
+ regexp[CARETTRIM] = re.compile(src[CARETTRIM], re.M)
264
+ caretTrimReplace = r"\1^"
265
+
266
+ CARET = R()
267
+ src[CARET] = "^" + src[LONECARET] + src[XRANGEPLAIN] + "$"
268
+ CARETLOOSE = R()
269
+ src[CARETLOOSE] = "^" + src[LONECARET] + src[XRANGEPLAINLOOSE] + "$"
270
+
271
+ # A simple gt/lt/eq thing, or just "" to indicate "any version"
272
+ COMPARATORLOOSE = R()
273
+ src[COMPARATORLOOSE] = "^" + src[GTLT] + "\\s*(" + LOOSEPLAIN + ")$|^$"
274
+ COMPARATOR = R()
275
+ src[COMPARATOR] = "^" + src[GTLT] + "\\s*(" + FULLPLAIN + ")$|^$"
276
+
277
+
278
+ # An expression to strip any whitespace between the gtlt and the thing
279
+ # it modifies, so that `> 1.2.3` ==> `>1.2.3`
280
+ COMPARATORTRIM = R()
281
+ src[COMPARATORTRIM] = "(\\s*)" + src[GTLT] + "\\s*(" + LOOSEPLAIN + "|" + src[XRANGEPLAIN] + ")"
282
+
283
+ # this one has to use the /g flag
284
+ regexp[COMPARATORTRIM] = re.compile(src[COMPARATORTRIM], re.M)
285
+ comparatorTrimReplace = r"\1\2\3"
286
+
287
+
288
+ # Something like `1.2.3 - 1.2.4`
289
+ # Note that these all use the loose form, because they'll be
290
+ # checked against either the strict or loose comparator form
291
+ # later.
292
+ HYPHENRANGE = R()
293
+ src[HYPHENRANGE] = (
294
+ "^\\s*(" + src[XRANGEPLAIN] + ")" + "\\s+-\\s+" + "(" + src[XRANGEPLAIN] + ")" + "\\s*$"
295
+ )
296
+
297
+ HYPHENRANGELOOSE = R()
298
+ src[HYPHENRANGELOOSE] = (
299
+ "^\\s*("
300
+ + src[XRANGEPLAINLOOSE]
301
+ + ")"
302
+ + "\\s+-\\s+"
303
+ + "("
304
+ + src[XRANGEPLAINLOOSE]
305
+ + ")"
306
+ + "\\s*$"
307
+ )
308
+
309
+ # Star ranges basically just allow anything at all.
310
+ STAR = R()
311
+ src[STAR] = "(<|>)?=?\\s*\\*"
312
+
313
+ # version name recovery for convenient
314
+ RECOVERYVERSIONNAME = R()
315
+ _n = src[NUMERICIDENTIFIER]
316
+ _pre = src[PRERELEASELOOSE]
317
+ src[RECOVERYVERSIONNAME] = f"v?({_n})(?:\\.({_n}))?{_pre}?"
318
+
319
+ # Compile to actual regexp objects.
320
+ # All are flag-free, unless they were created above with a flag.
321
+ for i in range(R.value()):
322
+ logger.debug("genregxp %s %s", i, src[i])
323
+ if i not in regexp:
324
+ regexp[i] = re.compile(src[i])
325
+
326
+
327
+ def parse(version, loose):
328
+ r = regexp[LOOSE] if loose else regexp[FULL]
329
+ m = r.search(version)
330
+ if m:
331
+ return semver(version, loose)
332
+ else:
333
+ return None
334
+
335
+
336
+ def valid(version, loose):
337
+ v = parse(version, loose)
338
+ if v.version:
339
+ return v
340
+ else:
341
+ return None
342
+
343
+
344
+ def clean(version, loose):
345
+ s = parse(version, loose)
346
+ if s:
347
+ return s.version
348
+ else:
349
+ return None
350
+
351
+
352
+ NUMERIC = re.compile(r"^\d+$")
353
+
30
354
 
31
355
  def semver(version, loose):
32
356
  if isinstance(version, SemVer):
@@ -65,7 +389,7 @@ class SemVer:
65
389
  self.prerelease = []
66
390
  else:
67
391
  self.prerelease = [
68
- (int(id) if NUMERIC.search(id) else id) for id in m.group(3).split(".")
392
+ (int(id_) if NUMERIC.search(id_) else id_) for id_ in m.group(3).split(".")
69
393
  ]
70
394
  else:
71
395
  # these are actually numbers
@@ -77,7 +401,7 @@ class SemVer:
77
401
  self.prerelease = []
78
402
  else:
79
403
  self.prerelease = [
80
- (int(id) if NUMERIC.search(id) else id) for id in m.group(4).split(".")
404
+ (int(id_) if NUMERIC.search(id_) else id_) for id_ in m.group(4).split(".")
81
405
  ]
82
406
  if m.group(5):
83
407
  self.build = m.group(5).split(".")
@@ -116,7 +440,7 @@ class SemVer:
116
440
  or compare_identifiers(str(self.patch), str(other.patch))
117
441
  )
118
442
 
119
- def compare_pre(self, other):
443
+ def compare_pre(self, other): # noqa PLR0911
120
444
  if not isinstance(other, SemVer):
121
445
  other = make_semver(other, self.loose)
122
446
 
@@ -232,10 +556,81 @@ class SemVer:
232
556
  return self
233
557
 
234
558
 
559
+ def inc(version, release, loose, identifier=None): # wow!
560
+ try:
561
+ return make_semver(version, loose).inc(release, identifier=identifier).version
562
+ except Exception as e:
563
+ logger.debug(e, exc_info=5)
564
+ return None
565
+
566
+
567
+ def compare_identifiers(a, b):
568
+ anum = NUMERIC.search(a)
569
+ bnum = NUMERIC.search(b)
570
+
571
+ if anum and bnum:
572
+ a = int(a)
573
+ b = int(b)
574
+
575
+ if anum and not bnum:
576
+ return -1
577
+ elif bnum and not anum:
578
+ return 1
579
+ elif a < b:
580
+ return -1
581
+ elif a > b:
582
+ return 1
583
+ else:
584
+ return 0
585
+
586
+
587
+ def rcompare_identifiers(a, b):
588
+ return compare_identifiers(b, a)
589
+
590
+
235
591
  def compare(a, b, loose):
236
592
  return make_semver(a, loose).compare(b)
237
593
 
238
594
 
595
+ def compare_loose(a, b):
596
+ return compare(a, b, True)
597
+
598
+
599
+ def rcompare(a, b, loose):
600
+ return compare(b, a, loose)
601
+
602
+
603
+ def make_key_function(loose):
604
+ def key_function(version):
605
+ v = make_semver(version, loose)
606
+ key = (v.major, v.minor, v.patch)
607
+ if v.prerelease: # noqa SIM108
608
+ key = key + tuple(v.prerelease)
609
+ else:
610
+ # NOT having a prerelease is > having one
611
+ key = (*key, float("inf"))
612
+
613
+ return key
614
+
615
+ return key_function
616
+
617
+
618
+ loose_key_function = make_key_function(True)
619
+ full_key_function = make_key_function(True)
620
+
621
+
622
+ def sort(list_, loose):
623
+ keyf = loose_key_function if loose else full_key_function
624
+ list_.sort(key=keyf)
625
+ return list_
626
+
627
+
628
+ def rsort(list_, loose):
629
+ keyf = loose_key_function if loose else full_key_function
630
+ list_.sort(key=keyf, reverse=True)
631
+ return list_
632
+
633
+
239
634
  def gt(a, b, loose):
240
635
  return compare(a, b, loose) > 0
241
636
 
@@ -244,9 +639,581 @@ def lt(a, b, loose):
244
639
  return compare(a, b, loose) < 0
245
640
 
246
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
+
247
650
  def gte(a, b, loose):
248
651
  return compare(a, b, loose) >= 0
249
652
 
250
653
 
251
654
  def lte(a, b, loose):
252
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