Last active
August 17, 2024 03:58
-
-
Save westurner/80e81cd4bf9b79acb5989622f2621655 to your computer and use it in GitHub Desktop.
find all python*.exe and pip*.exe binaries on os.environ['PATH']
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
""" | |
Something like this as e.g site.discover | |
(for use as ``python -m site.discover``) | |
could be helpful for explaining and diagnosing multiple pythons and pips | |
""" | |
import os | |
import re | |
try: | |
from shutil import which | |
except ImportError: | |
from distutils.spawn import find_executable | |
def which(a, mode=None, path=None): | |
""" | |
>>> inspect.getdoc(__import__('shutil').which) | |
[...] `mode` defaults to os.F_OK | os.X_OK. ` | |
""" | |
if path is None: | |
path = os.environ.get('PATH') | |
if mode is not None: | |
raise NotImplementedError() | |
return find_executable(a, path=path) | |
from subprocess import check_output, STDOUT | |
import collections | |
def discover(path=None, printsite=False, uniques=True): | |
""" | |
Find all ``python*`` and ``pip*`` executables and their version strings | |
within ``os.environ['PATH']`` | |
Kwargs: | |
path (str): os.pathsep-delimited path str | |
(defaults to ``os.environ['PATH']``) | |
printsite (bool): call ``<python> -m site`` | |
with each found python binary | |
uniques (bool): print uniques according to ``os.realpath`` at the end | |
Returns: | |
None | |
""" | |
if path is None: | |
path = os.environ['PATH'] | |
rgx = re.compile(r'(python|pip)\-?(\d\.?\d*)$') | |
uniques = collections.OrderedDict() | |
for directory in path.split(os.pathsep): | |
if not os.path.exists(directory): | |
# debug('path: {directory!r} is in $PATH {path!r}' | |
# " but doesn't exist" | |
# .format(directory=directory, path=path)) | |
continue | |
paths = sorted( | |
((match.group(0), match.group(2)) for match in | |
(rgx.match(name) for name in os.listdir(directory)) | |
if match), | |
key=lambda x: x[::-1], | |
reverse=True) | |
print(u'# %s' % directory) | |
for (name, ver) in paths: | |
pathjoined = os.path.join(directory, name) | |
binpath = which(pathjoined, path=directory) | |
realpath = os.path.realpath(binpath) | |
if binpath is None: | |
continue | |
if os.path.exists(pathjoined): | |
if os.path.islink(pathjoined): | |
print(( | |
u"%r is a symlink to %r, which doesn't exist" | |
% (pathjoined, binpath))) | |
# TODO: %r exists but isnt executable | |
verstring = check_output((binpath, '-V'), | |
stderr=STDOUT).rstrip() | |
uniques.setdefault(realpath, collections.OrderedDict()) \ | |
.setdefault(binpath, verstring) | |
print(u'%-11r %-5r %r' % (name, ver, verstring)) | |
if printsite and name.startswith('python'): | |
sitestring = check_output((binpath, '-m', 'site')) | |
lines = (u'> %s' % l for l in sitestring.splitlines()) | |
for line in lines: | |
print(line) | |
# TODO: check_output((binpath, '-m', 'pip')) | |
# TODO: AND/OR (?) only print uniques at the end | |
if uniques: | |
print('### UNIQUES') | |
for path in uniques: | |
print('## %s' % path) | |
paths = uniques.get(path) | |
for otherpath in sorted(paths): | |
print(otherpath) | |
print('') | |
if __name__ == '__main__': | |
discover() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment