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

Allow for optional PyApp variables #20

Merged
merged 3 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
14 changes: 12 additions & 2 deletions src/box/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,22 @@ def cli():
@click.option(
"-opt", "--optional-deps", help="Set optional dependencies for the project."
)
@click.option(
"--opt-pyapp-vars",
help=(
"Set optional PyApp variables for the project. "
"`Example: PYAPP_FULL_ISOLATION 1`"
),
type=str,
)
@click.option("-e", "--entry", help="Set the app entry for the project.")
@click.option(
"-py",
"--python-version",
type=click.Choice(ut.PYAPP_PYTHON_VERSIONS),
help="Set the python version to use with PyApp.",
)
def init(quiet, builder, optional_deps, entry, python_version):
def init(quiet, builder, optional_deps, entry, python_version, opt_pyapp_vars):
"""Initialize a new project in the current folder."""
ut.check_pyproject()
my_init = InitializeProject(
Expand All @@ -50,6 +58,7 @@ def init(quiet, builder, optional_deps, entry, python_version):
optional_deps=optional_deps,
app_entry=entry,
python_version=python_version,
opt_pyapp_vars=opt_pyapp_vars,
)
my_init.initialize()

Expand Down Expand Up @@ -87,7 +96,8 @@ def package(verbose, pyapp_source):
binary_file = my_packager.binary_name
fmt.success(
f"Project successfully packaged.\n"
f"You can find the executable file {binary_file.name} in the `target/release` folder."
f"You can find the executable file {binary_file.name} "
f"in the `target/release` folder."
)


Expand Down
8 changes: 8 additions & 0 deletions src/box/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,14 @@ def optional_dependencies(self) -> Union[str, None]:
except KeyError:
return None

@property
def optional_pyapp_variables(self) -> Dict:
"""Return optional pyapp variables as list (if set), otherwise empty dict."""
try:
return self._pyproject["tool"]["box"]["optional_pyapp_vars"]
except KeyError:
return {}

@property
def possible_app_entries(self) -> Dict:
"""Return [project.gui-scripts] or [project.scripts] entry if available.
Expand Down
31 changes: 31 additions & 0 deletions src/box/initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ def __init__(
optional_deps: str = None,
app_entry: str = None,
python_version: str = None,
opt_pyapp_vars: str = None,
):
"""Initialize the InitializeProject class.

