1*cda5da8dSAndroid Build Coastguard Worker"""Module for parsing and testing package version predicate strings. 2*cda5da8dSAndroid Build Coastguard Worker""" 3*cda5da8dSAndroid Build Coastguard Workerimport re 4*cda5da8dSAndroid Build Coastguard Workerimport distutils.version 5*cda5da8dSAndroid Build Coastguard Workerimport operator 6*cda5da8dSAndroid Build Coastguard Worker 7*cda5da8dSAndroid Build Coastguard Worker 8*cda5da8dSAndroid Build Coastguard Workerre_validPackage = re.compile(r"(?i)^\s*([a-z_]\w*(?:\.[a-z_]\w*)*)(.*)", 9*cda5da8dSAndroid Build Coastguard Worker re.ASCII) 10*cda5da8dSAndroid Build Coastguard Worker# (package) (rest) 11*cda5da8dSAndroid Build Coastguard Worker 12*cda5da8dSAndroid Build Coastguard Workerre_paren = re.compile(r"^\s*\((.*)\)\s*$") # (list) inside of parentheses 13*cda5da8dSAndroid Build Coastguard Workerre_splitComparison = re.compile(r"^\s*(<=|>=|<|>|!=|==)\s*([^\s,]+)\s*$") 14*cda5da8dSAndroid Build Coastguard Worker# (comp) (version) 15*cda5da8dSAndroid Build Coastguard Worker 16*cda5da8dSAndroid Build Coastguard Worker 17*cda5da8dSAndroid Build Coastguard Workerdef splitUp(pred): 18*cda5da8dSAndroid Build Coastguard Worker """Parse a single version comparison. 19*cda5da8dSAndroid Build Coastguard Worker 20*cda5da8dSAndroid Build Coastguard Worker Return (comparison string, StrictVersion) 21*cda5da8dSAndroid Build Coastguard Worker """ 22*cda5da8dSAndroid Build Coastguard Worker res = re_splitComparison.match(pred) 23*cda5da8dSAndroid Build Coastguard Worker if not res: 24*cda5da8dSAndroid Build Coastguard Worker raise ValueError("bad package restriction syntax: %r" % pred) 25*cda5da8dSAndroid Build Coastguard Worker comp, verStr = res.groups() 26*cda5da8dSAndroid Build Coastguard Worker return (comp, distutils.version.StrictVersion(verStr)) 27*cda5da8dSAndroid Build Coastguard Worker 28*cda5da8dSAndroid Build Coastguard Workercompmap = {"<": operator.lt, "<=": operator.le, "==": operator.eq, 29*cda5da8dSAndroid Build Coastguard Worker ">": operator.gt, ">=": operator.ge, "!=": operator.ne} 30*cda5da8dSAndroid Build Coastguard Worker 31*cda5da8dSAndroid Build Coastguard Workerclass VersionPredicate: 32*cda5da8dSAndroid Build Coastguard Worker """Parse and test package version predicates. 33*cda5da8dSAndroid Build Coastguard Worker 34*cda5da8dSAndroid Build Coastguard Worker >>> v = VersionPredicate('pyepat.abc (>1.0, <3333.3a1, !=1555.1b3)') 35*cda5da8dSAndroid Build Coastguard Worker 36*cda5da8dSAndroid Build Coastguard Worker The `name` attribute provides the full dotted name that is given:: 37*cda5da8dSAndroid Build Coastguard Worker 38*cda5da8dSAndroid Build Coastguard Worker >>> v.name 39*cda5da8dSAndroid Build Coastguard Worker 'pyepat.abc' 40*cda5da8dSAndroid Build Coastguard Worker 41*cda5da8dSAndroid Build Coastguard Worker The str() of a `VersionPredicate` provides a normalized 42*cda5da8dSAndroid Build Coastguard Worker human-readable version of the expression:: 43*cda5da8dSAndroid Build Coastguard Worker 44*cda5da8dSAndroid Build Coastguard Worker >>> print(v) 45*cda5da8dSAndroid Build Coastguard Worker pyepat.abc (> 1.0, < 3333.3a1, != 1555.1b3) 46*cda5da8dSAndroid Build Coastguard Worker 47*cda5da8dSAndroid Build Coastguard Worker The `satisfied_by()` method can be used to determine with a given 48*cda5da8dSAndroid Build Coastguard Worker version number is included in the set described by the version 49*cda5da8dSAndroid Build Coastguard Worker restrictions:: 50*cda5da8dSAndroid Build Coastguard Worker 51*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('1.1') 52*cda5da8dSAndroid Build Coastguard Worker True 53*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('1.4') 54*cda5da8dSAndroid Build Coastguard Worker True 55*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('1.0') 56*cda5da8dSAndroid Build Coastguard Worker False 57*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('4444.4') 58*cda5da8dSAndroid Build Coastguard Worker False 59*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('1555.1b3') 60*cda5da8dSAndroid Build Coastguard Worker False 61*cda5da8dSAndroid Build Coastguard Worker 62*cda5da8dSAndroid Build Coastguard Worker `VersionPredicate` is flexible in accepting extra whitespace:: 63*cda5da8dSAndroid Build Coastguard Worker 64*cda5da8dSAndroid Build Coastguard Worker >>> v = VersionPredicate(' pat( == 0.1 ) ') 65*cda5da8dSAndroid Build Coastguard Worker >>> v.name 66*cda5da8dSAndroid Build Coastguard Worker 'pat' 67*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('0.1') 68*cda5da8dSAndroid Build Coastguard Worker True 69*cda5da8dSAndroid Build Coastguard Worker >>> v.satisfied_by('0.2') 70*cda5da8dSAndroid Build Coastguard Worker False 71*cda5da8dSAndroid Build Coastguard Worker 72*cda5da8dSAndroid Build Coastguard Worker If any version numbers passed in do not conform to the 73*cda5da8dSAndroid Build Coastguard Worker restrictions of `StrictVersion`, a `ValueError` is raised:: 74*cda5da8dSAndroid Build Coastguard Worker 75*cda5da8dSAndroid Build Coastguard Worker >>> v = VersionPredicate('p1.p2.p3.p4(>=1.0, <=1.3a1, !=1.2zb3)') 76*cda5da8dSAndroid Build Coastguard Worker Traceback (most recent call last): 77*cda5da8dSAndroid Build Coastguard Worker ... 78*cda5da8dSAndroid Build Coastguard Worker ValueError: invalid version number '1.2zb3' 79*cda5da8dSAndroid Build Coastguard Worker 80*cda5da8dSAndroid Build Coastguard Worker It the module or package name given does not conform to what's 81*cda5da8dSAndroid Build Coastguard Worker allowed as a legal module or package name, `ValueError` is 82*cda5da8dSAndroid Build Coastguard Worker raised:: 83*cda5da8dSAndroid Build Coastguard Worker 84*cda5da8dSAndroid Build Coastguard Worker >>> v = VersionPredicate('foo-bar') 85*cda5da8dSAndroid Build Coastguard Worker Traceback (most recent call last): 86*cda5da8dSAndroid Build Coastguard Worker ... 87*cda5da8dSAndroid Build Coastguard Worker ValueError: expected parenthesized list: '-bar' 88*cda5da8dSAndroid Build Coastguard Worker 89*cda5da8dSAndroid Build Coastguard Worker >>> v = VersionPredicate('foo bar (12.21)') 90*cda5da8dSAndroid Build Coastguard Worker Traceback (most recent call last): 91*cda5da8dSAndroid Build Coastguard Worker ... 92*cda5da8dSAndroid Build Coastguard Worker ValueError: expected parenthesized list: 'bar (12.21)' 93*cda5da8dSAndroid Build Coastguard Worker 94*cda5da8dSAndroid Build Coastguard Worker """ 95*cda5da8dSAndroid Build Coastguard Worker 96*cda5da8dSAndroid Build Coastguard Worker def __init__(self, versionPredicateStr): 97*cda5da8dSAndroid Build Coastguard Worker """Parse a version predicate string. 98*cda5da8dSAndroid Build Coastguard Worker """ 99*cda5da8dSAndroid Build Coastguard Worker # Fields: 100*cda5da8dSAndroid Build Coastguard Worker # name: package name 101*cda5da8dSAndroid Build Coastguard Worker # pred: list of (comparison string, StrictVersion) 102*cda5da8dSAndroid Build Coastguard Worker 103*cda5da8dSAndroid Build Coastguard Worker versionPredicateStr = versionPredicateStr.strip() 104*cda5da8dSAndroid Build Coastguard Worker if not versionPredicateStr: 105*cda5da8dSAndroid Build Coastguard Worker raise ValueError("empty package restriction") 106*cda5da8dSAndroid Build Coastguard Worker match = re_validPackage.match(versionPredicateStr) 107*cda5da8dSAndroid Build Coastguard Worker if not match: 108*cda5da8dSAndroid Build Coastguard Worker raise ValueError("bad package name in %r" % versionPredicateStr) 109*cda5da8dSAndroid Build Coastguard Worker self.name, paren = match.groups() 110*cda5da8dSAndroid Build Coastguard Worker paren = paren.strip() 111*cda5da8dSAndroid Build Coastguard Worker if paren: 112*cda5da8dSAndroid Build Coastguard Worker match = re_paren.match(paren) 113*cda5da8dSAndroid Build Coastguard Worker if not match: 114*cda5da8dSAndroid Build Coastguard Worker raise ValueError("expected parenthesized list: %r" % paren) 115*cda5da8dSAndroid Build Coastguard Worker str = match.groups()[0] 116*cda5da8dSAndroid Build Coastguard Worker self.pred = [splitUp(aPred) for aPred in str.split(",")] 117*cda5da8dSAndroid Build Coastguard Worker if not self.pred: 118*cda5da8dSAndroid Build Coastguard Worker raise ValueError("empty parenthesized list in %r" 119*cda5da8dSAndroid Build Coastguard Worker % versionPredicateStr) 120*cda5da8dSAndroid Build Coastguard Worker else: 121*cda5da8dSAndroid Build Coastguard Worker self.pred = [] 122*cda5da8dSAndroid Build Coastguard Worker 123*cda5da8dSAndroid Build Coastguard Worker def __str__(self): 124*cda5da8dSAndroid Build Coastguard Worker if self.pred: 125*cda5da8dSAndroid Build Coastguard Worker seq = [cond + " " + str(ver) for cond, ver in self.pred] 126*cda5da8dSAndroid Build Coastguard Worker return self.name + " (" + ", ".join(seq) + ")" 127*cda5da8dSAndroid Build Coastguard Worker else: 128*cda5da8dSAndroid Build Coastguard Worker return self.name 129*cda5da8dSAndroid Build Coastguard Worker 130*cda5da8dSAndroid Build Coastguard Worker def satisfied_by(self, version): 131*cda5da8dSAndroid Build Coastguard Worker """True if version is compatible with all the predicates in self. 132*cda5da8dSAndroid Build Coastguard Worker The parameter version must be acceptable to the StrictVersion 133*cda5da8dSAndroid Build Coastguard Worker constructor. It may be either a string or StrictVersion. 134*cda5da8dSAndroid Build Coastguard Worker """ 135*cda5da8dSAndroid Build Coastguard Worker for cond, ver in self.pred: 136*cda5da8dSAndroid Build Coastguard Worker if not compmap[cond](version, ver): 137*cda5da8dSAndroid Build Coastguard Worker return False 138*cda5da8dSAndroid Build Coastguard Worker return True 139*cda5da8dSAndroid Build Coastguard Worker 140*cda5da8dSAndroid Build Coastguard Worker 141*cda5da8dSAndroid Build Coastguard Worker_provision_rx = None 142*cda5da8dSAndroid Build Coastguard Worker 143*cda5da8dSAndroid Build Coastguard Workerdef split_provision(value): 144*cda5da8dSAndroid Build Coastguard Worker """Return the name and optional version number of a provision. 145*cda5da8dSAndroid Build Coastguard Worker 146*cda5da8dSAndroid Build Coastguard Worker The version number, if given, will be returned as a `StrictVersion` 147*cda5da8dSAndroid Build Coastguard Worker instance, otherwise it will be `None`. 148*cda5da8dSAndroid Build Coastguard Worker 149*cda5da8dSAndroid Build Coastguard Worker >>> split_provision('mypkg') 150*cda5da8dSAndroid Build Coastguard Worker ('mypkg', None) 151*cda5da8dSAndroid Build Coastguard Worker >>> split_provision(' mypkg( 1.2 ) ') 152*cda5da8dSAndroid Build Coastguard Worker ('mypkg', StrictVersion ('1.2')) 153*cda5da8dSAndroid Build Coastguard Worker """ 154*cda5da8dSAndroid Build Coastguard Worker global _provision_rx 155*cda5da8dSAndroid Build Coastguard Worker if _provision_rx is None: 156*cda5da8dSAndroid Build Coastguard Worker _provision_rx = re.compile( 157*cda5da8dSAndroid Build Coastguard Worker r"([a-zA-Z_]\w*(?:\.[a-zA-Z_]\w*)*)(?:\s*\(\s*([^)\s]+)\s*\))?$", 158*cda5da8dSAndroid Build Coastguard Worker re.ASCII) 159*cda5da8dSAndroid Build Coastguard Worker value = value.strip() 160*cda5da8dSAndroid Build Coastguard Worker m = _provision_rx.match(value) 161*cda5da8dSAndroid Build Coastguard Worker if not m: 162*cda5da8dSAndroid Build Coastguard Worker raise ValueError("illegal provides specification: %r" % value) 163*cda5da8dSAndroid Build Coastguard Worker ver = m.group(2) or None 164*cda5da8dSAndroid Build Coastguard Worker if ver: 165*cda5da8dSAndroid Build Coastguard Worker ver = distutils.version.StrictVersion(ver) 166*cda5da8dSAndroid Build Coastguard Worker return m.group(1), ver 167