Skip to content

Commit 96d1574

Browse files
authored
feat(pex): support building PEX archives to inherit from the sys.path (#619)
By default PEX archives are built to operate fully hermetically, which is usually a good idea. However, sometimes it is necessary to use packages from the hosting environment. One such example is AWS Lambda; the recommended approach for using PEX to run on Lambda is to build in this way.[^1] Mapping of this flag through from the build system largely follows the equivalent feature in Pants.[^2] --- ### Changes are visible to end-users: yes - Searched for relevant documentation and updated as needed: yes - Breaking change (forces users to change their own code or config):no - Suggested release notes appear below: no ### Test plan - Covered by existing test cases - Manual testing; please provide instructions so we can reproduce: 1. Edit `//py/tests/py-pex-binary:print_modules_pex` to have an explicit `inherit_path` of a chosen value (e.g. "prefer"). 2. `bazel build //py/tests/py-pex-binary:print_modules_pex`. 3. `tar xfO bazel-bin/py/tests/py-pex-binary/print_modules_pex.pex PEX-INFO | jq '.inherit_path'` 4. Expect result: `"prefer"`. Do above except without passing an explicit `inherit_path` should give `"false"`, which confirms the default semantics are preserved. [^1]: https://github.com/pex-tool/lambdex/blob/main/MIGRATING.md [^2]: https://www.pantsbuild.org/stable/reference/targets/pex_binary#inherit_path
1 parent 8022c89 commit 96d1574

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

docs/pex.md

Lines changed: 3 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

py/private/py_pex_binary.bzl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ def _py_python_pex_impl(ctx):
106106
args.add(ctx.attr.python_shebang, format = "--python-shebang=%s")
107107
args.add(py_toolchain.python, format = "--python=%s")
108108

109+
if ctx.attr.inherit_path != "":
110+
args.add(ctx.attr.inherit_path, format = "--inherit-path=%s")
111+
109112
py_version = py_toolchain.interpreter_version_info
110113
args.add_all(
111114
[
@@ -135,6 +138,15 @@ _attrs = dict({
135138
doc = "Environment variables to set when running the pex binary.",
136139
default = {},
137140
),
141+
"inherit_path": attr.string(
142+
doc = """\
143+
Whether to inherit the `sys.path` (aka PYTHONPATH) of the environment that the binary runs in.
144+
145+
Use `false` to not inherit `sys.path`; use `fallback` to inherit `sys.path` after packaged
146+
dependencies; and use `prefer` to inherit `sys.path` before packaged dependencies.
147+
""",
148+
values = ["false", "fallback", "prefer"],
149+
),
138150
"python_shebang": attr.string(default = "#!/usr/bin/env python3"),
139151
"python_interpreter_constraints": attr.string_list(
140152
default = ["CPython=={major}.{minor}.*"],

py/tools/pex/main.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import sys
1111
from pex.pex_builder import Check,PEXBuilder
12+
from pex.inherit_path import InheritPath
1213
from pex.interpreter import PythonInterpreter
1314
from pex.interpreter_constraints import InterpreterConstraint
1415
from pex.layout import Layout
@@ -26,6 +27,13 @@ def __call__(self, parser, namespace, value, option_str=None):
2627
)
2728
self.default.append(tuple(components))
2829

30+
31+
class InheritPathAction(Action):
32+
def __call__(self, parser, namespace, value, option_str=None):
33+
value = InheritPath.for_value(value)
34+
setattr(namespace, self.dest, value)
35+
36+
2937
parser = ArgumentParser(fromfile_prefix_chars='@')
3038

3139
parser.add_argument(
@@ -107,6 +115,13 @@ def __call__(self, parser, namespace, value, option_str=None):
107115
action="append",
108116
)
109117

118+
parser.add_argument(
119+
"--inherit-path",
120+
dest="inherit_path",
121+
default=None,
122+
action=InheritPathAction,
123+
)
124+
110125
options = parser.parse_args(args = sys.argv[1:])
111126

112127
# Monkey patch bootstrap template to inject some templated environment variables.
@@ -157,6 +172,8 @@ def __call__(self, parser, namespace, value, option_str=None):
157172
InterpreterConstraint.parse(constraint)
158173
for constraint in options.constraints
159174
]
175+
if options.inherit_path is not None:
176+
pex_info.inherit_path = options.inherit_path
160177

161178
for dep in options.dependencies:
162179
dist = Distribution.load(dep)

0 commit comments

Comments
 (0)