semver.py 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  1. # -*- coding:utf-8 -*-
  2. # This file comes from https://github.com/podhmo/python-semver/blob/b42e9896e391e086b773fc621b23fa299d16b874/semver/__init__.py
  3. #
  4. # It is licensed under the following license:
  5. #
  6. # MIT License
  7. # Copyright (c) 2016 podhmo
  8. # Permission is hereby granted, free of charge, to any person obtaining a copy
  9. # of this software and associated documentation files (the "Software"), to deal
  10. # in the Software without restriction, including without limitation the rights
  11. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12. # copies of the Software, and to permit persons to whom the Software is
  13. # furnished to do so, subject to the following conditions:
  14. # The above copyright notice and this permission notice shall be included in all
  15. # copies or substantial portions of the Software.
  16. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  17. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  18. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  19. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  20. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  21. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  22. # SOFTWARE.
  23. import logging
  24. import re
  25. logger = logging.getLogger(__name__)
  26. SEMVER_SPEC_VERSION = '2.0.0'
  27. # Python 2/3 compatibility
  28. try:
  29. string_type = basestring
  30. except NameError:
  31. string_type = str
  32. class _R(object):
  33. def __init__(self, i):
  34. self.i = i
  35. def __call__(self):
  36. v = self.i
  37. self.i += 1
  38. return v
  39. def value(self):
  40. return self.i
  41. class Extendlist(list):
  42. def __setitem__(self, i, v):
  43. try:
  44. list.__setitem__(self, i, v)
  45. except IndexError:
  46. if len(self) == i:
  47. self.append(v)
  48. else:
  49. raise
  50. def list_get(xs, i):
  51. try:
  52. return xs[i]
  53. except IndexError:
  54. return None
  55. R = _R(0)
  56. src = Extendlist()
  57. regexp = {}
  58. # The following Regular Expressions can be used for tokenizing,
  59. # validating, and parsing SemVer version strings.
  60. # ## Numeric Identifier
  61. # A single `0`, or a non-zero digit followed by zero or more digits.
  62. NUMERICIDENTIFIER = R()
  63. src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'
  64. NUMERICIDENTIFIERLOOSE = R()
  65. src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'
  66. # ## Non-numeric Identifier
  67. # Zero or more digits, followed by a letter or hyphen, and then zero or
  68. # more letters, digits, or hyphens.
  69. NONNUMERICIDENTIFIER = R()
  70. src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'
  71. # ## Main Version
  72. # Three dot-separated numeric identifiers.
  73. MAINVERSION = R()
  74. src[MAINVERSION] = ('(' + src[NUMERICIDENTIFIER] + ')\\.' +
  75. '(' + src[NUMERICIDENTIFIER] + ')\\.' +
  76. '(' + src[NUMERICIDENTIFIER] + ')')
  77. MAINVERSIONLOOSE = R()
  78. src[MAINVERSIONLOOSE] = ('(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
  79. '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
  80. '(' + src[NUMERICIDENTIFIERLOOSE] + ')')
  81. # ## Pre-release Version Identifier
  82. # A numeric identifier, or a non-numeric identifier.
  83. PRERELEASEIDENTIFIER = R()
  84. src[PRERELEASEIDENTIFIER] = ('(?:' + src[NUMERICIDENTIFIER] +
  85. '|' + src[NONNUMERICIDENTIFIER] + ')')
  86. PRERELEASEIDENTIFIERLOOSE = R()
  87. src[PRERELEASEIDENTIFIERLOOSE] = ('(?:' + src[NUMERICIDENTIFIERLOOSE] +
  88. '|' + src[NONNUMERICIDENTIFIER] + ')')
  89. # ## Pre-release Version
  90. # Hyphen, followed by one or more dot-separated pre-release version
  91. # identifiers.
  92. PRERELEASE = R()
  93. src[PRERELEASE] = ('(?:-(' + src[PRERELEASEIDENTIFIER] +
  94. '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))')
  95. PRERELEASELOOSE = R()
  96. src[PRERELEASELOOSE] = ('(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] +
  97. '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))')
  98. # ## Build Metadata Identifier
  99. # Any combination of digits, letters, or hyphens.
  100. BUILDIDENTIFIER = R()
  101. src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'
  102. # ## Build Metadata
  103. # Plus sign, followed by one or more period-separated build metadata
  104. # identifiers.
  105. BUILD = R()
  106. src[BUILD] = ('(?:\\+(' + src[BUILDIDENTIFIER] +
  107. '(?:\\.' + src[BUILDIDENTIFIER] + ')*))')
  108. # ## Full Version String
  109. # A main version, followed optionally by a pre-release version and
  110. # build metadata.
  111. # Note that the only major, minor, patch, and pre-release sections of
  112. # the version string are capturing groups. The build metadata is not a
  113. # capturing group, because it should not ever be used in version
  114. # comparison.
  115. FULL = R()
  116. FULLPLAIN = ('v?' + src[MAINVERSION] + src[PRERELEASE] + '?' + src[BUILD] + '?')
  117. src[FULL] = '^' + FULLPLAIN + '$'
  118. # like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
  119. # also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
  120. # common in the npm registry.
  121. LOOSEPLAIN = ('[v=\\s]*' + src[MAINVERSIONLOOSE] +
  122. src[PRERELEASELOOSE] + '?' +
  123. src[BUILD] + '?')
  124. LOOSE = R()
  125. src[LOOSE] = '^' + LOOSEPLAIN + '$'
  126. GTLT = R()
  127. src[GTLT] = '((?:<|>)?=?)'
  128. # Something like "2.*" or "1.2.x".
  129. # Note that "x.x" is a valid xRange identifier, meaning "any version"
  130. # Only the first item is strictly required.
  131. XRANGEIDENTIFIERLOOSE = R()
  132. src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'
  133. XRANGEIDENTIFIER = R()
  134. src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'
  135. XRANGEPLAIN = R()
  136. src[XRANGEPLAIN] = ('[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' +
  137. '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
  138. '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
  139. '(?:' + src[PRERELEASE] + ')?' +
  140. src[BUILD] + '?' +
  141. ')?)?')
  142. XRANGEPLAINLOOSE = R()
  143. src[XRANGEPLAINLOOSE] = ('[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
  144. '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
  145. '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
  146. '(?:' + src[PRERELEASELOOSE] + ')?' +
  147. src[BUILD] + '?' +
  148. ')?)?')
  149. XRANGE = R()
  150. src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'
  151. XRANGELOOSE = R()
  152. src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'
  153. # Tilde ranges.
  154. # Meaning is "reasonably at or greater than"
  155. LONETILDE = R()
  156. src[LONETILDE] = '(?:~>?)'
  157. TILDETRIM = R()
  158. src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'
  159. regexp[TILDETRIM] = re.compile(src[TILDETRIM], re.M)
  160. tildeTrimReplace = r'\1~'
  161. TILDE = R()
  162. src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'
  163. TILDELOOSE = R()
  164. src[TILDELOOSE] = ('^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$')
  165. # Caret ranges.
  166. # Meaning is "at least and backwards compatible with"
  167. LONECARET = R()
  168. src[LONECARET] = '(?:\\^)'
  169. CARETTRIM = R()
  170. src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'
  171. regexp[CARETTRIM] = re.compile(src[CARETTRIM], re.M)
  172. caretTrimReplace = r'\1^'
  173. CARET = R()
  174. src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'
  175. CARETLOOSE = R()
  176. src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'
  177. # A simple gt/lt/eq thing, or just "" to indicate "any version"
  178. COMPARATORLOOSE = R()
  179. src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'
  180. COMPARATOR = R()
  181. src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'
  182. # An expression to strip any whitespace between the gtlt and the thing
  183. # it modifies, so that `> 1.2.3` ==> `>1.2.3`
  184. COMPARATORTRIM = R()
  185. src[COMPARATORTRIM] = ('(\\s*)' + src[GTLT] +
  186. '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')')
  187. # this one has to use the /g flag
  188. regexp[COMPARATORTRIM] = re.compile(src[COMPARATORTRIM], re.M)
  189. comparatorTrimReplace = r'\1\2\3'
  190. # Something like `1.2.3 - 1.2.4`
  191. # Note that these all use the loose form, because they'll be
  192. # checked against either the strict or loose comparator form
  193. # later.
  194. HYPHENRANGE = R()
  195. src[HYPHENRANGE] = ('^\\s*(' + src[XRANGEPLAIN] + ')' +
  196. '\\s+-\\s+' +
  197. '(' + src[XRANGEPLAIN] + ')' +
  198. '\\s*$')
  199. HYPHENRANGELOOSE = R()
  200. src[HYPHENRANGELOOSE] = ('^\\s*(' + src[XRANGEPLAINLOOSE] + ')' +
  201. '\\s+-\\s+' +
  202. '(' + src[XRANGEPLAINLOOSE] + ')' +
  203. '\\s*$')
  204. # Star ranges basically just allow anything at all.
  205. STAR = R()
  206. src[STAR] = '(<|>)?=?\\s*\\*'
  207. # version name recovery for convenient
  208. RECOVERYVERSIONNAME = R()
  209. src[RECOVERYVERSIONNAME] = ('v?({n})(?:\\.({n}))?{pre}?'.format(n=src[NUMERICIDENTIFIER], pre=src[PRERELEASELOOSE]))
  210. # Compile to actual regexp objects.
  211. # All are flag-free, unless they were created above with a flag.
  212. for i in range(R.value()):
  213. logger.debug("genregxp %s %s", i, src[i])
  214. if i not in regexp:
  215. regexp[i] = re.compile(src[i])
  216. def parse(version, loose):
  217. if loose:
  218. r = regexp[LOOSE]
  219. else:
  220. r = regexp[FULL]
  221. m = r.search(version)
  222. if m:
  223. return semver(version, loose)
  224. else:
  225. return None
  226. def valid(version, loose):
  227. v = parse(version, loose)
  228. if v.version:
  229. return v
  230. else:
  231. return None
  232. def clean(version, loose):
  233. s = parse(version, loose)
  234. if s:
  235. return s.version
  236. else:
  237. return None
  238. NUMERIC = re.compile("^\d+$")
  239. def semver(version, loose):
  240. if isinstance(version, SemVer):
  241. if version.loose == loose:
  242. return version
  243. else:
  244. version = version.version
  245. elif not isinstance(version, string_type): # xxx:
  246. raise ValueError("Invalid Version: {}".format(version))
  247. """
  248. if (!(this instanceof SemVer))
  249. return new SemVer(version, loose);
  250. """
  251. return SemVer(version, loose)
  252. make_semver = semver
  253. class SemVer(object):
  254. def __init__(self, version, loose):
  255. logger.debug("SemVer %s, %s", version, loose)
  256. self.loose = loose
  257. self.raw = version
  258. m = regexp[LOOSE if loose else FULL].search(version.strip())
  259. if not m:
  260. if not loose:
  261. raise ValueError("Invalid Version: {}".format(version))
  262. m = regexp[RECOVERYVERSIONNAME].search(version.strip())
  263. self.major = int(m.group(1)) if m.group(1) else 0
  264. self.minor = int(m.group(2)) if m.group(2) else 0
  265. self.patch = 0
  266. if not m.group(3):
  267. self.prerelease = []
  268. else:
  269. self.prerelease = [(int(id) if NUMERIC.search(id) else id)
  270. for id in m.group(3).split(".")]
  271. else:
  272. # these are actually numbers
  273. self.major = int(m.group(1))
  274. self.minor = int(m.group(2))
  275. self.patch = int(m.group(3))
  276. # numberify any prerelease numeric ids
  277. if not m.group(4):
  278. self.prerelease = []
  279. else:
  280. self.prerelease = [(int(id) if NUMERIC.search(id) else id)
  281. for id in m.group(4).split(".")]
  282. if m.group(5):
  283. self.build = m.group(5).split(".")
  284. else:
  285. self.build = []
  286. self.format() # xxx:
  287. def format(self):
  288. self.version = "{}.{}.{}".format(self.major, self.minor, self.patch)
  289. if len(self.prerelease) > 0:
  290. self.version += ("-{}".format(".".join(str(v) for v in self.prerelease)))
  291. return self.version
  292. def __repr__(self):
  293. return "<SemVer {} >".format(self)
  294. def __str__(self):
  295. return self.version
  296. def compare(self, other):
  297. logger.debug('SemVer.compare %s %s %s', self.version, self.loose, other)
  298. if not isinstance(other, SemVer):
  299. other = make_semver(other, self.loose)
  300. result = self.compare_main(other) or self.compare_pre(other)
  301. logger.debug("compare result %s", result)
  302. return result
  303. def compare_main(self, other):
  304. if not isinstance(other, SemVer):
  305. other = make_semver(other, self.loose)
  306. return (compare_identifiers(str(self.major), str(other.major)) or
  307. compare_identifiers(str(self.minor), str(other.minor)) or
  308. compare_identifiers(str(self.patch), str(other.patch)))
  309. def compare_pre(self, other):
  310. if not isinstance(other, SemVer):
  311. other = make_semver(other, self.loose)
  312. # NOT having a prerelease is > having one
  313. is_self_more_than_zero = len(self.prerelease) > 0
  314. is_other_more_than_zero = len(other.prerelease) > 0
  315. if not is_self_more_than_zero and is_other_more_than_zero:
  316. return 1
  317. elif is_self_more_than_zero and not is_other_more_than_zero:
  318. return -1
  319. elif not is_self_more_than_zero and not is_other_more_than_zero:
  320. return 0
  321. i = 0
  322. while True:
  323. a = list_get(self.prerelease, i)
  324. b = list_get(other.prerelease, i)
  325. logger.debug("prerelease compare %s: %s %s", i, a, b)
  326. i += 1
  327. if a is None and b is None:
  328. return 0
  329. elif b is None:
  330. return 1
  331. elif a is None:
  332. return -1
  333. elif a == b:
  334. continue
  335. else:
  336. return compare_identifiers(str(a), str(b))
  337. def inc(self, release, identifier=None):
  338. logger.debug("inc release %s %s", self.prerelease, release)
  339. if release == 'premajor':
  340. self.prerelease = []
  341. self.patch = 0
  342. self.minor = 0
  343. self.major += 1
  344. self.inc('pre', identifier=identifier)
  345. elif release == "preminor":
  346. self.prerelease = []
  347. self.patch = 0
  348. self.minor += 1
  349. self.inc('pre', identifier=identifier)
  350. elif release == "prepatch":
  351. # If this is already a prerelease, it will bump to the next version
  352. # drop any prereleases that might already exist, since they are not
  353. # relevant at this point.
  354. self.prerelease = []
  355. self.inc('patch', identifier=identifier)
  356. self.inc('pre', identifier=identifier)
  357. elif release == 'prerelease':
  358. # If the input is a non-prerelease version, this acts the same as
  359. # prepatch.
  360. if len(self.prerelease) == 0:
  361. self.inc("patch", identifier=identifier)
  362. self.inc("pre", identifier=identifier)
  363. elif release == "major":
  364. # If this is a pre-major version, bump up to the same major version.
  365. # Otherwise increment major.
  366. # 1.0.0-5 bumps to 1.0.0
  367. # 1.1.0 bumps to 2.0.0
  368. if self.minor != 0 or self.patch != 0 or len(self.prerelease) == 0:
  369. self.major += 1
  370. self.minor = 0
  371. self.patch = 0
  372. self.prerelease = []
  373. elif release == "minor":
  374. # If this is a pre-minor version, bump up to the same minor version.
  375. # Otherwise increment minor.
  376. # 1.2.0-5 bumps to 1.2.0
  377. # 1.2.1 bumps to 1.3.0
  378. if self.patch != 0 or len(self.prerelease) == 0:
  379. self.minor += 1
  380. self.patch = 0
  381. self.prerelease = []
  382. elif release == "patch":
  383. # If this is not a pre-release version, it will increment the patch.
  384. # If it is a pre-release it will bump up to the same patch version.
  385. # 1.2.0-5 patches to 1.2.0
  386. # 1.2.0 patches to 1.2.1
  387. if len(self.prerelease) == 0:
  388. self.patch += 1
  389. self.prerelease = []
  390. elif release == "pre":
  391. # This probably shouldn't be used publicly.
  392. # 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
  393. logger.debug("inc prerelease %s", self.prerelease)
  394. if len(self.prerelease) == 0:
  395. self.prerelease = [0]
  396. else:
  397. i = len(self.prerelease) - 1
  398. while i >= 0:
  399. if isinstance(self.prerelease[i], int):
  400. self.prerelease[i] += 1
  401. i -= 2
  402. i -= 1
  403. # ## this is needless code in python ##
  404. # if i == -1: # didn't increment anything
  405. # self.prerelease.append(0)
  406. if identifier is not None:
  407. # 1.2.0-beta.1 bumps to 1.2.0-beta.2,
  408. # 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
  409. if self.prerelease[0] == identifier:
  410. if not isinstance(self.prerelease[1], int):
  411. self.prerelease = [identifier, 0]
  412. else:
  413. self.prerelease = [identifier, 0]
  414. else:
  415. raise ValueError('invalid increment argument: {}'.format(release))
  416. self.format()
  417. self.raw = self.version
  418. return self
  419. def inc(version, release, loose, identifier=None): # wow!
  420. try:
  421. return make_semver(version, loose).inc(release, identifier=identifier).version
  422. except Exception as e:
  423. logger.debug(e, exc_info=5)
  424. return None
  425. def compare_identifiers(a, b):
  426. anum = NUMERIC.search(a)
  427. bnum = NUMERIC.search(b)
  428. if anum and bnum:
  429. a = int(a)
  430. b = int(b)
  431. if anum and not bnum:
  432. return -1
  433. elif bnum and not anum:
  434. return 1
  435. elif a < b:
  436. return -1
  437. elif a > b:
  438. return 1
  439. else:
  440. return 0
  441. def rcompare_identifiers(a, b):
  442. return compare_identifiers(b, a)
  443. def compare(a, b, loose):
  444. return make_semver(a, loose).compare(b)
  445. def compare_loose(a, b):
  446. return compare(a, b, True)
  447. def rcompare(a, b, loose):
  448. return compare(b, a, loose)
  449. def make_key_function(loose):
  450. def key_function(version):
  451. v = make_semver(version, loose)
  452. key = (v.major, v.minor, v.patch)
  453. if v.prerelease:
  454. key = key + tuple(v.prerelease)
  455. else:
  456. # NOT having a prerelease is > having one
  457. key = key + (float('inf'),)
  458. return key
  459. return key_function
  460. loose_key_function = make_key_function(True)
  461. full_key_function = make_key_function(True)
  462. def sort(list, loose):
  463. keyf = loose_key_function if loose else full_key_function
  464. list.sort(key=keyf)
  465. return list
  466. def rsort(list, loose):
  467. keyf = loose_key_function if loose else full_key_function
  468. list.sort(key=keyf, reverse=True)
  469. return list
  470. def gt(a, b, loose):
  471. return compare(a, b, loose) > 0
  472. def lt(a, b, loose):
  473. return compare(a, b, loose) < 0
  474. def eq(a, b, loose):
  475. return compare(a, b, loose) == 0
  476. def neq(a, b, loose):
  477. return compare(a, b, loose) != 0
  478. def gte(a, b, loose):
  479. return compare(a, b, loose) >= 0
  480. def lte(a, b, loose):
  481. return compare(a, b, loose) <= 0
  482. def cmp(a, op, b, loose):
  483. logger.debug("cmp: %s", op)
  484. if op == "===":
  485. return a == b
  486. elif op == "!==":
  487. return a != b
  488. elif op == "" or op == "=" or op == "==":
  489. return eq(a, b, loose)
  490. elif op == "!=":
  491. return neq(a, b, loose)
  492. elif op == ">":
  493. return gt(a, b, loose)
  494. elif op == ">=":
  495. return gte(a, b, loose)
  496. elif op == "<":
  497. return lt(a, b, loose)
  498. elif op == "<=":
  499. return lte(a, b, loose)
  500. else:
  501. raise ValueError("Invalid operator: {}".format(op))
  502. def comparator(comp, loose):
  503. if isinstance(comp, Comparator):
  504. if(comp.loose == loose):
  505. return comp
  506. else:
  507. comp = comp.value
  508. # if (!(this instanceof Comparator))
  509. # return new Comparator(comp, loose)
  510. return Comparator(comp, loose)
  511. make_comparator = comparator
  512. ANY = object()
  513. class Comparator(object):
  514. semver = None
  515. def __init__(self, comp, loose):
  516. logger.debug("comparator: %s %s", comp, loose)
  517. self.loose = loose
  518. self.parse(comp)
  519. if self.semver == ANY:
  520. self.value = ""
  521. else:
  522. self.value = self.operator + self.semver.version
  523. def parse(self, comp):
  524. if self.loose:
  525. r = regexp[COMPARATORLOOSE]
  526. else:
  527. r = regexp[COMPARATOR]
  528. logger.debug("parse comp=%s", comp)
  529. m = r.search(comp)
  530. if m is None:
  531. raise ValueError("Invalid comparator: {}".format(comp))
  532. self.operator = m.group(1)
  533. # if it literally is just '>' or '' then allow anything.
  534. if m.group(2) is None:
  535. self.semver = ANY
  536. else:
  537. self.semver = semver(m.group(2), self.loose)
  538. def __repr__(self):
  539. return '<SemVer Comparator "{}">'.format(self)
  540. def __str__(self):
  541. return self.value
  542. def test(self, version):
  543. logger.debug('Comparator, test %s, %s', version, self.loose)
  544. if self.semver == ANY:
  545. return True
  546. else:
  547. return cmp(version, self.operator, self.semver, self.loose)
  548. def make_range(range_, loose):
  549. if isinstance(range_, Range) and range_.loose == loose:
  550. return range_
  551. # if (!(this instanceof Range))
  552. # return new Range(range, loose);
  553. return Range(range_, loose)
  554. class Range(object):
  555. def __init__(self, range_, loose):
  556. self.loose = loose
  557. # First, split based on boolean or ||
  558. self.raw = range_
  559. xs = [self.parse_range(r.strip()) for r in re.split(r"\s*\|\|\s*", range_)]
  560. self.set = [r for r in xs if r]
  561. if not len(self.set):
  562. raise ValueError("Invalid SemVer Range: {}".format(range_))
  563. self.format()
  564. def __repr__(self):
  565. return '<SemVer Range "{}">'.format(self.range)
  566. def format(self):
  567. self.range = "||".join([" ".join(c.value for c in comps).strip() for comps in self.set]).strip()
  568. logger.debug("Range format %s", self.range)
  569. return self.range
  570. def __str__(self):
  571. return self.range
  572. def parse_range(self, range_):
  573. loose = self.loose
  574. logger.debug('range %s %s', range_, loose)
  575. # `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
  576. if loose:
  577. hr = regexp[HYPHENRANGELOOSE]
  578. else:
  579. hr = regexp[HYPHENRANGE]
  580. range_ = hr.sub(hyphen_replace, range_,)
  581. logger.debug('hyphen replace %s', range_)
  582. # `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
  583. range_ = regexp[COMPARATORTRIM].sub(comparatorTrimReplace, range_)
  584. logger.debug('comparator trim %s, %s', range_, regexp[COMPARATORTRIM])
  585. # `~ 1.2.3` => `~1.2.3`
  586. range_ = regexp[TILDETRIM].sub(tildeTrimReplace, range_)
  587. # `^ 1.2.3` => `^1.2.3`
  588. range_ = regexp[CARETTRIM].sub(caretTrimReplace, range_)
  589. # normalize spaces
  590. range_ = " ".join(re.split("\s+", range_))
  591. # At this point, the range is completely trimmed and
  592. # ready to be split into comparators.
  593. if loose:
  594. comp_re = regexp[COMPARATORLOOSE]
  595. else:
  596. comp_re = regexp[COMPARATOR]
  597. set_ = re.split("\s+", ' '.join([parse_comparator(comp, loose) for comp in range_.split(" ")]))
  598. if self.loose:
  599. # in loose mode, throw out any that are not valid comparators
  600. set_ = [comp for comp in set_ if comp_re.search(comp)]
  601. set_ = [make_comparator(comp, loose) for comp in set_]
  602. return set_
  603. def test(self, version):
  604. if not version: # xxx
  605. return False
  606. if isinstance(version, string_type):
  607. version = make_semver(version, loose=self.loose)
  608. for e in self.set:
  609. if test_set(e, version):
  610. return True
  611. return False
  612. # Mostly just for testing and legacy API reasons
  613. def to_comparators(range_, loose):
  614. return [" ".join([c.value for c in comp]).strip().split(" ")
  615. for comp in make_range(range_, loose).set]
  616. # comprised of xranges, tildes, stars, and gtlt's at this point.
  617. # already replaced the hyphen ranges
  618. # turn into a set of JUST comparators.
  619. def parse_comparator(comp, loose):
  620. logger.debug('comp %s', comp)
  621. comp = replace_carets(comp, loose)
  622. logger.debug('caret %s', comp)
  623. comp = replace_tildes(comp, loose)
  624. logger.debug('tildes %s', comp)
  625. comp = replace_xranges(comp, loose)
  626. logger.debug('xrange %s', comp)
  627. comp = replace_stars(comp, loose)
  628. logger.debug('stars %s', comp)
  629. return comp
  630. def is_x(id):
  631. return id is None or id == "" or id.lower() == "x" or id == "*"
  632. # ~, ~> --> * (any, kinda silly)
  633. # ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
  634. # ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
  635. # ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
  636. # ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
  637. # ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
  638. def replace_tildes(comp, loose):
  639. return " ".join([replace_tilde(c, loose)
  640. for c in re.split("\s+", comp.strip())])
  641. def replace_tilde(comp, loose):
  642. if loose:
  643. r = regexp[TILDELOOSE]
  644. else:
  645. r = regexp[TILDE]
  646. def repl(mob):
  647. _ = mob.group(0)
  648. M, m, p, pr, _ = mob.groups()
  649. logger.debug("tilde %s %s %s %s %s %s", comp, _, M, m, p, pr)
  650. if is_x(M):
  651. ret = ""
  652. elif is_x(m):
  653. ret = '>=' + M + '.0.0 <' + str(int(M) + 1) + '.0.0'
  654. elif is_x(p):
  655. # ~1.2 == >=1.2.0 <1.3.0
  656. ret = '>=' + M + '.' + m + '.0 <' + M + '.' + str(int(m) + 1) + '.0'
  657. elif pr:
  658. logger.debug("replaceTilde pr %s", pr)
  659. if (pr[0] != "-"):
  660. pr = '-' + pr
  661. ret = '>=' + M + '.' + m + '.' + p + pr + ' <' + M + '.' + str(int(m) + 1) + '.0'
  662. else:
  663. # ~1.2.3 == >=1.2.3 <1.3.0
  664. ret = '>=' + M + '.' + m + '.' + p + ' <' + M + '.' + str(int(m) + 1) + '.0'
  665. logger.debug('tilde return, %s', ret)
  666. return ret
  667. return r.sub(repl, comp)
  668. # ^ --> * (any, kinda silly)
  669. # ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
  670. # ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
  671. # ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
  672. # ^1.2.3 --> >=1.2.3 <2.0.0
  673. # ^1.2.0 --> >=1.2.0 <2.0.0
  674. def replace_carets(comp, loose):
  675. return " ".join([replace_caret(c, loose)
  676. for c in re.split("\s+", comp.strip())])
  677. def replace_caret(comp, loose):
  678. if loose:
  679. r = regexp[CARETLOOSE]
  680. else:
  681. r = regexp[CARET]
  682. def repl(mob):
  683. m0 = mob.group(0)
  684. M, m, p, pr, _ = mob.groups()
  685. logger.debug("caret %s %s %s %s %s %s", comp, m0, M, m, p, pr)
  686. if is_x(M):
  687. ret = ""
  688. elif is_x(m):
  689. ret = '>=' + M + '.0.0 <' + str((int(M) + 1)) + '.0.0'
  690. elif is_x(p):
  691. if M == "0":
  692. ret = '>=' + M + '.' + m + '.0 <' + M + '.' + str((int(m) + 1)) + '.0'
  693. else:
  694. ret = '>=' + M + '.' + m + '.0 <' + str(int(M) + 1) + '.0.0'
  695. elif pr:
  696. logger.debug('replaceCaret pr %s', pr)
  697. if pr[0] != "-":
  698. pr = "-" + pr
  699. if M == "0":
  700. if m == "0":
  701. ret = '>=' + M + '.' + m + '.' + (p or "") + pr + ' <' + M + '.' + m + "." + str(int(p or 0) + 1)
  702. else:
  703. ret = '>=' + M + '.' + m + '.' + (p or "") + pr + ' <' + M + '.' + str(int(m) + 1) + '.0'
  704. else:
  705. ret = '>=' + M + '.' + m + '.' + (p or "") + pr + ' <' + str(int(M) + 1) + '.0.0'
  706. else:
  707. if M == "0":
  708. if m == "0":
  709. ret = '>=' + M + '.' + m + '.' + (p or "") + ' <' + M + '.' + m + "." + str(int(p or 0) + 1)
  710. else:
  711. ret = '>=' + M + '.' + m + '.' + (p or "") + ' <' + M + '.' + str((int(m) + 1)) + '.0'
  712. else:
  713. ret = '>=' + M + '.' + m + '.' + (p or "") + ' <' + str(int(M) + 1) + '.0.0'
  714. logger.debug('caret return %s', ret)
  715. return ret
  716. return r.sub(repl, comp)
  717. def replace_xranges(comp, loose):
  718. logger.debug('replaceXRanges %s %s', comp, loose)
  719. return " ".join([replace_xrange(c, loose)
  720. for c in re.split("\s+", comp.strip())])
  721. def replace_xrange(comp, loose):
  722. comp = comp.strip()
  723. if loose:
  724. r = regexp[XRANGELOOSE]
  725. else:
  726. r = regexp[XRANGE]
  727. def repl(mob):
  728. ret = mob.group(0)
  729. gtlt, M, m, p, pr, _ = mob.groups()
  730. logger.debug("xrange %s %s %s %s %s %s %s", comp, ret, gtlt, M, m, p, pr)
  731. xM = is_x(M)
  732. xm = xM or is_x(m)
  733. xp = xm or is_x(p)
  734. any_x = xp
  735. if gtlt == "=" and any_x:
  736. gtlt = ""
  737. logger.debug("xrange gtlt=%s any_x=%s", gtlt, any_x)
  738. if xM:
  739. if gtlt == '>' or gtlt == '<':
  740. # nothing is allowed
  741. ret = '<0.0.0'
  742. else:
  743. ret = '*'
  744. elif gtlt and any_x:
  745. # replace X with 0, and then append the -0 min-prerelease
  746. if xm:
  747. m = 0
  748. if xp:
  749. p = 0
  750. if gtlt == ">":
  751. # >1 => >=2.0.0
  752. # >1.2 => >=1.3.0
  753. # >1.2.3 => >= 1.2.4
  754. gtlt = ">="
  755. if xm:
  756. M = int(M) + 1
  757. m = 0
  758. p = 0
  759. elif xp:
  760. m = int(m) + 1
  761. p = 0
  762. elif gtlt == '<=':
  763. # <=0.7.x is actually <0.8.0, since any 0.7.x should
  764. # pass. Similarly, <=7.x is actually <8.0.0, etc.
  765. gtlt = '<'
  766. if xm:
  767. M = int(M) + 1
  768. else:
  769. m = int(m) + 1
  770. ret = gtlt + str(M) + '.' + str(m) + '.' + str(p)
  771. elif xm:
  772. ret = '>=' + M + '.0.0 <' + str(int(M) + 1) + '.0.0'
  773. elif xp:
  774. ret = '>=' + M + '.' + m + '.0 <' + M + '.' + str(int(m) + 1) + '.0'
  775. logger.debug('xRange return %s', ret)
  776. return ret
  777. return r.sub(repl, comp)
  778. # Because * is AND-ed with everything else in the comparator,
  779. # and '' means "any version", just remove the *s entirely.
  780. def replace_stars(comp, loose):
  781. logger.debug('replaceStars %s %s', comp, loose)
  782. # Looseness is ignored here. star is always as loose as it gets!
  783. return regexp[STAR].sub("", comp.strip())
  784. # This function is passed to string.replace(re[HYPHENRANGE])
  785. # M, m, patch, prerelease, build
  786. # 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
  787. # 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
  788. # 1.2 - 3.4 => >=1.2.0 <3.5.0
  789. def hyphen_replace(mob):
  790. from_, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr, tb = mob.groups()
  791. if is_x(fM):
  792. from_ = ""
  793. elif is_x(fm):
  794. from_ = '>=' + fM + '.0.0'
  795. elif is_x(fp):
  796. from_ = '>=' + fM + '.' + fm + '.0'
  797. else:
  798. from_ = ">=" + from_
  799. if is_x(tM):
  800. to = ""
  801. elif is_x(tm):
  802. to = '<' + str(int(tM) + 1) + '.0.0'
  803. elif is_x(tp):
  804. to = '<' + tM + '.' + str(int(tm) + 1) + '.0'
  805. elif tpr:
  806. to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr
  807. else:
  808. to = '<=' + to
  809. return (from_ + ' ' + to).strip()
  810. def test_set(set_, version):
  811. for e in set_:
  812. if not e.test(version):
  813. return False
  814. if len(version.prerelease) > 0:
  815. # Find the set of versions that are allowed to have prereleases
  816. # For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
  817. # That should allow `1.2.3-pr.2` to pass.
  818. # However, `1.2.4-alpha.notready` should NOT be allowed,
  819. # even though it's within the range set by the comparators.
  820. for e in set_:
  821. if e.semver == ANY:
  822. continue
  823. if len(e.semver.prerelease) > 0:
  824. allowed = e.semver
  825. if allowed.major == version.major and allowed.minor == version.minor and allowed.patch == version.patch:
  826. return True
  827. # Version has a -pre, but it's not one of the ones we like.
  828. return False
  829. return True
  830. def satisfies(version, range_, loose=False):
  831. try:
  832. range_ = make_range(range_, loose)
  833. except Exception as e:
  834. return False
  835. return range_.test(version)
  836. def max_satisfying(versions, range_, loose=False):
  837. try:
  838. range_ob = make_range(range_, loose=loose)
  839. except:
  840. return None
  841. max_ = None
  842. max_sv = None
  843. for v in versions:
  844. if range_ob.test(v): # satisfies(v, range_, loose=loose)
  845. if max_ is None or max_sv.compare(v) == -1: # compare(max, v, true)
  846. max_ = v
  847. max_sv = make_semver(max_, loose=loose)
  848. return max_
  849. def valid_range(range_, loose):
  850. try:
  851. # Return '*' instead of '' so that truthiness works.
  852. # This will throw if it's invalid anyway
  853. return make_range(range_, loose).range or "*"
  854. except:
  855. return None
  856. # Determine if version is less than all the versions possible in the range
  857. def ltr(version, range_, loose):
  858. return outside(version, range_, "<", loose)
  859. # Determine if version is greater than all the versions possible in the range.
  860. def rtr(version, range_, loose):
  861. return outside(version, range_, ">", loose)
  862. def outside(version, range_, hilo, loose):
  863. version = make_semver(version, loose)
  864. range_ = make_range(range_, loose)
  865. if hilo == ">":
  866. gtfn = gt
  867. ltefn = lte
  868. ltfn = lt
  869. comp = ">"
  870. ecomp = ">="
  871. elif hilo == "<":
  872. gtfn = lt
  873. ltefn = gte
  874. ltfn = gt
  875. comp = "<"
  876. ecomp = "<="
  877. else:
  878. raise ValueError("Must provide a hilo val of '<' or '>'")
  879. # If it satisifes the range it is not outside
  880. if satisfies(version, range_, loose):
  881. return False
  882. # From now on, variable terms are as if we're in "gtr" mode.
  883. # but note that everything is flipped for the "ltr" function.
  884. for comparators in range_.set:
  885. high = None
  886. low = None
  887. for comparator in comparators:
  888. high = high or comparator
  889. low = low or comparator
  890. if gtfn(comparator.semver, high.semver, loose):
  891. high = comparator
  892. elif ltfn(comparator.semver, low.semver, loose):
  893. low = comparator
  894. # If the edge version comparator has a operator then our version
  895. # isn't outside it
  896. if high.operator == comp or high.operator == ecomp:
  897. return False
  898. # If the lowest version comparator has an operator and our version
  899. # is less than it then it isn't higher than the range
  900. if (not low.operator or low.operator == comp) and ltefn(version, low.semver):
  901. return False
  902. elif low.operator == ecomp and ltfn(version, low.semver):
  903. return False
  904. return True