예제 #1
0
    def get_wheel_list_to_install(self):
        os.chdir(self.curdir)

        in_bin = os.path.relpath(self.in_bin, start=self.curdir)

        our_whl_path = os.path.join(in_bin, "ourwheel")
        our_wheels = []
        our_wheels_set = set()
        if os.path.exists(our_whl_path):
            our_wheels = [
                os.path.join(our_whl_path, whl)
                for whl in os.listdir(our_whl_path) if whl.endswith('.whl')
            ]
            our_wheels_set = set(
                [parse_wheel_filename(whl).project for whl in our_wheels])

        ext_whl_path = os.path.join(self.in_bin, "extwheel")
        ext_wheels = []
        ext_src = []
        if os.path.exists(ext_whl_path):
            ext_wheels = [
                os.path.join(in_bin, "extwheel", whl)
                for whl in os.listdir(os.path.join(self.in_bin, "extwheel"))
                if whl.endswith('.whl')
                and parse_wheel_filename(whl).project not in our_wheels_set
            ]
            ext_src = [
                os.path.join(in_bin, "extwheel", whl)
                for whl in os.listdir(os.path.join(self.in_bin, "extwheel"))
                if whl.endswith('tar.gz') or whl.endswith('tar.bz2')
            ]

        return ext_wheels + our_wheels + ext_src
예제 #2
0
def test_parse_wheel_filename_path() -> None:
    parsed = parse_wheel_filename('dist/foo-1.0-py3-none-any.whl')
    assert parsed == ParsedWheelFilename(
        project='foo',
        version='1.0',
        build=None,
        python_tags=['py3'],
        abi_tags=['none'],
        platform_tags=['any'],
    )
    assert str(parsed) == 'foo-1.0-py3-none-any.whl'
예제 #3
0
def test_parse_wheel_filename_path() -> None:
    parsed = parse_wheel_filename("dist/foo-1.0-py3-none-any.whl")
    assert parsed == ParsedWheelFilename(
        project="foo",
        version="1.0",
        build=None,
        python_tags=["py3"],
        abi_tags=["none"],
        platform_tags=["any"],
    )
    assert str(parsed) == "foo-1.0-py3-none-any.whl"
예제 #4
0
def is_compatible(package):
    """
    Check whether the given python package is a wheel compatible with the
    current platform and python interpreter.

    Compatibility is based on https://www.python.org/dev/peps/pep-0425/
    """
    try:
        w = parse_wheel_filename(package)
        for systag in tags.sys_tags():
            for tag in w.tag_triples():
                if systag in tags.parse_tag(tag):
                    return True
    except InvalidFilenameError:
        return False
예제 #5
0
 def from_wheel(cls, path: Union[str, os.PathLike]) -> 'WheelContents':
     """ Construct a `WheelContents` from the wheel at the given path """
     whlname = parse_wheel_filename(path)
     with open(path, 'rb') as fp, ZipFile(fp) as zf:
         dist_info_dir, data_dir = find_wheel_dirs(
             zf.namelist(),
             whlname.project,
             whlname.version,
         )
         if data_dir is None:
             data_dir = f'{whlname.project}-{whlname.version}.data'
         wc = cls(dist_info_dir=dist_info_dir, data_dir=data_dir)
         try:
             wheel_info = zf.getinfo(f'{dist_info_dir}/WHEEL')
         except KeyError:
             raise WheelValidationError('No WHEEL file in wheel')
         with zf.open(wheel_info) as wf:
             for line in TextIOWrapper(wf, 'utf-8'):
                 m = ROOT_IS_PURELIB_RGX.fullmatch(line)
                 if m:
                     rip = m.group(1)
                     if rip.lower() == 'true':
                         wc.root_is_purelib = True
                     elif rip.lower() == 'false':
                         wc.root_is_purelib = False
                     else:
                         raise WheelValidationError(
                             f'Invalid Root-Is-Purelib value in WHEEL'
                             f' file: {rip!r}')
                     break
             else:
                 raise WheelValidationError(
                     'Root-Is-Purelib header not found in WHEEL file')
         try:
             record_info = zf.getinfo(f'{dist_info_dir}/RECORD')
         except KeyError:
             raise WheelValidationError('No RECORD file in wheel')
         with zf.open(record_info) as rf:
             wc.add_record_file(TextIOWrapper(rf, 'utf-8', newline=''))
     wc.validate_tree()
     return wc
