晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。   林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。   见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝)   既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。   南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。 sh-3ll

HOME


sh-3ll 1.0
DIR:/opt/cloudlinux/venv/lib64/python3.11/site-packages/clselect/clselectpython/
Upload File :
Current File : //opt/cloudlinux/venv/lib64/python3.11/site-packages/clselect/clselectpython/__init__.py
# coding: utf-8
import errno
import glob
import os
import re
from subprocess import STDOUT, CalledProcessError, check_output
from typing import Dict

# Used both for config and some state like available_versions cache
# mainly because it's easier to put it in cagefs as a single dir
# We have symlink to that dir in /etc
# for ease of use the selector config and avoiding problem with cagefs

CONFIG_DIR = "/usr/share/l.v.e-manager/cl.python"

ALT_NAMES = "alt-python"
# It's also used to construct base_dir:
ALT_PYTHON_PREFIX = "/opt/alt/python"


class PythonError(Exception):
    """Top level error class for admin's part of Python selector"""

    pass


class PythonConfigError(PythonError):
    """Generic error class for PythonConfig"""

    pass


def create_config_dirs():
    try:
        os.makedirs(CONFIG_DIR)
    except OSError as e:
        if e.errno != errno.EEXIST:  # ignore only "File exists" error
            raise


def is_major_version(ver):
    """Return True if specified MAJOR version is valid for processing"""
    if not isinstance(ver, str):
        return False
    if not re.match(r"^\d+\.\d+$", ver):
        return False
    return True


def scan_python_versions() -> Dict[str, Dict[str, str]]:
    """
    Search CloudLinux Python interpreters and return information about them.

    Returns:
        Dictionary mapping major Python versions (e.g., "2.7", "3.9") to
        dictionaries containing the full version and root path.
    """
    result = {}

    for alt_python_folder in glob.glob(f"{ALT_PYTHON_PREFIX}*"):
        # Extract the folder name without path (e.g., "python27")
        folder_name = os.path.basename(alt_python_folder)

        # Extract version digits from folder name
        version_digits = "".join(filter(str.isdigit, folder_name))

        # Skip paths without at least 2 digits in the version - for them the version can't be determined
        # Primary example is alt-python-internal, see CLOS-3346
        if len(version_digits) < 2:
            continue

        # Format version as major.minor (e.g., "2.7" from "27")
        major_minor_version = f"{version_digits[0]}.{version_digits[1:]}"

        # Construct path to Python binary using os.path.join for cross-platform compatibility
        python_bin = os.path.join(alt_python_folder, "bin", f"python{major_minor_version}")

        if not os.path.isfile(python_bin):
            continue

        try:
            # Get full Python version string
            full_version_output = check_output([python_bin, "-V"], text=True, stderr=STDOUT)

            # Extract version number from output like "Python 3.9.10"
            parts = full_version_output.strip().split()
            if len(parts) >= 2:
                full_ver = parts[1]
                result[major_minor_version] = {
                    "full_version": full_ver,
                    "root_path": alt_python_folder,
                }
        except CalledProcessError:
            # Skip this interpreter if there's an error running it
            continue

    return result


__all__ = (
    "CONFIG_DIR",
    "ALT_NAMES",
    "ALT_PYTHON_PREFIX",
    "PythonError",
    "PythonConfigError",
    "create_config_dirs",
    "is_major_version",
    "scan_python_versions",
)