Files
vaiola/pythonapp/Env/StandaloneEnv.py
Bacruru Sakaguchi 9e5e214944 initial commit
2025-09-12 17:10:13 +07:00

120 lines
4.2 KiB
Python

import os
import platform
import subprocess
import urllib.request
import zipfile
import tarfile
import re
from typing import List
try:
from .Env import Env
except ImportError:
from Env import Env
class StandalonePythonEnv(Env):
def __init__(self, path: str, version: str):
super().__init__()
self.version = version
self.path = path
self.python_path = self._get_python_executable_path()
self.pip_path = self._get_pip_executable_path()
def _get_platform_info(self):
system = platform.system().lower()
arch = platform.machine().lower()
if arch in ['x86_64', 'amd64']:
arch = 'amd64'
elif arch in ['i386', 'i686', 'x86']:
arch = 'win32' if system == 'windows' else 'x86'
return system, arch
def _get_python_executable_path(self):
system, _ = self._get_platform_info()
if system == 'windows':
return os.path.join(self.path, 'python.exe')
return os.path.join(self.path, 'bin', 'python3')
def _get_pip_executable_path(self):
system, _ = self._get_platform_info()
if system == 'windows':
return os.path.join(self.path, 'Scripts', 'pip.exe')
return os.path.join(self.path, 'bin', 'pip')
def _download_and_extract(self):
system, arch = self._get_platform_info()
base_url = 'https://www.python.org/ftp/python'
if system == 'windows':
url = f'{base_url}/{self.version}/python-{self.version}-embed-{arch}.zip'
extract_path = self.path
zip_path = os.path.join(self.path, 'python.zip')
os.makedirs(self.path, exist_ok=True)
urllib.request.urlretrieve(url, zip_path)
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(extract_path)
os.remove(zip_path)
elif system == 'linux':
url = f'{base_url}/{self.version}/Python-{self.version}.tgz'
tar_path = os.path.join(self.path, 'python.tgz')
os.makedirs(self.path, exist_ok=True)
urllib.request.urlretrieve(url, tar_path)
with tarfile.open(tar_path, 'r:gz') as tar_ref:
if hasattr(tarfile, 'data_filter'):
tar_ref.extractall(self.path, filter='data')
else:
tar_ref.extractall(self.path)
os.remove(tar_path)
# Компиляция Python из исходников
source_dir = os.path.join(self.path, f'Python-{self.version}')
subprocess.run([
'./configure', f'--prefix={self.path}',
'--enable-optimizations',
'--with-ensurepip=install'
], cwd=source_dir, check=True)
subprocess.run(['make', '-j8'], cwd=source_dir, check=True)
subprocess.run(['make', 'install'], cwd=source_dir, check=True)
import shutil
shutil.copy2(os.path.join(self.path, 'bin', 'pip3'), os.path.join(self.path, 'bin', 'pip'))
pass
else:
raise NotImplementedError(f"Unsupported platform: {system}")
def create(self):
self._download_and_extract()
def install_pkgs(self, pkgs: List[str], repo: str = None, extra :str = None):
command = [self.pip_path, 'install'] + pkgs
if repo: command.extend(["--index-url", repo])
if extra: command.extend(["--extra-index-url", extra])
subprocess.run(command, check=True)
@staticmethod
def get_available_versions():
import requests
from bs4 import BeautifulSoup
url = 'https://www.python.org/downloads/'
response = requests.get(url)
soup = BeautifulSoup(response.text, 'html.parser')
versions = []
for release in soup.select('.release-list .release-number'):
version_text = release.text.strip().split()[-1]
if re.match(r'^\d+\.\d+\.\d+$', version_text):
versions.append(version_text)
return sorted(versions, key=lambda x: tuple(map(int, x.split('.'))), reverse=True)
if __name__ == '__main__':
env = StandalonePythonEnv(os.path.join('/tmp', 'build/test_standalone'), '3.12.9')
env.create()
env.install_pkgs(['requests', 'numpy'])