Expand All @@ -31,10 +32,12 @@ def __init__(
:param optional_deps: Optional dependencies for the project.
:param app_entry: App entry for the project.
:param python_version: Python version for the project.
:param opt_pyapp_vars: Optional PyApp variables to set.
"""
self._quiet = quiet
self._builder = builder
self._optional_deps = optional_deps
self._opt_paypp_vars = opt_pyapp_vars
self._app_entry = app_entry
self._python_version = python_version

Expand All @@ -52,6 +55,7 @@ def initialize(self):
self._set_optional_deps()
self._set_app_entry()
self._set_python_version()
self._set_optional_pyapp_variables()

if not self._quiet:
fmt.success("Project initialized.")
Expand Down Expand Up @@ -141,6 +145,33 @@ def _set_optional_deps(self):
if opt_deps != "":
pyproject_writer("optional_deps", opt_deps)

def _set_optional_pyapp_variables(self):
"""Set optional environmental variables for PyApp."""
opt_vars = None
if self._opt_paypp_vars:
opt_vars = self._opt_paypp_vars
elif not self._quiet:
opt_vars = click.prompt(
"Please enter optional PyApp variables to set. "
"Example: `PYAPP_SKIP_INSTALL 1 PYAPP_FULL_ISOLATION 1",
type=str,
default="",
)

if opt_vars == "":
opt_vars = None

if opt_vars:
opt_vars = opt_vars.split()
if len(opt_vars) % 2 != 0:
fmt.warning("Invalid number of variables. Please try again.")
self._set_optional_pyapp_variables()
else:
opt_vars_dict = {}
for i in range(0, len(opt_vars), 2):
opt_vars_dict[opt_vars[i]] = opt_vars[i + 1]
pyproject_writer("optional_pyapp_vars", opt_vars_dict)

def _set_pyproj(self):
"""Check if the pyproject.toml file is valid."""
try:
Expand Down
3 changes: 3 additions & 0 deletions src/box/packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,9 @@ def _set_env(self):
os.environ["PYAPP_PYTHON_VERSION"] = py_version
if value := self.config.optional_dependencies:
os.environ["PYAPP_PIP_OPTIONAL_DEPS"] = value
optional_pyapp_vars = self.config.optional_pyapp_variables
for key, value in optional_pyapp_vars.items():
os.environ[key] = value

# STATIC METHODS #
@staticmethod
Expand Down
37 changes: 34 additions & 3 deletions tests/cli/test_cli_initialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
import box.utils as ut


@pytest.mark.parametrize("app_entry", ["\n\nhello\n3.8", "\ngui\n127\nhello\n"])
@pytest.mark.parametrize(
"app_entry", ["\n\nhello\n3.8\nPYAPP_FULL_ISOLATION 1", "\ngui\n127\nhello\n\n"]
)
def test_initialize_project_app_entry_typed(rye_project_no_box, app_entry):
"""Initialize a new project."""
# modify pyproject.toml to contain an app entry
Expand All @@ -33,7 +35,7 @@ def test_initialize_project_app_entry_typed(rye_project_no_box, app_entry):

# assert name is in pyproject.toml
pyproj = PyProjectParser()
app_entry_exp = app_entry.split("\n")[-2]
app_entry_exp = app_entry.split("\n")[-3]
assert pyproj.app_entry == app_entry_exp

# assert that extra dependencies were set
Expand All @@ -44,11 +46,22 @@ def test_initialize_project_app_entry_typed(rye_project_no_box, app_entry):
assert pyproj.builder == "rye"

# assert default python version
py_version_exp = app_entry.split("\n")[-1]
py_version_exp = app_entry.split("\n")[-2]
if py_version_exp == "":
py_version_exp = ut.PYAPP_PYTHON_VERSIONS[-1]
assert pyproj.python_version == py_version_exp

# optional pyapp variables
optional_pyapp_vars_exp = app_entry.split("\n")[-1]
if optional_pyapp_vars_exp == "":
exp_dict = {}
else:
tmp_split = optional_pyapp_vars_exp.split()
exp_dict = {}
for it in range(0, len(tmp_split), 2):
exp_dict[tmp_split[it]] = tmp_split[it + 1]
assert pyproj.optional_pyapp_variables == exp_dict


def test_initialize_with_options(rye_project_no_box):
"""Initialize a new project with options."""
Expand All @@ -70,6 +83,8 @@ def test_initialize_with_options(rye_project_no_box):
optional_deps,
"-b",
builder,
"--opt-pyapp-vars",
"PYAPP_FULL_ISOLATION 1",
],
)

Expand All @@ -85,6 +100,7 @@ def test_initialize_with_options(rye_project_no_box):
assert pyproj.python_version == py_version
assert pyproj.optional_dependencies == optional_deps
assert pyproj.app_entry == entry_point
assert pyproj.optional_pyapp_variables == {"PYAPP_FULL_ISOLATION": "1"}


def test_initialize_project_quiet(rye_project_no_box):
Expand Down Expand Up @@ -113,6 +129,21 @@ def test_initialize_project_builders(rye_project_no_box, builder):
assert pyproj.builder == builder


def test_initialize_project_wrong_number_of_pyapp_vars(rye_project_no_box):
"""Initialize a new project with a specific builder."""
runner = CliRunner()
result = runner.invoke(
cli,
["init"],
input="\n\nhello\n\nPYAPP_FULL_ISOLATION 1 2\nPYAPP_FULL_ISOLATION 1",
)
assert result.exit_code == 0

pyproj = PyProjectParser()
exp_dict = {"PYAPP_FULL_ISOLATION": "1"}
assert pyproj.optional_pyapp_variables == exp_dict


def test_initialize_project_quiet_no_project_script(rye_project_no_box):
"""Initialize a new project quietly with app_entry as the package name."""
runner = CliRunner()
Expand Down
9 changes: 8 additions & 1 deletion tests/func/test_packager.py
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@ def test_package_pyapp_cargo_and_move(rye_project, mocker, binary_extensions):


@pytest.mark.parametrize("opt_deps", ["gui", None])
def test_set_env(rye_project, opt_deps, mocker):
@pytest.mark.parametrize("opt_pyapp_vars", ["PYAPP_SOMETHING 2", None])
def test_set_env(rye_project, opt_deps, opt_pyapp_vars, mocker):
"""Set environment for `PyApp` packaging."""
config = PyProjectParser()
exec_spec = config.app_entry
Expand All @@ -245,6 +246,10 @@ def test_set_env(rye_project, opt_deps, mocker):
# write optional deps to the pyproject.toml
if opt_deps:
pyproject_writer("optional_deps", opt_deps)
if opt_pyapp_vars:
tmp_split = opt_pyapp_vars.split()
opt_pyapp_vars = {tmp_split[0]: tmp_split[1]}
pyproject_writer("optional_pyapp_vars", opt_pyapp_vars)

packager = PackageApp()
packager.build()
Expand All @@ -258,6 +263,8 @@ def test_set_env(rye_project, opt_deps, mocker):
assert os.environ["PYAPP_PYTHON_VERSION"] == ut.PYAPP_PYTHON_VERSIONS[-1]
if opt_deps:
assert os.environ["PYAPP_PIP_OPTIONAL_DEPS"] == opt_deps
if opt_pyapp_vars:
assert os.environ["PYAPP_SOMETHING"] == "2"


def test_set_env_delete_existing(rye_project, mocker):
Expand Down
Loading