Skip to content

Commit be279d8

Browse files
committed
Added code to mrc_call_python_function, mrc_get_python_enum_value,
mrc_get_python_variable, and mrc_set_python_variable blocks to check whether the module, class, enum, function/method, or variable still exists. Put appropriate warnings on blocks. Simplified the parameters for functions that create blocks for the toolbox.
1 parent bfffb90 commit be279d8

File tree

9 files changed

+816
-313
lines changed

9 files changed

+816
-313
lines changed

src/blocks/mrc_call_python_function.ts

Lines changed: 381 additions & 245 deletions
Large diffs are not rendered by default.

src/blocks/mrc_get_python_enum_value.ts

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,9 @@
2323
import * as Blockly from 'blockly';
2424
import { Order } from 'blockly/python';
2525

26-
import { getOutputCheck } from './utils/python';
26+
import { getEnumData, getOutputCheck } from './utils/python';
2727
import { EnumData } from './utils/python_json_types';
28+
import { Editor } from '../editor/editor';
2829
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
2930
import { createFieldDropdown } from '../fields/FieldDropdown';
3031
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
@@ -40,6 +41,8 @@ export const BLOCK_NAME = 'mrc_get_python_enum_value';
4041
const FIELD_ENUM_CLASS_NAME = 'ENUM_TYPE';
4142
const FIELD_ENUM_VALUE = 'ENUM_VALUE';
4243

44+
const WARNING_ID_ENUM_CHANGED = 'enum changed';
45+
4346
// Variables and functions used for populating the drop down field for the enum values.
4447

4548
const PythonEnumValues = Object.create(null);
@@ -132,10 +135,55 @@ const GET_PYTHON_ENUM_VALUE = {
132135
this.setOutput(true);
133136
}
134137
// Create the drop-down with the enum values.
135-
const enumValues = PythonEnumValues[this.mrcEnumType];
136-
this.getInput('ENUM')!
137-
.appendField(createFieldDropdown(enumValues), FIELD_ENUM_VALUE);
138-
}
138+
let enumValues = PythonEnumValues[this.mrcEnumType];
139+
if (enumValues) {
140+
this.getInput('ENUM')!
141+
.appendField(createFieldDropdown(enumValues), FIELD_ENUM_VALUE);
142+
} else {
143+
// This enum no longer exists. Create a field to hold the enum value.
144+
// In checkBlock, we'll put a warning on the block.
145+
this.getInput('ENUM')!
146+
.appendField(createFieldNonEditableText(''), FIELD_ENUM_VALUE)
147+
}
148+
},
149+
150+
/**
151+
* mrcOnLoad is called for each GetPythonEnumValueBlock when the blocks are loaded in the blockly
152+
* workspace.
153+
*/
154+
mrcOnLoad: function(this: GetPythonEnumValueBlock, _editor: Editor): void {
155+
this.checkBlock();
156+
},
157+
checkBlock: function(this: GetPythonEnumValueBlock): void {
158+
const warnings: string[] = [];
159+
160+
const enumClassName = this.getFieldValue(FIELD_ENUM_CLASS_NAME);
161+
const enumData = getEnumData(enumClassName);
162+
if (enumData) {
163+
const blockEnumValue = this.getFieldValue(FIELD_ENUM_VALUE);
164+
if (!enumData.enumValues.includes(blockEnumValue)) {
165+
warnings.push(Blockly.Msg.WARNING_GET_ENUM_VALUE_MISSING_VALUE);
166+
}
167+
} else {
168+
warnings.push(Blockly.Msg.WARNING_GET_ENUM_VALUE_MISSING_ENUM);
169+
}
170+
171+
if (warnings.length) {
172+
// Add a warnings to the block.
173+
const warningText = warnings.join('\n\n');
174+
this.setWarningText(warningText, WARNING_ID_ENUM_CHANGED);
175+
const icon = this.getIcon(Blockly.icons.IconType.WARNING);
176+
if (icon) {
177+
icon.setBubbleVisible(true);
178+
}
179+
if (this.rendered) {
180+
(this as unknown as Blockly.BlockSvg).bringToFront();
181+
}
182+
} else {
183+
// Clear the existing warning on the block.
184+
this.setWarningText(null, WARNING_ID_ENUM_CHANGED);
185+
}
186+
},
139187
};
140188

