Skip to content

Commit

Permalink
Support for new Python language model (#3235)
Browse files Browse the repository at this point in the history
* Sketch preview project type.

* Switch to language model rather than separate (sub) languages.

* Exclude Python v2+ from creating functions on project create.

* Store project language model in workspace settings.

* Add project-level function source.

* Use "isolated mode" detection of functions for Python v2+.

* Open function .py file on project creation.

* Detect Python language model.

* Add Python preview to VS Code init list.

* Extract python preview model into constant.

* Sketch prompt for Python function location.

* Sketch selecting a script.

* Sketch opening template.

* Append new function to file.

* Enforce storage configuration for Python V2+.

* Exclude extension bundles from Python V2+ projects.

* Check for valid Functions version.

* Complete merge from `main`.

* Resolve linter warnings.

* Updates per PM feedback.

* Updates per PR feedback.

* Shift subwizard to separate class.

* Move Python location script up front.

* Move Python script name prompt up front.

* Switch to local content provider.

* Fake Markdown for trigger template.

* Tweak text.

* Move content to query string.

* Sketch embedding PyStein templates in extension.

* Add Python Preview templates to verified list.

* Exclude OpenAPI function from PyStein projects.

* Account for lack of new files in function creation.

* Use template for project.

* Fix finding MD content in template.

* Disable name prompt for V2.

* Update required Function host version, add whitespace during append.

* Update version number.

* Replace direct use of fs-extra.

* Add notes for preview Python model numbering.

* More notes.

* Show getting started MD on creation.

* Do not expose properties commands from PyStein functions in tree.

* Exclude PyStein from re-adding bundle property on function create.

* Updates to documentation per feedback in PR.

* Update supported PyStein version number and comments.

* Allow opening MD to the side.

* Use opt-out list instead of opt-in.
  • Loading branch information
philliphoff committed Aug 19, 2022
1 parent 0f10d45 commit 925796c
Show file tree
Hide file tree
Showing 46 changed files with 5,919 additions and 187 deletions.
9 changes: 8 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "vscode-azurefunctions",
"displayName": "Azure Functions",
"description": "%azureFunctions.description%",
"version": "1.7.5-alpha.0",
"version": "1.8.0-alpha.0",
"publisher": "ms-azuretools",
"icon": "resources/azure-functions.png",
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
Expand Down Expand Up @@ -77,6 +77,7 @@
"onCommand:azureFunctions.viewDeploymentLogs",
"onCommand:azureFunctions.viewProperties",
"onDebugInitialConfigurations",
"onFileSystem:vscode-azurefunctions-static-content",
"onUri",
"onView:azureWorkspace",
"workspaceContains:**/host.json"
Expand Down Expand Up @@ -833,6 +834,12 @@
""
]
},
"azureFunctions.projectLanguageModel": {
"scope": "resource",
"type": "number",
"description": "%azureFunctions.projectLanguageModel%",
"minimum": 1
},
"azureFunctions.projectTemplateKey": {
"scope": "resource",
"type": "string",
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@
"azureFunctions.problemMatchers.funcPythonWatch": "Azure Functions Python problems (watch mode)",
"azureFunctions.problemMatchers.funcWatch": "Azure Functions problems (watch mode)",
"azureFunctions.projectLanguage": "The default language to use when performing operations like \"Create New Function\".",
"azureFunctions.projectLanguageModel": "The default language model to use when performing operations like \"Create New Function\".",
"azureFunctions.projectLanguage.preview": "(Preview)",
"azureFunctions.projectOpenBehavior": "The behavior to use after creating a new project. The options are \"AddToWorkspace\", \"OpenInNewWindow\", or \"OpenInCurrentWindow\".",
"azureFunctions.projectRuntime": "The default version of the Azure Functions runtime to use when performing operations like \"Create New Function\".",
Expand Down
4,968 changes: 4,968 additions & 0 deletions resources/backupTemplates/pystein/templates/templates.json

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions src/LocalResourceProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import * as path from 'path';
import { Disposable, workspace, WorkspaceFolder } from "vscode";
import { tryGetFunctionProjectRoot } from "./commands/createNewProject/verifyIsProject";
import { getFunctionAppName, getJavaDebugSubpath } from "./commands/initProjectForVSCode/InitVSCodeStep/JavaInitVSCodeStep";
import { funcVersionSetting, JavaBuildTool, javaBuildTool, ProjectLanguage, projectLanguageSetting } from "./constants";
import { funcVersionSetting, JavaBuildTool, javaBuildTool, ProjectLanguage, projectLanguageModelSetting, projectLanguageSetting } from "./constants";
import { FuncVersion, tryParseFuncVersion } from "./FuncVersion";
import { localize } from "./localize";
import { InitLocalProjectTreeItem } from "./tree/localProject/InitLocalProjectTreeItem";
Expand All @@ -35,6 +35,7 @@ export class FunctionsLocalResourceProvider implements WorkspaceResourceProvider
if (projectPath) {
try {
const language: ProjectLanguage | undefined = getWorkspaceSetting(projectLanguageSetting, projectPath);
const languageModel: number | undefined = getWorkspaceSetting(projectLanguageModelSetting, projectPath);
const version: FuncVersion | undefined = tryParseFuncVersion(getWorkspaceSetting(funcVersionSetting, projectPath));
if (language === undefined || version === undefined) {
children.push(new InitLocalProjectTreeItem(parent, projectPath, folder));
Expand All @@ -52,7 +53,7 @@ export class FunctionsLocalResourceProvider implements WorkspaceResourceProvider
}


const treeItem: LocalProjectTreeItem = new LocalProjectTreeItem(parent, { effectiveProjectPath, folder, language, version, preCompiledProjectPath, isIsolated });
const treeItem: LocalProjectTreeItem = new LocalProjectTreeItem(parent, { effectiveProjectPath, folder, language, languageModel, version, preCompiledProjectPath, isIsolated });
this._projectDisposables.push(treeItem);
children.push(treeItem);
}
Expand Down
2 changes: 1 addition & 1 deletion src/commands/addBinding/BindingListStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class BindingListStep extends AzureWizardPromptStep<IBindingWizardContext
const language: ProjectLanguage = nonNullProp(context, 'language');
const version: FuncVersion = nonNullProp(context, 'version');
const templateProvider = ext.templateProvider.get(context);
const templates: IBindingTemplate[] = await templateProvider.getBindingTemplates(context, context.projectPath, language, version);
const templates: IBindingTemplate[] = await templateProvider.getBindingTemplates(context, context.projectPath, language, undefined, version);
return templates
.filter(b => b.direction.toLowerCase() === direction.toLowerCase())
.sort((a, b) => a.displayName.localeCompare(b.displayName))
Expand Down
6 changes: 4 additions & 2 deletions src/commands/addBinding/addBinding.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ export async function addBinding(context: IActionContext, data: Uri | LocalFunct
workspaceFolder = nonNullValue(getContainingWorkspace(functionJsonPath), 'workspaceFolder');
workspacePath = workspaceFolder.uri.fsPath;
projectPath = await tryGetFunctionProjectRoot(context, workspaceFolder, 'modalPrompt') || workspacePath;
[language, version] = await verifyInitForVSCode(context, projectPath);
const verifiedInit = await verifyInitForVSCode(context, projectPath);
language = verifiedInit.language;
version = verifiedInit.version;
} else {
if (!data) {
const noItemFoundErrorMessage: string = localize('noLocalProject', 'No matching functions found. C# and Java projects do not support this operation.');
Expand All @@ -48,7 +50,7 @@ export async function addBinding(context: IActionContext, data: Uri | LocalFunct
workspaceFolder = projectTi.workspaceFolder;
workspacePath = projectTi.workspacePath;
projectPath = projectTi.effectiveProjectPath;
language = projectTi.langauge;
language = projectTi.language;
version = projectTi.version;
}

Expand Down
3 changes: 2 additions & 1 deletion src/commands/createFunction/FunctionCreateStepBase.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,8 @@ function runPostFunctionCreateSteps(func: ICachedFunction): void {
void callWithTelemetryAndErrorHandling('postFunctionCreate', async (context: IActionContext) => {
context.telemetry.suppressIfSuccessful = true;

if (getContainingWorkspace(func.projectPath)) {
// If function creation created a new file, open it in an editor...
if (func.newFilePath && getContainingWorkspace(func.projectPath)) {
if (await AzExtFsExtra.pathExists(func.newFilePath)) {
await window.showTextDocument(await workspace.openTextDocument(Uri.file(func.newFilePath)));
}
Expand Down
104 changes: 16 additions & 88 deletions src/commands/createFunction/FunctionListStep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,32 +3,19 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { AzureWizardExecuteStep, AzureWizardPromptStep, IActionContext, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions } from '@microsoft/vscode-azext-utils';
import { AzureWizardPromptStep, IActionContext, IAzureQuickPickItem, IAzureQuickPickOptions, IWizardOptions } from '@microsoft/vscode-azext-utils';
import * as escape from 'escape-string-regexp';
import { JavaBuildTool, ProjectLanguage, TemplateFilter, templateFilterSetting } from '../../constants';
import { canValidateAzureWebJobStorageOnDebug } from '../../debug/validatePreDebug';
import { ext } from '../../extensionVariables';
import { getAzureWebJobsStorage } from '../../funcConfig/local.settings';
import { FuncVersion } from '../../FuncVersion';
import { localize } from '../../localize';
import { IFunctionTemplate } from '../../templates/IFunctionTemplate';
import { nonNullProp } from '../../utils/nonNull';
import { isPythonV2Plus } from '../../utils/pythonUtils';
import { getWorkspaceSetting, updateWorkspaceSetting } from '../../vsCodeConfig/settings';
import { addBindingSettingSteps } from '../addBinding/settingSteps/addBindingSettingSteps';
import { AzureWebJobsStorageExecuteStep } from '../appSettings/AzureWebJobsStorageExecuteStep';
import { AzureWebJobsStoragePromptStep } from '../appSettings/AzureWebJobsStoragePromptStep';
import { JavaPackageNameStep } from '../createNewProject/javaSteps/JavaPackageNameStep';
import { DotnetFunctionCreateStep } from './dotnetSteps/DotnetFunctionCreateStep';
import { DotnetFunctionNameStep } from './dotnetSteps/DotnetFunctionNameStep';
import { DotnetNamespaceStep } from './dotnetSteps/DotnetNamespaceStep';
import { FunctionSubWizard } from './FunctionSubWizard';
import { IFunctionWizardContext } from './IFunctionWizardContext';
import { JavaFunctionCreateStep } from './javaSteps/JavaFunctionCreateStep';
import { JavaFunctionNameStep } from './javaSteps/JavaFunctionNameStep';
import { OpenAPICreateStep } from './openAPISteps/OpenAPICreateStep';
import { OpenAPIGetSpecificationFileStep } from './openAPISteps/OpenAPIGetSpecificationFileStep';
import { ScriptFunctionCreateStep } from './scriptSteps/ScriptFunctionCreateStep';
import { ScriptFunctionNameStep } from './scriptSteps/ScriptFunctionNameStep';
import { TypeScriptFunctionCreateStep } from './scriptSteps/TypeScriptFunctionCreateStep';
import { PythonLocationStep } from './scriptSteps/PythonLocationStep';

export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardContext> {
public hideStepCount: boolean = true;
Expand All @@ -47,7 +34,7 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
const language: ProjectLanguage = nonNullProp(context, 'language');
const version: FuncVersion = nonNullProp(context, 'version');
const templateProvider = ext.templateProvider.get(context);
const templates: IFunctionTemplate[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, version, TemplateFilter.All, context.projectTemplateKey);
const templates: IFunctionTemplate[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, TemplateFilter.All, context.projectTemplateKey);
const foundTemplate: IFunctionTemplate | undefined = templates.find((t: IFunctionTemplate) => {
if (options.templateId) {
const actualId: string = t.id.toLowerCase();
Expand All @@ -69,75 +56,15 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
}

public async getSubWizard(context: IFunctionWizardContext): Promise<IWizardOptions<IFunctionWizardContext> | undefined> {
const template: IFunctionTemplate | undefined = context.functionTemplate;
if (template) {
const promptSteps: AzureWizardPromptStep<IFunctionWizardContext>[] = [];
switch (context.language) {
case ProjectLanguage.Java:
promptSteps.push(new JavaPackageNameStep(), new JavaFunctionNameStep());
break;
case ProjectLanguage.CSharp:
case ProjectLanguage.FSharp:
promptSteps.push(new DotnetFunctionNameStep(), new DotnetNamespaceStep());
break;
default:
promptSteps.push(new ScriptFunctionNameStep());
break;
}

// Add settings to context that were programmatically passed in
for (const key of Object.keys(this._functionSettings)) {
context[key.toLowerCase()] = this._functionSettings[key];
}

addBindingSettingSteps(template.userPromptedSettings, promptSteps);

const executeSteps: AzureWizardExecuteStep<IFunctionWizardContext>[] = [];
switch (context.language) {
case ProjectLanguage.Java:
executeSteps.push(new JavaFunctionCreateStep());
break;
case ProjectLanguage.CSharp:
case ProjectLanguage.FSharp:
executeSteps.push(await DotnetFunctionCreateStep.createStep(context));
break;
case ProjectLanguage.TypeScript:
executeSteps.push(new TypeScriptFunctionCreateStep());
break;
default:
executeSteps.push(new ScriptFunctionCreateStep());
break;
}

if ((!template.isHttpTrigger && !template.isSqlBindingTemplate) && !canValidateAzureWebJobStorageOnDebug(context.language) && !await getAzureWebJobsStorage(context, context.projectPath)) {
promptSteps.push(new AzureWebJobsStoragePromptStep());
executeSteps.push(new AzureWebJobsStorageExecuteStep());
}

const title: string = localize('createFunction', 'Create new {0}', template.name);
return { promptSteps, executeSteps, title };
} else if (context.generateFromOpenAPI) {
const promptSteps: AzureWizardPromptStep<IFunctionWizardContext>[] = [];
const executeSteps: AzureWizardExecuteStep<IFunctionWizardContext>[] = [];

switch (context.language) {
case ProjectLanguage.Java:
promptSteps.push(new JavaPackageNameStep());
break;
case ProjectLanguage.CSharp:
promptSteps.push(new DotnetNamespaceStep());
break;
default:
break;
}

promptSteps.push(new OpenAPIGetSpecificationFileStep());
executeSteps.push(await OpenAPICreateStep.createStep(context));
const isV2PythonModel = isPythonV2Plus(context.language, context.languageModel);

const title: string = localize('createFunction', 'Create new {0}', 'HTTP Triggers from OpenAPI (v2/v3) Specification File');
return { promptSteps, executeSteps, title };
if (isV2PythonModel) {
return {
// TODO: Title?
promptSteps: [ new PythonLocationStep(this._functionSettings) ]
};
} else {
return undefined;
return await FunctionSubWizard.createSubWizard(context, this._functionSettings);
}
}

Expand Down Expand Up @@ -169,7 +96,7 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
context.generateFromOpenAPI = true;
break;
} else if (result === 'reloadTemplates') {
await templateProvider.clearTemplateCache(context, context.projectPath, nonNullProp(context, 'language'), nonNullProp(context, 'version'));
await templateProvider.clearTemplateCache(context, context.projectPath, nonNullProp(context, 'language'), context.languageModel, nonNullProp(context, 'version'));
context.telemetry.properties.reloaded = 'true';
} else {
context.functionTemplate = result;
Expand All @@ -185,9 +112,10 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte

private async getPicks(context: IFunctionWizardContext, templateFilter: TemplateFilter): Promise<IAzureQuickPickItem<IFunctionTemplate | TemplatePromptResult>[]> {
const language: ProjectLanguage = nonNullProp(context, 'language');
const languageModel = context.languageModel;
const version: FuncVersion = nonNullProp(context, 'version');
const templateProvider = ext.templateProvider.get(context);
const templates: IFunctionTemplate[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, version, templateFilter, context.projectTemplateKey);
const templates: IFunctionTemplate[] = await templateProvider.getFunctionTemplates(context, context.projectPath, language, context.languageModel, version, templateFilter, context.projectTemplateKey);
context.telemetry.measurements.templateCount = templates.length;
const picks: IAzureQuickPickItem<IFunctionTemplate | TemplatePromptResult>[] = templates
.sort((a, b) => sortTemplates(a, b, templateFilter))
Expand All @@ -208,7 +136,7 @@ export class FunctionListStep extends AzureWizardPromptStep<IFunctionWizardConte
data: <IFunctionTemplate | TemplatePromptResult><unknown>undefined,
onPicked: () => { /* do nothing */ }
})
} else if (language === ProjectLanguage.CSharp || language === ProjectLanguage.Java || language === ProjectLanguage.Python || language === ProjectLanguage.TypeScript) {
} else if (language === ProjectLanguage.CSharp || language === ProjectLanguage.Java || (language === ProjectLanguage.Python && !isPythonV2Plus(language, languageModel)) || language === ProjectLanguage.TypeScript) {
// NOTE: Only show this if we actually found other templates
picks.push({
label: localize('openAPI', 'HTTP trigger(s) from OpenAPI V2/V3 Specification (Preview)'),
Expand Down

0 comments on commit 925796c

Please sign in to comment.