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

Support local Python application development #4662

Merged
merged 53 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
1b0ad2d
wip: run streamlit command
seeM Sep 3, 2024
e368206
wip: add title to url action bar
seeM Sep 3, 2024
bdfb2bf
wip: terminal app service
seeM Sep 3, 2024
a29427d
wip: tasks attempt & extract `runApp` function
seeM Sep 5, 2024
9b22451
initial commands for popular frameworks
seeM Sep 5, 2024
63105a4
rework api: `registerApplicationFramework`
seeM Sep 5, 2024
86cf964
rename to runner
seeM Sep 5, 2024
cfe5ab1
add helper functions
seeM Sep 6, 2024
2a57584
move to positron api
seeM Sep 9, 2024
8e38750
Revert "wip: add title to url action bar"
seeM Sep 9, 2024
acd89fd
Revert "wip: terminal app service"
seeM Sep 9, 2024
26bb909
get flask/fastapi app name from document text
seeM Sep 10, 2024
39238df
add shiny
seeM Sep 10, 2024
4a7c531
try to determine app name from code
seeM Sep 10, 2024
9817ec5
add positron-run-app extension
seeM Sep 11, 2024
e2fd7fc
`positron-run-app.runApplication`
seeM Sep 11, 2024
cc3210a
Revert "add helper functions"
seeM Sep 11, 2024
503d2f1
Revert "move to positron api"
seeM Sep 11, 2024
063110e
unused import
seeM Sep 11, 2024
b9eb459
use terminal shell integration instead of controlling the port
seeM Sep 11, 2024
a973479
simplify api
seeM Sep 11, 2024
b3c626c
simplify api more
seeM Sep 11, 2024
77be7c2
cleanup
seeM Sep 12, 2024
de0ea8d
workbench integration
seeM Sep 12, 2024
c62c5fb
format
seeM Sep 12, 2024
84d1df3
docs
seeM Sep 13, 2024
697bf17
add a test
seeM Sep 13, 2024
750b810
feature flag
seeM Sep 13, 2024
8ec4574
comment about merging commands
seeM Sep 13, 2024
8ec28d3
docs
seeM Sep 13, 2024
5ca2d49
format
seeM Sep 13, 2024
fc99c45
use a temp dir
seeM Sep 16, 2024
88634ba
alphabetize
seeM Sep 16, 2024
4a538cb
prompt to enable shell integration and rerun the app
seeM Sep 17, 2024
59d341a
use fastapi-cli if it's installed
seeM Sep 17, 2024
9cb7a6d
untildify python paths before resolving
seeM Sep 17, 2024
7bc952f
handle shell integration not being supported
seeM Sep 17, 2024
9cc54f7
move web app commands
seeM Sep 19, 2024
1132d86
initial web app command tests
seeM Sep 19, 2024
ca223ff
finish python tests
seeM Sep 20, 2024
b30f78e
remove exec shiny command
seeM Sep 20, 2024
532c695
fix command constants + test
seeM Sep 20, 2024
67cdcd5
finalize tests
seeM Sep 20, 2024
7a15aaf
show shell integration not supported warning until it is supported; a…
seeM Sep 20, 2024
70ab512
cat file causing error
seeM Sep 20, 2024
2b96150
bump extension cache key
seeM Sep 20, 2024
fddcb43
disable shell integration globally
seeM Sep 20, 2024
d9e2eef
remove feature flag
seeM Sep 20, 2024
a8058ed
fix: update shell integration supported state when it becomes supported
seeM Sep 20, 2024
fc9f68d
reroder run commands
seeM Sep 20, 2024
52b9013
add right click menu items
seeM Sep 20, 2024
a731d7d
fix run app tests
seeM Sep 23, 2024
724cd17
use asRelativePath
seeM Sep 23, 2024
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
5 changes: 5 additions & 0 deletions .vscode-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ const extensions = [
workspaceFolder: path.join(os.tmpdir(), `positron-connections-${Math.floor(Math.random() * 100000)}`),
mocha: { timeout: 60_000 }
},
{
label: 'positron-run-app',
workspaceFolder: 'extensions/positron-run-app/test-workspace',
mocha: { timeout: 60_000 }
},
// --- End Positron ---
{
label: 'microsoft-authentication',
Expand Down
1 change: 1 addition & 0 deletions build/gulpfile.extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ const compilations = [
'extensions/positron-notebooks/tsconfig.json',
'extensions/positron-r/tsconfig.json',
'extensions/positron-rstudio-keymap/tsconfig.json',
'extensions/positron-run-app/tsconfig.json',
'extensions/positron-python/tsconfig.json',
'extensions/positron-proxy/tsconfig.json',
'extensions/positron-viewer/tsconfig.json',
Expand Down
1 change: 1 addition & 0 deletions build/npm/dirs.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const dirs = [
'extensions/positron-notebooks',
'extensions/positron-r',
'extensions/positron-rstudio-keymap',
'extensions/positron-run-app',
'extensions/positron-python',
'extensions/positron-proxy',
'extensions/positron-viewer',
Expand Down
82 changes: 44 additions & 38 deletions extensions/positron-python/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,42 +305,35 @@
"command": "python.execDashInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execDashInTerminal.title%",
"enablement": "pythonAppFramework == dash && config.python.enableWebAppSupport"
"enablement": "pythonAppFramework == dash"
},
{
"category": "Python",
"command": "python.execFastAPIInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execFastAPIInTerminal.title%",
"enablement": "pythonAppFramework == fastapi && config.python.enableWebAppSupport"
"enablement": "pythonAppFramework == fastapi"
},
{
"category": "Python",
"command": "python.execFlaskInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execFlaskInTerminal.title%",
"enablement": "pythonAppFramework == flask && config.python.enableWebAppSupport"
"enablement": "pythonAppFramework == flask"
},
{
"category": "Python",
"command": "python.execGradioInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execGradioInTerminal.title%",
"enablement": "pythonAppFramework == gradio && config.python.enableWebAppSupport"
},
{
"category": "Python",
"command": "python.execShinyInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execShinyInTerminal.title%",
"enablement": "pythonAppFramework == shiny && config.python.enableWebAppSupport"
"enablement": "pythonAppFramework == gradio"
},
{
"category": "Python",
"command": "python.execStreamlitInTerminal",
"icon": "$(play)",
"title": "%python.command.python.execStreamlitInTerminal.title%",
"enablement": "pythonAppFramework == streamlit && config.python.enableWebAppSupport"
"enablement": "pythonAppFramework == streamlit"
},
{
"category": "Python",
Expand Down Expand Up @@ -482,12 +475,6 @@
"scope": "application",
"type": "boolean"
},
"python.enableWebAppSupport": {
"defaut": false,
"description": "%python.enableWebAppSupport.description%",
"scope": "application",
"type": "boolean"
},
"python.envFile": {
"default": "${workspaceFolder}/.env",
"description": "%python.envFile.description%",
Expand Down Expand Up @@ -1224,7 +1211,7 @@
"command": "python.execInConsole",
"key": "ctrl+shift+enter",
"mac": "cmd+shift+enter",
"when": "editorTextFocus && editorLangId == python && !findInputFocussed && !replaceInputFocussed && !jupyter.ownsSelection && !notebookEditorFocused"
"when": "editorTextFocus && editorLangId == python && !findInputFocussed && !replaceInputFocussed && !jupyter.ownsSelection && !notebookEditorFocused && !pythonAppFramework"
},
{
"command": "python.execSelectionInConsole",
Expand Down Expand Up @@ -1364,7 +1351,7 @@
"category": "Python",
"command": "python.execInConsole",
"title": "%python.command.python.execInConsole.title%",
"when": "!virtualWorkspace && shellExecutionSupported && editorLangId == python"
"when": "!virtualWorkspace && shellExecutionSupported && editorLangId == python && !pythonAppFramework"
},
{
"category": "Python",
Expand Down Expand Up @@ -1490,7 +1477,32 @@
{
"command": "python.execInConsole",
"group": "Python",
"when": "resourceLangId == python && !virtualWorkspace && shellExecutionSupported"
"when": "resourceLangId == python && !virtualWorkspace && shellExecutionSupported && !pythonAppFramework"
},
{
"command": "python.execDashInTerminal",
"group": "Python",
"when": "pythonAppFramework == dash"
},
{
"command": "python.execGradioInTerminal",
"group": "Python",
"when": "pythonAppFramework == gradio"
},
{
"command": "python.execFastAPIInTerminal",
"group": "Python",
"when": "pythonAppFramework == fastapi"
},
{
"command": "python.execFlaskInTerminal",
"group": "Python",
"when": "pythonAppFramework == flask"
},
{
"command": "python.execStreamlitInTerminal",
"group": "Python",
"when": "pythonAppFramework == streamlit"
},
{
"command": "python.execInTerminal",
Expand Down Expand Up @@ -1525,43 +1537,37 @@
"command": "python.execInConsole",
"group": "navigation@0",
"title": "%python.command.python.execInConsole.title%",
"when": "resourceLangId == python && !isInDiffEditor && !virtualWorkspace && shellExecutionSupported"
"when": "resourceLangId == python && !isInDiffEditor && !virtualWorkspace && shellExecutionSupported && !pythonAppFramework"
},
{
"command": "python.execDashInTerminal",
"group": "navigation@1",
"group": "navigation@0",
"title": "%python.command.python.execDashInTerminal.title%",
"when": "pythonAppFramework == dash && config.python.enableWebAppSupport"
"when": "pythonAppFramework == dash"
},
{
"command": "python.execGradioInTerminal",
"group": "navigation@1",
"group": "navigation@0",
"title": "%python.command.python.execGradioInTerminal.title%",
"when": "pythonAppFramework == gradio && config.python.enableWebAppSupport"
"when": "pythonAppFramework == gradio"
},
{
"command": "python.execFastAPIInTerminal",
"group": "navigation@1",
"group": "navigation@0",
"title": "%python.command.python.execFastAPIInTerminal.title%",
"when": "pythonAppFramework == fastapi && config.python.enableWebAppSupport"
"when": "pythonAppFramework == fastapi"
},
{
"command": "python.execFlaskInTerminal",
"group": "navigation@1",
"group": "navigation@0",
"title": "%python.command.python.execFlaskInTerminal.title%",
"when": "pythonAppFramework == flask && config.python.enableWebAppSupport"
},
{
"command": "python.execShinyInTerminal",
"group": "navigation@1",
"title": "%python.command.python.execShinyInTerminal.title%",
"when": "pythonAppFramework == shiny && config.python.enableWebAppSupport"
"when": "pythonAppFramework == flask"
},
{
"command": "python.execStreamlitInTerminal",
"group": "navigation@1",
"group": "navigation@0",
"title": "%python.command.python.execStreamlitInTerminal.title%",
"when": "pythonAppFramework == streamlit && config.python.enableWebAppSupport"
"when": "pythonAppFramework == streamlit"
},
{
"command": "python.execInTerminal-icon",
Expand Down
4 changes: 1 addition & 3 deletions extensions/positron-python/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@
"python.command.python.createTerminal.title": "Create Terminal",
"python.command.python.execInTerminal.title": "Run Python File in Terminal",
"python.command.python.execDashInTerminal.title": "Run Dash App in Terminal",
"python.command.python.execFastAPIInTerminal.title": "Run FastAPI in Terminal",
"python.command.python.execFastAPIInTerminal.title": "Run FastAPI App in Terminal",
"python.command.python.execFlaskInTerminal.title": "Run Flask App in Terminal",
"python.command.python.execGradioInTerminal.title": "Run Gradio App in Terminal",
"python.command.python.execShinyInTerminal.title": "Run Shiny App in Terminal",
"python.command.python.execStreamlitInTerminal.title": "Run Streamlit App in Terminal",
"python.command.python.execInConsole.title": "Run Python File in Console",
"python.command.python.debugInTerminal.title": "Debug Python File in Terminal",
Expand Down Expand Up @@ -44,7 +43,6 @@
"python.debugger.deprecatedMessage": "This configuration will be deprecated soon. Please replace `python` with `debugpy` to use the new Python Debugger extension.",
"python.defaultInterpreterPath.description": "Path to default Python to use when extension loads up for the first time, no longer used once an interpreter is selected for the workspace. See [here](https://aka.ms/AAfekmf) to understand when this is used",
"python.diagnostics.sourceMapsEnabled.description": "Enable source map support for meaningful stack traces in error logs.",
"python.enableWebAppSupport.description": "Enable experimental support for Python applications",
"python.envFile.description": "Absolute path to a file containing environment variable definitions.",
"python.experiments.enabled.description": "Enables A/B tests experiments in the Python extension. If enabled, you may get included in proposed enhancements and/or features.",
"python.experiments.optInto.description": "List of experiment to opt into. If empty, user is assigned the default experiment groups. See [here](https://github.com/microsoft/vscode-python/wiki/AB-Experiments) for more details.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,12 +102,11 @@ export interface ICommandNameArgumentTypeMapping extends ICommandNameWithoutArgu
[Commands.Exec_In_Terminal_Icon]: [undefined, Uri];
[Commands.Debug_In_Terminal]: [Uri];
// --- Start Positron ---
[Commands.Exec_Dash_In_Terminal]: [undefined, Uri];
[Commands.Exec_FastAPI_In_Terminal]: [undefined, Uri];
[Commands.Exec_Flask_In_Terminal]: [undefined, Uri];
[Commands.Exec_Gradio_In_Terminal]: [undefined, Uri];
[Commands.Exec_Shiny_In_Terminal]: [undefined, Uri];
[Commands.Exec_Streamlit_In_Terminal]: [undefined, Uri];
[Commands.Exec_Dash_In_Terminal]: [];
[Commands.Exec_FastAPI_In_Terminal]: [];
[Commands.Exec_Flask_In_Terminal]: [];
[Commands.Exec_Gradio_In_Terminal]: [];
[Commands.Exec_Streamlit_In_Terminal]: [];
[Commands.Exec_In_Console]: [];
[Commands.Focus_Positron_Console]: [];
// --- End Positron ---
Expand Down
1 change: 0 additions & 1 deletion extensions/positron-python/src/client/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@ export namespace Commands {
export const Exec_FastAPI_In_Terminal = 'python.execFastAPIInTerminal';
export const Exec_Flask_In_Terminal = 'python.execFlaskInTerminal';
export const Exec_Gradio_In_Terminal = 'python.execGradioInTerminal';
export const Exec_Shiny_In_Terminal = 'python.execShinyInTerminal';
export const Exec_Streamlit_In_Terminal = 'python.execStreamlitInTerminal';
export const Exec_In_Console = 'python.execInConsole';
export const Exec_Selection_In_Console = 'python.execSelectionInConsole';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,10 @@ export function translateProductToModule(product: Product): string {
return 'ensurepip';
case Product.python:
return 'python';
// --- Start Positron ---
case Product.fastapiCli:
return 'fastapi_cli';
// --- End Positron ---
default: {
throw new Error(`Product ${product} cannot be installed as a Python Module.`);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export class ProductService implements IProductService {
this.ProductTypes.set(Product.pip, ProductType.DataScience);
this.ProductTypes.set(Product.ensurepip, ProductType.DataScience);
this.ProductTypes.set(Product.python, ProductType.Python);
// --- Start Positron ---
this.ProductTypes.set(Product.fastapiCli, ProductType.DataScience);
// --- End Positron ---
}
public getProductType(product: Product): ProductType {
return this.ProductTypes.get(product)!;
Expand Down
3 changes: 3 additions & 0 deletions extensions/positron-python/src/client/common/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export enum Product {
pip = 27,
ensurepip = 28,
python = 29,
// --- Start Positron ---
fastapiCli = 101,
// --- End Positron ---
}

export const IInstaller = Symbol('IInstaller');
Expand Down
2 changes: 1 addition & 1 deletion extensions/positron-python/src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ export async function activate(context: IExtensionContext): Promise<PythonExtens

// --- Start Positron ---

activatePositron(serviceContainer, context)
activatePositron(serviceContainer)
// Run in the background.
.ignoreErrors();

Expand Down
68 changes: 68 additions & 0 deletions extensions/positron-python/src/client/positron-run-app.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/*---------------------------------------------------------------------------------------------
* Copyright (C) 2024 Posit Software, PBC. All rights reserved.
* Licensed under the Elastic License 2.0. See LICENSE.txt for license information.
*--------------------------------------------------------------------------------------------*/

// eslint-disable-next-line import/no-unresolved
import * as positron from 'positron';
import * as vscode from 'vscode';

/**
* Represents options returned from ${@link RunAppOptions.getTerminalOptions}.
*/
export interface RunAppTerminalOptions {
/**
* The command line to run in the terminal.
*/
commandLine: string;

/**
* The optional environment variables to create the terminal with.
*/
env?: { [key: string]: string | null | undefined };
}

/**
* Represents options for the ${@link PositronRunApp.runApplication} function.
*/
export interface RunAppOptions {
/**
* The human-readable label for the application e.g. `'Shiny'`, also used as the ${@link vscode.Terminal.name}.
*/
name: string;

/**
* A function that will be called to get the terminal options for running the application.
*
* @param runtime The language runtime metadata for the document's language.
* @param document The document to run.
* @param port The port to run the application on, if known.
* @param urlPrefix The URL prefix to use, if known.
* @returns The terminal options for running the application. Return `undefined` to abort the run.
*/
getTerminalOptions: (
runtime: positron.LanguageRuntimeMetadata,
document: vscode.TextDocument,
port?: string,
urlPrefix?: string,
) => RunAppTerminalOptions | undefined | Promise<RunAppTerminalOptions | undefined>;

/**
* The optional URL path at which to preview the application.
*/
urlPath?: string;
}

/**
* The public API of the Positron Run App extension.
*/
export interface PositronRunApp {
/**
* Run an application in the terminal.
*
* @param options Options for running the application.
* @returns If terminal shell integration is supported, resolves when the application server has
* started, otherwise resolves when the command has been sent to the terminal.
*/
runApplication(options: RunAppOptions): Promise<void>;
}
13 changes: 7 additions & 6 deletions extensions/positron-python/src/client/positron/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ import { EnvironmentType } from '../pythonEnvironments/info';
import { isProblematicCondaEnvironment } from '../interpreter/configuration/environmentTypeComparer';
import { Interpreters } from '../common/utils/localize';
import { IApplicationShell } from '../common/application/types';
import { activateAppDetection } from './webAppContexts';
import { activateAppDetection as activateWebAppDetection } from './webAppContexts';
import { activateWebAppCommands } from './webAppCommands';

export async function activatePositron(
serviceContainer: IServiceContainer,
context: vscode.ExtensionContext,
): Promise<void> {
export async function activatePositron(serviceContainer: IServiceContainer): Promise<void> {
try {
const disposables = serviceContainer.get<IDisposableRegistry>(IDisposableRegistry);
// Register a command to check if ipykernel is installed for a given interpreter.
Expand Down Expand Up @@ -77,7 +75,10 @@ export async function activatePositron(
);

// Activate detection for web applications
activateAppDetection(context.subscriptions);
activateWebAppDetection(disposables);

// Activate web application commands.
activateWebAppCommands(serviceContainer, disposables);

traceInfo('activatePositron: done!');
} catch (ex) {
Expand Down
Loading
Loading