Skip to content

Commit 5b173e3

Browse files
borgstromkwlzn
authored andcommitted
Allow passing a preamble file to the CLI (pex-tool#400)
This PR adds a new CLI option, -p or --preamble-file, that is used to provide a preamble to the pex builder object. We use pex via its API in the internal build tooling at NerdWallet (👋, we're neighbors!). We've been leveraging it for multi-platform support, but now that pex-tool#394 / 1.2.9 has landed the only thing missing from the CLI that we leverage is the preamble in the builder, which was pretty easy to hook up. Once this PR has landed we can remove all of the API integration and just rely on the pex CLI! The CLI option and the PEXBuilder preamble functionality are covered by tests. Local functional testing: (pex-foo) evanborgstrom@evanborgstrom /tmp/preamble — u:34 j:1 (21:49:54 08.16) pex-tool#536 ❯❯❯ echo 'print "foo!"' > preamble (pex-foo) evanborgstrom@evanborgstrom /tmp/preamble — u:34 j:1 (21:50:11 08.16) pex-tool#537 ❯❯❯ pex -p preamble foo! Python 2.7.13 (default, Jun 7 2017, 11:02:53) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> (pex-foo) evanborgstrom@evanborgstrom /tmp/preamble — u:34 j:1 (21:50:18 08.16) pex-tool#538 ❯❯❯ pex Python 2.7.13 (default, Jun 7 2017, 11:02:53) [GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>>
1 parent 9df12b1 commit 5b173e3

File tree

3 files changed

+56
-2
lines changed

3 files changed

+56
-2
lines changed

pex/bin/pex.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,14 @@ def configure_clp():
355355
help='The name of the generated .pex file: Omiting this will run PEX '
356356
'immediately and not save it to a file.')
357357

358+
parser.add_option(
359+
'-p', '--preamble-file',
360+
dest='preamble_file',
361+
metavar='FILE',
362+
default=None,
363+
type=str,
364+
help='The name of a file to be included as the preamble for the generated .pex file')
365+
358366
parser.add_option(
359367
'-r', '--requirement',
360368
dest='requirement_files',
@@ -513,8 +521,15 @@ def build_pex(args, options, resolver_option_builder):
513521
if not interpreters:
514522
die('Could not find compatible interpreter', CANNOT_SETUP_INTERPRETER)
515523

524+
try:
525+
with open(options.preamble_file) as preamble_fd:
526+
preamble = preamble_fd.read()
527+
except TypeError:
528+
# options.preamble_file is None
529+
preamble = None
530+
516531
interpreter = _lowest_version_interpreter(interpreters)
517-
pex_builder = PEXBuilder(path=safe_mkdtemp(), interpreter=interpreter)
532+
pex_builder = PEXBuilder(path=safe_mkdtemp(), interpreter=interpreter, preamble=preamble)
518533

519534
pex_info = pex_builder.info
520535
pex_info.zip_safe = options.zip_safe

tests/test_pex_binary.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
from contextlib import contextmanager
55
from optparse import OptionParser
6+
from tempfile import NamedTemporaryFile
67

7-
from pex.bin.pex import configure_clp, configure_clp_pex_resolution
8+
from pex.bin.pex import build_pex, configure_clp, configure_clp_pex_resolution
9+
from pex.compatibility import to_bytes
810
from pex.fetcher import PyPIFetcher
911
from pex.package import SourcePackage, WheelPackage
1012
from pex.resolver_options import ResolverOptionsBuilder
@@ -88,6 +90,19 @@ def test_clp_constraints_txt():
8890
assert options.constraint_files == ['requirements1.txt']
8991

9092

93+
def test_clp_preamble_file():
94+
with NamedTemporaryFile() as tmpfile:
95+
tmpfile.write(to_bytes('print "foo!"'))
96+
tmpfile.flush()
97+
98+
parser, resolver_options_builder = configure_clp()
99+
options, reqs = parser.parse_args(args=['--preamble-file', tmpfile.name])
100+
assert options.preamble_file == tmpfile.name
101+
102+
pex_builder = build_pex(reqs, options, resolver_options_builder)
103+
assert pex_builder._preamble == to_bytes('print "foo!"')
104+
105+
91106
def test_clp_prereleases():
92107
with parser_pair() as (builder, parser):
93108
configure_clp_pex_resolution(parser, builder)

tests/test_pex_builder.py

+24
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,30 @@ def builder(shebang):
101101
assert fp.read(len(expected_preamble)) == expected_preamble
102102

103103

104+
def test_pex_builder_preamble():
105+
with temporary_dir() as td:
106+
target = os.path.join(td, 'foo.pex')
107+
should_create = os.path.join(td, 'foo.1')
108+
109+
tempfile_preamble = "\n".join([
110+
"import sys",
111+
"open('{0}', 'w').close()".format(should_create),
112+
"sys.exit(3)"
113+
])
114+
115+
pex_builder = PEXBuilder(preamble=tempfile_preamble)
116+
pex_builder.build(target)
117+
118+
assert not os.path.exists(should_create)
119+
120+
pex = PEX(target)
121+
process = pex.run(blocking=False)
122+
process.wait()
123+
124+
assert process.returncode == 3
125+
assert os.path.exists(should_create)
126+
127+
104128
def test_pex_builder_compilation():
105129
with nested(temporary_dir(), temporary_dir(), temporary_dir()) as (td1, td2, td3):
106130
src = os.path.join(td1, 'src.py')

0 commit comments

Comments
 (0)