python.ts

This commit is contained in:
2026-01-25 22:46:49 +08:00
parent e488cb54e8
commit ef797bce21

181
python-ts/main.ts Normal file → Executable file
View File

@@ -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 { import {
execCommand, execCommand,
@@ -9,16 +9,15 @@ import {
ProcessBar, ProcessBar,
readFileToString, readFileToString,
resolveFilename, resolveFilename,
term,
writeStringToFile,
} from "https://global.hatter.ink/script/get/@16/deno-commons-mod.ts"; } from "https://global.hatter.ink/script/get/@16/deno-commons-mod.ts";
import {parseArgs} from "jsr:@std/cli/parse-args"; import { parseArgs } from "jsr:@std/cli/parse-args";
import {writeStringToFile} from "../libraries/deno-commons-mod.ts";
const PYTHON_CONFIG_FILE = "~/.config/python-config.json"; const PYTHON_CONFIG_FILE = "~/.config/python-config.json";
const PYTHON_VENV_DEFAULT_BASE_DIR = "~/.venv/"; const PYTHON_VENV_DEFAULT_BASE_DIR = "~/.venv/";
interface PythonConfig { interface PythonConfig {
// default_version?: string;
// default_profile?: string;
python_venv_base_path?: string; python_venv_base_path?: string;
versions: Map<string, PythonVersion>; versions: Map<string, PythonVersion>;
profiles?: Map<string, PythonVenv>; profiles?: Map<string, PythonVenv>;
@@ -36,6 +35,11 @@ interface PythonVenv {
comment?: string; comment?: string;
} }
async function isFile(path: string): Promise<boolean> {
const fileInfo = await Deno.stat(path);
return fileInfo?.isFile;
}
async function loadPythonConfig(): Promise<PythonConfig> { async function loadPythonConfig(): Promise<PythonConfig> {
const pythonConfigFile = resolveFilename(PYTHON_CONFIG_FILE); const pythonConfigFile = resolveFilename(PYTHON_CONFIG_FILE);
const pythonConfigJson = await readFileToString(pythonConfigFile); const pythonConfigJson = await readFileToString(pythonConfigFile);
@@ -53,25 +57,56 @@ async function savePythonConfig(pythonConfig: PythonConfig): Promise<void> {
); );
} }
async function findVirtualEnv(pythonVenvVersion: string): Promise<PythonVenv> { function handleHelp(_args: string[]) {
const pythonConfig = await loadPythonConfig(); const help = [];
if (!pythonConfig.profiles) { help.push(
throw "No Python venvs configured"; `${
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]")
} }
const pythonVenv = pythonConfig.profiles[pythonVenvVersion]; ${term.green("python.ts")} ${term.bold("add-python")} - add python version ${
if (!pythonVenv) { term.yellow("[alias: add-py]")
throw `Python venv not found: ${pythonVenvVersion}`;
} }
return pythonVenv; ${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(); const pythonConfig = await loadPythonConfig();
if (!pythonVersion) { if (!pythonVersion) {
throw `No Python version assigned.`; throw `No Python version assigned.`;
} }
if (!pythonVenv) { if (!pythonVenv) {
throw `No Python venv assigned.`; throw `No Python virtual environment assigned.`;
} }
const pythonVersionProfile = pythonConfig.versions[pythonVersion]; const pythonVersionProfile = pythonConfig.versions[pythonVersion];
if (!pythonVersionProfile) { if (!pythonVersionProfile) {
@@ -84,13 +119,15 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) {
const pythonVenvProfile = pythonConfig.profiles && const pythonVenvProfile = pythonConfig.profiles &&
pythonConfig.profiles[pythonVenv]; pythonConfig.profiles[pythonVenv];
if (pythonVenvProfile) { if (pythonVenvProfile) {
throw `Python venv already exists: ${pythonVenv}`; throw `Python virtual environment already exists: ${pythonVenv}`;
} }
const pythonVenvBaseDir = resolveFilename( const pythonVenvBaseDir = resolveFilename(
pythonConfig.python_venv_base_path || PYTHON_VENV_DEFAULT_BASE_DIR, pythonConfig.python_venv_base_path || PYTHON_VENV_DEFAULT_BASE_DIR,
); );
if (!await existsPath(pythonVenvBaseDir)) { if (!await existsPath(pythonVenvBaseDir)) {
log.info(`Make python venv base dir: ${pythonVenvBaseDir}`); log.info(
`Make python virtual environment base dir: ${pythonVenvBaseDir}`,
);
Deno.mkdirSync(pythonVenvBaseDir); Deno.mkdirSync(pythonVenvBaseDir);
} }
const pythonVenvLeafDir = `${pythonVenv}_python_${realPythonVersion}`; const pythonVenvLeafDir = `${pythonVenv}_python_${realPythonVersion}`;
@@ -105,12 +142,18 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) {
const pythonVenvArgs = ["-m", "venv", pythonVenvDir]; const pythonVenvArgs = ["-m", "venv", pythonVenvDir];
log.info("Create Python venv, python:", [python3Cmd, pythonVenvArgs]); log.info("Create Python venv, python:", [python3Cmd, pythonVenvArgs]);
await new ProcessBar("Creating Python virtual environment").call(
async () => {
await execCommandShell(python3Cmd, pythonVenvArgs); await execCommandShell(python3Cmd, pythonVenvArgs);
},
);
const newPythonVenvProfile: PythonVenv = { const newPythonVenvProfile: PythonVenv = {
version: realPythonVersion, version: realPythonVersion,
path: pythonVenvDir, path: pythonVenvDir,
comment: `Python venv:${pythonVenv}, version: ${realPythonVersion}`, comment: `Python venv:${pythonVenv}, version: ${realPythonVersion}${
comment ? (", user comment: " + comment) : ""
}`,
}; };
if (!pythonConfig.profiles) { if (!pythonConfig.profiles) {
pythonConfig.profiles = {}; pythonConfig.profiles = {};
@@ -119,40 +162,14 @@ async function addVirtualEnv(pythonVersion: string | null, pythonVenv: string) {
await savePythonConfig(pythonConfig); await savePythonConfig(pythonConfig);
} }
async function isFile(path: string): Promise<boolean> {
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[]) { async function handlePython(args: string[]) {
const flags = parseArgs(Deno.args, { const flags = parseArgs(Deno.args, {
boolean: ["help"], boolean: ["help"],
string: ["version"],
alias: {
V: "version",
},
}); });
if (flags.help) { if (flags.help) {
console.log("Help massage for python"); console.log("Help massage for python");
return; return;
} }
if (!flags.version) {
const pythonConfig = await loadPythonConfig(); const pythonConfig = await loadPythonConfig();
if (!pythonConfig.versions) { if (!pythonConfig.versions) {
log.error("No Python versions configured"); log.error("No Python versions configured");
@@ -176,17 +193,11 @@ async function handlePython(args: string[]) {
maxVersionLength - version.length, maxVersionLength - version.length,
); );
console.log( console.log(
`- Python ${version} ${versionPadding}: ${pythonVersion.path} [version: ${ "- Python:",
pythonVersion.version || "unknown" version,
}]`, pythonVersion,
); );
} }
return;
}
const pythonVirtualEnv = await findVirtualEnv(flags.version);
// TODO
} }
async function getPythonVersion(pythonBinPath: string): Promise<string> { async function getPythonVersion(pythonBinPath: string): Promise<string> {
@@ -204,8 +215,10 @@ async function handleAddPython(args: string[]) {
boolean: ["help"], boolean: ["help"],
string: ["path"], string: ["path"],
}); });
if (flags.help) { if (args.length === 0 || flags.help) {
console.log("Help massage for python add-python"); console.log(`Help massage for python add-python
python.ts add-python --path python-path`);
return; return;
} }
if (!flags.path) { if (!flags.path) {
@@ -290,7 +303,14 @@ python.ts venv [--name|-n filter-name]`);
return; return;
} }
filterProfilesCount++; 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[]) { async function handleAddVenv(args: string[]) {
const flags = parseArgs(args, { const flags = parseArgs(args, {
boolean: ["help"], boolean: ["help"],
string: ["version", "venv"], string: ["version", "venv", "comment"],
alias: { alias: {
V: "version", V: "version",
c: "comment",
}, },
}); });
if (args.length === 0 || flags.help) { if (args.length === 0 || flags.help) {
console.log(`Help massage for add-venv 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; return;
} }
if (!flags.version) { if (!flags.version) {
@@ -326,7 +347,7 @@ python.ts add-venv --version 3.10 --venv test-env`);
log.error("Venv is missing"); log.error("Venv is missing");
return; return;
} }
await addVirtualEnv(flags.version, flags.venv); await addVirtualEnv(flags.version, flags.venv, flags.comment);
} }
async function handleRemoveVenv(args: string[]) { async function handleRemoveVenv(args: string[]) {
@@ -348,7 +369,7 @@ python.ts remove-venv --venv test-env`);
const pythonVenvProfile = pythonConfig.profiles && const pythonVenvProfile = pythonConfig.profiles &&
pythonConfig.profiles[flags.venv]; pythonConfig.profiles[flags.venv];
if (!pythonVenvProfile) { if (!pythonVenvProfile) {
throw `Python venv not exists: ${flags.venv}`; throw `Python virtual environment not exists: ${flags.venv}`;
} }
console.log( 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() { async function main() {
const args = Deno.args; const args = Deno.args;
@@ -391,6 +439,8 @@ async function main() {
handleHelp(remainingArgs); handleHelp(remainingArgs);
break; break;
case "py": case "py":
case "ver":
case "version":
case "python": case "python":
await handlePython(remainingArgs); await handlePython(remainingArgs);
break; break;
@@ -398,16 +448,24 @@ async function main() {
case "add-python": case "add-python":
await handleAddPython(remainingArgs); await handleAddPython(remainingArgs);
break; break;
case "env":
case "venv": case "venv":
await handleVenv(remainingArgs); await handleVenv(remainingArgs);
break; break;
case "add-venv": case "add-venv":
case "add-env":
await handleAddVenv(remainingArgs); await handleAddVenv(remainingArgs);
break; break;
case "rm-venv": case "rm-venv":
case "rm-env":
case "remove-venv": case "remove-venv":
await handleRemoveVenv(remainingArgs); await handleRemoveVenv(remainingArgs);
break; break;
case "active":
case "active-env":
case "active-venv":
await handleActiveVenv(remainingArgs);
break;
default: default:
log.error(`Unknown subcommand: ${subcommand}`); log.error(`Unknown subcommand: ${subcommand}`);
break; break;
@@ -415,3 +473,6 @@ async function main() {
} }
main().catch((e) => log.error(e)); main().catch((e) => log.error(e));
// @SCRIPT-SIGNATURE-V1: yk-r1.ES256.20260125T224529+08:00.MEUCIQCxxJGIlQDOd0HEbsC0
// 1WCLBtgT7T2TMyTgs9TMCWw97QIgINUQ6SNPJ0NePXi12pp66pCoxh37VqSqaE9TURldmNs=