Skip to content

Commit

Permalink
Extract LockedResolve test helpers.
Browse files Browse the repository at this point in the history
These will be needed for `pex3 lock {create,export}` testing.

Work towards pex-tool#1401 and pex-tool#1414.
  • Loading branch information
jsirois committed Sep 23, 2021
1 parent 623cd76 commit 17fcb35
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 71 deletions.
8 changes: 6 additions & 2 deletions pex/pex_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -579,8 +579,12 @@ def _prepare_bootstrap(self):
bootstrap_packages.extend(["commands", "tools", "tools/commands"])
for package in bootstrap_packages:
for fn in provider.resource_listdir(package):
if not (provider.resource_isdir(os.path.join(package, fn)) or fn.endswith(".pyc")):
rel_path = os.path.join(package, fn)
rel_path = os.path.join(package, fn)
if not (
provider.resource_isdir(rel_path)
or fn.endswith(".pyc")
or fn.endswith("testing.py")
):
data = provider.get_resource_string(source_name, rel_path)
self._chroot.write(
data,
Expand Down
71 changes: 71 additions & 0 deletions pex/resolve/testing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Copyright 2021 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

from __future__ import absolute_import

from pex.pep_503 import ProjectName
from pex.resolve.locked_resolve import Artifact, LockedRequirement, LockedResolve
from pex.third_party.pkg_resources import Requirement
from pex.typing import TYPE_CHECKING

if TYPE_CHECKING:
import attr # vendor:skip
else:
from pex.third_party import attr


def normalize_artifact(
artifact, # type: Artifact
skip_urls=False, # type: bool
):
# type: (...) -> Artifact
return attr.evolve(artifact, url="") if skip_urls else artifact


def normalize_locked_requirement(
locked_req, # type: LockedRequirement
skip_additional_artifacts=False, # type: bool
skip_urls=False, # type: bool
):
# type: (...) -> LockedRequirement

# We always normalize the following:
# 1. If an input requirement is not pinned, its locked equivalent always will be; so just check
# matching project names.
# 2. Creating a lock using a lock file as input will differ from a creating a lock using
# requirement strings in its via descriptions for each requirement; so don't compare vias at
# all.
return attr.evolve(
locked_req,
artifact=normalize_artifact(locked_req.artifact, skip_urls=skip_urls),
requirement=Requirement.parse(str(ProjectName(locked_req.requirement.project_name))),
additional_artifacts=()
if skip_additional_artifacts
else tuple(
sorted(
normalize_artifact(a, skip_urls=skip_urls) for a in locked_req.additional_artifacts
)
),
via=(),
)


def normalize_locked_resolve(
lock, # type: LockedResolve
skip_additional_artifacts=False, # type: bool
skip_urls=False, # type: bool
):
# type: (...) -> LockedResolve
return attr.evolve(
lock,
locked_requirements=tuple(
sorted(
normalize_locked_requirement(
locked_req,
skip_additional_artifacts=skip_additional_artifacts,
skip_urls=skip_urls,
)
for locked_req in lock.locked_requirements
)
),
)
70 changes: 3 additions & 67 deletions tests/integration/test_locked_resolve.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,9 @@

from pex import dist_metadata, resolver
from pex.distribution_target import DistributionTarget
from pex.pep_503 import ProjectName
from pex.resolve.locked_resolve import (
Artifact,
LockConfiguration,
LockedRequirement,
LockedResolve,
LockStyle,
Pin,
)
from pex.resolve.locked_resolve import LockConfiguration, LockStyle, Pin
from pex.resolve.testing import normalize_locked_resolve
from pex.resolver import Downloaded, LocalDistribution
from pex.third_party.pkg_resources import Requirement
from pex.typing import TYPE_CHECKING
from pex.util import CacheHelper

Expand All @@ -36,62 +28,6 @@ def normalize_local_dist(local_dist):
return attr.evolve(local_dist, path=os.path.basename(local_dist.path))


def normalize_artifact(
artifact, # type: Artifact
skip_urls=False, # type: bool
):
# type: (...) -> Artifact
return attr.evolve(artifact, url="") if skip_urls else artifact


def normalize_locked_req(
locked_req, # type: LockedRequirement
skip_additional_artifacts=False, # type: bool
skip_urls=False, # type: bool
):
# type: (...) -> LockedRequirement

# We always normalize the following:
# 1. If an input requirement is not pinned, its locked equivalent always will be; so just check
# matching project names.
# 2. A download using a lock file will differ from a download using requirement strings in its
# via descriptions for each requirement; so don't compare vias at all.
return attr.evolve(
locked_req,
artifact=normalize_artifact(locked_req.artifact, skip_urls=skip_urls),
requirement=Requirement.parse(str(ProjectName(locked_req.requirement.project_name))),
additional_artifacts=()
if skip_additional_artifacts
else tuple(
sorted(
normalize_artifact(a, skip_urls=skip_urls) for a in locked_req.additional_artifacts
)
),
via=(),
)


def normalize_lock(
lock, # type: LockedResolve
skip_additional_artifacts=False, # type: bool
skip_urls=False, # type: bool
):
# type: (...) -> LockedResolve
return attr.evolve(
lock,
locked_requirements=tuple(
sorted(
normalize_locked_req(
locked_req,
skip_additional_artifacts=skip_additional_artifacts,
skip_urls=skip_urls,
)
for locked_req in lock.locked_requirements
)
),
)


def normalize(
downloaded, # type: Downloaded
skip_additional_artifacts=False, # type: bool
Expand All @@ -107,7 +43,7 @@ def normalize(
),
locks=tuple(
sorted(
normalize_lock(
normalize_locked_resolve(
lock, skip_additional_artifacts=skip_additional_artifacts, skip_urls=skip_urls
)
for lock in downloaded.locks
Expand Down
48 changes: 46 additions & 2 deletions tests/test_pex_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

import pytest

from pex.common import safe_open, temporary_dir, touch
from pex.common import open_zip, safe_open, temporary_dir, touch
from pex.compatibility import WINDOWS
from pex.executor import Executor
from pex.layout import Layout
Expand All @@ -21,7 +21,7 @@
from pex.variables import ENV

if TYPE_CHECKING:
from typing import Any, Iterator, List
from typing import Any, Iterator, List, Set

exe_main = """
import sys
Expand Down Expand Up @@ -360,3 +360,47 @@ def test_pex_builder_packed(tmpdir):
assert zipfile.is_zipfile(cached_dist_zip)

assert filecmp.cmp(spread_dist_zip, cached_dist_zip, shallow=False)


@pytest.mark.parametrize(
"copy_mode", [pytest.param(copy_mode, id=copy_mode.value) for copy_mode in CopyMode.values()]
)
@pytest.mark.parametrize(
"layout", [pytest.param(layout, id=layout.value) for layout in Layout.values()]
)
def test_pex_builder_exclude_bootstrap_testing(
tmpdir, # type: Any
copy_mode, # type: CopyMode.Value
layout, # type: Layout.Value
):
# type: (...) -> None

pex_path = os.path.join(str(tmpdir), "empty.pex")
pb = PEXBuilder(copy_mode=copy_mode)
pb.build(pex_path, layout=layout)

bootstrap_location = os.path.join(pex_path, pb.info.bootstrap)
bootstrap_files = set() # type: Set[str]
if Layout.ZIPAPP == layout:
with open_zip(pex_path) as zf:
bootstrap_files.update(
os.path.relpath(f, pb.info.bootstrap)
for f in zf.namelist()
if f.startswith(pb.info.bootstrap)
)
elif Layout.PACKED == layout:
with open_zip(bootstrap_location) as zf:
bootstrap_files.update(zf.namelist())
else:
bootstrap_files.update(
os.path.relpath(os.path.join(root, f), bootstrap_location)
for root, _, files in os.walk(bootstrap_location)
for f in files
)

assert {"pex/pex_bootstrapper.py", "pex/pex_info.py", "pex/pex.py"}.issubset(
bootstrap_files
), "Expected the `.bootstrap` to contain at least some of the key Pex runtime modules."
assert not [
f for f in bootstrap_files if f.endswith(("testing.py", "testing.pyc"))
], "Expected testing support files to be stripped from the Pex `.bootstrap`."

0 comments on commit 17fcb35

Please sign in to comment.