diff --git a/news/158.bugfix.rst b/news/158.bugfix.rst new file mode 100644 index 0000000..8d97513 --- /dev/null +++ b/news/158.bugfix.rst @@ -0,0 +1,15 @@ +Fix full-version matching for Python installations discovered via the Windows +py launcher. + +``py --list-paths`` only reports ``major.minor`` (e.g. ``3.11``), so +``PyLauncherFinder`` was storing ``patch=None`` for every entry. Callers that +searched for a specific full version such as ``3.11.9`` (e.g. from +``python_full_version`` in a Pipfile) received no match because +``matches(patch=9)`` evaluated ``None == 9 → False``. + +The finder now queries each discovered executable for its real version string +(e.g. ``3.11.9``) via ``get_python_version()`` after the py-launcher lookup, +so that full-version searches succeed. If the executable cannot be queried the +code falls back gracefully to the ``major.minor`` string reported by the py +launcher. + diff --git a/src/pythonfinder/finders/py_launcher_finder.py b/src/pythonfinder/finders/py_launcher_finder.py index ce08faf..ae4afe5 100644 --- a/src/pythonfinder/finders/py_launcher_finder.py +++ b/src/pythonfinder/finders/py_launcher_finder.py @@ -92,7 +92,10 @@ def _create_python_info_from_py_launcher( Create a PythonInfo object from py launcher information. Args: - version: The Python version (e.g. "3.11"). + version: The Python version reported by the py launcher (e.g. "3.11"). + This is typically only major.minor; the full patch version is + resolved by querying the executable so that searches for a + specific full version (e.g. "3.11.9") succeed. path: The path to the Python executable. is_default: "*" if this is the default version, "" otherwise. @@ -102,7 +105,7 @@ def _create_python_info_from_py_launcher( if not path or not os.path.exists(path): return None - # Parse the version + # Parse the version reported by the py launcher (usually major.minor only). try: version_data = parse_python_version(version) except InvalidPythonVersion: @@ -110,6 +113,22 @@ def _create_python_info_from_py_launcher( raise return None + # `py --list-paths` only reports major.minor (e.g. "3.11"). Resolve the + # real patch version by querying the executable so that callers searching + # for a full version string like "3.11.9" can match correctly. + if version_data.get("patch") is None: + try: + from ..utils.version_utils import get_python_version + + full_version = get_python_version(path) + full_version_data = parse_python_version(full_version) + version_data = full_version_data + version = full_version + except Exception: + # If we can't run the executable, fall back to the major.minor + # version string provided by the py launcher. + pass + # Create the PythonInfo object return PythonInfo( path=Path(path),