[WIP] Complete package manager command class refactoring#1621
Draft
edvilme wants to merge 23 commits into
Draft
Conversation
- Create base command infrastructure in src/managers/base/commands/
- PackageManagerCommand base class with minimal interface
- CommandSettings interface for execution configuration
- Six template classes (Install, Uninstall, List, Version, AvailableVersions, ListDirectNames)
- Each template loads command-specific settings from VS Code config
- Implement pip/uv commands in src/managers/builtin/commands/
- PipInstallCommand, PipUninstallCommand, PipListCommand, etc.
- UvInstallCommand, UvUninstallCommand, UvListCommand, etc.
- UV uses 'pip' subcommand syntax vs Pip uses '-m pip'
- Implement conda commands in src/managers/conda/commands/
- CondaInstallCommand, CondaUninstallCommand, CondaListCommand
- CondaVersionCommand, CondaAvailableVersionsCommand, CondaListDirectNamesCommand
- Implement poetry commands in src/managers/poetry/commands/
- PoetryInstallCommand, PoetryUninstallCommand, PoetryListCommand
- PoetryVersionCommand, PoetryAvailableVersionsCommand, PoetryListDirectNamesCommand
- Remove redundant old command types
- Delete PackageManagerCommand.ts (namespace interface)
- Delete PackageManagerCommandArguments.ts
- Delete BuiltinCommandExecutor (executor pattern not needed)
- Restore package managers to original state (no integration yet)
- Package managers remain unchanged from main branch
- New command classes available for future integration
Architecture: Three-level hierarchy
- Level 1: Base class (PackageManagerCommand) - minimal shared interface
- Level 2: Templates (InstallCommand, etc.) - load settings, define execute signature
- Level 3: Concrete (Pip/Uv/Conda/Poetry commands) - implement buildCommand() and execute()
Design principles:
- Persisting arguments stored in constructor (pythonExecutable, indexUrl, settings)
- Ephemeral arguments passed to execute() method (packages, upgrade flags)
- Settings automatically loaded from python-envs.packageManager.{commandType}CommandArgs
- Direct runPython calls with timeout support, no wrapper layer
- Update CommandConstructorOptions to accept pythonExecutable
- Modify PipInstallCommand/PipUninstallCommand to use pythonExecutable for pip
- Modify UvInstallCommand/UvUninstallCommand to add --python flag with pythonExecutable
- Create parsePackageSpecs utility to convert string[] to {packageName, version}[] objects
- Integrate pip install/uninstall in pipPackageManager.manage():
- Detect UV via shouldUseUv()
- Choose PipInstallCommand or UvInstallCommand based on UV detection
- Execute commands directly with parsed package specs
- Replace managePackages() utils call with direct command execution
- Remove unused managePackages import from pipPackageManager
- Fix syntax error in UvAvailableVersionsCommand (missing closing brace)
- Defer Conda and Poetry command implementation for future phases
Architecture simplification:
- pythonExecutable now serves dual purpose:
- Pip: environment.execInfo.run.executable (Python interpreter path)
- UV: environment.execInfo.run.executable (passed with --python flag to uv pip)
- No redundant environmentExecutable parameter needed
- Commands execute with timeout from settings.executionTimeout
Status: Pip/UV install/uninstall fully integrated and compiling
Pending: List, Version, AvailableVersions, ListDirectNames commands for Pip/UV
Pending: Full implementation for Conda and Poetry managers
- Delete builtinCommandExecutor.ts (executor pattern was rejected in favor of direct command calls) - Remove managePackages() function from builtin/utils.ts (now handled by direct command execution) - Still needed in condaUtils.ts for conda integration Retained utilities: - refreshPipPackages() - used by getPackages() - refreshPipDirectPackageNames() - used by getDirectPackageNames() - normalizePackageName() - used by getDirectPackageNames() - parsePackageSpecs() - used by manage() for install/uninstall commands - processEditableInstallArgs() - used by PipInstallCommand All command execution for pip/uv now goes through direct command class instantiation with no intermediary dispatcher layer.
…utility - Create src/managers/conda/commands/ with install.ts and uninstall.ts - Implement CondaInstallCommand and CondaUninstallCommand classes - CondaInstallCommand: ['install', '-y', '-c', 'conda-forge', ...packages] - CondaUninstallCommand: ['remove', '-y', ...packages] - Create conda/helpers.ts with runConda wrapper function - Integrate conda commands into condaPackageManager.manage() - Direct instantiation of CondaInstallCommand/CondaUninstallCommand - Use parsePackageSpecs() from builtin/utils for package conversion - No intermediary dispatcher or managePackages utility - Remove managePackages() function from: - src/managers/builtin/utils.ts (no longer used by pip/uv) - src/managers/conda/condaUtils.ts (replaced by direct command execution) Result: - All package managers (pip, uv, conda) now use direct command class execution - No indirection layer or dispatcher patterns - managePackages utility completely eliminated - Both managers tested and compiling successfully Remove thin runConda wrapper - use runCondaExecutable directly - Delete src/managers/conda/helpers.ts (unnecessary indirection) - Update CondaInstallCommand to import and call runCondaExecutable directly - Update CondaUninstallCommand to import and call runCondaExecutable directly Simplifies conda command execution by removing wrapper layer per user preference
- Replace getPackages() to use PipListCommand/UvListCommand - Detects UV vs Pip and instantiates appropriate command - Calls execute() which returns PackageInfo[] - Replace getVersion() to use PipVersionCommand/UvVersionCommand - Direct command execution instead of inline pip/uv parsing - Replace getPackageAvailableVersions() to use PipAvailableVersionsCommand/UvAvailableVersionsCommand - Commands handle JSON parsing and version extraction - Still respects pip version check (< 21.2.0 returns undefined) - Replace getDirectPackageNames() to use PipListDirectNamesCommand/UvListDirectNamesCommand - Commands return package names, manager normalizes them Result: - All pipPackageManager operations now use the three-level command hierarchy - Removed imports of refreshPipPackages, refreshPipDirectPackageNames - No more inline runPython/runUV calls in getVersion/getPackageAvailableVersions - Consistent pattern: detect UV, choose command class, instantiate, execute() Remove parsePipIndexVersionsJson function and its tests - Delete src/test/managers/builtin/pipVersions.unit.test.ts - This file only tested parsePipIndexVersionsJson which is no longer needed - Remove parsePipIndexVersionsJson from pipPackageManager.ts - JSON parsing is now handled inside PipAvailableVersionsCommand - and UvAvailableVersionsCommand.execute() methods - Remove unused rcompare import from @renovatebot/pep440 The command classes now handle all parsing internally, eliminating need for this utility function in the package manager.
…commands New Conda Commands: - CondaListCommand: runs 'conda list -p <path> --json' - Takes environmentPath as ephemeral argument to execute() - Returns PackageInfo[] with name, displayName, version, description - CondaVersionCommand: runs 'conda --version' - Returns version string in format 'conda X.Y.Z' - CondaAvailableVersionsCommand: runs 'conda search <package> --json' - Takes packageName, returns unique versions as string[] - Note: conda search returns all builds, we deduplicate versions Integration into condaPackageManager: - getPackages() uses CondaListCommand - getVersion() uses CondaVersionCommand - getPackageAvailableVersions() uses CondaAvailableVersionsCommand Removed: - Direct runCondaExecutable() calls in condaPackageManager - Inline JSON parsing and version filtering logic - Unused rcompare import (no longer needed for sorting) - traceError import (error handling moved to command level) Result: - All conda operations (install, uninstall, list, version, availableVersions) now use the three-level command hierarchy - Note: conda has no ListDirectNames equivalent (user confirmed --from-history is inaccurate)
Implement all Poetry operations (Add, Remove, Show, Version, ShowTopLevel) as concrete command classes extending the base template classes: - PoetryAddCommand: extends InstallCommand, uses 'poetry add' - PoetryRemoveCommand: extends UninstallCommand, uses 'poetry remove' - PoetryShowCommand: extends ListCommand, uses 'poetry show --no-ansi' - PoetryVersionCommand: extends VersionCommand, gets poetry version - PoetryShowTopLevelCommand: extends ListDirectNamesCommand, uses 'poetry show --top-level' Refactor poetryPackageManager methods to use command classes directly: - manage(): instantiates Add/Remove commands - fetchPackagesFromTool(): uses PoetryShowCommand - getVersion(): uses PoetryVersionCommand - getDirectPackageNames(): uses PoetryShowTopLevelCommand - getPackageAvailableVersions(): returns undefined (Poetry doesn't support this) Remove direct poetry command calls and unused imports. All operations now follow the three-level command class pattern.
…ands Add comprehensive JSDoc comments to all Poetry command classes that include: - Parsed Command: The exact CLI command structure with arguments - Official Documentation: Links to Poetry CLI documentation - Description: What each command does and notable flags Commands documented: - PoetryAddCommand: poetry add [--allow-prereleases] <package> - PoetryRemoveCommand: poetry remove <package> - PoetryShowCommand: poetry show --no-ansi - PoetryVersionCommand: poetry --version - PoetryShowTopLevelCommand: poetry show --no-ansi --top-level This enables identification of false/invalid commands and provides reference documentation for maintainers.
Move the Poetry command documentation (parsed command, official documentation, and description) from the ephemeral argument interfaces to the command classes where they are more visible and semantically appropriate.
…managers Add comprehensive JSDoc comments to all 16 concrete command classes with: - Parsed Command: The exact CLI command structure with arguments - Official Documentation: Links to official tool documentation - Description: What each command does and notable flags/behavior Pip commands (6): - PipInstallCommand: python -m pip install - PipUninstallCommand: python -m pip uninstall -y - PipListCommand: python -m pip list --format=json - PipVersionCommand: python -m pip --version - PipAvailableVersionsCommand: python -m pip index versions --json - PipListDirectNamesCommand: python -m pip list --not-required UV commands (6): - UvInstallCommand: uv pip install --python - UvUninstallCommand: uv pip uninstall -y --python - UvListCommand: uv pip list --format=json - UvVersionCommand: uv --version - UvAvailableVersionsCommand: uv pip index versions --json - UvListDirectNamesCommand: uv pip list --not-required Conda commands (5): - CondaInstallCommand: conda install -y -c conda-forge - CondaUninstallCommand: conda remove -y - CondaListCommand: conda list -p --json - CondaVersionCommand: conda --version - CondaAvailableVersionsCommand: conda search --json Poetry commands already have documentation from previous commit. This enables identification of false/invalid commands and provides reference documentation across all package managers.
…tions Replace direct helper calls (runPython, runUV) with command class usage: - refreshPipPackages() now uses PipListCommand/UvListCommand - refreshPipDirectPackageNames() now uses PipListDirectNamesCommand/UvListDirectNamesCommand Remove: - execPipList() function - no longer needed - PIP_LIST_TIMEOUT_MS constant - no longer used - parsePipListJson import - parsing now handled by command classes This eliminates intermediate abstraction layers and uses the consistent command class pattern throughout the codebase.
- Export PackageManagerCommand and supporting types (CommandSettings, CommandType, CommandResult, CommandConstructorOptions) - Export 6 abstract base command classes: InstallCommand, UninstallCommand, ListCommand, VersionCommand, AvailableVersionsCommand, ListDirectNamesCommand - Add comprehensive JSDoc documentation for each command class explaining purpose, usage patterns, and examples - Extensions can now implement custom package managers by extending these base classes - All 1269 unit tests passing, webpack compilation successful
- Remove CommandType type from commandSettings.ts - not used by command implementations - Remove CommandSettings interface from commandSettings.ts - will be added later with proper design - Remove CommandSettings property from all 6 base command classes - Remove CommandSettings initialization logic from base command class constructors - Remove all this.settings.executionTimeout references in concrete implementations - Replace with default 300000ms timeout value (hardcoded for now until CommandSettings reintroduced) - Remove CommandSettings and CommandType from public API exports in src/api.ts - All 1269 unit tests passing, webpack compilation successful
- Add protected timeout: number = 300000 property to all 6 base command classes - Add protected config property to each base class using getConfiguration() for command-specific settings - InstallCommand: 'python-envs.packageManager.installCommandArgs' - UninstallCommand: 'python-envs.packageManager.uninstallCommandArgs' - ListCommand: 'python-envs.packageManager.listCommandArgs' - VersionCommand: 'python-envs.packageManager.versionCommandArgs' - AvailableVersionsCommand: 'python-envs.packageManager.availableVersionsCommandArgs' - ListDirectNamesCommand: 'python-envs.packageManager.listDirectNamesCommandArgs' - Update all concrete command implementations to use this.timeout instead of hardcoded 300000 - Import getConfiguration in all base command classes - All 1269 unit tests passing, webpack compilation successful
- Define InstallEphemeralArgs in InstallCommand base class with packages and upgrade fields - Define UninstallEphemeralArgs in UninstallCommand base class with packages field - Define AvailableVersionsEphemeralArgs in AvailableVersionsCommand base class with packageName, pythonVersion, and includePrerelease fields - Add abstract buildCommand method signature to each base class that uses the appropriate ephemeral args type - Export ephemeral argument types from src/managers/base/commands/index.ts - Remove duplicate type definitions from concrete implementations (PipInstallCommand, UvInstallCommand, PipUninstallCommand, UvUninstallCommand, PipAvailableVersionsCommand, UvAvailableVersionsCommand) - Update concrete implementations to import ephemeral argument types from base commands - Improves polymorphic design - eliminates duplication and establishes clear command type interface - All 1269 unit tests passing, webpack compilation successful
- Update execute method signatures in base classes to accept ephemeral args objects - InstallCommand.execute(ephemeralArgs: InstallEphemeralArgs) - UninstallCommand.execute(ephemeralArgs: UninstallEphemeralArgs) - AvailableVersionsCommand.execute(ephemeralArgs: AvailableVersionsEphemeralArgs) - Update all concrete implementations (Pip, UV, Conda, Poetry) to use ephemeral args - Remove local duplicate ephemeral arg type definitions in Conda commands - Update Poetry commands (Add, Remove) to use base class types - Update all callers to pass ephemeral args objects instead of individual parameters - pipPackageManager.ts: install, uninstall, availableVersions calls - condaPackageManager.ts: install, uninstall, availableVersions calls - poetryPackageManager.ts: add, remove calls - Improves consistency - both buildCommand and execute now use the same ephemeral args interface - All 1269 unit tests passing, webpack compilation successful
- Rename InstallEphemeralArgs -> InstallExecuteArgs - Rename UninstallEphemeralArgs -> UninstallExecuteArgs - Rename AvailableVersionsEphemeralArgs -> AvailableVersionsExecuteArgs - Rename all ephemeralArgs parameter names to executeArgs throughout - Update all base classes, concrete implementations, and callers - Update documentation comments from 'ephemeral' to execution-focused language - All 1269 unit tests passing, webpack compilation successful - Improves naming clarity: 'executeArgs' better describes parameters passed to execute methods
…ed CommandResult - Rename src/managers/base/commands/commandSettings.ts to packageManagerCommand.ts - File now named after its primary export (PackageManagerCommand) - Remove unused CommandResult interface - Not used anywhere in the codebase, only exported in API - Update all imports in base command classes to use new filename - availableVersions.ts, install.ts, list.ts, listDirectNames.ts, uninstall.ts, version.ts - Update index.ts to import from packageManagerCommand instead of commandSettings - Remove CommandResult from public API export - Delete unused src/managers/builtin/commands/commandSettings.ts file - All 1269 unit tests passing, webpack compilation successful
- Add protected timeout property with 300000ms default to PackageManagerCommand - Remove duplicate timeout declarations from all 6 command-specific classes - InstallCommand, UninstallCommand, AvailableVersionsCommand - ListCommand, VersionCommand, ListDirectNamesCommand - Subclasses can still override if needed for custom timeout behavior - Centralizes timeout configuration at the base level - All 1269 unit tests passing, webpack compilation successful
- Add configSection optional parameter to CommandConstructorOptions - Move getConfiguration() call to PackageManagerCommand constructor - Remove duplicate config assignments from all 6 base command classes - InstallCommand, UninstallCommand, AvailableVersionsCommand - ListCommand, VersionCommand, ListDirectNamesCommand - Each derived class now passes its config section via super() call - Reduces boilerplate by 6 duplicate lines (1 per command class) - Makes adding new command types easier (no config setup needed) - Centralized pattern: python-envs.packageManager.\ - All 1269 unit tests passing, webpack compilation successful
…stall - Add manageCommandOptions variable in manage() and runPoetryManage() methods - Consolidate pythonExecutable, log, and cancellationToken in single object - Reuse manageCommandOptions for both uninstall and install command creation - Eliminates repeated object literal duplication in install/uninstall blocks - Applied to: PipPackageManager, CondaPackageManager, PoetryPackageManager - Import CommandConstructorOptions type in all three managers - Clear variable name (manageCommandOptions) indicates scope and purpose - All 1269 unit tests passing, webpack compilation successful
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Doing some streamlining to the commands so they can be reused, extended and cached with no utils functions. So far this is an experiment
Summary
This PR completes the comprehensive refactoring of package manager command handling to use a clean three-level command class architecture.
Changes
✅ Completed
Architecture
Three-Level Command Class Hierarchy:
All package managers now use consistent pattern:
Testing
Files Modified
Key files: