diff --git a/python_tools/json_util.py b/python_tools/json_util.py index 307aa90f..d8be89d0 100644 --- a/python_tools/json_util.py +++ b/python_tools/json_util.py @@ -62,6 +62,10 @@ _DICT_FULL_MODULE_NAME_TO_MODULE_NAME = { + 'blocks_base_classes.opmode': 'blocks_base_classes', + 'blocks_base_classes.mechanism': 'blocks_base_classes', + 'blocks_base_classes.robot_base': 'blocks_base_classes', + 'hal._wpiHal': 'hal', 'hal.simulation._simulation': 'hal.simulation', diff --git a/python_tools/python_util.py b/python_tools/python_util.py index 26565b08..834745f3 100644 --- a/python_tools/python_util.py +++ b/python_tools/python_util.py @@ -139,15 +139,6 @@ def processSignature(signature_line: str) -> tuple[str, list[str], list[str], li def ignoreMember(parent, key: str, member): - """ - if inspect.ismodule(parent): - if hasattr(parent, '__all__'): - if key not in parent.__all__: - return True - else: - return True - """ - if inspect.ismodule(member): # Member is a module. if not inspect.ismodule(parent): diff --git a/src/blocks/utils/generated/server_python_scripts.json b/src/blocks/utils/generated/server_python_scripts.json index 1b76672b..7ca10aba 100644 --- a/src/blocks/utils/generated/server_python_scripts.json +++ b/src/blocks/utils/generated/server_python_scripts.json @@ -2,14 +2,14 @@ "aliases": {}, "classes": [ { - "className": "blocks_base_classes.mechanism.Mechanism", + "className": "blocks_base_classes.Mechanism", "classVariables": [], "constructors": [ { "args": [], - "declaringClassName": "blocks_base_classes.mechanism.Mechanism", + "declaringClassName": "blocks_base_classes.Mechanism", "functionName": "__init__", - "returnType": "blocks_base_classes.mechanism.Mechanism", + "returnType": "blocks_base_classes.Mechanism", "tooltip": "" } ], @@ -20,10 +20,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.mechanism.Mechanism" + "type": "blocks_base_classes.Mechanism" } ], - "declaringClassName": "blocks_base_classes.mechanism.Mechanism", + "declaringClassName": "blocks_base_classes.Mechanism", "functionName": "start", "returnType": "None", "tooltip": "" @@ -33,10 +33,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.mechanism.Mechanism" + "type": "blocks_base_classes.Mechanism" } ], - "declaringClassName": "blocks_base_classes.mechanism.Mechanism", + "declaringClassName": "blocks_base_classes.Mechanism", "functionName": "stop", "returnType": "None", "tooltip": "" @@ -46,21 +46,21 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.mechanism.Mechanism" + "type": "blocks_base_classes.Mechanism" } ], - "declaringClassName": "blocks_base_classes.mechanism.Mechanism", + "declaringClassName": "blocks_base_classes.Mechanism", "functionName": "update", "returnType": "None", "tooltip": "" } ], "instanceVariables": [], - "moduleName": "blocks_base_classes.mechanism", + "moduleName": "blocks_base_classes", "staticMethods": [] }, { - "className": "blocks_base_classes.opmode.OpMode", + "className": "blocks_base_classes.OpMode", "classVariables": [], "constructors": [ { @@ -68,12 +68,12 @@ { "defaultValue": "", "name": "robot", - "type": "blocks_base_classes.robot_base.RobotBase" + "type": "blocks_base_classes.RobotBase" } ], - "declaringClassName": "blocks_base_classes.opmode.OpMode", + "declaringClassName": "blocks_base_classes.OpMode", "functionName": "__init__", - "returnType": "blocks_base_classes.opmode.OpMode", + "returnType": "blocks_base_classes.OpMode", "tooltip": "" } ], @@ -84,10 +84,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.opmode.OpMode" + "type": "blocks_base_classes.OpMode" } ], - "declaringClassName": "blocks_base_classes.opmode.OpMode", + "declaringClassName": "blocks_base_classes.OpMode", "functionName": "loop", "returnType": "None", "tooltip": "" @@ -97,10 +97,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.opmode.OpMode" + "type": "blocks_base_classes.OpMode" } ], - "declaringClassName": "blocks_base_classes.opmode.OpMode", + "declaringClassName": "blocks_base_classes.OpMode", "functionName": "start", "returnType": "None", "tooltip": "" @@ -110,28 +110,28 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.opmode.OpMode" + "type": "blocks_base_classes.OpMode" } ], - "declaringClassName": "blocks_base_classes.opmode.OpMode", + "declaringClassName": "blocks_base_classes.OpMode", "functionName": "stop", "returnType": "None", "tooltip": "" } ], "instanceVariables": [], - "moduleName": "blocks_base_classes.opmode", + "moduleName": "blocks_base_classes", "staticMethods": [] }, { - "className": "blocks_base_classes.robot_base.RobotBase", + "className": "blocks_base_classes.RobotBase", "classVariables": [], "constructors": [ { "args": [], - "declaringClassName": "blocks_base_classes.robot_base.RobotBase", + "declaringClassName": "blocks_base_classes.RobotBase", "functionName": "__init__", - "returnType": "blocks_base_classes.robot_base.RobotBase", + "returnType": "blocks_base_classes.RobotBase", "tooltip": "" } ], @@ -142,10 +142,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.robot_base.RobotBase" + "type": "blocks_base_classes.RobotBase" } ], - "declaringClassName": "blocks_base_classes.robot_base.RobotBase", + "declaringClassName": "blocks_base_classes.RobotBase", "functionName": "start", "returnType": "None", "tooltip": "" @@ -155,10 +155,10 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.robot_base.RobotBase" + "type": "blocks_base_classes.RobotBase" } ], - "declaringClassName": "blocks_base_classes.robot_base.RobotBase", + "declaringClassName": "blocks_base_classes.RobotBase", "functionName": "stop", "returnType": "None", "tooltip": "" @@ -168,17 +168,17 @@ { "defaultValue": "", "name": "self", - "type": "blocks_base_classes.robot_base.RobotBase" + "type": "blocks_base_classes.RobotBase" } ], - "declaringClassName": "blocks_base_classes.robot_base.RobotBase", + "declaringClassName": "blocks_base_classes.RobotBase", "functionName": "update", "returnType": "None", "tooltip": "" } ], "instanceVariables": [], - "moduleName": "blocks_base_classes.robot_base", + "moduleName": "blocks_base_classes", "staticMethods": [] } ], @@ -192,19 +192,19 @@ { "enums": [], "functions": [], - "moduleName": "blocks_base_classes.mechanism", + "moduleName": "blocks_base_classes", "moduleVariables": [] }, { "enums": [], "functions": [], - "moduleName": "blocks_base_classes.opmode", + "moduleName": "blocks_base_classes", "moduleVariables": [] }, { "enums": [], "functions": [], - "moduleName": "blocks_base_classes.robot_base", + "moduleName": "blocks_base_classes", "moduleVariables": [] } ], diff --git a/src/blocks/utils/python.ts b/src/blocks/utils/python.ts index 45304b93..d18e1b30 100644 --- a/src/blocks/utils/python.ts +++ b/src/blocks/utils/python.ts @@ -31,6 +31,12 @@ import * as SetPythonVariable from "../mrc_set_python_variable"; // Utilities related to blocks for python modules and classes, including those from RobotPy, external samples, etc. +export const MODULE_NAME_BLOCKS_BASE_CLASSES = 'blocks_base_classes'; +export const CLASS_NAME_ROBOT_BASE = MODULE_NAME_BLOCKS_BASE_CLASSES + '.RobotBase'; +export const CLASS_NAME_OPMODE = MODULE_NAME_BLOCKS_BASE_CLASSES + '.OpMode'; +export const CLASS_NAME_MECHANISM = MODULE_NAME_BLOCKS_BASE_CLASSES + '.Mechanism'; + + export const robotPyData = generatedRobotPyData as PythonData; const externalSamplesData = generatedExternalSamplesData as PythonData const serverPythonScripts = generatedServerPythonScripts as PythonData; diff --git a/src/editor/extended_python_generator.ts b/src/editor/extended_python_generator.ts index 207bd553..c7f4abdc 100644 --- a/src/editor/extended_python_generator.ts +++ b/src/editor/extended_python_generator.ts @@ -23,6 +23,11 @@ import * as Blockly from 'blockly/core'; import { PythonGenerator } from 'blockly/python'; import { GeneratorContext } from './generator_context'; import * as MechanismContainerHolder from '../blocks/mrc_mechanism_component_holder'; +import { + MODULE_NAME_BLOCKS_BASE_CLASSES, + CLASS_NAME_OPMODE, + getClassData, +} from '../blocks/utils/python'; export class OpModeDetails { constructor(private name: string, private group : string, private enabled : boolean, private type : string) {} @@ -31,7 +36,7 @@ export class OpModeDetails { if (this.enabled){ code += '@' + this.type + "\n"; - + if (this.name){ code += '@Name(' + className + ', "' + this.name + '")\n'; } @@ -44,7 +49,7 @@ export class OpModeDetails { imports() : string{ let code = ''; if (this.enabled){ - code += 'from blocks_base_classes import ' + this.type; + code += 'from ' + MODULE_NAME_BLOCKS_BASE_CLASSES + ' import ' + this.type; if (this.name){ code += ', Name'; } @@ -69,7 +74,7 @@ export class ExtendedPythonGenerator extends PythonGenerator { private ports: {[key: string]: string} = Object.create(null); // Opmode details private details : OpModeDetails | null = null; - + constructor() { super('Python'); } @@ -144,16 +149,20 @@ export class ExtendedPythonGenerator extends PythonGenerator { /** * Add an import statement for a python module. + * If the given moduleOrClass is in the blocks_base_classes package, the simple name is returned. */ - addImport(importModule: string): void { - const baseClasses = ['RobotBase', 'OpMode', 'Mechanism']; - if (baseClasses.includes(importModule)) { - this.definitions_['import_' + importModule] = 'from blocks_base_classes import ' + importModule; - } - else{ - this.definitions_['import_' + importModule] = 'import ' + importModule; + addImport(moduleOrClass: string): string { + const key = 'import_' + moduleOrClass; + + if (moduleOrClass.startsWith(MODULE_NAME_BLOCKS_BASE_CLASSES + '.') && + moduleOrClass.lastIndexOf('.') == MODULE_NAME_BLOCKS_BASE_CLASSES.length) { + const simpleName = moduleOrClass.substring(MODULE_NAME_BLOCKS_BASE_CLASSES.length + 1); + this.definitions_[key] = 'from ' + MODULE_NAME_BLOCKS_BASE_CLASSES + ' import ' + simpleName; + return simpleName; } + this.definitions_[key] = 'import ' + moduleOrClass; + return ''; } /** @@ -167,7 +176,7 @@ export class ExtendedPythonGenerator extends PythonGenerator { this.events[funcName] = { 'sender': sender, 'eventName': eventName,} - } + } /** * Add a Hardware Port @@ -194,17 +203,20 @@ export class ExtendedPythonGenerator extends PythonGenerator { finish(code: string): string { if (this.context && this.workspace) { const className = this.context.getClassName(); - const classParent = this.context.getClassParent(); + const baseClassName = this.context.getBaseClassName(); const decorations = this.details?.decorations(className); const import_decorations = this.details?.imports(); - if (import_decorations){ + if (import_decorations) { this.definitions_['import_decorations'] = import_decorations; } - this.addImport(classParent); + const simpleBaseClassName = this.addImport(baseClassName); + if (!simpleBaseClassName) { + throw new Error('addImport for ' + baseClassName + ' did not return a valid simple name') + } - const classDef = 'class ' + className + '(' + classParent + '):\n'; + const classDef = 'class ' + className + '(' + simpleBaseClassName + '):\n'; const classMethods = []; if (this.events && Object.keys(this.events).length > 0) { @@ -236,31 +248,32 @@ export class ExtendedPythonGenerator extends PythonGenerator { } getClassSpecificForInit() : string{ - let classParent = this.context?.getClassParent(); - if (classParent == 'OpMode'){ + if (this.context?.getBaseClassName() == CLASS_NAME_OPMODE) { return 'robot' } return '' } /** - * This returns the list of methods that are derived from so that mrc_class_method_def + * This returns the list of methods that are derived from so that mrc_class_method_def * knows whether to call super() or not. * @returns list of method names */ getBaseClassMethods() : string[] { - // TODO(lizlooney): the names of base class methods should not be hard coded. - let classParent = this.context?.getClassParent(); - if (classParent == 'OpMode'){ - return ['start', 'loop', 'stop']; - } - else if (classParent == 'Mechanism') { - return ['start', 'update', 'stop']; - } - else if (classParent == 'RobotBase'){ - return ['start', 'update', 'stop']; + const methodNames: string[] = []; + + const baseClassName = this.context?.getBaseClassName(); + if (baseClassName) { + const classData = getClassData(baseClassName); + if (!classData) { + throw new Error('ClassData not found for ' + baseClassName); + } + classData.instanceMethods.forEach(functionData => { + methodNames.push(functionData.functionName); + }); } - return []; + + return methodNames; } /** diff --git a/src/editor/generator_context.ts b/src/editor/generator_context.ts index 86c4c1a2..32543a8c 100644 --- a/src/editor/generator_context.ts +++ b/src/editor/generator_context.ts @@ -20,6 +20,7 @@ */ import * as commonStorage from '../storage/common_storage'; +import { CLASS_NAME_ROBOT_BASE, CLASS_NAME_OPMODE, CLASS_NAME_MECHANISM } from '../blocks/utils/python'; export function createGeneratorContext(): GeneratorContext { @@ -63,18 +64,18 @@ export class GeneratorContext { return this.module.className; } - getClassParent(): string { + getBaseClassName(): string { if (!this.module) { - throw new Error('getClassParent: this.module is null.'); + throw new Error('getParentClassName: this.module is null.'); } if (this.module.moduleType === commonStorage.MODULE_TYPE_ROBOT) { - return 'RobotBase'; + return CLASS_NAME_ROBOT_BASE; } if (this.module.moduleType === commonStorage.MODULE_TYPE_OPMODE) { - return 'OpMode'; + return CLASS_NAME_OPMODE; } if (this.module.moduleType === commonStorage.MODULE_TYPE_MECHANISM) { - return 'Mechanism'; + return CLASS_NAME_MECHANISM; } return ''; } diff --git a/src/storage/common_storage.ts b/src/storage/common_storage.ts index a6729d1d..1afe4a22 100644 --- a/src/storage/common_storage.ts +++ b/src/storage/common_storage.ts @@ -83,7 +83,7 @@ export const MODULE_TYPE_ROBOT = 'robot'; export const MODULE_TYPE_MECHANISM = 'mechanism'; export const MODULE_TYPE_OPMODE = 'opmode'; -export const ROBOT_CLASS_NAME = 'Robot'; +const CLASS_NAME_ROBOT = 'Robot'; const DELIMITER_PREFIX = 'BlocksContent'; const MARKER_BLOCKS_CONTENT = 'blocksContent: '; @@ -526,7 +526,7 @@ export function newRobotContent(projectName: string): string { projectName: projectName, moduleName: projectName, dateModifiedMillis: 0, - className: ROBOT_CLASS_NAME, + className: CLASS_NAME_ROBOT, }; return startingBlocksToModuleContent(module, startingRobotBlocks); @@ -711,7 +711,7 @@ export async function produceDownloadProjectBlob( export function getClassNameForModule(moduleType: string, moduleName: string) { return (moduleType == MODULE_TYPE_ROBOT) - ? ROBOT_CLASS_NAME + ? CLASS_NAME_ROBOT : snakeCaseToPascalCase(moduleName); } diff --git a/src/toolbox/methods_category.ts b/src/toolbox/methods_category.ts index 8da4fbcb..8415c784 100644 --- a/src/toolbox/methods_category.ts +++ b/src/toolbox/methods_category.ts @@ -24,6 +24,7 @@ import * as Blockly from 'blockly/core'; import * as toolboxItems from './items'; import * as commonStorage from '../storage/common_storage'; import { MRC_CATEGORY_STYLE_METHODS } from '../themes/styles'; +import { CLASS_NAME_ROBOT_BASE, CLASS_NAME_OPMODE, CLASS_NAME_MECHANISM } from '../blocks/utils/python'; import { addInstanceWithinBlocks } from '../blocks/mrc_call_python_function'; import { createCustomMethodBlock, getBaseClassBlocks } from '../blocks/mrc_class_method_def'; import { Editor } from '../editor/editor'; @@ -40,9 +41,9 @@ export const getCategory = () => ({ export class MethodsCategory { private currentModule: commonStorage.Module | null = null; - private robotClassBlocks = getBaseClassBlocks('blocks_base_classes.robot_base.RobotBase'); - private mechanismClassBlocks = getBaseClassBlocks('blocks_base_classes.mechanism.Mechanism'); - private opmodeClassBlocks = getBaseClassBlocks('blocks_base_classes.opmode.OpMode'); + private robotClassBlocks = getBaseClassBlocks(CLASS_NAME_ROBOT_BASE); + private mechanismClassBlocks = getBaseClassBlocks(CLASS_NAME_MECHANISM); + private opmodeClassBlocks = getBaseClassBlocks(CLASS_NAME_OPMODE); constructor(blocklyWorkspace: Blockly.WorkspaceSvg) { blocklyWorkspace.registerToolboxCategoryCallback(CUSTOM_CATEGORY_METHODS, this.methodsFlyout.bind(this));