Skip to content

Commit 84552e5

Browse files
authored
Merge pull request #78 from DavidCEllis/dedupe-linux
Deduplicate results on Linux
2 parents d18844d + 52636b4 commit 84552e5

File tree

4 files changed

+35
-11
lines changed

4 files changed

+35
-11
lines changed

src/ducktools/pythonfinder/__main__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,8 @@ def display_local_installs(
190190

191191
alternate_implementations = False
192192

193+
real_sys_executable = os.path.realpath(sys.executable)
194+
193195
# First collect the strings
194196
for install in installs:
195197
if min_ver and install.version < min_ver_tuple:
@@ -207,7 +209,7 @@ def display_local_installs(
207209
if install.architecture == "32bit":
208210
version_str = f"^{version_str}"
209211

210-
if install.executable == sys.executable:
212+
if install.real_executable == real_sys_executable:
211213
version_str = f"*{version_str}"
212214
elif (
213215
sys.prefix != sys.base_prefix

src/ducktools/pythonfinder/darwin/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def get_path_pythons(
4848
finder: DetailFinder | None = None,
4949
known_paths: dict[str, str] | None = None,
5050
) -> Iterator[PythonInstall]:
51-
51+
5252
known_paths = KNOWN_MANAGED_PATHS if known_paths is None else known_paths
5353

5454
return linux.get_path_pythons(finder=finder, known_paths=known_paths)
@@ -69,6 +69,6 @@ def get_python_installs(
6969
]
7070
with finder:
7171
for py in itertools.chain.from_iterable(chain_commands):
72-
if py.executable not in listed_pythons:
72+
if py.real_executable not in listed_pythons:
7373
yield py
74-
listed_pythons.add(py.executable)
74+
listed_pythons.add(py.real_executable)

src/ducktools/pythonfinder/linux/__init__.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,17 @@ def get_python_installs(
9898
*,
9999
finder: DetailFinder | None = None,
100100
) -> Iterator[PythonInstall]:
101-
listed_pythons = set()
101+
listed_bins: set[str] = set()
102102

103103
finder = DetailFinder() if finder is None else finder
104104

105-
chain_commands = [
105+
chain_commands: list[Iterator[PythonInstall]] = [
106106
get_pyenv_pythons(finder=finder),
107107
get_uv_pythons(finder=finder),
108108
get_path_pythons(finder=finder),
109109
]
110110
with finder:
111111
for py in itertools.chain.from_iterable(chain_commands):
112-
if py.executable not in listed_pythons:
112+
if py.real_executable not in listed_bins:
113113
yield py
114-
listed_pythons.add(py.executable)
114+
listed_bins.add(py.real_executable)

src/ducktools/pythonfinder/shared.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class PythonInstall(Prefab):
327327
paths: dict[str, str] = attribute(default_factory=dict)
328328
shadowed: bool = attribute(default=False, serialize=False)
329329
_implementation_version: tuple[int, int, int, str, int] | None = attribute(default=None, private=True)
330+
_real_executable: str | None = attribute(default=None, private=True)
330331

331332
def __prefab_post_init__(
332333
self,
@@ -337,14 +338,34 @@ def __prefab_post_init__(
337338
# Add the extras to avoid breaking
338339
self.version = tuple([*version, "final", 0]) # type: ignore
339340
else:
340-
self.version = version
341+
self.version = version # type: ignore
342+
343+
@property
344+
def real_executable(self) -> str:
345+
"""
346+
:return: Path to the executable with any symlinks resolved
347+
"""
348+
if self._real_executable is None:
349+
self._real_executable = os.path.realpath(self.executable)
350+
return self._real_executable
341351

342352
@property
343353
def version_str(self) -> str:
354+
"""
355+
:return: Python version as a string
356+
"""
344357
return version_tuple_to_str(self.version)
345358

346359
@property
347360
def implementation_version(self) -> tuple[int, int, int, str, int] | None:
361+
"""
362+
The implementation version may differ from the 'Version' for implementations
363+
other than CPython.
364+
365+
This specifically returns the version of the implementation
366+
367+
:return: Implementation version as tuple (major, minor, micro, releaselevel, serial)
368+
"""
348369
if self._implementation_version is None:
349370
if implementation_ver := self.metadata.get(f"{self.implementation}_version"):
350371
if len(implementation_ver) == 3:
@@ -358,10 +379,11 @@ def implementation_version(self) -> tuple[int, int, int, str, int] | None:
358379

359380
@property
360381
def implementation_version_str(self) -> str:
382+
"""
383+
:return: Version of the implementation as a string
384+
"""
361385
return version_tuple_to_str(self.implementation_version)
362386

363-
# Typing these classmethods would require an import
364-
# This is not acceptable for performance reasons
365387
@classmethod
366388
def from_str(
367389
cls,

0 commit comments

Comments
 (0)