예제 #6
0
def test_bad_path() -> None:
    with pytest.raises(InvalidFilenameError) as excinfo:
        parse_wheel_filename(os.path.join('dist', 'foo-0.1.whl'))
    assert excinfo.value.filename == 'foo-0.1.whl'
    assert str(excinfo.value) == "Invalid wheel filename: 'foo-0.1.whl'"
예제 #7
0
def test_bad_filename(filename: str) -> None:
    with pytest.raises(InvalidFilenameError) as excinfo:
        parse_wheel_filename(filename)
    assert excinfo.value.filename == filename
    assert str(excinfo.value) == f'Invalid wheel filename: {filename!r}'
예제 #8
0
def test_parse_wheel_filename(filename: str,
                              expected: ParsedWheelFilename) -> None:
    parsed = parse_wheel_filename(filename)
    assert parsed == expected
    assert str(parsed) == filename
예제 #9
0
 def __init__(self, path):
     self.path = Path(path)
     self.parsed_filename = parse_wheel_filename(self.path)
     self.fp = None
     self.zipfile = None
     self._dist_info = None
예제 #10
0
def update_data_from_pypi():
    for package in tqdm(packages, unit="packages"):
        resp = http.request("GET", f"https://pypi.org/pypi/{package}/json")

        if resp.status != 200:
            continue
        try:
            resp = json.loads(resp.data.decode("utf-8"))
        except Exception:
            continue
        try:
            version = Version(resp["info"]["version"])
        except InvalidVersion:  # The latest release has an invalid version, skip
            continue
        latest_version = max(to_versions(resp["releases"].keys()))

        # Favor pre-releases over non-pre-releases
        if version < latest_version:
            version = latest_version
            new_resp = http.request(
                "GET",
                f"https://pypi.org/pypi/{package}/{latest_version}/json")
            if new_resp.status == 200:
                resp = json.loads(new_resp.data.decode("utf-8"))

        # Get the exact string for the version that we found
        for strv in resp["releases"]:
            try:
                if Version(strv) == version:
                    str_version = strv
                    break
            except InvalidVersion:
                continue
        else:
            raise ValueError("???")

        # Check to see if we already have this version or not
        with closing(db.cursor()) as cur:
            cur.execute(
                "SELECT name FROM packages WHERE name = ? AND version = ?;",
                (package, str_version),
            )
            if cur.fetchone():
                continue

        # If we don't have 'requires_dist' information install
        # locally and investigate the installed package
        if False and resp["info"]["requires_dist"] is None:
            new_resp = get_metadata_by_install(package, resp)
            if new_resp is not None:
                resp = new_resp

        requires_python = resp["info"]["requires_python"] or ""
        urequires_dist = [
            normalize_requires_dist(x)
            for x in resp["info"]["requires_dist"] or []
        ]
        urequires_dist = sorted(urequires_dist, key=requires_dist_sort_key)

        requires_dist = {"specifiers": [], "dists": []}
        requires_extras = {}
        yanked = []

        releases = resp["releases"][str_version]
        uploaded_at = None if not releases else min(x["upload_time"]
                                                    for x in releases)
        wheel_filenames = [
            x["filename"] for x in releases if x["filename"].endswith(".whl")
        ]
        has_binary_wheel = False

        for filename in wheel_filenames:
            try:
                whl = parse_wheel_filename(filename)
            except InvalidFilenameError:
                continue
            python_tags, abi_tags, platform_tags = (
                whl.python_tags,
                whl.abi_tags,
                whl.platform_tags,
            )

            for wheel_data in itertools.product(python_tags, abi_tags,
                                                platform_tags):
                py, abi, plat = wheel_data
                db.execute(
                    """
                  INSERT INTO wheels (
                    name, version, filename, python, abi, platform
                  ) VALUES (?, ?, ?, ?, ?, ?);
                """,
                    (package, str_version, filename, py, abi, plat),
                )

            if abi_tags == ["none"] and platform_tags == ["any"]:
                continue

            has_binary_wheel = True

        db.execute(
            """
          INSERT OR IGNORE INTO packages (
            name, version, requires_python, has_binary_wheel, uploaded_at
          ) VALUES (?, ?, ?, ?, ?);
        """,
            (package, str_version, requires_python, has_binary_wheel,
             uploaded_at),
        )
        db.commit()

        for req in urequires_dist:
            extras = get_extras(req)
            req_no_specifiers = dist_from_requires_dist(req)
            specifier = specifier_from_requires_dist(req).replace(
                req_no_specifiers + " ", "", 1)
            if extras:
                for extra in extras:
                    db.execute(
                        """
                        INSERT OR IGNORE INTO deps (
                          name,
                          version,
                          dep_name,
                          dep_specifier,
                          extra
                        ) VALUES (?, ?, ?, ?, ?);
                    """,
                        (package, str_version, req_no_specifiers, specifier,
                         extra),
                    )
            else:
                db.execute(
                    """
                    INSERT OR IGNORE INTO deps (
                      name,
                      version,
                      dep_name,
                      dep_specifier
                    ) VALUES (?, ?, ?, ?);
                """,
                    (package, str_version, req_no_specifiers, specifier),
                )

        requires_dist["dists"] = sorted(set(requires_dist["dists"]))
        for extra, extra_info in list(requires_extras.items()):
            requires_extras[extra]["dists"] = sorted(set(extra_info["dists"]))

        for relv, downloads in resp["releases"].items():
            for download in downloads:
                if download["yanked"]:
                    yanked.append(relv)
                    break

        yanked = sorted_versions(set(yanked))
        if yanked:
            db.execute(
                "UPDATE packages SET yanked=1 WHERE name=? AND version=?;",
                (package, str_version),
            )

        db.commit()
