def test_object_actions() -> None: actions = object_actions(TestObject, parse_def(TestObject)) assert len(actions) == 2 for act in actions.values(): if act.name == TestObject.action_1.__name__: expected_meta = ActionMetadata(blocking=True) expected_meta.cancellable = True assert act.meta == expected_meta assert not act.returns assert act.description == "Short description." assert not act.disabled assert act.origins is None assert len(act.parameters) == 3 for param in act.parameters: if param.name == "param1": assert param.description == "str param" elif param.name == "param2": assert param.description == "int param" elif param.name == "param3": assert param.description == "bool param" else: pytest.fail( f"Unknown parameter: {param.name} of action {act.name}." ) elif act.name == TestObject.action_2.__name__: assert act.meta == ActionMetadata() assert len(act.returns) == 1 assert act.description == "Short description" assert not act.disabled assert act.origins is None assert not act.parameters else: pytest.fail(f"Unknown action: {act.name}.")
async def new_object_type_cb(req: srpc.o.NewObjectType.Request, ui: WsClient) -> None: async with ctx_write_lock(glob.LOCK.SpecialValues.ADDING_OBJECT, ui): meta = req.args if meta.type in glob.OBJECT_TYPES: raise Arcor2Exception("Object type already exists.") hlp.is_valid_type(meta.type) if meta.base not in glob.OBJECT_TYPES: raise Arcor2Exception( f"Unknown base object type '{meta.base}', " f"known types are: {', '.join(glob.OBJECT_TYPES.keys())}.") base = glob.OBJECT_TYPES[meta.base] if base.meta.disabled: raise Arcor2Exception("Base object is disabled.") assert base.type_def is not None if issubclass(base.type_def, Robot): raise Arcor2Exception("Can't subclass Robot.") meta.has_pose = issubclass(base.type_def, GenericWithPose) if not meta.has_pose and meta.object_model: raise Arcor2Exception( "Object without pose can't have collision model.") if req.dry_run: return None obj = meta.to_object_type() ast = new_object_type(glob.OBJECT_TYPES[meta.base].meta, meta) obj.source = tree_to_str(ast) if meta.object_model: if meta.object_model.type == Model3dType.MESH: # TODO check whether mesh id exists - if so, then use existing mesh, if not, upload a new one # ...get whole mesh (focus_points) based on mesh id assert meta.object_model.mesh try: meta.object_model.mesh = await storage.get_mesh( meta.object_model.mesh.id) except storage.ProjectServiceException as e: glob.logger.error(e) raise Arcor2Exception( f"Mesh ID {meta.object_model.mesh.id} does not exist.") else: meta.object_model.model().id = meta.type await storage.put_model(meta.object_model.model()) type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, base.type_def, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) assert issubclass(type_def, base.type_def) actions = object_actions(type_def, ast) await storage.update_object_type(obj) glob.OBJECT_TYPES[meta.type] = ObjectTypeData(meta, type_def, actions, ast) add_ancestor_actions(meta.type, glob.OBJECT_TYPES) evt = sevts.o.ChangedObjectTypes([meta]) evt.change_type = events.Event.Type.ADD asyncio.ensure_future(notif.broadcast_event(evt)) return None
async def get_object_data(object_types: ObjectTypeDict, obj_id: str) -> None: logger.debug(f"Processing {obj_id}.") if obj_id in object_types: logger.debug(f"{obj_id} already processed, skipping...") return obj_iddesc = await storage.get_object_type_iddesc(obj_id) if obj_id in glob.OBJECT_TYPES: assert obj_iddesc.modified assert glob.OBJECT_TYPES[ obj_id].meta.modified, f"Object {obj_id} does not have 'modified' in its meta." if obj_iddesc.modified == glob.OBJECT_TYPES[obj_id].meta.modified: logger.debug(f"No need to update {obj_id}.") return obj = await storage.get_object_type(obj_id) try: bases = otu.base_from_source(obj.source, obj_id) if not bases: logger.debug( f"{obj_id} is definitely not an ObjectType (subclass of {object.__name__}), maybe mixin?" ) return if bases[0] not in object_types.keys() | built_in_types_names(): logger.debug(f"Getting base class {bases[0]} for {obj_id}.") await get_object_data(object_types, bases[0]) for mixin in bases[1:]: mixin_obj = await storage.get_object_type(mixin) await hlp.run_in_executor( hlp.save_and_import_type_def, mixin_obj.source, mixin_obj.id, object, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) except Arcor2Exception as e: logger.error( f"Disabling ObjectType {obj.id}: can't get a base. {str(e)}") object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "ObjectType disabled.", disabled=True, problem="Can't get base.", modified=obj.modified)) return logger.debug(f"Updating {obj_id}.") try: type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, Generic, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) except Arcor2Exception as e: logger.debug(f"{obj.id} is probably not an ObjectType. {str(e)}") return assert issubclass(type_def, Generic) try: meta = meta_from_def(type_def) except Arcor2Exception as e: logger.error(f"Disabling ObjectType {obj.id}.") logger.debug(e, exc_info=True) object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "ObjectType disabled.", disabled=True, problem=str(e), modified=obj.modified)) return meta.modified = obj.modified if obj.model: try: model = await storage.get_model(obj.model.id, obj.model.type) except Arcor2Exception as e: logger.error( f"{obj.model.id}: failed to get collision model of type {obj.model.type}. {str(e)}" ) meta.disabled = True meta.problem = "Can't get collision model." object_types[obj_id] = ObjectTypeData(meta) return if isinstance(model, Mesh) and model.data_id not in await storage.files_ids(): logger.error( f"Disabling {meta.type} as its mesh file {model.data_id} does not exist." ) meta.disabled = True meta.problem = "Mesh file does not exist." object_types[obj_id] = ObjectTypeData(meta) return kwargs = {model.type().value.lower(): model} meta.object_model = ObjectModel(model.type(), **kwargs) # type: ignore ast = parse(obj.source) otd = ObjectTypeData(meta, type_def, object_actions(type_def, ast), ast) object_types[obj_id] = otd
async def get_object_data(object_types: ObjectTypeDict, obj_id: str) -> None: glob.logger.debug(f"Processing {obj_id}.") if obj_id in object_types: glob.logger.debug(f"{obj_id} already processed, skipping...") return obj = await storage.get_object_type(obj_id) if obj_id in glob.OBJECT_TYPES and glob.OBJECT_TYPES[obj_id].type_def is not None: stored_type_def = glob.OBJECT_TYPES[obj_id].type_def assert stored_type_def # TODO do not compare sources but 'modified` # the code we get from type_def has Unix line endings, while the code from Project service might have Windows... obj.source = convert_line_endings_to_unix(obj.source) if get_containing_module_sources(stored_type_def) == obj.source: glob.logger.debug(f"No need to update {obj_id}.") return try: bases = otu.base_from_source(obj.source, obj_id) if bases and bases[0] not in object_types.keys() | built_in_types_names(): glob.logger.debug(f"Getting base class {bases[0]} for {obj_id}.") await get_object_data(object_types, bases[0]) for mixin in bases[1:]: mixin_obj = await storage.get_object_type(mixin) await hlp.run_in_executor( hlp.save_and_import_type_def, mixin_obj.source, mixin_obj.id, object, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) except Arcor2Exception as e: glob.logger.warn(f"Disabling object type {obj.id}: can't get a base. {str(e)}") object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "Object type disabled.", disabled=True, problem="Can't get base.") ) return glob.logger.debug(f"Updating {obj_id}.") try: type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, Generic, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) except Arcor2Exception as e: glob.logger.debug(f"{obj.id} is probably not an object type. {str(e)}") return assert issubclass(type_def, Generic) try: meta = meta_from_def(type_def) otu.get_settings_def(type_def) # just to check if settings are ok except Arcor2Exception as e: glob.logger.warning(f"Disabling object type {obj.id}.") glob.logger.debug(e, exc_info=True) object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "Object type disabled.", disabled=True, problem=str(e)) ) return if obj.model: try: model = await storage.get_model(obj.model.id, obj.model.type) except Arcor2Exception: glob.logger.error(f"{obj.model.id}: failed to get collision model of type {obj.model.type}.") meta.disabled = True meta.problem = "Can't get collision model." object_types[obj_id] = ObjectTypeData(meta) return kwargs = {model.type().value.lower(): model} meta.object_model = ObjectModel(model.type(), **kwargs) # type: ignore ast = parse(obj.source) otd = ObjectTypeData(meta, type_def, object_actions(type_def, ast), ast) object_types[obj_id] = otd
async def get_object_data(object_types: ObjectTypeDict, obj_id: str) -> None: glob.logger.debug(f"Processing {obj_id}.") if obj_id in object_types: glob.logger.debug(f"{obj_id} already processed, skipping...") return obj = await storage.get_object_type(obj_id) if obj_id in glob.OBJECT_TYPES and glob.OBJECT_TYPES[ obj_id].type_def is not None: stored_type_def = glob.OBJECT_TYPES[obj_id].type_def assert stored_type_def if hash(get_containing_module_sources(stored_type_def)) == hash( obj.source): glob.logger.debug(f"No need to update {obj_id}.") return try: base = otu.base_from_source(obj.source, obj_id) if base and base not in object_types.keys() | built_in_types_names(): glob.logger.debug(f"Getting base class {base} for {obj_id}.") await get_object_data(object_types, base) except Arcor2Exception: object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "Object type disabled.", disabled=True, problem="Can't get base.")) return glob.logger.debug(f"Updating {obj_id}.") try: type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, Generic, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) assert issubclass(type_def, Generic) meta = meta_from_def(type_def) otu.get_settings_def(type_def) # just to check if settings are ok except Arcor2Exception as e: glob.logger.warning(f"Disabling object type {obj.id}.") glob.logger.debug(e, exc_info=True) object_types[obj_id] = ObjectTypeData( ObjectTypeMeta(obj_id, "Object type disabled.", disabled=True, problem=str(e))) return if obj.model: try: model = await storage.get_model(obj.model.id, obj.model.type) except Arcor2Exception: glob.logger.error( f"{obj.model.id}: failed to get collision model of type {obj.model.type}." ) meta.disabled = True meta.problem = "Can't get collision model." object_types[obj_id] = ObjectTypeData(meta) return kwargs = {model.type().value.lower(): model} meta.object_model = ObjectModel(model.type(), **kwargs) # type: ignore ast = parse(obj.source) otd = ObjectTypeData(meta, type_def, object_actions(type_def, ast), ast) object_types[obj_id] = otd
async def new_object_type_cb(req: srpc.o.NewObjectType.Request, ui: WsClient) -> None: async with ctx_write_lock(glob.LOCK.SpecialValues.ADDING_OBJECT, glob.USERS.user_name(ui)): meta = req.args if meta.type in glob.OBJECT_TYPES: raise Arcor2Exception("Object type already exists.") hlp.is_valid_type(meta.type) if meta.base not in glob.OBJECT_TYPES: raise Arcor2Exception( f"Unknown base object type '{meta.base}', " f"known types are: {', '.join(glob.OBJECT_TYPES.keys())}.") base = glob.OBJECT_TYPES[meta.base] if base.meta.disabled: raise Arcor2Exception("Base object is disabled.") assert base.type_def is not None if issubclass(base.type_def, Robot): raise Arcor2Exception("Can't subclass Robot.") meta.has_pose = issubclass(base.type_def, GenericWithPose) if issubclass(base.type_def, CollisionObject): if not meta.object_model: raise Arcor2Exception( "Objects based on CollisionObject must have collision model." ) else: if meta.object_model: raise Arcor2Exception( "Only objects based on CollisionObject can have collision model." ) if req.dry_run: return None obj = meta.to_object_type() ast = new_object_type(glob.OBJECT_TYPES[meta.base].meta, meta) obj.source = tree_to_str(ast) if meta.object_model: await update_object_model(meta, meta.object_model) type_def = await hlp.run_in_executor( hlp.save_and_import_type_def, obj.source, obj.id, base.type_def, settings.OBJECT_TYPE_PATH, settings.OBJECT_TYPE_MODULE, ) assert issubclass(type_def, base.type_def) actions = object_actions(type_def, ast) meta.modified = await storage.update_object_type(obj) glob.OBJECT_TYPES[meta.type] = ObjectTypeData(meta, type_def, actions, ast) add_ancestor_actions(meta.type, glob.OBJECT_TYPES) evt = sevts.o.ChangedObjectTypes([meta]) evt.change_type = events.Event.Type.ADD asyncio.ensure_future(notif.broadcast_event(evt)) return None