Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
markers.py253 linesDownload Raw Back to packaging
1# This file is dual licensed under the terms of the Apache License, Version2# 2.0, and the BSD License. See the LICENSE file in the root of this repository3# for complete details.4 5import operator6import os7import platform8import sys9from typing import Any, Callable, Dict, List, Optional, Tuple, Union10 11from ._parser import (12    MarkerAtom,13    MarkerList,14    Op,15    Value,16    Variable,17    parse_marker as _parse_marker,18)19from ._tokenizer import ParserSyntaxError20from .specifiers import InvalidSpecifier, Specifier21from .utils import canonicalize_name22 23__all__ = [24    "InvalidMarker",25    "UndefinedComparison",26    "UndefinedEnvironmentName",27    "Marker",28    "default_environment",29]30 31Operator = Callable[[str, str], bool]32 33 34class InvalidMarker(ValueError):35    """36    An invalid marker was found, users should refer to PEP 508.37    """38 39 40class UndefinedComparison(ValueError):41    """42    An invalid operation was attempted on a value that doesn't support it.43    """44 45 46class UndefinedEnvironmentName(ValueError):47    """48    A name was attempted to be used that does not exist inside of the49    environment.50    """51 52 53def _normalize_extra_values(results: Any) -> Any:54    """55    Normalize extra values.56    """57    if isinstance(results[0], tuple):58        lhs, op, rhs = results[0]59        if isinstance(lhs, Variable) and lhs.value == "extra":60            normalized_extra = canonicalize_name(rhs.value)61            rhs = Value(normalized_extra)62        elif isinstance(rhs, Variable) and rhs.value == "extra":63            normalized_extra = canonicalize_name(lhs.value)64            lhs = Value(normalized_extra)65        results[0] = lhs, op, rhs66    return results67 68 69def _format_marker(70    marker: Union[List[str], MarkerAtom, str], first: Optional[bool] = True71) -> str:72 73    assert isinstance(marker, (list, tuple, str))74 75    # Sometimes we have a structure like [[...]] which is a single item list76    # where the single item is itself it's own list. In that case we want skip77    # the rest of this function so that we don't get extraneous () on the78    # outside.79    if (80        isinstance(marker, list)81        and len(marker) == 182        and isinstance(marker[0], (list, tuple))83    ):84        return _format_marker(marker[0])85 86    if isinstance(marker, list):87        inner = (_format_marker(m, first=False) for m in marker)88        if first:89            return " ".join(inner)90        else:91            return "(" + " ".join(inner) + ")"92    elif isinstance(marker, tuple):93        return " ".join([m.serialize() for m in marker])94    else:95        return marker96 97 98_operators: Dict[str, Operator] = {99    "in": lambda lhs, rhs: lhs in rhs,100    "not in": lambda lhs, rhs: lhs not in rhs,101    "<": operator.lt,102    "<=": operator.le,103    "==": operator.eq,104    "!=": operator.ne,105    ">=": operator.ge,106    ">": operator.gt,107}108 109 110def _eval_op(lhs: str, op: Op, rhs: str) -> bool:111    try:112        spec = Specifier("".join([op.serialize(), rhs]))113    except InvalidSpecifier:114        pass115    else:116        return spec.contains(lhs, prereleases=True)117 118    oper: Optional[Operator] = _operators.get(op.serialize())119    if oper is None:120        raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.")121 122    return oper(lhs, rhs)123 124 125def _normalize(*values: str, key: str) -> Tuple[str, ...]:126    # PEP 685 – Comparison of extra names for optional distribution dependencies127    # https://peps.python.org/pep-0685/128    # > When comparing extra names, tools MUST normalize the names being129    # > compared using the semantics outlined in PEP 503 for names130    if key == "extra":131        return tuple(canonicalize_name(v) for v in values)132 133    # other environment markers don't have such standards134    return values135 136 137def _evaluate_markers(markers: MarkerList, environment: Dict[str, str]) -> bool:138    groups: List[List[bool]] = [[]]139 140    for marker in markers:141        assert isinstance(marker, (list, tuple, str))142 143        if isinstance(marker, list):144            groups[-1].append(_evaluate_markers(marker, environment))145        elif isinstance(marker, tuple):146            lhs, op, rhs = marker147 148            if isinstance(lhs, Variable):149                environment_key = lhs.value150                lhs_value = environment[environment_key]151                rhs_value = rhs.value152            else:153                lhs_value = lhs.value154                environment_key = rhs.value155                rhs_value = environment[environment_key]156 157            lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key)158            groups[-1].append(_eval_op(lhs_value, op, rhs_value))159        else:160            assert marker in ["and", "or"]161            if marker == "or":162                groups.append([])163 164    return any(all(item) for item in groups)165 166 167def format_full_version(info: "sys._version_info") -> str:168    version = "{0.major}.{0.minor}.{0.micro}".format(info)169    kind = info.releaselevel170    if kind != "final":171        version += kind[0] + str(info.serial)172    return version173 174 175def default_environment() -> Dict[str, str]:176    iver = format_full_version(sys.implementation.version)177    implementation_name = sys.implementation.name178    return {179        "implementation_name": implementation_name,180        "implementation_version": iver,181        "os_name": os.name,182        "platform_machine": platform.machine(),183        "platform_release": platform.release(),184        "platform_system": platform.system(),185        "platform_version": platform.version(),186        "python_full_version": platform.python_version(),187        "platform_python_implementation": platform.python_implementation(),188        "python_version": ".".join(platform.python_version_tuple()[:2]),189        "sys_platform": sys.platform,190    }191 192 193class Marker:194    def __init__(self, marker: str) -> None:195        # Note: We create a Marker object without calling this constructor in196        #       packaging.requirements.Requirement. If any additional logic is197        #       added here, make sure to mirror/adapt Requirement.198        try:199            self._markers = _normalize_extra_values(_parse_marker(marker))200            # The attribute `_markers` can be described in terms of a recursive type:201            # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]]202            #203            # For example, the following expression:204            # python_version > "3.6" or (python_version == "3.6" and os_name == "unix")205            #206            # is parsed into:207            # [208            #     (<Variable('python_version')>, <Op('>')>, <Value('3.6')>),209            #     'and',210            #     [211            #         (<Variable('python_version')>, <Op('==')>, <Value('3.6')>),212            #         'or',213            #         (<Variable('os_name')>, <Op('==')>, <Value('unix')>)214            #     ]215            # ]216        except ParserSyntaxError as e:217            raise InvalidMarker(str(e)) from e218 219    def __str__(self) -> str:220        return _format_marker(self._markers)221 222    def __repr__(self) -> str:223        return f"<Marker('{self}')>"224 225    def __hash__(self) -> int:226        return hash((self.__class__.__name__, str(self)))227 228    def __eq__(self, other: Any) -> bool:229        if not isinstance(other, Marker):230            return NotImplemented231 232        return str(self) == str(other)233 234    def evaluate(self, environment: Optional[Dict[str, str]] = None) -> bool:235        """Evaluate a marker.236 237        Return the boolean from evaluating the given marker against the238        environment. environment is an optional argument to override all or239        part of the determined environment.240 241        The environment is determined from the current Python process.242        """243        current_environment = default_environment()244        current_environment["extra"] = ""245        if environment is not None:246            current_environment.update(environment)247            # The API used to allow setting extra to None. We need to handle this248            # case for backwards compatibility.249            if current_environment["extra"] is None:250                current_environment["extra"] = ""251 252        return _evaluate_markers(self._markers, current_environment)253 
codekingpro/portable-devtools · Team Ai