From 91b358bf04717baec30fe71638ffa253b039c8fe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 May 2026 16:51:40 +0000 Subject: [PATCH 1/2] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/astral-sh/ruff-pre-commit: v0.15.13 → v0.15.14](https://github.com/astral-sh/ruff-pre-commit/compare/v0.15.13...v0.15.14) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a043dc5..115a438 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -19,7 +19,7 @@ repos: hooks: - id: pyproject-fmt - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.15.13" + rev: "v0.15.14" hooks: - id: ruff-format - id: ruff From 34bdb62fab76434688d1536bfcb1957fc89b330f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bern=C3=A1t=20G=C3=A1bor?= Date: Wed, 27 May 2026 14:04:17 -0700 Subject: [PATCH 2/2] Reduce try block statement counts for PLW0717 Extract helper functions to keep try clauses under the 5-statement limit enforced by ruff 0.15.14. --- src/python_discovery/_discovery.py | 16 ++++--- src/python_discovery/_py_info.py | 58 +++++++++++------------- src/python_discovery/_windows/_pep514.py | 22 ++++----- tasks/release.py | 44 ++++++++++-------- 4 files changed, 72 insertions(+), 68 deletions(-) diff --git a/src/python_discovery/_discovery.py b/src/python_discovery/_discovery.py index 67df15b..ab5cbcf 100644 --- a/src/python_discovery/_discovery.py +++ b/src/python_discovery/_discovery.py @@ -314,13 +314,7 @@ def __repr__(self) -> str: content += " with =>" for file_path in self.path.iterdir(): try: - if file_path.is_dir(): - continue - if IS_WIN: # pragma: win32 cover - pathext = self.env.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";") - if not any(file_path.name.upper().endswith(ext) for ext in pathext): - continue - elif not (file_path.stat().st_mode & os.X_OK): + if not self._is_executable(file_path): continue except OSError: pass @@ -328,6 +322,14 @@ def __repr__(self) -> str: content += file_path.name return content + def _is_executable(self, file_path: Path) -> bool: + if file_path.is_dir(): + return False + if IS_WIN: # pragma: win32 cover + pathext = self.env.get("PATHEXT", ".COM;.EXE;.BAT;.CMD").split(";") + return any(file_path.name.upper().endswith(ext) for ext in pathext) + return bool(file_path.stat().st_mode & os.X_OK) + def path_exe_finder( spec: PythonSpec, *, all_implementations: bool = False diff --git a/src/python_discovery/_py_info.py b/src/python_discovery/_py_info.py index 8b89e95..2679ae4 100644 --- a/src/python_discovery/_py_info.py +++ b/src/python_discovery/_py_info.py @@ -16,6 +16,7 @@ from typing import TYPE_CHECKING, ClassVar, Final, NamedTuple if TYPE_CHECKING: + import tkinter as tk from collections.abc import Generator, Mapping from ._cache import PyInfoCache @@ -165,42 +166,37 @@ def _get_tcl_tk_libs() -> tuple[ try: tcl = tk.Tcl() tcl_lib = tcl.eval("info library") - - # Try to get TK library path directly first - try: - tk_lib = tcl.eval("set tk_library") - if tk_lib and os.path.isdir(tk_lib): - pass # We found it directly - else: - tk_lib = None # Reset if invalid - except tk.TclError: - tk_lib = None - - # If direct query failed, try constructing the path - if tk_lib is None: - tk_version = tcl.eval("package require Tk") - tcl_parent = os.path.dirname(tcl_lib) - - # Try different version formats - version_variants = [ - tk_version, # Full version like "8.6.12" - ".".join(tk_version.split(".")[:2]), # Major.minor like "8.6" - tk_version.split(".")[0], # Just major like "8" - ] - - for version in version_variants: - tk_lib_path = os.path.join(tcl_parent, f"tk{version}") - if not os.path.isdir(tk_lib_path): - continue - if os.path.exists(os.path.join(tk_lib_path, "tk.tcl")): - tk_lib = tk_lib_path - break - + tk_lib = PythonInfo._resolve_tk_lib(tcl, tcl_lib) except tk.TclError: pass return tcl_lib, tk_lib + @staticmethod + def _query_tk_library(tcl: tk.Tk) -> str | None: # pragma: no cover + """Try to get the TK library path directly from Tcl.""" + import tkinter as tk # noqa: PLC0415 + + try: + if (tk_lib := tcl.eval("set tk_library")) and os.path.isdir(tk_lib): + return tk_lib + except tk.TclError: + pass + return None + + @staticmethod + def _resolve_tk_lib(tcl: tk.Tk, tcl_lib: str) -> str | None: # pragma: no cover + """Resolve the TK library path by direct query or path construction.""" + if (tk_lib := PythonInfo._query_tk_library(tcl)) is not None: + return tk_lib + tk_version = tcl.eval("package require Tk") + tcl_parent = os.path.dirname(tcl_lib) + for version in (tk_version, ".".join(tk_version.split(".")[:2]), tk_version.split(".")[0]): + tk_lib_path = os.path.join(tcl_parent, f"tk{version}") + if os.path.isdir(tk_lib_path) and os.path.exists(os.path.join(tk_lib_path, "tk.tcl")): + return tk_lib_path + return None + def _fast_get_system_executable(self) -> str | None: """Try to get the system executable by just looking at properties.""" # if we're not in a virtual environment, this is already a system python, so return the original executable diff --git a/src/python_discovery/_windows/_pep514.py b/src/python_discovery/_windows/_pep514.py index f9f7e40..474747a 100644 --- a/src/python_discovery/_windows/_pep514.py +++ b/src/python_discovery/_windows/_pep514.py @@ -124,23 +124,23 @@ def load_exe(hive_name: str, company: str, company_key: Any, tag: str) -> tuple[ key_path = f"{hive_name}/{company}/{tag}" try: with winreg.OpenKeyEx(company_key, rf"{tag}\InstallPath") as ip_key, ip_key: # ty: ignore[unresolved-attribute] - exe = get_value(ip_key, "ExecutablePath") - if exe is None: - ip = get_value(ip_key, None) - if ip is None: - msg(key_path, "no ExecutablePath or default for it") - - else: - exe = os.path.join(ip, "python.exe") - if exe is not None and os.path.exists(exe): - args = get_value(ip_key, "ExecutableArguments") - return exe, args + if (exe := _resolve_exe(ip_key, key_path)) is not None and os.path.exists(exe): + return exe, get_value(ip_key, "ExecutableArguments") msg(key_path, f"could not load exe with value {exe}") except OSError: msg(f"{key_path}/InstallPath", "missing") return None +def _resolve_exe(ip_key: Any, key_path: str) -> str | None: # noqa: ANN401 + if (exe := get_value(ip_key, "ExecutablePath")) is not None: + return exe + if (ip := get_value(ip_key, None)) is None: + msg(key_path, "no ExecutablePath or default for it") + return None + return os.path.join(ip, "python.exe") + + def load_arch_data(hive_name: str, company: str, tag: str, tag_key: Any, default_arch: int) -> int | None: # noqa: ANN401 arch_str = get_value(tag_key, "SysArchitecture") if arch_str is not None: diff --git a/tasks/release.py b/tasks/release.py index 97a6123..92e0a5d 100644 --- a/tasks/release.py +++ b/tasks/release.py @@ -19,29 +19,13 @@ def main(version_str: str) -> None: msg = "Current repository is dirty. Please commit any changes and try again." raise RuntimeError(msg) upstream, release_branch = create_release_branch(repo, version) + original_main_sha = upstream.refs.main.commit.hexsha main_pushed = False tag_pushed = False release_created = False - original_main_sha = upstream.refs.main.commit.hexsha try: - release_commit = release_changelog(repo, version) - tag = tag_release_commit(release_commit, repo, version) - print("push release commit") # noqa: T201 - repo.git.push(upstream.name, f"{release_branch}:main", "-f") - main_pushed = True - print("push release tag") # noqa: T201 - repo.git.push(upstream.name, tag, "-f") - tag_pushed = True - create_github_release(version) - release_created = True - print("checkout main to new release and delete release branch") # noqa: T201 - repo.heads.main.checkout() - repo.delete_head(release_branch, force=True) - print("delete remote release branch") # noqa: T201 - repo.git.push(upstream.name, f":{release_branch}", "--no-verify") - upstream.fetch() - repo.git.reset("--hard", f"{upstream.name}/main") - print("All done!") # noqa: T201 + main_pushed, tag_pushed, release_created = push_release(repo, upstream, release_branch, version) + finalize_release(repo, upstream, release_branch) except Exception: cleanup_failed_release( repo, @@ -76,6 +60,17 @@ def get_upstream(repo: Repo) -> Remote: raise RuntimeError(msg) +def push_release(repo: Repo, upstream: Remote, release_branch: Head, version: Version) -> tuple[bool, bool, bool]: + release_commit = release_changelog(repo, version) + tag = tag_release_commit(release_commit, repo, version) + print("push release commit") # noqa: T201 + repo.git.push(upstream.name, f"{release_branch}:main", "-f") + print("push release tag") # noqa: T201 + repo.git.push(upstream.name, tag, "-f") + create_github_release(version) + return True, True, True + + def release_changelog(repo: Repo, version: Version) -> Commit: print("generate release commit") # noqa: T201 check_call(["towncrier", "build", "--yes", "--version", version.public], cwd=str(ROOT_SRC_DIR)) # noqa: S607 @@ -121,6 +116,17 @@ def create_github_release(version: Version) -> None: raise +def finalize_release(repo: Repo, upstream: Remote, release_branch: Head) -> None: + print("checkout main to new release and delete release branch") # noqa: T201 + repo.heads.main.checkout() + repo.delete_head(release_branch, force=True) + print("delete remote release branch") # noqa: T201 + repo.git.push(upstream.name, f":{release_branch}", "--no-verify") + upstream.fetch() + repo.git.reset("--hard", f"{upstream.name}/main") + print("All done!") # noqa: T201 + + def cleanup_failed_release( # noqa: PLR0913 repo: Repo, upstream: Remote,