141189
export const setup = function() {

src/blocks/mrc_get_python_variable.ts

Lines changed: 148 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,18 @@ import { Order } from 'blockly/python';
2626
import {
2727
createModuleOrClassVariableSetterBlock,
2828
createInstanceVariableSetterBlock } from '../blocks/mrc_set_python_variable';
29-
import { getAllowedTypesForSetCheck, getOutputCheck } from './utils/python';
30-
import { VariableGettersAndSetters } from './utils/python_json_types';
29+
import {
30+
getAllowedTypesForSetCheck,
31+
getClassData,
32+
getModuleData,
33+
getOutputCheck } from './utils/python';
34+
import {
35+
ClassData,
36+
ModuleData,
37+
organizeVarDataByType,
38+
VariableGettersAndSetters } from './utils/python_json_types';
3139
import * as variable from './utils/variable';
40+
import { Editor } from '../editor/editor';
3241
import { ExtendedPythonGenerator } from '../editor/extended_python_generator';
3342
import { createFieldDropdown } from '../fields/FieldDropdown';
3443
import { createFieldNonEditableText } from '../fields/FieldNonEditableText';
@@ -50,6 +59,8 @@ export enum VariableKind {
5059
const FIELD_MODULE_OR_CLASS_NAME = 'MODULE_OR_CLASS';
5160
const FIELD_VARIABLE_NAME = 'VAR';
5261

62+
const WARNING_ID_VARIABLE_CHANGED = 'variable changed';
63+
5364
// Variables and functions used for populating the drop down field for the variable names.
5465

5566
const PythonVariableGetterNames = Object.create(null);
@@ -258,7 +269,112 @@ const GET_PYTHON_VARIABLE = {
258269
.setAlign(Blockly.inputs.Align.RIGHT)
259270
.appendField(this.mrcSelfLabel);
260271
}
261-
}
272+
},
273+
274+
/**
275+
* mrcOnLoad is called for each GetPythonVariableBlock when the blocks are loaded in the blockly
276+
* workspace.
277+
*/
278+
mrcOnLoad: function(this: GetPythonVariableBlock, _editor: Editor): void {
279+
this.checkBlock();
280+
},
281+
checkBlock: function(this: GetPythonVariableBlock): void {
282+
const warnings: string[] = [];
283+
284+
switch (this.mrcVarKind) {
285+
case VariableKind.MODULE:
286+
this.checkModuleVariable(warnings);
287+
break;
288+
case VariableKind.CLASS:
289+
this.checkClassVariable(warnings);
290+
break;
291+
case VariableKind.INSTANCE:
292+
this.checkInstanceVariable(warnings);
293+
break;
294+
}
295+
296+
if (warnings.length) {
297+
// Add a warnings to the block.
298+
const warningText = warnings.join('\n\n');
299+
this.setWarningText(warningText, WARNING_ID_VARIABLE_CHANGED);
300+
const icon = this.getIcon(Blockly.icons.IconType.WARNING);
301+
if (icon) {
302+
icon.setBubbleVisible(true);
303+
}
304+
if (this.rendered) {
305+
(this as unknown as Blockly.BlockSvg).bringToFront();
306+
}
307+
} else {
308+
// Clear the existing warning on the block.
309+
this.setWarningText(null, WARNING_ID_VARIABLE_CHANGED);
310+
}
311+
},
312+
checkModuleVariable: function(this: GetPythonVariableBlock, warnings: string[]): void {
313+
// If this block is getting a module variable, check whether the module and variable still
314+
// exist. If the module or variable doesn't exist, put a visible warning on this block.
315+
const moduleName = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
316+
const moduleData = getModuleData(moduleName);
317+
if (moduleData) {
318+
let foundVariable = false;
319+
const blockVarName = this.getFieldValue(FIELD_VARIABLE_NAME);
320+
for (const varData of moduleData.moduleVariables) {
321+
if (blockVarName === varData.name &&
322+
this.mrcVarType === varData.type) {
323+
foundVariable = true;
324+
break;
325+
}
326+
}
327+
if (!foundVariable) {
328+
warnings.push(Blockly.Msg.WARNING_GET_MODULE_VARIABLE_MISSING_VARIABLE);
329+
}
330+
} else {
331+
warnings.push(Blockly.Msg.WARNING_GET_MODULE_VARIABLE_MISSING_MODULE);
332+
}
333+
},
334+
checkClassVariable: function(this: GetPythonVariableBlock, warnings: string[]): void {
335+
// If this block is getting a class variable, check whether the class and variable still
336+
// exist. If the class or variable doesn't exist, put a visible warning on this block.
337+
const className = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
338+
const classData = getClassData(className);
339+
if (classData) {
340+
let foundVariable = false;
341+
const blockVarName = this.getFieldValue(FIELD_VARIABLE_NAME);
342+
for (const varData of classData.classVariables) {
343+
if (blockVarName === varData.name &&
344+
this.mrcVarType === varData.type) {
345+
foundVariable = true;
346+
break;
347+
}
348+
}
349+
if (!foundVariable) {
350+
warnings.push(Blockly.Msg.WARNING_GET_CLASS_VARIABLE_MISSING_VARIABLE);
351+
}
352+
} else {
353+
warnings.push(Blockly.Msg.WARNING_GET_CLASS_VARIABLE_MISSING_CLASS);
354+
}
355+
},
356+
checkInstanceVariable: function(this: GetPythonVariableBlock, warnings: string[]): void {
357+
// If this block is getting an instance variable, check whether the class and variable still
358+
// exist. If the class or variable doesn't exist, put a visible warning on this block.
359+
const className = this.getFieldValue(FIELD_MODULE_OR_CLASS_NAME);
360+
const classData = getClassData(className);
361+
if (classData) {
362+
let foundVariable = false;
363+
const blockVarName = this.getFieldValue(FIELD_VARIABLE_NAME);
364+
for (const varData of classData.instanceVariables) {
365+
if (blockVarName === varData.name &&
366+
this.mrcVarType === varData.type) {
367+
foundVariable = true;
368+
break;
369+
}
370+
}
371+
if (!foundVariable) {
372+
warnings.push(Blockly.Msg.WARNING_GET_INSTANCE_VARIABLE_MISSING_VARIABLE);
373+
}
374+
} else {
375+
warnings.push(Blockly.Msg.WARNING_GET_INSTANCE_VARIABLE_MISSING_CLASS);
376+
}
377+
},
262378
};
263379

