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

Have bootstrap_pex_env pull in contents of pexrc files #747

Closed
26 changes: 22 additions & 4 deletions pex/pex_bootstrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from __future__ import absolute_import, print_function

import json
import os
import sys

Expand Down Expand Up @@ -173,9 +174,22 @@ def maybe_reexec_pex(compatibility_constraints=None):
os.execv(target_binary, cmdline)


def _bootstrap(entry_point):
def _bootstrap(entry_point, include_pexrc=False, extra_vars=None):
from .pex_info import PexInfo
pex_info = PexInfo.from_pex(entry_point)

# Pull in the default ENV, which is an instance of pex.variables.Variables, which reads
# from the default pexrc locations.
if include_pexrc:
pex_info.update(PexInfo.from_env())

# Override env settings using extra passed-in vars
if extra_vars:
if isinstance(extra_vars, dict):
Copy link
Member

Choose a reason for hiding this comment

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

How about just accepting **extra_vars and pushing json back to the user that needs it. It's terrifying that we have a consumer of this API at all, and as a relunctant / conservative library maintainer I'd much rather keep API surface area minimal.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not totally sure what you mean here, would you mind clarifying?

Are you saying this should only allow properly formatted json here, and require the user to convert from a dict if necessary?

Copy link
Author

@rtkjliviero rtkjliviero Aug 21, 2019

Choose a reason for hiding this comment

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

@jsirois, if you could clarify I'm more than happy to update this PR.

Copy link
Member

Choose a reason for hiding this comment

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

I just mean support the minimal api, which is allowing the user to pass environment variables via **extra_vars. If code that call this wants to support a json encoding of the env vars that code can decode the json to a dict to pass in as **extra_vars.

Concreteley, just:

def _bootstrap(entry_point, include_pexrc=False, **extra_vars):
  from .pex_info import PexInfo
  pex_info = PexInfo.from_pex(entry_point)
  env = dict(PEX_IGNORE_RCFILES=str(not include_pexrc), **extra_vars)
  pex_info.update(PexInfo.from_env(env))

Calling code in an application can do this if it want to unburdening Pex from supporting json in this API in perpetuity:

_bootstrap(entry_point, **json.loads(extra_vars_json_string))

And, of course, where this is really important is in the public API function, so the **extra_vars style should propagate up to bootstrap_pex_env.

pex_info.update(PexInfo.from_json(json.dumps(extra_vars)))
elif isinstance(extra_vars, str):
pex_info.update(PexInfo.from_json(extra_vars))

pex_warnings.configure_warnings(pex_info)

from .finders import register_finders
Expand All @@ -199,9 +213,13 @@ def is_compressed(entry_point):
return os.path.exists(entry_point) and not os.path.exists(os.path.join(entry_point, PexInfo.PATH))


def bootstrap_pex_env(entry_point):
"""Bootstrap the current runtime environment using a given pex."""
pex_info = _bootstrap(entry_point)
def bootstrap_pex_env(entry_point, include_pexrc=False, extra_vars=None):
"""Bootstrap the current runtime environment using a given pex.

Optionally, pexrc files from 'standard' locations can be included - see
pex.variables.Variables.from_rc() - or runtime-specific overrides (dict or json)
"""
pex_info = _bootstrap(entry_point, include_pexrc=include_pexrc, extra_vars=extra_vars)

from .environment import PEXEnvironment
PEXEnvironment(entry_point, pex_info).activate()
Binary file added tests/empty_test_fixture.pex
Binary file not shown.
50 changes: 49 additions & 1 deletion tests/test_pex_bootstrapper.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
# Copyright 2015 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).

import json
import os
import pytest

from pex.interpreter import PythonInterpreter
from pex.pex_bootstrapper import find_compatible_interpreters
from pex.pex_bootstrapper import find_compatible_interpreters, _bootstrap
from pex.testing import IS_PYPY, PY27, PY35, PY36, ensure_python_interpreter

EMPTY_FIXTURE_PEX = os.path.join(os.path.dirname(__file__), 'empty_test_fixture.pex')


@pytest.mark.skipif(IS_PYPY)
def test_find_compatible_interpreters():
Expand Down Expand Up @@ -37,3 +41,47 @@ def find_interpreters(*constraints):

interpreters = find_compatible_interpreters(compatibility_constraints=['<3'])
assert set(interpreters).issubset(all_known_interpreters)


def test_bootstrap():
"""Simple test against an empty .pex file"""
pex_info = _bootstrap(EMPTY_FIXTURE_PEX)
# code_hash is arbitrary; it's just what was written into the test fixture.
# It is only here to ensure that we're actually reading the file, and not
# returning a defaults-only PexInfo.
assert pex_info.code_hash == 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
assert pex_info.requirements == []
assert pex_info.zip_safe is True


def test_bootstrap_with_vars_dict():
"""Test that extra_vars passed in as a dict are considered"""
extra_vars = {
"pex_root": "some_dir",
"script": "my_dict_test_script"
}
pex_info = _bootstrap(EMPTY_FIXTURE_PEX, include_pexrc=False, extra_vars=extra_vars)
assert pex_info.pex_root == "some_dir"
assert pex_info.script == "my_dict_test_script"


def test_bootstrap_with_vars_json():
"""Test that extra_vars passed in as a JSON string are considered"""
extra_vars = {
"pex_root": "some_dir",
"script": "my_json_test_script"
}
json_vars = json.dumps(extra_vars)
pex_info = _bootstrap(EMPTY_FIXTURE_PEX, include_pexrc=False, extra_vars=json_vars)
assert pex_info.pex_root == "some_dir"
assert pex_info.script == "my_json_test_script"


def test_bootstrap_with_pexrc():
"""Test that pexrc files are considered"""
pass


def test_bootstrap_with_pexrc_and_vars():
"""Test that both pexrc files and passed-in extra_vars are considered in a suitable order"""
pass