Skip to content

Commit 8754281

Browse files
authored
Export DESKTOP_FILE for PEX scie .desktop apps. (#3100)
1 parent dd3de21 commit 8754281

File tree

8 files changed

+104
-13
lines changed

8 files changed

+104
-13
lines changed

CHANGES.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Release Notes
22

3+
## 2.89.0
4+
5+
This release exports the path of the installed `.desktop` file as the `DESKTOP_FILE` environment
6+
variable for commands in `--scie-icon` and `--scie-desktop-file` PEX scies. The `DESKTOP_FILE`
7+
path may not exist, but if it does it can be used to implement desktop application uninstallation
8+
in the PEX scie application code.
9+
10+
* Export `DESKTOP_FILE` for PEX scie .desktop apps. (#3100)
11+
312
## 2.88.1
413

514
This release fixes `.desktop` files installed by `--scie-icon` and `--scie-desktop-file` PEX scies

pex/scie/configure-binding.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ def write_bindings(
2222
env_file, # type: str
2323
pex, # type: str
2424
venv_bin_dir=None, # type: Optional[str]
25+
desktop_file=None, # type: Optional[str]
2526
):
2627
# type: (...) -> None
2728

@@ -31,6 +32,8 @@ def write_bindings(
3132
if venv_bin_dir:
3233
print("VIRTUAL_ENV=" + os.path.dirname(venv_bin_dir), file=fp)
3334
print("VENV_BIN_DIR_PLUS_SEP=" + venv_bin_dir + os.path.sep, file=fp)
35+
if desktop_file:
36+
print("DESKTOP_FILE=" + desktop_file, file=fp)
3437

3538

3639
class PexDirNotFound(Exception):
@@ -67,15 +70,14 @@ def _desktop_install_path(app_name):
6770
def desktop_install(
6871
app_name, # type: str
6972
desktop_file, # type: str
73+
desktop_install_path, # type: str
7074
scie_jump, # type: str
7175
scie_lift, # type: str
7276
scie_exe, # type: str
7377
icon=None, # type: Optional[str]
7478
):
7579
# type: (...) -> None
7680

77-
desktop_install_path = _desktop_install_path(app_name)
78-
7981
try:
8082
os.makedirs(os.path.dirname(desktop_install_path))
8183
except OSError as e:
@@ -105,23 +107,21 @@ class UninstallError(Exception):
105107
pass
106108

107109

108-
def desktop_uninstall(app_name):
110+
def desktop_uninstall(desktop_file):
109111
# type: (str) -> None
110-
desktop_install_path = _desktop_install_path(app_name)
111112
try:
112-
os.unlink(desktop_install_path)
113+
os.unlink(desktop_file)
113114
except OSError as e:
114115
if e.errno != errno.ENOENT:
115116
raise UninstallError(
116-
"Failed to uninstall {desktop_file}: {err}".format(
117-
desktop_file=desktop_install_path, err=e
118-
)
117+
"Failed to uninstall {desktop_file}: {err}".format(desktop_file=desktop_file, err=e)
119118
)
120119

121120

122121
def prompt_desktop_install(
123122
app_name, # type: str
124123
desktop_file, # type: str
124+
desktop_install_path, # type: str
125125
scie_jump, # type: str
126126
scie_lift, # type: str
127127
scie_exe, # type: str
@@ -142,6 +142,7 @@ def prompt_desktop_install(
142142
desktop_install(
143143
app_name=app_name,
144144
desktop_file=desktop_file,
145+
desktop_install_path=desktop_install_path,
145146
scie_jump=scie_jump,
146147
scie_lift=scie_lift,
147148
scie_exe=scie_exe,
@@ -187,7 +188,9 @@ def prompt_desktop_install(
187188
)
188189
)
189190

191+
desktop_install_path = None # type: Optional[str]
190192
if options.desktop_file:
193+
desktop_install_path = _desktop_install_path(options.scie_name)
191194
exe = os.environ["SCIE"]
192195
override_install_desktop_file = os.environ.get("CONFIGURE_DESKTOP_INSTALL", "").lower()
193196
if override_install_desktop_file == "prompt" or (
@@ -196,6 +199,7 @@ def prompt_desktop_install(
196199
prompt_desktop_install(
197200
app_name=options.scie_name,
198201
desktop_file=options.desktop_file,
202+
desktop_install_path=desktop_install_path,
199203
scie_jump=options.scie_jump,
200204
scie_lift=options.scie_lift,
201205
scie_exe=exe,
@@ -207,17 +211,19 @@ def prompt_desktop_install(
207211
desktop_install(
208212
app_name=options.scie_name,
209213
desktop_file=options.desktop_file,
214+
desktop_install_path=desktop_install_path,
210215
scie_jump=options.scie_jump,
211216
scie_lift=options.scie_lift,
212217
scie_exe=exe,
213218
icon=options.icon,
214219
)
215220
elif override_install_desktop_file == "uninstall":
216-
desktop_uninstall(app_name=options.scie_name)
221+
desktop_uninstall(desktop_file=desktop_install_path)
217222

218223
write_bindings(
219224
env_file=os.environ["SCIE_BINDING_ENV"],
220225
pex=pex,
221226
venv_bin_dir=os.path.join(pex, options.venv_bin_dir) if options.venv_bin_dir else None,
227+
desktop_file=desktop_install_path,
222228
)
223229
sys.exit(0)

pex/scie/science.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ def create_commands(platform):
165165
replace[env_name] = "{{scie.bindings.resources:BIND_RESOURCE_{env_name}}}".format(
166166
env_name=env_name
167167
)
168+
if configuration.options.desktop_app:
169+
replace["DESKTOP_FILE"] = "{scie.bindings.configure:DESKTOP_FILE}"
168170
if replace:
169171
env["replace"] = replace
170172

@@ -234,6 +236,9 @@ def create_cmd(named_entry_point):
234236
remove_exact.append("PEX_SCRIPT")
235237
replace["PEX_MODULE"] = str(named_entry_point.entry_point)
236238

239+
if configuration.options.desktop_app:
240+
replace["DESKTOP_FILE"] = "{scie.bindings.configure:DESKTOP_FILE}"
241+
237242
return {
238243
"name": named_entry_point.name,
239244
"env": env,
@@ -294,6 +299,8 @@ def create_cmd(named_entry_point):
294299
else:
295300
cmd["exe"] = script
296301
cmd["args"] = pex_info.inject_args
302+
if configuration.options.desktop_app:
303+
cmd["env"]["replace"]["DESKTOP_FILE"] = "{scie.bindings.configure:DESKTOP_FILE}"
297304
yield cmd
298305
else:
299306
cmd = {
@@ -309,6 +316,8 @@ def create_cmd(named_entry_point):
309316
}
310317
if pex_info.venv:
311318
cmd["env"]["replace"]["VIRTUAL_ENV"] = "{scie.bindings.configure:VIRTUAL_ENV}"
319+
if configuration.options.desktop_app:
320+
cmd["env"]["replace"]["DESKTOP_FILE"] = "{scie.bindings.configure:DESKTOP_FILE}"
312321
yield cmd
313322

314323
# Try to give the PEX the extracted filename expected by the user. This should work in almost

pex/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# Copyright 2015 Pex project contributors.
22
# Licensed under the Apache License, Version 2.0 (see LICENSE).
33

4-
__version__ = "2.88.1"
4+
__version__ = "2.89.0"

tests/integration/cli/commands/test_issue_1741.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@ def test_prereleases(
3030
# N.B.: httpx released an incompatible pre-release itself (1.0.dev1) on 7/2/2025.
3131
# This constraint avoids that.
3232
print("httpx<1", file=fp)
33+
# N.B.: pkg_resources was removed from setuptools in the 82.0.0 release and is used by
34+
# opentelemetry.instrumentation.dependencies.
35+
print("setuptools<82", file=fp)
3336

3437
lockfile = tmpdir.join("lock")
3538
run_pex3(

tests/integration/resolve/pep_691/test_api.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ def test_invalid_json():
5353
# type: () -> None
5454

5555
assert_error(
56-
url="https://example.org",
56+
url="https://docs.pex-tool.org",
5757
content_type="application/vnd.pypi.simple.v1+json",
5858
expected_exception_type=Client.Error,
5959
prefix=(
60-
"PEP-691 API request to https://example.org for application/vnd.pypi.simple.v1+json "
60+
"PEP-691 API request to https://docs.pex-tool.org for application/vnd.pypi.simple.v1+json "
6161
"returned invalid JSON: "
6262
),
6363
)

tests/integration/test_issue_749.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ def test_pkg_resource_early_import_on_pex_path():
4242
run_pex_command(
4343
[
4444
"autopep8==1.6.0",
45-
"setuptools",
45+
# N.B.: pkg_resources was removed from setuptools in the 82.0.0 release.
46+
"setuptools<82",
4647
"-D",
4748
src_dir,
4849
"--entry-point",

tests/integration/test_scie_desktop_install.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,66 @@ def assert_quoting(string):
271271
assert_desktop_entry(
272272
scie_base=tmpdir.join("nce-space"), xdg_data_home=safe_mkdir(tmpdir.join("XDG DATA HOME"))
273273
)
274+
275+
276+
@skip_if_no_provider
277+
@pytest.mark.skipif(not IS_LINUX, reason="PEX scie desktop installs are only supported for Linux.")
278+
def test_desktop_file_env_var(tmpdir):
279+
# type: (Tempdir) -> None
280+
281+
pex_root = tmpdir.join("pex-root")
282+
icon = tmpdir.join("file.ico")
283+
icon_contents = "42"
284+
with open(icon, "w") as fp:
285+
fp.write(icon_contents)
286+
287+
exe = tmpdir.join("exe.py")
288+
with open(exe, "w") as fp:
289+
fp.write(
290+
dedent(
291+
"""\
292+
import os
293+
import sys
294+
295+
296+
desktop_file = os.environ["DESKTOP_FILE"]
297+
if sys.argv[1:] == ["--uninstall"]:
298+
os.unlink(desktop_file)
299+
else:
300+
print(desktop_file)
301+
"""
302+
)
303+
)
304+
305+
scie = tmpdir.join("example")
306+
run_pex_command(
307+
args=[
308+
"--runtime-pex-root",
309+
pex_root,
310+
"--exe",
311+
exe,
312+
"--scie",
313+
"eager",
314+
"--scie-only",
315+
"--scie-icon",
316+
icon,
317+
"--no-scie-prompt-desktop-install",
318+
"-o",
319+
scie,
320+
]
321+
).assert_success()
322+
323+
xdg_data_home = safe_mkdir(tmpdir.join("XDG_DATA_HOME"))
324+
installed_desktop_file = os.path.join(xdg_data_home, "applications", "example.desktop")
325+
assert not os.path.isfile(installed_desktop_file)
326+
327+
scie_base = tmpdir.join("nce")
328+
env = make_env(SCIE_BASE=scie_base, XDG_DATA_HOME=xdg_data_home)
329+
assert (
330+
installed_desktop_file
331+
== subprocess.check_output(args=[scie], env=env).decode("utf-8").strip()
332+
)
333+
assert os.path.isfile(installed_desktop_file)
334+
335+
subprocess.check_call(args=[scie, "--uninstall"], env=env)
336+
assert not os.path.isfile(installed_desktop_file)

0 commit comments

Comments
 (0)