Coverage for adhoc-cicd-odoo-odoo / odoo / tools / parse_version.py: 61%

34 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-09 18:22 +0000

1# Part of Odoo. See LICENSE file for full copyright and licensing details. 

2 

3## this functions are taken from the setuptools package (version 0.6c8) 

4## http://peak.telecommunity.com/DevCenter/PkgResources#parsing-utilities 

5 

6import re 

7 

8component_re = re.compile(r'(\d+ | [a-z]+ | \.| -)', re.VERBOSE) 

9replace = {'pre':'c', 'preview':'c','-':'final-','_':'final-','rc':'c','dev':'@','saas':'','~':''}.get 

10 

11 

12def _parse_version_parts(s): 

13 for part in component_re.split(s): 

14 part = replace(part,part) 

15 if not part or part=='.': 

16 continue 

17 if part[:1] in '0123456789': 17 ↛ 20line 17 didn't jump to line 20 because the condition on line 17 was always true

18 yield part.zfill(8) # pad for numeric comparison 

19 else: 

20 yield '*'+part 

21 

22 yield '*final' # ensure that alpha/beta/candidate are before final 

23 

24 

25def parse_version(s: str) -> tuple[str, ...]: 

26 """Convert a version string to a chronologically-sortable key 

27 

28 This is a rough cross between distutils' StrictVersion and LooseVersion; 

29 if you give it versions that would work with StrictVersion, then it behaves 

30 the same; otherwise it acts like a slightly-smarter LooseVersion. It is 

31 *possible* to create pathological version coding schemes that will fool 

32 this parser, but they should be very rare in practice. 

33 

34 The returned value will be a tuple of strings. Numeric portions of the 

35 version are padded to 8 digits so they will compare numerically, but 

36 without relying on how numbers compare relative to strings. Dots are 

37 dropped, but dashes are retained. Trailing zeros between alpha segments 

38 or dashes are suppressed, so that e.g. "2.4.0" is considered the same as 

39 "2.4". Alphanumeric parts are lower-cased. 

40 

41 The algorithm assumes that strings like "-" and any alpha string that 

42 alphabetically follows "final" represents a "patch level". So, "2.4-1" 

43 is assumed to be a branch or patch of "2.4", and therefore "2.4.1" is 

44 considered newer than "2.4-1", which in turn is newer than "2.4". 

45 

46 Strings like "a", "b", "c", "alpha", "beta", "candidate" and so on (that 

47 come before "final" alphabetically) are assumed to be pre-release versions, 

48 so that the version "2.4" is considered newer than "2.4a1". 

49 

50 Finally, to handle miscellaneous cases, the strings "pre", "preview", and 

51 "rc" are treated as if they were "c", i.e. as though they were release 

52 candidates, and therefore are not as new as a version string that does not 

53 contain them. 

54 """ 

55 parts: list[str] = [] 

56 for part in _parse_version_parts((s or '0.1').lower()): 

57 if part.startswith('*'): 

58 if part<'*final': # remove '-' before a prerelease tag 58 ↛ 59line 58 didn't jump to line 59 because the condition on line 58 was never true

59 while parts and parts[-1]=='*final-': parts.pop() 

60 # remove trailing zeros from each series of numeric parts 

61 while parts and parts[-1]=='00000000': 

62 parts.pop() 

63 parts.append(part) 

64 return tuple(parts) 

65 

66if __name__ == '__main__': 66 ↛ 67line 66 didn't jump to line 67 because the condition on line 66 was never true

67 def chk(lst, verbose=False): 

68 pvs = [] 

69 for v in lst: 

70 pv = parse_version(v) 

71 pvs.append(pv) 

72 if verbose: 

73 print(v, pv) 

74 

75 for a, b in zip(pvs, pvs[1:]): 

76 assert a < b, '%s < %s == %s' % (a, b, a < b) 

77 

78 chk(('0', '4.2', '4.2.3.4', '5.0.0-alpha', '5.0.0-rc1', '5.0.0-rc1.1', '5.0.0_rc2', '5.0.0_rc3', '5.0.0'), False) 

79 chk(('5.0.0-0_rc3', '5.0.0-1dev', '5.0.0-1'), False) 

80