264380
export const setup = function() {
@@ -303,20 +419,25 @@ export const pythonFromBlock = function(
303419
// Functions used for creating blocks for the toolbox.
304420

305421
export function addModuleVariableBlocks(
306-
moduleName: string,
307-
varsByType: {[key: string]: VariableGettersAndSetters},
422+
moduleData: ModuleData,
308423
contents: toolboxItems.ContentsType[]) {
309-
addModuleOrClassVariableBlocks(
310-
VariableKind.MODULE, moduleName, moduleName, varsByType, contents);
424+
if (moduleData.moduleVariables.length) {
425+
const varsByType: {[key: string]: VariableGettersAndSetters} =
426+
organizeVarDataByType(moduleData.moduleVariables);
427+
addModuleOrClassVariableBlocks(
428+
VariableKind.MODULE, moduleData.moduleName, moduleData.moduleName, varsByType, contents);
429+
}
311430
}
312431

313432
export function addClassVariableBlocks(
314-
importModule: string,
315-
className: string,
316-
varsByType: {[key: string]: VariableGettersAndSetters},
433+
classData: ClassData,
317434
contents: toolboxItems.ContentsType[]) {
318-
addModuleOrClassVariableBlocks(
319-
VariableKind.CLASS, importModule, className, varsByType, contents);
435+
if (classData.classVariables.length) {
436+
const varsByType: {[key: string]: VariableGettersAndSetters} =
437+
organizeVarDataByType(classData.classVariables);
438+
addModuleOrClassVariableBlocks(
439+
VariableKind.CLASS, classData.moduleName, classData.className, varsByType, contents);
440+
}
320441
}
321442

322443
function addModuleOrClassVariableBlocks(
@@ -334,7 +455,7 @@ function addModuleOrClassVariableBlocks(
334455
contents.push(getterBlock);
335456
if (variableGettersAndSetters.varNamesForSetter.includes(varName)) {
336457
const setterBlock = createModuleOrClassVariableSetterBlock(
337-
VariableKind.CLASS, importModule, moduleOrClassName, varType, varName);
458+
varKind, importModule, moduleOrClassName, varType, varName);
338459
contents.push(setterBlock);
339460
}
340461
}
@@ -360,18 +481,21 @@ function createModuleOrClassVariableGetterBlock(
360481
}
361482

362483
export function addInstanceVariableBlocks(
363-
className: string,
364-
varsByType: {[key: string]: VariableGettersAndSetters},
484+
classData: ClassData,
365485
contents: toolboxItems.ContentsType[]) {
366-
for (const varType in varsByType) {
367-
const variableGettersAndSetters = varsByType[varType];
368-
for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) {
369-
const varName = variableGettersAndSetters.varNamesForGetter[i];
370-
const getterBlock = createInstanceVariableGetterBlock(className, varType, varName);
371-
contents.push(getterBlock);
372-
if (variableGettersAndSetters.varNamesForSetter.includes(varName)) {
373-
const setterBlock = createInstanceVariableSetterBlock(className, varType, varName);
374-
contents.push(setterBlock);
486+
if (classData.instanceVariables.length) {
487+
const varsByType: {[key: string]: VariableGettersAndSetters} =
488+
organizeVarDataByType(classData.instanceVariables);
489+
for (const varType in varsByType) {
490+
const variableGettersAndSetters = varsByType[varType];
491+
for (let i = 0; i < variableGettersAndSetters.varNamesForGetter.length; i++) {
492+
const varName = variableGettersAndSetters.varNamesForGetter[i];
493+
const getterBlock = createInstanceVariableGetterBlock(classData.className, varType, varName);
494+
contents.push(getterBlock);
495+
if (variableGettersAndSetters.varNamesForSetter.includes(varName)) {
496+
const setterBlock = createInstanceVariableSetterBlock(classData.className, varType, varName);
497+
contents.push(setterBlock);
498+
}
375499
}
376500
}
377501
}

0 commit comments

Comments
 (0)