Skip to content

Commit

Permalink
Various cleanups prompted by Benjy's review.
Browse files Browse the repository at this point in the history
  • Loading branch information
jsirois committed Mar 16, 2024
1 parent 038bcbb commit 20fa1de
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 106 deletions.
2 changes: 1 addition & 1 deletion pex/finders.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
from pex.common import is_python_script, open_zip, safe_mkdtemp
from pex.dist_metadata import Distribution, DistributionType, EntryPoint
from pex.pep_376 import InstalledWheel
from pex.pep_427 import Wheel
from pex.pep_503 import ProjectName
from pex.typing import TYPE_CHECKING, cast
from pex.wheel import Wheel

if TYPE_CHECKING:
from typing import Dict, Iterable, Optional
Expand Down
29 changes: 10 additions & 19 deletions pex/pep_376.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from pex.typing import TYPE_CHECKING, cast
from pex.util import CacheHelper
from pex.venv.virtualenv import Virtualenv
from pex.wheel import WHEEL
from pex.wheel import WHEEL, WheelMetadataLoadError

if TYPE_CHECKING:
from typing import Callable, Iterable, Iterator, Optional, Protocol, Text, Tuple, Union
Expand Down Expand Up @@ -142,20 +142,11 @@ def denormalized_path(
size = attr.ib(default=None) # type: Optional[int]


class InstalledWheelError(Exception):
pass


class LoadError(InstalledWheelError):
"""Indicates an installed wheel was not loadable at a particular path."""


class ReinstallError(InstalledWheelError):
"""Indicates an error re-installing an installed wheel."""


@attr.s(frozen=True)
class InstalledWheel(object):
class LoadError(Exception):
"""Indicates an installed wheel was not loadable at a particular path."""

_LAYOUT_JSON_FILENAME = ".layout.json"

@classmethod
Expand Down Expand Up @@ -201,20 +192,20 @@ def load(cls, prefix_dir):
with open(layout_file) as fp:
layout = json.load(fp)
except (IOError, OSError) as e:
raise LoadError(
raise cls.LoadError(
"Failed to load an installed wheel layout from {layout_file}: {err}".format(
layout_file=layout_file, err=e
)
)
if not isinstance(layout, dict):
raise LoadError(
raise cls.LoadError(
"The installed wheel layout file at {layout_file} must contain a single top-level "
"object, found: {value}.".format(layout_file=layout_file, value=layout)
)
stash_dir = layout.get("stash_dir")
record_relpath = layout.get("record_relpath")
if not stash_dir or not record_relpath:
raise LoadError(
raise cls.LoadError(
"The installed wheel layout file at {layout_file} must contain an object with both "
"`stash_dir` and `record_relpath` attributes, found: {value}".format(
layout_file=layout_file, value=layout
Expand All @@ -226,11 +217,11 @@ def load(cls, prefix_dir):
# N.B.: Caching root_is_purelib was not part of the original InstalledWheel layout data; so
# we materialize the property if needed to support older installed wheel chroots.
root_is_purelib = layout.get("root_is_purelib")
if type(root_is_purelib) is not bool:
if root_is_purelib is None:
try:
wheel = WHEEL.load(prefix_dir)
except WHEEL.LoadError as e:
raise LoadError(
except WheelMetadataLoadError as e:
raise cls.LoadError(
"Failed to determine if installed wheel at {location} is platform-specific: "
"{err}".format(location=prefix_dir, err=e)
)
Expand Down
74 changes: 2 additions & 72 deletions pex/pep_427.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@
from pex import pex_warnings
from pex.common import chmod_plus_x, is_pyc_file, iter_copytree, open_zip, safe_open, touch
from pex.compatibility import commonpath, get_stdout_bytes_buffer
from pex.dist_metadata import DistMetadata, Distribution, MetadataFiles, ProjectNameAndVersion
from pex.dist_metadata import Distribution, ProjectNameAndVersion
from pex.enum import Enum
from pex.interpreter import PythonInterpreter
from pex.pep_376 import InstalledFile, InstalledWheel, Record
from pex.pep_503 import ProjectName
from pex.typing import TYPE_CHECKING, cast
from pex.wheel import WHEEL
from pex.wheel import Wheel

if TYPE_CHECKING:
from typing import ( # noqa
Expand Down Expand Up @@ -158,76 +158,6 @@ def install_wheel_interpreter(
)


class WheelLoadError(WheelError):
"""Indicates loading a wheel from disk."""


@attr.s(frozen=True)
class Wheel(object):
@classmethod
def load(cls, wheel_path):
# type: (str) -> Wheel

try:
metadata = WHEEL.load(wheel_path)
except WHEEL.LoadError as e:
raise WheelLoadError(str(e))

metadata_path = metadata.files.metadata_file_rel_path("WHEEL")
if not metadata_path:
raise WheelLoadError(
"Could not find WHEEL metadata in {wheel}.".format(wheel=wheel_path)
)

wheel_metadata_dir = os.path.dirname(metadata_path)
if not wheel_metadata_dir.endswith(".dist-info"):
raise WheelLoadError(
"Expected WHEEL metadata for {wheel} to be housed in a .dist-info directory, but was "
"found at {wheel_metadata_path}.".format(
wheel=wheel_path, wheel_metadata_path=metadata_path
)
)
# Although not crisply defined, all PEPs lead to PEP-508 which restricts project names
# to ASCII: https://peps.python.org/pep-0508/#names. Likewise, version numbers are also
# restricted to ASCII: https://peps.python.org/pep-0440/. Since the `.dist-info` dir
# path is defined as `<project name>-<version>.dist-info` in
# https://peps.python.org/pep-0427/, we are safe in assuming ASCII overall for the wheel
# metadata dir path.
metadata_dir = str(wheel_metadata_dir)

data_dir = re.sub(r"\.dist-info$", ".data", metadata_dir)

return cls(
location=wheel_path,
metadata_dir=metadata_dir,
metadata_files=metadata.files,
metadata=metadata,
data_dir=data_dir,
)

location = attr.ib() # type: str
metadata_dir = attr.ib() # type: str
metadata_files = attr.ib() # type: MetadataFiles
metadata = attr.ib() # type: WHEEL
data_dir = attr.ib() # type: str

@property
def root_is_purelib(self):
# type: () -> bool
return self.metadata.root_is_purelib

def dist_metadata(self):
return DistMetadata.from_metadata_files(self.metadata_files)

def metadata_path(self, *components):
# typ: (*str) -> str
return os.path.join(self.metadata_dir, *components)

def data_path(self, *components):
# typ: (*str) -> str
return os.path.join(self.data_dir, *components)


def install_wheel(
wheel_path, # type: str
install_paths, # type: InstallPaths
Expand Down
4 changes: 2 additions & 2 deletions pex/resolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from pex.jobs import Raise, SpawnedJob, execute_parallel, iter_map_parallel
from pex.network_configuration import NetworkConfiguration
from pex.orderedset import OrderedSet
from pex.pep_376 import InstalledWheel, LoadError
from pex.pep_376 import InstalledWheel
from pex.pep_425 import CompatibilityTags
from pex.pep_427 import InstallableType, WheelError, install_wheel_chroot
from pex.pep_503 import ProjectName
Expand Down Expand Up @@ -481,7 +481,7 @@ def finalize_install(self, install_requests):
cached_fingerprint = None # type: Optional[str]
try:
installed_wheel = InstalledWheel.load(self.install_chroot)
except LoadError:
except InstalledWheel.LoadError:
# We support legacy chroots below by calculating the chroot fingerprint just in time.
pass
else:
Expand Down
10 changes: 5 additions & 5 deletions pex/venv/installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,7 @@
from pex.dist_metadata import Distribution
from pex.environment import PEXEnvironment
from pex.orderedset import OrderedSet
from pex.pep_376 import InstalledWheel, LoadError
from pex.pep_427 import Wheel, WheelLoadError
from pex.pep_376 import InstalledWheel
from pex.pep_440 import Version
from pex.pep_503 import ProjectName
from pex.pex import PEX
Expand All @@ -26,6 +25,7 @@
from pex.venv.bin_path import BinPath
from pex.venv.install_scope import InstallScope
from pex.venv.virtualenv import PipUnavailableError, Virtualenv
from pex.wheel import Wheel, WheelMetadataLoadError

if TYPE_CHECKING:
import typing
Expand Down Expand Up @@ -258,7 +258,7 @@ def _populate_flat_deps(
installed_wheel = InstalledWheel.load(dist.location)
for src, dst in installed_wheel.reinstall_flat(target_dir=dest_dir, symlink=symlink):
yield src, dst
except LoadError:
except InstalledWheel.LoadError:
for src, dst in _populate_legacy_dist(
dest_dir=dest_dir, bin_dir=dest_dir, dist=dist, symlink=symlink
):
Expand Down Expand Up @@ -471,10 +471,10 @@ def _populate_venv_deps(
venv, symlink=symlink, rel_extra_path=rel_extra_path
):
yield src, dst
except LoadError:
except InstalledWheel.LoadError:
try:
wheel = Wheel.load(dist.location)
except WheelLoadError:
except WheelMetadataLoadError:
site_packages_dir = venv.site_packages_dir
else:
site_packages_dir = venv.purelib if wheel.root_is_purelib else venv.platlib
Expand Down
84 changes: 78 additions & 6 deletions pex/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,17 @@

from __future__ import absolute_import

import os
import re
from email.message import Message

from pex.dist_metadata import MetadataFiles, MetadataType, load_metadata, parse_message
from pex.dist_metadata import (
DistMetadata,
MetadataFiles,
MetadataType,
load_metadata,
parse_message,
)
from pex.typing import TYPE_CHECKING, cast

if TYPE_CHECKING:
Expand All @@ -16,16 +24,17 @@
from pex.third_party import attr


class WheelMetadataLoadError(Exception):
"""Indicates an error loading WHEEL metadata."""


@attr.s(frozen=True)
class WHEEL(object):
"""The .whl WHEEL metadata file.
See item 6 here for the WHEEL file contents: https://peps.python.org/pep-0427/#file-contents
"""

class LoadError(Exception):
"""Indicates an error loading WHEEL metadata."""

_CACHE = {} # type: Dict[Text, WHEEL]

@classmethod
Expand All @@ -35,13 +44,13 @@ def load(cls, location):
if not wheel:
metadata_files = load_metadata(location, restrict_types_to=(MetadataType.DIST_INFO,))
if not metadata_files:
raise cls.LoadError(
raise WheelMetadataLoadError(
"Could not find any metadata in {wheel}.".format(wheel=location)
)

metadata_bytes = metadata_files.read("WHEEL")
if not metadata_bytes:
raise cls.LoadError(
raise WheelMetadataLoadError(
"Could not find WHEEL metadata in {wheel}.".format(wheel=location)
)
metadata = parse_message(metadata_bytes)
Expand All @@ -59,3 +68,66 @@ def root_is_purelib(self):
# See:
# https://peps.python.org/pep-0427/#installing-a-wheel-distribution-1-0-py32-none-any-whl
return cast(bool, "true" == self.metadata.get("Root-Is-Purelib"))


@attr.s(frozen=True)
class Wheel(object):
@classmethod
def load(cls, wheel_path):
# type: (str) -> Wheel

metadata = WHEEL.load(wheel_path)

metadata_path = metadata.files.metadata_file_rel_path("WHEEL")
if not metadata_path:
raise WheelMetadataLoadError(
"Could not find WHEEL metadata in {wheel}.".format(wheel=wheel_path)
)

wheel_metadata_dir = os.path.dirname(metadata_path)
if not wheel_metadata_dir.endswith(".dist-info"):
raise WheelMetadataLoadError(
"Expected WHEEL metadata for {wheel} to be housed in a .dist-info directory, but was "
"found at {wheel_metadata_path}.".format(
wheel=wheel_path, wheel_metadata_path=metadata_path
)
)
# Although not crisply defined, all PEPs lead to PEP-508 which restricts project names
# to ASCII: https://peps.python.org/pep-0508/#names. Likewise, version numbers are also
# restricted to ASCII: https://peps.python.org/pep-0440/. Since the `.dist-info` dir
# path is defined as `<project name>-<version>.dist-info` in
# https://peps.python.org/pep-0427/, we are safe in assuming ASCII overall for the wheel
# metadata dir path.
metadata_dir = str(wheel_metadata_dir)

data_dir = re.sub(r"\.dist-info$", ".data", metadata_dir)

return cls(
location=wheel_path,
metadata_dir=metadata_dir,
metadata_files=metadata.files,
metadata=metadata,
data_dir=data_dir,
)

location = attr.ib() # type: str
metadata_dir = attr.ib() # type: str
metadata_files = attr.ib() # type: MetadataFiles
metadata = attr.ib() # type: WHEEL
data_dir = attr.ib() # type: str

@property
def root_is_purelib(self):
# type: () -> bool
return self.metadata.root_is_purelib

def dist_metadata(self):
return DistMetadata.from_metadata_files(self.metadata_files)

def metadata_path(self, *components):
# typ: (*str) -> str
return os.path.join(self.metadata_dir, *components)

def data_path(self, *components):
# typ: (*str) -> str
return os.path.join(self.data_dir, *components)
2 changes: 1 addition & 1 deletion testing/docker.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@

@attr.s(frozen=True)
class DockerVirtualenvRunner(object):
"""Run's code in a venv created by Virtualenv at /virtualenv.venv in a docker container."""
"""Runs code in a venv created by Virtualenv at /virtualenv.venv in a docker container."""

@classmethod
def create(
Expand Down

0 comments on commit 20fa1de

Please sign in to comment.