Skip to content

Commit

Permalink
Allow passing a preamble file to the CLI (pex-tool#400)
Browse files Browse the repository at this point in the history
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)
>>>
  • Loading branch information
borgstrom authored and kwlzn committed Aug 25, 2017
1 parent 9df12b1 commit 5b173e3
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 2 deletions.
17 changes: 16 additions & 1 deletion pex/bin/pex.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,14 @@ def configure_clp():
help='The name of the generated .pex file: Omiting this will run PEX '
'immediately and not save it to a file.')

parser.add_option(
'-p', '--preamble-file',
dest='preamble_file',
metavar='FILE',
default=None,
type=str,
help='The name of a file to be included as the preamble for the generated .pex file')

parser.add_option(
'-r', '--requirement',
dest='requirement_files',
Expand Down Expand Up @@ -513,8 +521,15 @@ def build_pex(args, options, resolver_option_builder):
if not interpreters:
die('Could not find compatible interpreter', CANNOT_SETUP_INTERPRETER)

try:
with open(options.preamble_file) as preamble_fd:
preamble = preamble_fd.read()
except TypeError:
# options.preamble_file is None
preamble = None

interpreter = _lowest_version_interpreter(interpreters)
pex_builder = PEXBuilder(path=safe_mkdtemp(), interpreter=interpreter)
pex_builder = PEXBuilder(path=safe_mkdtemp(), interpreter=interpreter, preamble=preamble)

pex_info = pex_builder.info
pex_info.zip_safe = options.zip_safe
Expand Down
17 changes: 16 additions & 1 deletion tests/test_pex_binary.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@

from contextlib import contextmanager
from optparse import OptionParser
from tempfile import NamedTemporaryFile

from pex.bin.pex import configure_clp, configure_clp_pex_resolution
from pex.bin.pex import build_pex, configure_clp, configure_clp_pex_resolution
from pex.compatibility import to_bytes
from pex.fetcher import PyPIFetcher
from pex.package import SourcePackage, WheelPackage
from pex.resolver_options import ResolverOptionsBuilder
Expand Down Expand Up @@ -88,6 +90,19 @@ def test_clp_constraints_txt():
assert options.constraint_files == ['requirements1.txt']


def test_clp_preamble_file():
with NamedTemporaryFile() as tmpfile:
tmpfile.write(to_bytes('print "foo!"'))
tmpfile.flush()

parser, resolver_options_builder = configure_clp()
options, reqs = parser.parse_args(args=['--preamble-file', tmpfile.name])
assert options.preamble_file == tmpfile.name

pex_builder = build_pex(reqs, options, resolver_options_builder)
assert pex_builder._preamble == to_bytes('print "foo!"')


def test_clp_prereleases():
with parser_pair() as (builder, parser):
configure_clp_pex_resolution(parser, builder)
Expand Down
24 changes: 24 additions & 0 deletions tests/test_pex_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,30 @@ def builder(shebang):
assert fp.read(len(expected_preamble)) == expected_preamble


def test_pex_builder_preamble():
with temporary_dir() as td:
target = os.path.join(td, 'foo.pex')
should_create = os.path.join(td, 'foo.1')

tempfile_preamble = "\n".join([
"import sys",
"open('{0}', 'w').close()".format(should_create),
"sys.exit(3)"
])

pex_builder = PEXBuilder(preamble=tempfile_preamble)
pex_builder.build(target)

assert not os.path.exists(should_create)

pex = PEX(target)
process = pex.run(blocking=False)
process.wait()

assert process.returncode == 3
assert os.path.exists(should_create)


def test_pex_builder_compilation():
with nested(temporary_dir(), temporary_dir(), temporary_dir()) as (td1, td2, td3):
src = os.path.join(td1, 'src.py')
Expand Down

0 comments on commit 5b173e3

Please sign in to comment.