예제 #1
0
def clean_requires_python(candidates):
    """Get a cleaned list of all the candidates with valid specifiers in the
    `requires_python` attributes."""
    all_candidates = []
    sys_version = ".".join(map(str, sys.version_info[:3]))
    from pipenv.vendor.packaging.version import parse as parse_version

    py_version = parse_version(
        os.environ.get("PIP_PYTHON_VERSION", sys_version))
    for c in candidates:
        requires_python = _get_requires_python(c)
        if requires_python:
            # Old specifications had people setting this to single digits
            # which is effectively the same as '>=digit,<digit+1'
            if requires_python.isdigit():
                requires_python = ">={0},<{1}".format(requires_python,
                                                      int(requires_python) + 1)
            try:
                specifierset = SpecifierSet(requires_python)
            except InvalidSpecifier:
                continue
            else:
                if not specifierset.contains(py_version):
                    continue
        all_candidates.append(c)
    return all_candidates
예제 #2
0
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
예제 #3
0
def normalize_specifier_set(specs):
    # type: (Union[str, SpecifierSet]) -> Optional[Set[Specifier]]
    """Given a specifier set, a string, or an iterable, normalize the
    specifiers.

    .. note:: This function exists largely to deal with ``pyzmq`` which handles
        the ``requires_python`` specifier incorrectly, using ``3.7*`` rather than
        the correct form of ``3.7.*``.  This workaround can likely go away if
        we ever introduce enforcement for metadata standards on PyPI.

    :param Union[str, SpecifierSet] specs: Supplied specifiers to normalize
    :return: A new set of specifiers or specifierset
    :rtype: Union[Set[Specifier], :class:`~packaging.specifiers.SpecifierSet`]
    """
    if not specs:
        return None
    if isinstance(specs, set):
        return specs
    # when we aren't dealing with a string at all, we can normalize this as usual
    elif not isinstance(specs, str):
        return {_format_pyspec(spec) for spec in specs}
    spec_list = []
    for spec in specs.split(","):
        spec = spec.strip()
        if spec.endswith(".*"):
            spec = spec[:-2]
        spec = spec.rstrip("*")
        spec_list.append(spec)
    return normalize_specifier_set(SpecifierSet(",".join(spec_list)))
예제 #4
0
파일: parser.py 프로젝트: wwjiang007/pipenv
 def parse(self):
     """
     Parse a Pipfile.lock (as seen in pipenv)
     :return:
     """
     try:
         data = json.loads(self.obj.content, object_pairs_hook=OrderedDict)
         if data:
             for package_type in ['default', 'develop']:
                 if package_type in data:
                     for name, meta in data[package_type].items():
                         # skip VCS dependencies
                         if 'version' not in meta:
                             continue
                         specs = meta['version']
                         hashes = meta['hashes']
                         self.obj.dependencies.append(
                             Dependency(
                                 name=name,
                                 specs=SpecifierSet(specs),
                                 dependency_type=filetypes.pipfile_lock,
                                 hashes=hashes,
                                 line=''.join([name, specs]),
                                 section=package_type))
     except ValueError:
         pass
예제 #5
0
def fix_requires_python_marker(requires_python):
    from pipenv.vendor.packaging.requirements import Requirement as PackagingRequirement

    marker_str = ""
    if any(
            requires_python.startswith(op)
            for op in Specifier._operators.keys()):
        spec_dict = defaultdict(set)
        # We are checking first if we have  leading specifier operator
        # if not, we can assume we should be doing a == comparison
        specifierset = list(SpecifierSet(requires_python))
        # for multiple specifiers, the correct way to represent that in
        # a specifierset is `Requirement('fakepkg; python_version<"3.0,>=2.6"')`
        marker_key = Variable("python_version")
        for spec in specifierset:
            operator, val = spec._spec
            cleaned_val = Value(val).serialize().replace('"', "")
            spec_dict[Op(operator).serialize()].add(cleaned_val)
        marker_str = " and ".join([
            "{0}{1}'{2}'".format(marker_key.serialize(), op, ",".join(vals))
            for op, vals in spec_dict.items()
        ])
    marker_to_add = PackagingRequirement(
        "fakepkg; {0}".format(marker_str)).marker
    return marker_to_add
예제 #6
0
def validate_specifiers(instance, attr_, value):
    if value == "":
        return True
    try:
        SpecifierSet(value)
    except (InvalidMarker, InvalidSpecifier):
        raise ValueError("Invalid Specifiers {0}".format(value))
예제 #7
0
def create_specifierset(spec=None):
    # type: (Optional[str]) -> SpecifierSet
    if isinstance(spec, SpecifierSet):
        return spec
    elif isinstance(spec, (set, list, tuple)):
        spec = " and ".join(spec)
    if spec is None:
        spec = ""
    return SpecifierSet(spec)
