Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 20 additions & 14 deletions internal/runtime/python/python.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,12 +216,13 @@ func installRequirementsTxt(fs afero.Fs, projectDirPath string) (output string,
}

// installPyProjectToml handles adding slack-cli-hooks to pyproject.toml
func installPyProjectToml(fs afero.Fs, projectDirPath string) (output string, err error) {
func installPyProjectToml(ctx context.Context, ios iostreams.IOStreamer, fs afero.Fs, projectDirPath string) (output string, err error) {
pyProjectFilePath := filepath.Join(projectDirPath, "pyproject.toml")

file, err := afero.ReadFile(fs, pyProjectFilePath)
if err != nil {
return fmt.Sprintf("Error reading pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: could not read pyproject.toml: %s", err)
return "Skipped updating pyproject.toml because file cannot be read", nil
}

fileData := string(file)
Expand All @@ -235,25 +236,26 @@ func installPyProjectToml(fs afero.Fs, projectDirPath string) (output string, er
var config map[string]interface{}
err = toml.Unmarshal(file, &config)
if err != nil {
return fmt.Sprintf("Error parsing pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: could not parse pyproject.toml: %s", err)
return "Skipped updating pyproject.toml because invalid TOML", nil
}

// Verify `project` section and `project.dependencies` array exist
projectSection, exists := config["project"]
if !exists {
err := fmt.Errorf("pyproject.toml missing project section")
return fmt.Sprintf("Error updating pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: pyproject.toml missing [project] section")
return "Skipped updating pyproject.toml because project section missing", nil
}

projectMap, ok := projectSection.(map[string]interface{})
if !ok {
err := fmt.Errorf("pyproject.toml project section is not a valid format")
return fmt.Sprintf("Error updating pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: pyproject.toml [project] section is not a valid format")
return "Skipped updating pyproject.toml because project section invalid", nil
}

if _, exists := projectMap["dependencies"]; !exists {
err := fmt.Errorf("pyproject.toml missing dependencies array")
return fmt.Sprintf("Error updating pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: pyproject.toml missing dependencies array in [project] section")
return "Skipped updating pyproject.toml because dependencies missing", nil
}

// Use string manipulation to add the dependency while preserving formatting.
Expand All @@ -264,8 +266,8 @@ func installPyProjectToml(fs afero.Fs, projectDirPath string) (output string, er
matches := dependenciesRegex.FindStringSubmatch(fileData)

if len(matches) == 0 {
err := fmt.Errorf("pyproject.toml missing dependencies array")
return fmt.Sprintf("Error updating pyproject.toml: %s", err), err
ios.PrintDebug(ctx, "Warning: pyproject.toml dependencies array could not be located")
return "Skipped updating pyproject.toml because dependencies missing", nil
}

prefix := matches[1] // "...dependencies = ["
Expand Down Expand Up @@ -347,16 +349,20 @@ func (p *Python) InstallProjectDependencies(ctx context.Context, projectDirPath
// Handle requirements.txt if it exists
if hasRequirementsTxt {
output, err := installRequirementsTxt(fs, projectDirPath)
outputs = append(outputs, output)
if output != "" {
outputs = append(outputs, output)
}
Comment on lines +352 to +354
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 praise: Nice catch to expected paths!

if err != nil {
errs = append(errs, err)
}
}

// Handle pyproject.toml if it exists
if hasPyProjectToml {
output, err := installPyProjectToml(fs, projectDirPath)
outputs = append(outputs, output)
output, err := installPyProjectToml(ctx, ios, fs, projectDirPath)
if output != "" {
outputs = append(outputs, output)
}
if err != nil {
errs = append(errs, err)
}
Expand Down
30 changes: 19 additions & 11 deletions internal/runtime/python/python_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,13 @@ dependencies = ["pytest==8.3.2"]`,
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
ctx := slackcontext.MockContext(t.Context())
fs := slackdeps.NewFsMock()
os := slackdeps.NewOsMock()
os.AddDefaultMocks()
cfg := config.NewConfig(fs, os)
ios := iostreams.NewIOStreamsMock(cfg, fs, os)
ios.AddDefaultMocks()
projectDirPath := "/path/to/project"

// Create pyproject.toml
Expand All @@ -240,7 +246,7 @@ dependencies = ["pytest==8.3.2"]`,
require.NoError(t, err)

// Test
output, err := installPyProjectToml(fs, projectDirPath)
output, err := installPyProjectToml(ctx, ios, fs, projectDirPath)

// Assertions
if tc.expectedError {
Expand All @@ -249,7 +255,9 @@ dependencies = ["pytest==8.3.2"]`,
require.NoError(t, err)
}

require.Contains(t, output, tc.expectedOutput)
if tc.expectedOutput != "" {
require.Contains(t, output, tc.expectedOutput)
}

// Verify file content contains expected strings
content, err := afero.ReadFile(fs, pyprojectPath)
Expand Down Expand Up @@ -527,29 +535,29 @@ dependencies = [
expectedOutputs: []string{"Updated requirements.txt"},
expectedError: false,
},
"Error when pyproject.toml has no dependencies array": {
"Warning when pyproject.toml has no dependencies array": {
existingFiles: map[string]string{
"pyproject.toml": `[project]
name = "my-app"`,
},
expectedOutputs: []string{"Error updating pyproject.toml: pyproject.toml missing dependencies array"},
expectedError: true,
expectedOutputs: []string{"Skipped updating pyproject.toml because dependencies missing"},
expectedError: false,
},
"Error when pyproject.toml has no [project] section": {
"Warning when pyproject.toml has no [project] section": {
existingFiles: map[string]string{
"pyproject.toml": `[tool.black]
line-length = 88`,
},
expectedOutputs: []string{"Error updating pyproject.toml: pyproject.toml missing project section"},
expectedError: true,
expectedOutputs: []string{"Skipped updating pyproject.toml because project section missing"},
expectedError: false,
},
"Error when pyproject.toml is invalid TOML": {
"Warning when pyproject.toml is invalid TOML": {
existingFiles: map[string]string{
"pyproject.toml": `[project
name = "broken`,
},
expectedOutputs: []string{"Error parsing pyproject.toml"},
expectedError: true,
expectedOutputs: []string{"Skipped updating pyproject.toml because invalid TOML"},
expectedError: false,
},
}
for name, tc := range tests {
Expand Down
Loading