Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for --scie-busybox. #2468

Merged
merged 16 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions pex/scie/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
from pex.pep_440 import Version
from pex.scie import science
from pex.scie.model import (
BusyBoxEntryPoints,
ModuleEntryPoint,
ScieConfiguration,
ScieInfo,
ScieOptions,
Expand All @@ -24,8 +26,7 @@
from pex.variables import ENV, Variables

if TYPE_CHECKING:
from typing import Iterator, Optional, Tuple, Union

from typing import Iterator, List, Optional, Tuple, Union

__all__ = (
"ScieConfiguration",
Expand Down Expand Up @@ -72,6 +73,28 @@ def register_options(parser):
"https://science.scie.app.".format(lazy=ScieStyle.LAZY, eager=ScieStyle.EAGER)
),
)
parser.add_argument(
"--scie-busybox",
dest="scie_busybox",
type=str,
default=[],
action="append",
help=(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor feedback: these docs look good, but they're a bit of a wall of text that'll usually be read in the context of a terminal without any formatting aids, and thus I'd expect most people (even well-intentioned diligent ones) to fail to synthesise the information out of it.

Is it possible to make it easier for users by breaking it up into paragraphs and/or lists?

Copy link
Member Author

@jsirois jsirois Jul 29, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not - Argparse doesn't support formatting I know of. I've both avoided spending time on any bespoke argument system to remedy this or turning towards documentation, which needs major work going on 10 years now. One or both are coming, but I'm prioritizing robust users so far in favor of righting the Pex ship going on 8 years. I do finally feel close though.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah I see. Thanks for the background. 👍

"Make the PEX scie a BusyBox over the specified entry points. The entry points can be"
"the name of a console script embedded in one of the PEX distributions or else an "
"entry point specifier, which is a string of the form '<name>:<module>(:<function>)'; "
"e.g.: 'run-baz:foo.bar:baz' to execute the baz function in the foo.bar module as the "
"entry point named 'run-baz'. Multiple entry points can be specified at once using a "
"comma-separated list or the option can be specified multiple times. A BusyBox scie "
"has no default entrypoint; instead, when run it inspects argv0; if that matches one "
"of its embedded entry points, it runs that entry point; if not, it lists all"
" available entrypoints for you to pick from. To run a given entry point, you specify "
"it as the argument and all other arguments after that are forwarded to that entry "
"point. BusyBox PEX scies allow you to install all their contained entry points into a "
"given directory. For more information, run `SCIE=help <your PEX scie>` and review the "
"`install` command help."
),
)
parser.add_argument(
"--scie-platform",
dest="scie_platforms",
Expand Down Expand Up @@ -140,6 +163,11 @@ def render_options(options):
# type: (ScieOptions) -> str

args = ["--scie", str(options.style)]
if options.busybox_entrypoints:
args.append("--scie-busybox")
entrypoints = list(options.busybox_entrypoints.console_scripts)
entrypoints.extend(map(str, options.busybox_entrypoints.module_entry_points))
args.append(",".join(entrypoints))
for platform in options.platforms:
args.append("--scie-platform")
args.append(str(platform))
Expand All @@ -161,6 +189,28 @@ def extract_options(options):
if not options.scie_style:
return None

entry_points = None
if options.scie_busybox:
eps = [] # type: List[str]
for value in options.scie_busybox:
eps.extend(ep.strip() for ep in value.split(","))
if len(eps) == 1:
raise ValueError(
"A BusyBox PEX scie requires two or more entry points, you only supplied 1: "
"{ep!r}".format(ep=eps[0])
)
console_scripts = [] # type: List[str]
modules = [] # type: List[ModuleEntryPoint]
for ep in eps:
entry_point = ModuleEntryPoint.try_parse(ep)
if entry_point:
modules.append(entry_point)
else:
console_scripts.append(ep)
entry_points = BusyBoxEntryPoints(
console_scripts=tuple(console_scripts), module_entry_points=tuple(modules)
)

python_version = None # type: Optional[Union[Tuple[int, int], Tuple[int, int, int]]]
if options.scie_python_version:
if (
Expand Down Expand Up @@ -195,6 +245,7 @@ def extract_options(options):

return ScieOptions(
style=options.scie_style,
busybox_entrypoints=entry_points,
platforms=tuple(OrderedSet(options.scie_platforms)),
pbs_release=options.scie_pbs_release,
python_version=python_version,
Expand Down
23 changes: 23 additions & 0 deletions pex/scie/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,28 @@ class Value(Enum.Value):
EAGER = Value("eager")


@attr.s(frozen=True)
class ModuleEntryPoint(object):
@classmethod
def try_parse(cls, value):
# type: (str) -> Optional[ModuleEntryPoint]
name, _, entry_point = value.partition(":")
return cls(name, entry_point) if entry_point else None

name = attr.ib() # type: str
entry_point = attr.ib() # type: str

def __str__(self):
# type: () -> str
return "{name}:{entry_point}".format(name=self.name, entry_point=self.entry_point)


@attr.s(frozen=True)
class BusyBoxEntryPoints(object):
console_scripts = attr.ib() # type: Tuple[str, ...]
module_entry_points = attr.ib() # type: Tuple[ModuleEntryPoint, ...]


class _CurrentPlatform(object):
def __get__(self, obj, objtype=None):
# type: (...) -> SciePlatform.Value
Expand Down Expand Up @@ -133,6 +155,7 @@ def python_version(self):
@attr.s(frozen=True)
class ScieOptions(object):
style = attr.ib(default=ScieStyle.LAZY) # type: ScieStyle.Value
busybox_entrypoints = attr.ib(default=None) # type: Optional[BusyBoxEntryPoints]
platforms = attr.ib(default=()) # type: Tuple[SciePlatform.Value, ...]
pbs_release = attr.ib(default=None) # type: Optional[str]
python_version = attr.ib(
Expand Down
60 changes: 45 additions & 15 deletions pex/scie/science.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
from pex.hashing import Sha256
from pex.layout import Layout
from pex.pep_440 import Version
from pex.pex_info import PexInfo
from pex.pex import PEX
from pex.result import Error, try_
from pex.scie.model import ScieConfiguration, ScieInfo, SciePlatform, ScieStyle, ScieTarget
from pex.third_party.packaging.specifiers import SpecifierSet
Expand All @@ -29,7 +29,7 @@
from pex.variables import ENV, Variables, unzip_dir_relpath

if TYPE_CHECKING:
from typing import Any, Dict, Iterator, Optional, Union, cast
from typing import Any, Dict, Iterator, List, Optional, Union, cast

import attr # vendor:skip
import toml # vendor:skip
Expand Down Expand Up @@ -73,17 +73,17 @@ def _science_binary_url(suffix=""):
def create_manifests(
configuration, # type: ScieConfiguration
name, # type: str
pex_info, # type: PexInfo
layout, # type: Layout.Value
pex, # type: PEX
):
# type: (...) -> Iterator[Manifest]

pex_info = pex.pex_info(include_env_overrides=False)
pex_root = "{scie.bindings}/pex_root"
if pex_info.venv:
# We let the configure-binding calculate the venv dir at runtime since it depends on the
# interpreter executing the venv PEX.
installed_pex_dir = ""
elif layout is Layout.LOOSE:
elif pex.layout is Layout.LOOSE:
installed_pex_dir = "{pex}"
else:
production_assert(pex_info.pex_hash is not None)
Expand All @@ -94,6 +94,43 @@ def create_manifests(
"PEX_ROOT": pex_root,
}

commands = [] # type: List[Dict[str, Any]]
if configuration.options.busybox_entrypoints:
commands.extend(
{
"name": console_script,
"env": {
"default": env_default,
"replace": {"PEX_SCRIPT": console_script},
"remove_exact": ["PEX_INTERPRETER", "PEX_MODULE"],
},
"exe": "{scie.bindings.configure:PYTHON}",
"args": ["{scie.bindings.configure:PEX}"],
}
for console_script in configuration.options.busybox_entrypoints.console_scripts
)
commands.extend(
{
"name": mep.name,
"env": {
"default": env_default,
"replace": {"PEX_MODULE": mep.entry_point},
"remove_exact": ["PEX_INTERPRETER", "PEX_SCRIPT"],
},
"exe": "{scie.bindings.configure:PYTHON}",
"args": ["{scie.bindings.configure:PEX}"],
}
for mep in configuration.options.busybox_entrypoints.module_entry_points
)
else:
commands.append(
{
"env": {"default": env_default},
"exe": "{scie.bindings.configure:PYTHON}",
"args": ["{scie.bindings.configure:PEX}"],
}
)

lift = {
"name": name,
"ptex": {
Expand All @@ -103,13 +140,7 @@ def create_manifests(
},
"scie_jump": {"version": SCIE_JUMP_VERSION},
"files": [{"name": "configure-binding.py"}, {"name": "pex"}],
"commands": [
{
"env": {"default": env_default},
"exe": "{scie.bindings.configure:PYTHON}",
"args": ["{scie.bindings.configure:PEX}"],
}
],
"commands": commands,
"bindings": [
{
"env": {
Expand Down Expand Up @@ -287,12 +318,11 @@ def build(
env=env,
)
name = re.sub(r"\.pex$", "", os.path.basename(pex_file), flags=re.IGNORECASE)
pex_info = PexInfo.from_pex(pex_file)
layout = Layout.identify(pex_file)
pex = PEX(pex_file)
use_platform_suffix = len(configuration.targets) > 1

errors = OrderedDict() # type: OrderedDict[Manifest, str]
for manifest in create_manifests(configuration, name, pex_info, layout):
for manifest in create_manifests(configuration, name, pex):
args = [science, "--cache-dir", _science_dir(env, "cache")]
if env.PEX_VERBOSE:
args.append("-{verbosity}".format(verbosity="v" * env.PEX_VERBOSE))
Expand Down