xref: /aosp_15_r20/prebuilts/build-tools/common/py3-stdlib/distutils/versionpredicate.py (revision cda5da8d549138a6648c5ee6d7a49cf8f4a657be)
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