def get_specset(marker_list): # type: (List) -> Optional[SpecifierSet] specset = set() _last_str = "and" for marker_parts in marker_list: if isinstance(marker_parts, str): _last_str = marker_parts # noqa else: specset.update(_get_specifiers_from_markers(marker_parts)) specifiers = SpecifierSet() specifiers._specs = frozenset(specset) return specifiers
def _get_specs(specset): if specset is None: return if is_instance(specset, Specifier): new_specset = SpecifierSet() specs = set() specs.add(specset) new_specset._specs = frozenset(specs) specset = new_specset if isinstance(specset, str): specset = SpecifierSet(specset) result = [] for spec in set(specset): version = spec.version op = spec.operator if op in ("in", "not in"): versions = version.split(",") op = "==" if op == "in" else "!=" for ver in versions: result.append((op, _tuplize_version(ver.strip()))) else: result.append((spec.operator, _tuplize_version(spec.version))) return sorted(result, key=operator.itemgetter(1))
def parse_marker_dict(marker_dict): op = marker_dict["op"] lhs = marker_dict["lhs"] rhs = marker_dict["rhs"] # This is where the spec sets for each side land if we have an "or" operator side_spec_list = [] side_markers_list = [] finalized_marker = "" # And if we hit the end of the parse tree we use this format string to make a marker format_string = "{lhs} {op} {rhs}" specset = SpecifierSet() specs = set() # Essentially we will iterate over each side of the parsed marker if either one is # A mapping instance (i.e. a dictionary) and recursively parse and reduce the specset # Union the "and" specs, intersect the "or"s to find the most appropriate range if any(issubclass(type(side), Mapping) for side in (lhs, rhs)): for side in (lhs, rhs): side_specs = set() side_markers = set() if issubclass(type(side), Mapping): merged_side_specs, merged_side_markers = parse_marker_dict(side) side_specs.update(merged_side_specs) side_markers.update(merged_side_markers) else: marker = _ensure_marker(side) marker_parts = getattr(marker, "_markers", []) if marker_parts[0][0].value == "python_version": side_specs |= set(get_specset(marker_parts)) else: side_markers.add(str(marker)) side_spec_list.append(side_specs) side_markers_list.append(side_markers) if op == "and": # When we are "and"-ing things together, it probably makes the most sense # to reduce them here into a single PySpec instance specs = reduce(lambda x, y: set(x) | set(y), side_spec_list) markers = reduce(lambda x, y: set(x) | set(y), side_markers_list) if not specs and not markers: return specset, finalized_marker if markers and isinstance(markers, (tuple, list, Set)): finalized_marker = Marker(" and ".join([m for m in markers if m])) elif markers: finalized_marker = str(markers) specset._specs = frozenset(specs) return specset, finalized_marker # Actually when we "or" things as well we can also just turn them into a reduced # set using this logic now sides = reduce(lambda x, y: set(x) & set(y), side_spec_list) finalized_marker = " or ".join( [normalize_marker_str(m) for m in side_markers_list] ) specset._specs = frozenset(sorted(sides)) return specset, finalized_marker else: # At the tip of the tree we are dealing with strings all around and they just need # to be smashed together specs = set() if lhs == "python_version": format_string = "{lhs}{op}{rhs}" marker = Marker(format_string.format(**marker_dict)) marker_parts = getattr(marker, "_markers", []) _set = get_specset(marker_parts) if _set: specs |= set(_set) specset._specs = frozenset(specs) return specset, finalized_marker