Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate Python interpreter from inspector and pass selected python environment down through deployment #2556

Merged
merged 17 commits into from
Jan 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
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
6 changes: 6 additions & 0 deletions .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ jobs:
uses: ./.github/workflows/upload.yaml
secrets: inherit

# Integration Tests
# bats:
# needs: build
# secrets: inherit
# uses: ./.github/workflows/bats.yaml
Comment on lines +37 to +40
Copy link
Collaborator

Choose a reason for hiding this comment

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

We may want to do this in another PR for easier tracking. Maybe hold on merging this until we make an effort to remove.

Copy link
Collaborator

Choose a reason for hiding this comment

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

#2558 was opened to remove these so we can solve the conflict with whatever merges first.


vscode-ui:
needs:
- build
Expand Down
3 changes: 2 additions & 1 deletion cmd/publisher/commands/deploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,8 @@ func (cmd *DeployCmd) Run(args *cli_types.CommonArgs, ctx *cli_types.CLIContext)
stateStore.SaveName)

rExecutable := util.Path{}
publisher, err := publish.NewFromState(stateStore, rExecutable, events.NewCliEmitter(os.Stderr, ctx.Logger), ctx.Logger)
pythonExecutable := util.Path{}
publisher, err := publish.NewFromState(stateStore, rExecutable, pythonExecutable, events.NewCliEmitter(os.Stderr, ctx.Logger), ctx.Logger)
if err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion cmd/publisher/commands/redeploy.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ func (cmd *RedeployCmd) Run(args *cli_types.CommonArgs, ctx *cli_types.CLIContex
stateStore.ConfigName)

rExecutable := util.Path{}
publisher, err := publish.NewFromState(stateStore, rExecutable, events.NewCliEmitter(os.Stderr, ctx.Logger), ctx.Logger)
pythonExecutable := util.Path{}
publisher, err := publish.NewFromState(stateStore, rExecutable, pythonExecutable, events.NewCliEmitter(os.Stderr, ctx.Logger), ctx.Logger)
if err != nil {
return err
}
Expand Down
5 changes: 3 additions & 2 deletions cmd/publisher/commands/requirements_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/posit-dev/publisher/internal/cli_types"
"github.com/posit-dev/publisher/internal/inspect"
"github.com/posit-dev/publisher/internal/inspect/dependencies/pydeps"
"github.com/posit-dev/publisher/internal/util"
)