예제 #8
0
def clean_requires_python(candidates):
    """Get a cleaned list of all the candidates with valid specifiers in the `requires_python` attributes."""
    all_candidates = []
    py_version = parse_version(os.environ.get('PIP_PYTHON_VERSION', '.'.join(map(str, sys.version_info[:3]))))
    for c in candidates:
        if getattr(c, "requires_python", None):
            # Old specifications had people setting this to single digits
            # which is effectively the same as '>=digit,<digit+1'
            if len(c.requires_python) == 1 and c.requires_python in ("2", "3"):
                c.requires_python = '>={0},<{1!s}'.format(c.requires_python, int(c.requires_python) + 1)
            try:
                specifierset = SpecifierSet(c.requires_python)
            except InvalidSpecifier:
                continue
            else:
                if not specifierset.contains(py_version):
                    continue
        all_candidates.append(c)
    return all_candidates
예제 #9
0
파일: safety.py 프로젝트: wwjiang007/pipenv
def check(packages, key, db_mirror, cached, ignore_ids, proxy):
    key = key if key else os.environ.get("SAFETY_API_KEY", False)
    db = fetch_database(key=key, db=db_mirror, cached=cached, proxy=proxy)
    db_full = None
    vulnerable_packages = frozenset(db.keys())
    vulnerable = []
    for pkg in packages:
        # Ignore recursive files not resolved
        if isinstance(pkg, RequirementFile):
            continue

        # normalize the package name, the safety-db is converting underscores to dashes and uses
        # lowercase
        name = pkg.key.replace("_", "-").lower()

        if name in vulnerable_packages:
            # we have a candidate here, build the spec set
            for specifier in db[name]:
                spec_set = SpecifierSet(specifiers=specifier)
                if spec_set.contains(pkg.version):
                    if not db_full:
                        db_full = fetch_database(full=True, key=key, db=db_mirror, cached=cached, proxy=proxy)
                    for data in get_vulnerabilities(pkg=name, spec=specifier, db=db_full):
                        vuln_id = data.get("id").replace("pyup.io-", "")
                        cve_id = data.get("cve")
                        if cve_id:
                            cve_id = cve_id.split(",")[0].strip()
                        if vuln_id and vuln_id not in ignore_ids:
                            cve_meta = db_full.get("$meta", {}).get("cve", {}).get(cve_id, {})
                            vulnerable.append(
                                Vulnerability(
                                    name=name,
                                    spec=specifier,
                                    version=pkg.version,
                                    advisory=data.get("advisory"),
                                    vuln_id=vuln_id,
                                    cvssv2=cve_meta.get("cvssv2", None),
                                    cvssv3=cve_meta.get("cvssv3", None),
                                )
                            )
    return vulnerable
예제 #10
0
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))
예제 #11
0
 def from_info(cls, info):
     # type: ("PackageInfo") -> "Dependency"
     marker_str = ""
     specset_str, py_version_str = "", ""
     if info.requires_python:
         # XXX: Some markers are improperly formatted -- we already handle most cases
         # XXX: but learned about new broken formats, such as
         # XXX: python_version in "2.6 2.7 3.2 3.3" (note the lack of commas)
         # XXX: as a marker on a dependency of a library called 'pickleshare'
         # XXX: Some packages also have invalid markers with stray characters,
         # XXX: such as 'algoliasearch'
         try:
             marker = marker_from_specifier(info.requires_python)
         except Exception:
             marker_str = ""
         else:
             if not marker or not marker._markers:
                 marker_str = ""
             else:
                 marker_str = "{0!s}".format(marker)
     req_str = "{0}=={1}".format(info.name, info.version)
     if marker_str:
         req_str = "{0}; {1}".format(req_str, marker_str)
     req = PackagingRequirement(req_str)
     requires_python_str = (
         info.requires_python if info.requires_python is not None else ""
     )
     if req.specifier:
         specset_str = str(req.specifier)
     if requires_python_str:
         py_version_str = requires_python_str
     return cls(
         name=info.name,
         specifier=req.specifier,
         extras=tuple(sorted(set(req.extras)))
         if req.extras is not None
         else req.extras,
         requirement=req,
         from_extras=None,
         python_version=SpecifierSet(requires_python_str),
         markers=None,
         parent=None,
         specset_str=specset_str,
         python_version_str=py_version_str,
         marker_str=marker_str,
     )
예제 #12
0
파일: parser.py 프로젝트: wwjiang007/pipenv
 def parse(self):
     """
     Parse a Pipfile (as seen in pipenv)
     :return:
     """
     try:
         data = toml.loads(self.obj.content, _dict=OrderedDict)
         if data:
             for package_type in ['packages', 'dev-packages']:
                 if package_type in data:
                     for name, specs in data[package_type].items():
                         # skip on VCS dependencies
                         if not isinstance(specs, str):
                             continue
                         if specs == '*':
                             specs = ''
                         self.obj.dependencies.append(
                             Dependency(name=name,
                                        specs=SpecifierSet(specs),
                                        dependency_type=filetypes.pipfile,
                                        line=''.join([name, specs]),
                                        section=package_type))
     except (toml.TomlDecodeError, IndexError) as e:
         pass
예제 #13
0
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