django-for-runners_ynh/scripts/setup-pyrun.py
Jens Diemer 9117c6de27 Use pyrun for missing Python 3.11
Download and setup missing Python versions using eGenix PyRun from:
https://github.com/eGenix/egenix-pyrun/
2024-08-04 22:51:54 +02:00

151 lines
4 KiB
Python
Executable file

#!/usr/bin/env python3
"""
Download and setup missing Python versions using eGenix PyRun from:
https://github.com/eGenix/egenix-pyrun/
"""
import dataclasses
import json
import shutil
import subprocess
import sys
import tarfile
import urllib.request
from argparse import ArgumentParser
from pathlib import Path
from pprint import pprint
from urllib3.util import parse_url
DEFAULT_VERSION = '3.11'
GET_PIP_URL = 'https://bootstrap.pypa.io/get-pip.py' # https://github.com/eGenix/egenix-pyrun/issues/11
@dataclasses.dataclass
class ReleaseInfo:
version: str
urls: list[str]
def get(self, *, version):
for url in self.urls:
if f'-py{version}_' in url:
return url
def get_pyrun_release_info() -> ReleaseInfo:
api_url = "https://api.github.com/repos/eGenix/egenix-pyrun/releases"
with urllib.request.urlopen(api_url) as response:
data = response.read().decode()
releases = json.loads(data)
latest_release = releases[0]
# pprint(latest_release)
print(latest_release['html_url'])
print(latest_release['tag_name'])
print(latest_release['name'])
urls = []
for asset in latest_release['assets']:
urls.append(asset['browser_download_url'])
return ReleaseInfo(version=latest_release['name'], urls=urls)
def setup_pyrun(*, pyrun_version: str, destination: str):
print('_' * 100)
dest_path = Path(destination).expanduser()
dest_path.mkdir(parents=True, exist_ok=True)
print(f'Setup pyrun for Python {pyrun_version} to: {dest_path}')
release_info = get_pyrun_release_info()
pprint(release_info)
python_bin_name = f'python{pyrun_version}'
path = shutil.which(python_bin_name)
if path:
path = Path(path).resolve()
print(f'Found {python_bin_name} at {path}')
print(path.name)
if path.name.startswith('pyrun'):
print(f'{python_bin_name} is pyrun -> update')
else:
sys.exit(0)
url = release_info.get(version=pyrun_version)
if not url:
print(f'No PyRun release found for Python {pyrun_version}')
sys.exit(1)
print(f'Download {url}')
subprocess.check_call(
['wget', '--timestamp', url],
cwd=dest_path,
)
filename = Path(parse_url(url).path).name
print(f'{filename=}')
tgz_path = dest_path / filename
assert tgz_path.is_file(), f'{tgz_path=}'
final_path = dest_path / f'pyrun{pyrun_version}/'
print(f'Extract {tgz_path} to {final_path}...')
final_path.mkdir(parents=False, exist_ok=True)
with tarfile.open(tgz_path, "r:gz") as tgz_file:
for member in tgz_file.getmembers():
if ".." in member.name or member.name.startswith("/"):
raise ValueError(f"Unsafe file path detected: {member.name}")
tgz_file.extractall(path=final_path)
tgz_path.unlink()
pyrun_bin = final_path / 'bin' / f'pyrun{pyrun_version}'
print(f'Install pip/virtualenv for Python {pyrun_version}')
# FIXME: # https://github.com/eGenix/egenix-pyrun/issues/11
subprocess.check_call(
['wget', '--timestamp', GET_PIP_URL],
cwd=dest_path,
)
get_pip_path = dest_path / 'get-pip.py'
subprocess.check_call(
[pyrun_bin, get_pip_path],
)
get_pip_path.unlink()
subprocess.check_call(
[pyrun_bin, '-m', 'pip', 'install', '-U', 'virtualenv'],
)
print('\n')
print(f'Check {pyrun_bin}:')
subprocess.check_call([pyrun_bin, '-V'])
print('\n')
if __name__ == '__main__':
parser = ArgumentParser(description='Setup PyRun for a specific Python version.')
parser.add_argument(
'--version',
type=str,
default=DEFAULT_VERSION,
help=f'Python version to setup with PyRun (default: {DEFAULT_VERSION}),',
)
parser.add_argument(
'--destination',
type=str,
help=f'Destination path to store PyRun (e.g.: ~/.local/)',
)
args = parser.parse_args()
setup_pyrun(
pyrun_version=args.version,
destination=args.destination,
)