From ef797bce2158728b4d10a11c5043807ce7fdc8b2 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 25 Jan 2026 22:46:49 +0800 Subject: [PATCH] python.ts --- python-ts/main.ts | 231 +++++++++++++++++++++++++++++----------------- 1 file changed, 146 insertions(+), 85 deletions(-) mode change 100644 => 100755 python-ts/main.ts diff --git a/python-ts/main.ts b/python-ts/main.ts old mode 100644 new mode 100755 index 052c1b6..2e4f3f7 --- a/python-ts/main.ts +++ b/python-ts/main.ts @@ -1,4 +1,4 @@ -#!/usr/bin/env runts -- --allow-env --allow-run +#!/usr/bin/env runts -- --allow-env --allow-run --allow-import --allow-read --allow-write import { execCommand, @@ -9,16 +9,15 @@ import { ProcessBar, readFileToString, resolveFilename, + term, + writeStringToFile, } from "https://global.hatter.ink/script/get/@16/deno-commons-mod.ts"; -import {parseArgs} from "jsr:@std/cli/parse-args"; -import {writeStringToFile} from "../libraries/deno-commons-mod.ts"; +import { parseArgs } from "jsr:@std/cli/parse-args"; const PYTHON_CONFIG_FILE = "~/.config/python-config.json"; const PYTHON_VENV_DEFAULT_BASE_DIR = "~/.venv/"; interface PythonConfig { - // default_version?: string; - // default_profile?: string; python_venv_base_path?: string; versions: Map; profiles?: Map; @@ -36,6 +35,11 @@ interface PythonVenv { comment?: string; } +async function isFile(path: string): Promise { + const fileInfo = await Deno.stat(path); + return fileInfo?.isFile; +} + async function loadPythonConfig(): Promise { const pythonConfigFile = resolveFilename(PYTHON_CONFIG_FILE); const pythonConfigJson = await readFileToString(pythonConfigFile); @@ -53,25 +57,56 @@ async function savePythonConfig(pythonConfig: PythonConfig): Promise { ); } -async function findVirtualEnv(pythonVenvVersion: string): Promise { - const pythonConfig = await loadPythonConfig(); - if (!pythonConfig.profiles) { - throw "No Python venvs configured"; - } - const pythonVenv = pythonConfig.profiles[pythonVenvVersion]; - if (!pythonVenv) { - throw `Python venv not found: ${pythonVenvVersion}`; - } - return pythonVenv; +function handleHelp(_args: string[]) { + const help = []; + help.push( + `${ + term.green("python.ts") + } - Python version and virtual environment management tool + +${term.green("python.ts")} ${ + term.bold("python") + } - management python version ${ + term.yellow("[alias: py, ver, version]") + } +${term.green("python.ts")} ${term.bold("add-python")} - add python version ${ + term.yellow("[alias: add-py]") + } +${term.green("python.ts")} ${ + term.bold("venv") + } - management python virtual environment ${ + term.yellow("[alias: env]") + } +${term.green("python.ts")} ${ + term.bold("add-venv") + } - add python virtual environment ${term.yellow("[alias: add-env]")} +${term.green("python.ts")} ${ + term.bold("remove-venv") + } - remove python virtual environment ${ + term.yellow("[alias: rm-venv, rm-env]") + } +${term.green("python.ts")} ${ + term.bold("active-venv") + } - active python virtual environment ${ + term.yellow("[alias: active, active-env]") + }`, + ); + // source <(cat ~/.venv-python-3.13.5/bin/activate) + // source <(python.ts venv test1) + console.log(help.join("\n")); } -async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) { +async function addVirtualEnv( + pythonVersion: string | null, + pythonVenv: string, + comment?: string, +) { const pythonConfig = await loadPythonConfig(); if (!pythonVersion) { throw `No Python version assigned.`; } if (!pythonVenv) { - throw `No Python venv assigned.`; + throw `No Python virtual environment assigned.`; } const pythonVersionProfile = pythonConfig.versions[pythonVersion]; if (!pythonVersionProfile) { @@ -84,13 +119,15 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) { const pythonVenvProfile = pythonConfig.profiles && pythonConfig.profiles[pythonVenv]; if (pythonVenvProfile) { - throw `Python venv already exists: ${pythonVenv}`; + throw `Python virtual environment already exists: ${pythonVenv}`; } const pythonVenvBaseDir = resolveFilename( pythonConfig.python_venv_base_path || PYTHON_VENV_DEFAULT_BASE_DIR, ); if (!await existsPath(pythonVenvBaseDir)) { - log.info(`Make python venv base dir: ${pythonVenvBaseDir}`); + log.info( + `Make python virtual environment base dir: ${pythonVenvBaseDir}`, + ); Deno.mkdirSync(pythonVenvBaseDir); } const pythonVenvLeafDir = `${pythonVenv}_python_${realPythonVersion}`; @@ -105,12 +142,18 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) { const pythonVenvArgs = ["-m", "venv", pythonVenvDir]; log.info("Create Python venv, python:", [python3Cmd, pythonVenvArgs]); - await execCommandShell(python3Cmd, pythonVenvArgs); + await new ProcessBar("Creating Python virtual environment").call( + async () => { + await execCommandShell(python3Cmd, pythonVenvArgs); + }, + ); const newPythonVenvProfile: PythonVenv = { version: realPythonVersion, path: pythonVenvDir, - comment: `Python venv:${pythonVenv}, version: ${realPythonVersion}`, + comment: `Python venv:${pythonVenv}, version: ${realPythonVersion}${ + comment ? (", user comment: " + comment) : "" + }`, }; if (!pythonConfig.profiles) { pythonConfig.profiles = {}; @@ -119,74 +162,42 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) { await savePythonConfig(pythonConfig); } -async function isFile(path: string): Promise { - const fileInfo = await Deno.stat(path); - return fileInfo?.isFile; -} - -function handleHelp(_args: string[]) { - const help = []; - help.push( - `python.ts - Python version and virtual environment management tool - -python.ts python - management python version [alias: py] -python.ts add-python - add python version [alias: py] -python.ts venv - management python virtual environment -python.ts add-venv - add python virtual environment -python.ts remove-venv - remove python virtual environment`, - ); - // source <(cat ~/.venv-python-3.13.5/bin/activate) - // source <(python.ts venv test1) - console.log(help.join("\n")); -} - async function handlePython(args: string[]) { const flags = parseArgs(Deno.args, { boolean: ["help"], - string: ["version"], - alias: { - V: "version", - }, }); if (flags.help) { console.log("Help massage for python"); return; } - if (!flags.version) { - const pythonConfig = await loadPythonConfig(); - if (!pythonConfig.versions) { - log.error("No Python versions configured"); - return; - } - - const versions = []; - let maxVersionLength = 0; - for (let version in pythonConfig.versions) { - versions.push(version); - if (version.length > maxVersionLength) { - maxVersionLength = version.length; - } - } - versions.sort(); - - console.log(`Found ${versions.length} Python version(s)`); - for (let version in pythonConfig.versions) { - const pythonVersion = pythonConfig.versions[version]; - const versionPadding = " ".repeat( - maxVersionLength - version.length, - ); - console.log( - `- Python ${version} ${versionPadding}: ${pythonVersion.path} [version: ${ - pythonVersion.version || "unknown" - }]`, - ); - } - + const pythonConfig = await loadPythonConfig(); + if (!pythonConfig.versions) { + log.error("No Python versions configured"); return; } - const pythonVirtualEnv = await findVirtualEnv(flags.version); - // TODO + const versions = []; + let maxVersionLength = 0; + for (let version in pythonConfig.versions) { + versions.push(version); + if (version.length > maxVersionLength) { + maxVersionLength = version.length; + } + } + versions.sort(); + + console.log(`Found ${versions.length} Python version(s)`); + for (let version in pythonConfig.versions) { + const pythonVersion = pythonConfig.versions[version]; + const versionPadding = " ".repeat( + maxVersionLength - version.length, + ); + console.log( + "- Python:", + version, + pythonVersion, + ); + } } async function getPythonVersion(pythonBinPath: string): Promise { @@ -204,8 +215,10 @@ async function handleAddPython(args: string[]) { boolean: ["help"], string: ["path"], }); - if (flags.help) { - console.log("Help massage for python add-python"); + if (args.length === 0 || flags.help) { + console.log(`Help massage for python add-python + +python.ts add-python --path python-path`); return; } if (!flags.path) { @@ -290,7 +303,14 @@ python.ts venv [--name|-n filter-name]`); return; } filterProfilesCount++; - console.log("-", venv, pythonVenvProfile); + console.log( + "-", + venv, + pythonVenvProfile, + `, active virtual environment command: ${ + term.green("source <(python.ts active-venv " + venv + ")") + }`, + ); }, ); @@ -307,15 +327,16 @@ python.ts venv [--name|-n filter-name]`); async function handleAddVenv(args: string[]) { const flags = parseArgs(args, { boolean: ["help"], - string: ["version", "venv"], + string: ["version", "venv", "comment"], alias: { V: "version", + c: "comment", }, }); if (args.length === 0 || flags.help) { console.log(`Help massage for add-venv -python.ts add-venv --version 3.10 --venv test-env`); +python.ts add-venv --version 3.10 --venv test-env [--comment comment]`); return; } if (!flags.version) { @@ -326,7 +347,7 @@ python.ts add-venv --version 3.10 --venv test-env`); log.error("Venv is missing"); return; } - await addVirtualEnv(flags.version, flags.venv); + await addVirtualEnv(flags.version, flags.venv, flags.comment); } async function handleRemoveVenv(args: string[]) { @@ -348,7 +369,7 @@ python.ts remove-venv --venv test-env`); const pythonVenvProfile = pythonConfig.profiles && pythonConfig.profiles[flags.venv]; if (!pythonVenvProfile) { - throw `Python venv not exists: ${flags.venv}`; + throw `Python virtual environment not exists: ${flags.venv}`; } console.log( @@ -375,6 +396,33 @@ python.ts remove-venv --venv test-env`); } } +async function handleActiveVenv(args: string[]) { + if (args.length === 0) { + console.log(`Help massage for active-venv + +source <(python.rs active[-venv] test-env)`); + return; + } + + const venv = args[0]; + const pythonConfig = await loadPythonConfig(); + const pythonVenvProfile = pythonConfig.profiles && + pythonConfig.profiles[venv]; + if (!pythonVenvProfile) { + throw `Python virtual environment not exists: ${venv}`; + } + + const pythonVenvActiveFile = joinPath( + pythonVenvProfile.path, + "bin", + "activate", + ); + const pythonVenvActiveFileContent = await readFileToString( + pythonVenvActiveFile, + ); + console.log(pythonVenvActiveFileContent); +} + async function main() { const args = Deno.args; @@ -391,6 +439,8 @@ async function main() { handleHelp(remainingArgs); break; case "py": + case "ver": + case "version": case "python": await handlePython(remainingArgs); break; @@ -398,16 +448,24 @@ async function main() { case "add-python": await handleAddPython(remainingArgs); break; + case "env": case "venv": await handleVenv(remainingArgs); break; case "add-venv": + case "add-env": await handleAddVenv(remainingArgs); break; case "rm-venv": + case "rm-env": case "remove-venv": await handleRemoveVenv(remainingArgs); break; + case "active": + case "active-env": + case "active-venv": + await handleActiveVenv(remainingArgs); + break; default: log.error(`Unknown subcommand: ${subcommand}`); break; @@ -415,3 +473,6 @@ async function main() { } main().catch((e) => log.error(e)); + +// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260125T224529+08:00.MEUCIQCxxJGIlQDOd0HEbsC0 +// 1WCLBtgT7T2TMyTgs9TMCWw97QIgINUQ6SNPJ0NePXi12pp66pCoxh37VqSqaE9TURldmNs=