Expand All @@ -34,12 +35,12 @@ func (cmd *CreateRequirementsCommand) Run(args *cli_types.CommonArgs, ctx *cli_t
if exists && !cmd.Force {
return errRequirementsFileExists
}
inspector := inspect.NewPythonInspector(absPath, cmd.Python, ctx.Logger)
inspector, _ := inspect.NewPythonInspector(absPath, cmd.Python, ctx.Logger, nil, nil)
reqs, incomplete, pythonExecutable, err := inspector.ScanRequirements(absPath)
if err != nil {
return err
}
err = inspector.WriteRequirementsFile(reqPath, reqs)
err = pydeps.WriteRequirementsFile(reqPath, reqs, util.NewAbsolutePath("bogus python executable", nil))
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/publisher/commands/requirements_show.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (cmd *ShowRequirementsCommand) Run(args *cli_types.CommonArgs, ctx *cli_typ
if err != nil {
return err
}
inspector := inspect.NewPythonInspector(absPath, cmd.Python, ctx.Logger)
inspector, _ := inspect.NewPythonInspector(absPath, cmd.Python, ctx.Logger, nil, nil)
reqs, incomplete, pythonExecutable, err := inspector.ScanRequirements(absPath)
if err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions extensions/vscode/src/api/resources/ContentRecords.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,13 +76,15 @@ export class ContentRecords {
dir: string,
secrets?: Record<string, string>,
r?: string,
python?: string,
) {
const data = {
account: accountName,
config: configName,
secrets: secrets,
insecure: insecure,
r: r,
python: python,
};
const encodedTarget = encodeURIComponent(targetName);
return this.client.post<{ localId: string }>(
Expand Down
136 changes: 82 additions & 54 deletions extensions/vscode/src/utils/vscode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,6 @@ import { delay } from "./throttle";
import { substituteVariables } from "./variables";
import { LanguageRuntimeMetadata, PositronApi } from "positron";

export async function getPythonInterpreterPath(): Promise<string | undefined> {
const workspaceFolder = workspace.workspaceFolders?.[0];
if (workspaceFolder === undefined) {
return undefined;
}
let configuredPython: string | undefined;
try {
configuredPython = await commands.executeCommand<string>(
"python.interpreterPath",
{ workspaceFolder: workspaceFolder },
);
} catch (error: unknown) {
console.error(
"getPythonInterpreterPath was unable to execute command. Error = ",
error,
);
}
if (configuredPython === undefined) {
return undefined;
}
let python = substituteVariables(configuredPython, true);
const pythonUri = Uri.file(python);

if (await isDir(pythonUri)) {
// Configured python can be a directory such as a virtual environment.
const names = [
"bin/python",
"bin/python3",
"Scripts/python.exe",
"Scripts/python3.exe",
];
for (const name of names) {
const candidate = Uri.joinPath(pythonUri, name);
if (await fileExists(candidate)) {
python = candidate.fsPath;
}
}
}
console.log("Python interpreter path:", python);
return python;
}

declare global {
function acquirePositronApi(): PositronApi;
}
Expand All @@ -65,7 +23,9 @@ function getPositronApi(): PositronApi | null {
return positronApi;
}

export async function getRInterpreterPath(): Promise<string | undefined> {
export async function getPreferredRuntimeFromPositron(
languageId: string,
): Promise<string | undefined> {
const api = getPositronApi();

if (api) {
Expand All @@ -78,12 +38,13 @@ export async function getRInterpreterPath(): Promise<string | undefined> {

for (let i = 0; i < retries + 1; i++) {
try {
runtime = await api.runtime.getPreferredRuntime("r");
runtime = await api.runtime.getPreferredRuntime(languageId);
break;
} catch (error: unknown) {
// Delay and retry
console.error(
"getPreferredRuntime returned an error; retrying. ",
"getPreferredRuntime for %s returned an error; retrying. %s",
languageId,
error,
);
await delay(retryInterval);
Expand All @@ -92,18 +53,85 @@ export async function getRInterpreterPath(): Promise<string | undefined> {

if (runtime) {
const interpreter = runtime.runtimePath;
console.log("Using selected R interpreter", interpreter);
console.log("Using selected %s interpreter: %s", languageId, interpreter);
return interpreter;
} else {
console.log(
"Using default R interpreter because getPreferredRuntime did not return one",
);
}
console.log(
"Positron getPreferredRuntime for %s did not return a value",
languageId,
);
}
return undefined;
}

async function getPythonInterpreterFromVSCode(): Promise<string | undefined> {
const workspaceFolder = workspace.workspaceFolders?.[0];
if (workspaceFolder === undefined) {
return undefined;
}
let configuredPython: string | undefined;
try {
configuredPython = await commands.executeCommand<string>(
"python.interpreterPath",
{ workspaceFolder: workspaceFolder },
);
} catch (error: unknown) {
console.error(
"getPythonInterpreterFromPath was unable to execute command. Error = ",
error,
);
}
if (configuredPython === undefined) {
return undefined;
}
let python = substituteVariables(configuredPython, true);
const pythonUri = Uri.file(python);

if (await isDir(pythonUri)) {
// Configured python can be a directory such as a virtual environment.
const names = [
"bin/python",
"bin/python3",
"Scripts/python.exe",
"Scripts/python3.exe",
];
for (const name of names) {
const candidate = Uri.joinPath(pythonUri, name);
if (await fileExists(candidate)) {
python = candidate.fsPath;
}
}
}
console.log("Python interpreter from vscode:", python);
return python;
}

export async function getPythonInterpreterPath(): Promise<string | undefined> {
let python: string | undefined;
python = await getPreferredRuntimeFromPositron("python");
if (python !== undefined) {
console.log("Using selected Python interpreter", python);
return python;
}
python = await getPythonInterpreterFromVSCode();
if (python !== undefined) {
console.log("Using Python from VSCode", python);
return python;
}
// We don't know the interpreter path.
// The backend will run Python from PATH.
console.log("Python interpreter discovery unsuccessful.");
return python;
}

export async function getRInterpreterPath(): Promise<string | undefined> {
const r = await getPreferredRuntimeFromPositron("r");
if (r !== undefined) {
console.log("Using selected R interpreter", r);
return r;
}
// We don't know the interpreter path.
// The backend will run R from PATH.
console.log(
"Using default R interpreter because the Positron API is not available",
);
return undefined;
console.log("R interpreter discovery unsuccessful.");
return r;
}
2 changes: 2 additions & 0 deletions extensions/vscode/src/views/homeView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
try {
const api = await useApi();
const r = await getRInterpreterPath();
const python = await getPythonInterpreterPath();

const response = await api.contentRecords.publish(
deploymentName,
Expand All @@ -208,6 +209,7 @@ export class HomeViewProvider implements WebviewViewProvider, Disposable {
projectDir,
secrets,
r,
python,
);
deployProject(
deploymentName,
Expand Down
Loading