예제 #11
0
def wheel_sort_key(filename):
    """
    Returns a sort key for the given wheel filename that will be used to select
    the "preferred" or "default" wheel to display for a given project &
    version.

    General rules:

    - It is assumed that only wheels for the same version of the same project
      are ever compared, and so those parts of the filename are ignored.

    - Prefer more general wheels (e.g., pure Python) to more specific (e.g.,
      platform specific).

        - Prefer compability with more versions to fewer.
        - "any" is the most preferred platform.
        - "none" is the most preferred ABI.

    - Prefer compability with higher versions to lower.

    - Unrecognized values are ignored if possible, otherwise sorted at the
      bottom.

    Specific, arbitrary preferences:

    - Sort by Python tag first, then platform tag, then ABI tag, then
      "pyver-abi-platform" string (as a tiebreaker), then build tag.

    - Filenames that can't be parsed sort the lowest and sort relative to each
      other based on filename.

    - Python implementations: py (generic) > cp (CPython) > pp (PyPy) > jy
      (Jython) > ip (IronPython) > everything else

    - Platforms: any > manylinux > Linux > Windows > Mac OS X > everything else
    """

    try:
        whlname = parse_wheel_filename(filename)
    except ValueError:
        return (0, filename)

    if whlname.build is not None:
        n = re.fullmatch(r"(?P<buildno>\d+)(?P<buildstr>[^-]*)", whlname.build)
        if not n:
            return (0, filename)
        build_rank = (int(n.group("buildno")), n.group("buildstr"))
    else:
        build_rank = (-1, "")

    pyver_rank = []
    for py in whlname.python_tags:
        n = re.fullmatch(r"(\w+?)(\d[\d_]*)", py)
        if not n:
            return (0, filename)
        pyver_rank.append((
            PYTHON_PREFERENCES[n.group(1)],
            VersionNoDot(n.group(2)),
        ))
    pyver_rank.sort(reverse=True)

    ### TODO: distlib expects wheels to have only one ABI tag in their filename
    ### while wheel_inspect does not.  If the latter turns out to be the
    ### correct approach, adjust this code to handle multiple tags.
    abi = whlname.abi_tags[0]
    ### TODO: Should abi3 be given some rank?
    if abi == "none":
        abi_rank = (1, )
    else:
        n = re.fullmatch(r"(\wp)(\d+)(\w*)", abi)
        if n:
            py_imp, py_ver, flags = n.groups()
            abi_rank = (0, PYTHON_PREFERENCES[py_imp], VersionNoDot(py_ver),
                        flags)
        else:
            abi_rank = (0, -1, -1, "")

    platform_rank = []
    for plat in whlname.platform_tags:
        for rank, rgx in enumerate([
                r"macosx_10_(?P<version>\d+)_(?P<arch>\w+)",
                "macosx",
                "win32",
                "win64",
                "win_amd64",
                r"linux_(?P<arch>\w+)",
                r"manylinux(?P<version>\d+)_(?P<arch>\w+)",
                "any",
        ]):
            n = re.fullmatch(rgx, plat)
            if n:
                d = n.groupdict()
                version = d.get("version")
                version = int(version) if version is not None else -1
                arch = ARCH_PREFERENCES[d.get("arch")]
                platform_rank.append((rank, version, arch))
                break
        else:
            ### TODO: Don't discard
            pass
    platform_rank.sort(reverse=True)

    tiebreaker = "{}-{}-{}".format(
        ".".join(whlname.python_tags),
        ".".join(whlname.abi_tags),
        ".".join(whlname.platform_tags),
    )

    return (1, pyver_rank, platform_rank, abi_rank, tiebreaker, build_rank)
    def generate_install(self):
        os.chdir(self.curdir)
        os.chdir(self.output_dir)
        lines = []

        scmd = '%s\\vs_BuildTools.exe --layout %s/%s --lang en-US %s ' % (n(
            self.bin_dir), n(
                self.bin_dir), self.msvc.version, self.msvc.get_add_line())
        lines.append(scmd)

        scmd = '%s\\%s\\vs_BuildTools.exe -p  %s ' % (n(
            self.bin_dir), self.msvc.version, self.msvc.get_add_line())
        lines.append(scmd)

        # scmd = R'%s\wdk\wdksetup.exe /q ' % (self.bin_dir.replace('/', '\\') )
        # lines.append(scmd)

        scmd = fR'{self.bin_dirw}\wdk10\wdksetup.exe /q '
        lines.append(scmd)

        scmd = fR'{self.bin_dirw}\wdk8.1\wdksetup.exe /q '
        lines.append(scmd)

        scmd = fR'{self.bin_dirw}\msvc2013\vs_ultimate.exe /NoRestart /Passive  '
        lines.append(scmd)

        scmd = fR'{self.bin_dirw}\wsdk8\sdksetup.exe /q '
        lines.append(scmd)

        self.lines2bat("10-install-msvc", lines)

        # do not append installs
        lines = []

        scmd = '%s\\%s /passive TargetDir=%s ' % (n(
            self.bin_dir), self.python.artifact_name(), self.pythondir)
        lines.append(scmd)

        scmd = "%s\\python -m pip install wheel cython clcache " % (
            self.pythondir)
        lines.append(scmd)

        for ut_ in self.utilities + [self.tess, self.imagick]:
            install_dir = self.pythondir
            try:
                install_dir = os.path.join(self.buildroot, ut_.version)
            except:
                pass
            try:
                install_dir = ut_.path
            except:
                pass
            # if 'magick' in ut_.url:
            #     wtf = 1
            lines += ut_.install_me_lines(self.bin_dir, install_dir)

        our_wheels = [
            n(os.path.join(self.ourwheel_dir, whl))
            for whl in os.listdir(self.ourwheel_dir) if whl.endswith('.whl')
        ]
        our_wheels_set = set([
            parse_wheel_filename(whl.replace('\\', '/')).project
            for whl in our_wheels
        ])

        ext_wheels = [
            n(os.path.join(self.extwheel_dir, whl))
            for whl in os.listdir(self.extwheel_dir) if whl.endswith('.whl')
            and parse_wheel_filename(whl).project not in our_wheels_set
        ]
        ext_src = [
            n(os.path.join(self.extwheel_dir, whl))
            for whl in os.listdir(self.extwheel_dir)
            if whl.endswith('tar.gz') or whl.endswith('tar.bz2')
        ]

        for wheel in ext_wheels + our_wheels:
            scmd = '%s\\python -m pip install --upgrade   %s ' % (
                self.pythondir, wheel)  # --force-reinstall
            lines.append(scmd)

        self.lines2bat("11-install", lines)
        pass