Summary
The multi-language hooks feature (PR #7451) introduced pkg/tools/language/python_executor.go which contains Python venv/pip orchestration logic that partially duplicates existing code in pkg/project/framework_service_python.go. Both files independently import pkg/tools/python/ for low-level operations but reimplement the orchestration layer around those calls.
This issue tracks extracting shared helpers into pkg/tools/python/ where both consumers already depend, eliminating ~80-100 lines of duplication.
Duplications Identified
Venv Naming ({baseName}_env)
- Executor:
venvNameForDir(projectDir string) string (python_executor.go:408-416)
- Framework:
getVenvName(serviceConfig *ServiceConfig) string (framework_service_python.go:185-192)
- Action: Extract to
python.VenvNameForDir(projectDir string) string
- Character-for-character identical logic with different parameter types.
Venv Ensure (stat → create)
- Executor:
ensureVenv() — stat, is-dir check, errors.Is, create (python_executor.go:184-219)
- Framework: Inline in
Restore() — stat, os.IsNotExist, create (framework_service_python.go:62-79)
- Action: Extract to
python.Cli.EnsureVirtualEnv(ctx, workingDir, name, env) error
- Use executor's stricter implementation (has directory type check, modern
errors.Is).
Dep Install Dispatch (requirements.txt vs pyproject.toml)
- Executor:
installDeps() — switch on depFile (python_executor.go:223-251)
- Framework: Inline in
Restore() — if/else on depFile (framework_service_python.go:82-96)
- Action: Extract to
python.Cli.InstallDependencies(ctx, dir, venvName, depFile, env) error
- Detection stays separate (framework checks one dir; executor walks up). Only dispatch is shared.
Python Command Resolution (py/python/python3)
- Executor:
resolvePythonCmd(runner) string (python_executor.go:313-332)
- Framework: Uses
python.Cli.checkPath() — private (python.go:151-173)
- Action: Export existing
checkPath() as python.Cli.ResolveCommand() (string, error)
- Same resolution order (Windows: py→python; Unix: python3).
Venv Directory Layout (Scripts/bin)
- Executor:
resolvePythonPath() — builds venv python binary path (python_executor.go:297-307)
- Framework: Inline in
Run() — builds venv activation script path (python.go:130-136)
- Action: Extract to
python.VenvPythonPath(venvDir) string + python.VenvActivateCmd(venvDir) string
What Stays in the Executor (NOT consolidated)
These are hook-specific features the framework service doesn't need:
detectExistingVenv() — checks VIRTUAL_ENV env var, .venv, venv dirs
DiscoverProjectFile() — walk-up directory search from script location
hasPyvenvCfg() — venv validation heuristic
- Direct python binary invocation (vs shell activation)
Dependency Graph
\
pkg/tools/python/ ← both already import this (leaf package)
↑ ↑
| |
language/ project/
(hook executor) (framework service)
\\
No circular dependency risk — python/ imports neither language/ nor project/.
Estimated Impact
- ~80-100 lines removed across both consumers
- ~60 lines added to
pkg/tools/python/ (mostly moved, not new)
pythonTools interface simplified to 3 methods
Related
Summary
The multi-language hooks feature (PR #7451) introduced
pkg/tools/language/python_executor.gowhich contains Python venv/pip orchestration logic that partially duplicates existing code inpkg/project/framework_service_python.go. Both files independently importpkg/tools/python/for low-level operations but reimplement the orchestration layer around those calls.This issue tracks extracting shared helpers into
pkg/tools/python/where both consumers already depend, eliminating ~80-100 lines of duplication.Duplications Identified
Venv Naming ({baseName}_env)
venvNameForDir(projectDir string) string(python_executor.go:408-416)getVenvName(serviceConfig *ServiceConfig) string(framework_service_python.go:185-192)python.VenvNameForDir(projectDir string) stringVenv Ensure (stat → create)
ensureVenv()— stat, is-dir check,errors.Is, create (python_executor.go:184-219)Restore()— stat,os.IsNotExist, create (framework_service_python.go:62-79)python.Cli.EnsureVirtualEnv(ctx, workingDir, name, env) errorerrors.Is).Dep Install Dispatch (requirements.txt vs pyproject.toml)
installDeps()— switch on depFile (python_executor.go:223-251)Restore()— if/else on depFile (framework_service_python.go:82-96)python.Cli.InstallDependencies(ctx, dir, venvName, depFile, env) errorPython Command Resolution (py/python/python3)
resolvePythonCmd(runner) string(python_executor.go:313-332)python.Cli.checkPath()— private (python.go:151-173)checkPath()aspython.Cli.ResolveCommand() (string, error)Venv Directory Layout (Scripts/bin)
resolvePythonPath()— builds venv python binary path (python_executor.go:297-307)Run()— builds venv activation script path (python.go:130-136)python.VenvPythonPath(venvDir) string+python.VenvActivateCmd(venvDir) stringWhat Stays in the Executor (NOT consolidated)
These are hook-specific features the framework service doesn't need:
detectExistingVenv()— checks VIRTUAL_ENV env var, .venv, venv dirsDiscoverProjectFile()— walk-up directory search from script locationhasPyvenvCfg()— venv validation heuristicDependency Graph
\
pkg/tools/python/ ← both already import this (leaf package)
↑ ↑
| |
language/ project/
(hook executor) (framework service)
\\
No circular dependency risk —
python/imports neitherlanguage/norproject/.Estimated Impact
pkg/tools/python/(mostly moved, not new)pythonToolsinterface simplified to 3 methodsRelated