public inbox for git-commits@fedoraproject.org
help / color / mirror / Atom feed
From: Packit <hello@packit.dev>
To: git-commits@fedoraproject.org
Subject: [rpms/python-menuinst] rawhide: Update to 2.5.0 upstream release
Date: Sun, 07 Jun 2026 21:13:12 GMT [thread overview]
Message-ID: <178086679286.1.6522655512007233848.rpms-python-menuinst-ed647c1bafc1@fedoraproject.org> (raw)
A new commit has been pushed.
Repo : rpms/python-menuinst
Branch : rawhide
Commit : ed647c1bafc1761dc80473f79865205f528bc204
Author : Packit <hello@packit.dev>
Date : 2026-06-05T22:02:54-06:00
Stats : +503/-2 in 5 file(s)
URL : https://src.fedoraproject.org/rpms/python-menuinst/c/ed647c1bafc1761dc80473f79865205f528bc204?branch=rawhide
Log:
Update to 2.5.0 upstream release
- Add upstream patch to fix tests
- Resolves: rhbz#2484360
Commit authored by Packit automation (https://packit.dev/)
---
diff --git a/.gitignore b/.gitignore
index cd1e9aa..32420d2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/menuinst-2.0.0.tar.gz
/menuinst-2.3.1.tar.gz
/menuinst-2.4.2.tar.gz
+/menuinst-2.5.0.tar.gz
diff --git a/497.patch b/497.patch
new file mode 100644
index 0000000..81075b5
--- /dev/null
+++ b/497.patch
@@ -0,0 +1,495 @@
+From abbebb028fed2dd544be20652074af72b6728649 Mon Sep 17 00:00:00 2001
+From: Robin <randersson@anaconda.com>
+Date: Fri, 5 Jun 2026 09:59:31 -0400
+Subject: [PATCH 1/4] Gracefully handle permission errors with menuinst.toml
+
+---
+ menuinst/api.py | 17 ++++++++++--
+ tests/test_api.py | 4 +--
+ tests/test_metadata.py | 62 ++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 79 insertions(+), 4 deletions(-)
+
+diff --git a/menuinst/api.py b/menuinst/api.py
+index 5481a58f..211d1cc0 100644
+--- a/menuinst/api.py
++++ b/menuinst/api.py
+@@ -62,7 +62,14 @@ def record_shortcuts(
+ for path in paths:
+ shortcuts.append({"source": source, "path": str(path)})
+
+- write_menuinst_toml(prefix, data)
++ try:
++ write_menuinst_toml(prefix, data)
++ except PermissionError:
++ log.debug(
++ "Cannot write menuinst.toml to %s (permission denied). "
++ "Shortcut tracking will not be available for this prefix.",
++ prefix / "Menu",
++ )
+
+
+ def remove_shortcut_records(prefix: Path, source: str) -> None:
+@@ -86,7 +93,13 @@ def remove_shortcut_records(prefix: Path, source: str) -> None:
+ return # Nothing was removed
+
+ data["shortcuts"] = filtered
+- write_menuinst_toml(prefix, data)
++ try:
++ write_menuinst_toml(prefix, data)
++ except PermissionError:
++ log.debug(
++ "Cannot update menuinst.toml at %s (permission denied).",
++ prefix / "Menu",
++ )
+
+
+ def _load(
+diff --git a/tests/test_api.py b/tests/test_api.py
+index fc2e6c9c..0ed595d9 100644
+--- a/tests/test_api.py
++++ b/tests/test_api.py
+@@ -63,7 +63,7 @@ def check_output_from_shortcut(
+ tmp_base_path = mkdtemp()
+ delete_files.append(tmp_base_path)
+ (Path(tmp_base_path) / ".nonadmin").touch()
+- paths = install(abs_json_path, base_prefix=tmp_base_path)
++ paths = install(abs_json_path, target_prefix=sys.prefix, base_prefix=tmp_base_path)
+ try:
+ if action == "run_shortcut":
+ if PLATFORM == "win":
+@@ -114,7 +114,7 @@ def check_output_from_shortcut(
+ if paths:
+ delete_files += list(paths)
+ if remove_after:
+- remove(abs_json_path, base_prefix=tmp_base_path)
++ remove(abs_json_path, target_prefix=sys.prefix, base_prefix=tmp_base_path)
+ if PLATFORM == "osx" and action in ("open_file", "open_url"):
+ _lsregister(
+ "-kill",
+diff --git a/tests/test_metadata.py b/tests/test_metadata.py
+index 10ee7ef4..9b44365f 100644
+--- a/tests/test_metadata.py
++++ b/tests/test_metadata.py
+@@ -3,6 +3,9 @@
+ from __future__ import annotations
+
+ import json
++import logging
++import os
++import sys
+ from typing import TYPE_CHECKING
+
+ import pytest
+@@ -155,6 +158,65 @@ def test_distribution_name_only_written_to_base_prefix(self, tmp_path: Path) ->
+ assert "distribution_name" not in data
+ assert len(data["shortcuts"]) == 1
+
++ @pytest.mark.skipif(sys.platform == "win32", reason="chmod doesn't work on Windows")
++ def test_record_shortcuts_handles_permission_error(
++ self, tmp_path: Path, caplog
++ ) -> None:
++ """record_shortcuts() should not raise when prefix is read-only."""
++ # Create a read-only directory
++ readonly_prefix = tmp_path / "readonly"
++ readonly_prefix.mkdir()
++ os.chmod(readonly_prefix, 0o555)
++
++ try:
++ with caplog.at_level(logging.DEBUG):
++ # This should NOT raise PermissionError
++ record_shortcuts(
++ prefix=readonly_prefix,
++ base_prefix=readonly_prefix,
++ source="test.json",
++ paths=[tmp_path / "fake" / "path" / "shortcut.desktop"],
++ )
++
++ # Should log a debug message about permission denied
++ assert "permission denied" in caplog.text.lower()
++ finally:
++ # Restore permissions for cleanup
++ os.chmod(readonly_prefix, 0o755)
++
++ @pytest.mark.skipif(sys.platform == "win32", reason="chmod doesn't work on Windows")
++ def test_remove_shortcut_records_handles_permission_error(
++ self, tmp_path: Path, caplog
++ ) -> None:
++ """remove_shortcut_records() should not raise when prefix is read-only."""
++ # Create a prefix with a menuinst.toml
++ prefix = tmp_path / "prefix"
++ prefix.mkdir()
++ menu_dir = prefix / "Menu"
++ menu_dir.mkdir()
++
++ # Write initial TOML data
++ write_menuinst_toml(
++ prefix,
++ {"shortcuts": [{"source": "test.json", "path": "/fake/path"}]},
++ )
++
++ # Make the directory read-only
++ os.chmod(menu_dir, 0o555)
++ os.chmod(prefix, 0o555)
++
++ try:
++ with caplog.at_level(logging.DEBUG):
++ # This should NOT raise PermissionError
++ remove_shortcut_records(prefix, "test.json")
++
++ # Should log a debug message about permission denied
++ assert "permission denied" in caplog.text.lower()
++ finally:
++ # Restore permissions for cleanup
++ os.chmod(prefix, 0o755)
++ os.chmod(menu_dir, 0o755)
++
+
+ class TestInstallAdapter:
+ """Tests for _install_adapter recording correct source filename."""
+
+From 9001da642cd5866d11a776b4ae1575e3d00fb832 Mon Sep 17 00:00:00 2001
+From: Robin <randersson@anaconda.com>
+Date: Fri, 5 Jun 2026 11:59:07 -0400
+Subject: [PATCH 2/4] Update tests
+
+---
+ tests/test_api.py | 49 ++++++++++++++++++++++++++++++------------
+ tests/test_metadata.py | 4 +---
+ 2 files changed, 36 insertions(+), 17 deletions(-)
+
+diff --git a/tests/test_api.py b/tests/test_api.py
+index 0ed595d9..17ea419b 100644
+--- a/tests/test_api.py
++++ b/tests/test_api.py
+@@ -60,10 +60,29 @@ def check_output_from_shortcut(
+ abs_json_path = tmp.name
+ delete_files.append(abs_json_path)
+
+- tmp_base_path = mkdtemp()
+- delete_files.append(tmp_base_path)
+- (Path(tmp_base_path) / ".nonadmin").touch()
+- paths = install(abs_json_path, target_prefix=sys.prefix, base_prefix=tmp_base_path)
++ # Windows file/URL association tests need sys.prefix because symlinked Python
++ # doesn't work through the file association handler (registry lookup -> cmd -> batch).
++ # These tests only run on CI where sys.prefix is a writable conda environment.
++ use_real_prefix = PLATFORM == "win" and action in ("open_file", "open_url")
++
++ if use_real_prefix:
++ tmp_base_path = sys.prefix
++ else:
++ tmp_base_path = mkdtemp()
++ delete_files.append(tmp_base_path)
++ (Path(tmp_base_path) / ".nonadmin").touch()
++ # conda-meta makes conda treat this as a valid environment for activation
++ (Path(tmp_base_path) / "conda-meta").mkdir(exist_ok=True)
++ # Symlink to real Python so {{ PYTHON }} placeholder works
++ if PLATFORM == "win":
++ python_path = Path(tmp_base_path) / "python.exe"
++ else:
++ bin_dir = Path(tmp_base_path) / "bin"
++ bin_dir.mkdir(exist_ok=True)
++ python_path = bin_dir / "python"
++ python_path.symlink_to(sys.executable)
++
++ paths = install(abs_json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+ try:
+ if action == "run_shortcut":
+ if PLATFORM == "win":
+@@ -114,7 +133,7 @@ def check_output_from_shortcut(
+ if paths:
+ delete_files += list(paths)
+ if remove_after:
+- remove(abs_json_path, target_prefix=sys.prefix, base_prefix=tmp_base_path)
++ remove(abs_json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+ if PLATFORM == "osx" and action in ("open_file", "open_url"):
+ _lsregister(
+ "-kill",
+@@ -273,7 +292,7 @@ def test_precommands(delete_files):
+
+ @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only")
+ def test_entitlements(delete_files):
+- json_path, paths, *_ = check_output_from_shortcut(
++ json_path, paths, tmp_base_path, _ = check_output_from_shortcut(
+ delete_files, "entitlements.json", remove_after=False, expected_output="entitlements"
+ )
+ # verify signature
+@@ -300,12 +319,12 @@ def test_entitlements(delete_files):
+ else:
+ raise AssertionError("Didn't find Entitlements.plist")
+
+- remove(json_path)
++ remove(json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+
+
+ @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only")
+ def test_no_entitlements_no_signature(delete_files):
+- json_path, paths, *_ = check_output_from_shortcut(
++ json_path, paths, tmp_base_path, _ = check_output_from_shortcut(
+ delete_files, "sys-prefix.json", remove_after=False, expected_output=sys.prefix
+ )
+ app_dir = next(p for p in paths if p.name.endswith(".app"))
+@@ -316,12 +335,12 @@ def test_no_entitlements_no_signature(delete_files):
+ subprocess.check_call(["/usr/bin/codesign", "--verbose", "--verify", str(app_dir)])
+ with pytest.raises(subprocess.CalledProcessError):
+ subprocess.check_call(["/usr/bin/codesign", "--verbose", "--verify", str(launcher)])
+- remove(json_path)
++ remove(json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+
+
+ @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only")
+ def test_info_plist(delete_files):
+- json_path, paths, *_ = check_output_from_shortcut(
++ json_path, paths, tmp_base_path, _ = check_output_from_shortcut(
+ delete_files, "entitlements.json", remove_after=False, expected_output="entitlements"
+ )
+ metadata = json.loads(json_path.read_text())
+@@ -354,7 +373,7 @@ def test_info_plist(delete_files):
+ assert missing_items == []
+ assert incorrect_items == {}
+
+- remove(json_path)
++ remove(json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+
+
+ @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only")
+@@ -381,14 +400,14 @@ def test_info_plist_duplicate():
+
+ @pytest.mark.skipif(PLATFORM != "osx", reason="macOS only")
+ def test_osx_symlinks(delete_files):
+- json_path, paths, _, output = check_output_from_shortcut(
++ json_path, paths, tmp_base_path, output = check_output_from_shortcut(
+ delete_files, "osx_symlinks.json", remove_after=False
+ )
+ app_dir = next(p for p in paths if p.name.endswith(".app"))
+ symlinked_python = app_dir / "Contents" / "Resources" / "python"
+ assert output.strip() == str(symlinked_python)
+ assert symlinked_python.resolve() == (Path(DEFAULT_PREFIX) / "bin" / "python").resolve()
+- remove(json_path)
++ remove(json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
+
+
+ def _dump_ls_services():
+@@ -423,7 +442,9 @@ def test_file_type_association_no_event_handler(delete_files, request):
+ file_to_open=test_file,
+ remove_after=False,
+ )
+- request.addfinalizer(lambda: remove(abs_json_path, base_prefix=tmp_base_path))
++ request.addfinalizer(
++ lambda: remove(abs_json_path, target_prefix=tmp_base_path, base_prefix=tmp_base_path)
++ )
+ app_dir = next(p for p in paths if p.name.endswith(".app"))
+ info = app_dir / "Contents" / "Info.plist"
+ plist = plistlib.loads(info.read_bytes())
+diff --git a/tests/test_metadata.py b/tests/test_metadata.py
+index 9b44365f..f47bd301 100644
+--- a/tests/test_metadata.py
++++ b/tests/test_metadata.py
+@@ -159,9 +159,7 @@ def test_distribution_name_only_written_to_base_prefix(self, tmp_path: Path) ->
+ assert len(data["shortcuts"]) == 1
+
+ @pytest.mark.skipif(sys.platform == "win32", reason="chmod doesn't work on Windows")
+- def test_record_shortcuts_handles_permission_error(
+- self, tmp_path: Path, caplog
+- ) -> None:
++ def test_record_shortcuts_handles_permission_error(self, tmp_path: Path, caplog) -> None:
+ """record_shortcuts() should not raise when prefix is read-only."""
+ # Create a read-only directory
+ readonly_prefix = tmp_path / "readonly"
+
+From 66dd735f561bbc89bfc6d2633f6da856531ccb59 Mon Sep 17 00:00:00 2001
+From: Robin <randersson@anaconda.com>
+Date: Fri, 5 Jun 2026 12:11:03 -0400
+Subject: [PATCH 3/4] Add news
+
+---
+ news/497-improve-tests | 19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+ create mode 100644 news/497-improve-tests
+
+diff --git a/news/497-improve-tests b/news/497-improve-tests
+new file mode 100644
+index 00000000..23b95cf3
+--- /dev/null
++++ b/news/497-improve-tests
+@@ -0,0 +1,19 @@
++### Enhancements
++
++* <news item>
++
++### Bug fixes
++
++* Gracefully handle permission errors when writing `menuinst.toml` in read-only environments. (#496)
++
++### Deprecations
++
++* <news item>
++
++### Docs
++
++* <news item>
++
++### Other
++
++* Refactor some tests to use temporary directories instead of using `sys.prefix`. (#496)
+
+From 30e7f963651b08e5cbc4ba2b25c9d3e55afd5c9a Mon Sep 17 00:00:00 2001
+From: Robin <randersson@anaconda.com>
+Date: Fri, 5 Jun 2026 14:32:33 -0400
+Subject: [PATCH 4/4] Review fixes
+
+---
+ tests/test_api.py | 3 +-
+ tests/test_metadata.py | 83 ++++++++++++++++++++++--------------------
+ 2 files changed, 45 insertions(+), 41 deletions(-)
+
+diff --git a/tests/test_api.py b/tests/test_api.py
+index 17ea419b..ec149e18 100644
+--- a/tests/test_api.py
++++ b/tests/test_api.py
+@@ -73,7 +73,8 @@ def check_output_from_shortcut(
+ (Path(tmp_base_path) / ".nonadmin").touch()
+ # conda-meta makes conda treat this as a valid environment for activation
+ (Path(tmp_base_path) / "conda-meta").mkdir(exist_ok=True)
+- # Symlink to real Python so {{ PYTHON }} placeholder works
++ # Shortcuts use {{ PYTHON }} which resolves to prefix/python.exe or prefix/bin/python.
++ # We symlink to sys.executable; copying doesn't work (can't find runtime libraries).
+ if PLATFORM == "win":
+ python_path = Path(tmp_base_path) / "python.exe"
+ else:
+diff --git a/tests/test_metadata.py b/tests/test_metadata.py
+index f47bd301..00209d84 100644
+--- a/tests/test_metadata.py
++++ b/tests/test_metadata.py
+@@ -5,14 +5,13 @@
+ import json
+ import logging
+ import os
++import subprocess
+ import sys
++from contextlib import contextmanager
+ from typing import TYPE_CHECKING
+
+ import pytest
+
+-if TYPE_CHECKING:
+- from pathlib import Path
+-
+ from menuinst.api import (
+ _install_adapter,
+ record_shortcuts,
+@@ -22,11 +21,31 @@
+ from menuinst.platforms import Menu
+ from menuinst.utils import MENUINST_TOML_SCHEMA_VERSION, parse_schemaver, read_menuinst_toml
+
++if TYPE_CHECKING:
++ from pathlib import Path
++
+ # Placeholder distribution names for tests
+ DIST_NAME = "Something"
+ DIST_NAME_ALT = "SomethingElse"
+
+
++@contextmanager
++def make_readonly(path: "Path"):
++ """Make a path read-only, restoring permissions on exit."""
++ if sys.platform == "win32":
++ subprocess.run(["icacls", str(path), "/deny", f"{os.getlogin()}:(W)"], check=True)
++ try:
++ yield
++ finally:
++ subprocess.run(["icacls", str(path), "/remove:d", os.getlogin()], check=True)
++ else:
++ os.chmod(path, 0o555)
++ try:
++ yield
++ finally:
++ os.chmod(path, 0o755)
++
++
+ class TestGetDistributionName:
+ """Tests for Menu._get_distribution_name() resolution order."""
+
+@@ -158,62 +177,46 @@ def test_distribution_name_only_written_to_base_prefix(self, tmp_path: Path) ->
+ assert "distribution_name" not in data
+ assert len(data["shortcuts"]) == 1
+
+- @pytest.mark.skipif(sys.platform == "win32", reason="chmod doesn't work on Windows")
+ def test_record_shortcuts_handles_permission_error(self, tmp_path: Path, caplog) -> None:
+ """record_shortcuts() should not raise when prefix is read-only."""
+- # Create a read-only directory
+- readonly_prefix = tmp_path / "readonly"
+- readonly_prefix.mkdir()
+- os.chmod(readonly_prefix, 0o555)
++ prefix = tmp_path / "readonly"
++ prefix.mkdir()
++ menu_dir = prefix / "Menu"
++ menu_dir.mkdir()
+
+- try:
+- with caplog.at_level(logging.DEBUG):
+- # This should NOT raise PermissionError
+- record_shortcuts(
+- prefix=readonly_prefix,
+- base_prefix=readonly_prefix,
+- source="test.json",
+- paths=[tmp_path / "fake" / "path" / "shortcut.desktop"],
+- )
+-
+- # Should log a debug message about permission denied
+- assert "permission denied" in caplog.text.lower()
+- finally:
+- # Restore permissions for cleanup
+- os.chmod(readonly_prefix, 0o755)
++ # Pre-create empty TOML so the test is consistent with test_remove_* below
++ write_menuinst_toml(prefix, {})
++
++ with make_readonly(menu_dir), make_readonly(prefix), caplog.at_level(logging.DEBUG):
++ # This should NOT raise PermissionError
++ record_shortcuts(
++ prefix=prefix,
++ base_prefix=prefix,
++ source="test.json",
++ paths=[tmp_path / "fake" / "path" / "shortcut.desktop"],
++ )
++
++ assert "permission denied" in caplog.text.lower()
+
+- @pytest.mark.skipif(sys.platform == "win32", reason="chmod doesn't work on Windows")
+ def test_remove_shortcut_records_handles_permission_error(
+ self, tmp_path: Path, caplog
+ ) -> None:
+ """remove_shortcut_records() should not raise when prefix is read-only."""
+- # Create a prefix with a menuinst.toml
+ prefix = tmp_path / "prefix"
+ prefix.mkdir()
+ menu_dir = prefix / "Menu"
+ menu_dir.mkdir()
+
+- # Write initial TOML data
+ write_menuinst_toml(
+ prefix,
+ {"shortcuts": [{"source": "test.json", "path": "/fake/path"}]},
+ )
+
+- # Make the directory read-only
+- os.chmod(menu_dir, 0o555)
+- os.chmod(prefix, 0o555)
++ with make_readonly(menu_dir), make_readonly(prefix), caplog.at_level(logging.DEBUG):
++ # This should NOT raise PermissionError
++ remove_shortcut_records(prefix, "test.json")
+
+- try:
+- with caplog.at_level(logging.DEBUG):
+- # This should NOT raise PermissionError
+- remove_shortcut_records(prefix, "test.json")
+-
+- # Should log a debug message about permission denied
+- assert "permission denied" in caplog.text.lower()
+- finally:
+- # Restore permissions for cleanup
+- os.chmod(prefix, 0o755)
+- os.chmod(menu_dir, 0o755)
++ assert "permission denied" in caplog.text.lower()
+
+
+ class TestInstallAdapter:
diff --git a/README.packit b/README.packit
new file mode 100644
index 0000000..4e76cbe
--- /dev/null
+++ b/README.packit
@@ -0,0 +1,3 @@
+This repository is maintained by packit.
+https://packit.dev/
+The file was generated using packit 1.16.0.post1.dev8+g8a0482385.
diff --git a/python-menuinst.spec b/python-menuinst.spec
index 88e6a05..0b3f3d3 100644
--- a/python-menuinst.spec
+++ b/python-menuinst.spec
@@ -3,13 +3,15 @@
%bcond bootstrap 0
Name: python-%{srcname}
-Version: 2.4.2
+Version: 2.5.0
Release: %autorelease
Summary: Cross platform menu item installation
License: BSD-3-Clause
URL: https://github.com/conda/menuinst
Source: %{url}/archive/%{version}/%{srcname}-%{version}.tar.gz
+# Upstream patch to fix tests
+Patch: https://github.com/conda/menuinst/pull/497.patch
BuildArch: noarch
%if %{without bootstrap}
diff --git a/sources b/sources
index 96afd57..d5455e8 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-SHA512 (menuinst-2.4.2.tar.gz) = 3385f2a5cfcd47fe2d9170a28ca33e12f9e92ea884f48327dc5ff631322abcb3c0b547550cb95fc52ca2e51ae52cadbcadaac03aec58dc3e1e5f18b7dcf19199
+SHA512 (menuinst-2.5.0.tar.gz) = aa50d1b956d57cd988c97b36ebef34d2e122980f7e807e67b27b1431576d76cc9c62a48c803bb00889d185600c526db8ad06a4be2201663c7b1a2339af3f5864
reply other threads:[~2026-06-07 21:13 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=178086679286.1.6522655512007233848.rpms-python-menuinst-ed647c1bafc1@fedoraproject.org \
--to=hello@packit.dev \
--cc=git-commits@fedoraproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox