Skip to content

Commit

Permalink
Merge pull request #1413 from jeamland/jeamland/briefcase-dev-argv-hi…
Browse files Browse the repository at this point in the history
…larity

Move command-line args out of dev-mode Python invocation
  • Loading branch information
freakboy3742 authored Aug 21, 2023
2 parents d8ec7a1 + c3cc4ee commit a800db1
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 29 deletions.
1 change: 1 addition & 0 deletions changes/1413.bugfix.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The command line arguments used to configure the Python environment for ``briefcase dev`` no longer leak into the runtime environment on macOS.
28 changes: 23 additions & 5 deletions src/briefcase/commands/dev.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,25 @@ class DevCommand(RunAppMixin, BaseCommand):
output_format = None
description = "Run a Briefcase project in the dev environment."

# On macOS CoreFoundation/NSApplication will do its own independent parsing of argc/argv.
# This means that whatever we pass to the Python interpreter on start-up will also be
# visible to NSApplication which will interpret things like `-u` (used to make I/O
# unbuffered in CPython) as `-u [URL]` (a request to open a document by URL). This is,
# rather patently, Not Good.
# To avoid this causing unwanted hilarity, we use environment variables to configure the
# Python interpreter rather than command-line options.
DEV_ENVIRONMENT = {
# Equivalent of passing "-u"
"PYTHONUNBUFFERED": "1",
# Equivalent of passing "-X dev"
"PYTHONMALLOC": "debug",
"PYTHONASYNCIODEBUG": "1",
"PYTHONFAULTHANDLER": "1",
"PYTHONWARNINGS": "default",
# Equivalent of passing "-X utf8"
"PYTHONUTF8": "1",
}

@property
def platform(self):
"""The dev command always reports as the local platform."""
Expand Down Expand Up @@ -104,14 +123,13 @@ def run_dev_app(
:param passthrough: A list of arguments to pass to the app
"""
main_module = app.main_module(test_mode)

# Add in the environment settings to get Python in the state we want.
env.update(self.DEV_ENVIRONMENT)

app_popen = self.tools.subprocess.Popen(
[
sys.executable,
"-u",
"-X",
"dev",
"-X",
"utf8",
"-c",
(
"import runpy, sys;"
Expand Down
2 changes: 2 additions & 0 deletions tests/commands/dev/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
@pytest.fixture
def dev_command(tmp_path):
command = DevCommand(logger=Log(), console=Console(), base_path=tmp_path)
command.DEV_ENVIRONMENT.clear()
command.DEV_ENVIRONMENT["PYTHONSTUBSETTING"] = "23"
command.tools.subprocess = mock.MagicMock(spec_set=Subprocess)
return command

Expand Down
44 changes: 20 additions & 24 deletions tests/commands/dev/test_run_dev_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,13 @@ def test_dev_run(dev_command, first_app, tmp_path):
test_mode=False,
passthrough=[],
)

expected_env = {"a": 1, "b": 2, "c": 3}
expected_env.update(dev_command.DEV_ENVIRONMENT)

dev_command.tools.subprocess.Popen.assert_called_once_with(
[
sys.executable,
"-u",
"-X",
"dev",
"-X",
"utf8",
"-c",
(
"import runpy, sys;"
Expand All @@ -31,7 +30,7 @@ def test_dev_run(dev_command, first_app, tmp_path):
'runpy.run_module("first", run_name="__main__", alter_sys=True)'
),
],
env={"a": 1, "b": 2, "c": 3},
env=expected_env,
cwd=dev_command.tools.home_path,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Expand All @@ -57,14 +56,13 @@ def test_dev_run_with_args(dev_command, first_app, tmp_path):
test_mode=False,
passthrough=["foo", "bar", "--whiz"],
)

expected_env = {"a": 1, "b": 2, "c": 3}
expected_env.update(dev_command.DEV_ENVIRONMENT)

dev_command.tools.subprocess.Popen.assert_called_once_with(
[
sys.executable,
"-u",
"-X",
"dev",
"-X",
"utf8",
"-c",
(
"import runpy, sys;"
Expand All @@ -73,7 +71,7 @@ def test_dev_run_with_args(dev_command, first_app, tmp_path):
'runpy.run_module("first", run_name="__main__", alter_sys=True)'
),
],
env={"a": 1, "b": 2, "c": 3},
env=expected_env,
cwd=dev_command.tools.home_path,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Expand All @@ -99,14 +97,13 @@ def test_dev_test_mode(dev_command, first_app, tmp_path):
test_mode=True,
passthrough=[],
)

expected_env = {"a": 1, "b": 2, "c": 3}
expected_env.update(dev_command.DEV_ENVIRONMENT)

dev_command.tools.subprocess.Popen.assert_called_once_with(
[
sys.executable,
"-u",
"-X",
"dev",
"-X",
"utf8",
"-c",
(
"import runpy, sys;"
Expand All @@ -115,7 +112,7 @@ def test_dev_test_mode(dev_command, first_app, tmp_path):
'runpy.run_module("tests.first", run_name="__main__", alter_sys=True)'
),
],
env={"a": 1, "b": 2, "c": 3},
env=expected_env,
cwd=dev_command.tools.home_path,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Expand All @@ -141,14 +138,13 @@ def test_dev_test_mode_with_args(dev_command, first_app, tmp_path):
test_mode=True,
passthrough=["foo", "bar", "--whiz"],
)

expected_env = {"a": 1, "b": 2, "c": 3}
expected_env.update(dev_command.DEV_ENVIRONMENT)

dev_command.tools.subprocess.Popen.assert_called_once_with(
[
sys.executable,
"-u",
"-X",
"dev",
"-X",
"utf8",
"-c",
(
"import runpy, sys;"
Expand All @@ -157,7 +153,7 @@ def test_dev_test_mode_with_args(dev_command, first_app, tmp_path):
'runpy.run_module("tests.first", run_name="__main__", alter_sys=True)'
),
],
env={"a": 1, "b": 2, "c": 3},
env=expected_env,
cwd=dev_command.tools.home_path,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
Expand Down

0 comments on commit a800db1

Please # to comment.