def function_implemented(tree: AST, func_name: str) -> bool: """Body of unimplemented function (e.g. object/service feature) contains only 'raise Arcor2NotImplemented()'. :param tree: :return: """ try: func_def = find_function(func_name, tree) except SourceException: return False raises = find_raises(func_def) if len(raises) != 1: return True exc = raises[0].exc if isinstance(exc, Call): # raise Arcor2NotImplemented("something") assert isinstance(exc.func, Name) if exc.func.id == Arcor2NotImplemented.__name__: return False if isinstance(exc, Name) and exc.id == Arcor2NotImplemented.__name__: # raise Arcor2NotImplemented return False return True
def test_meta() -> None: meta = ParameterMeta(param_name, StringEnumPlugin.type_name()) StringEnumPlugin.meta(meta, TestObject.action, find_function(TestObject.action.__name__, parse_def(TestObject))) assert meta.extra extra = IntegerEnumExtra.from_json(meta.extra) assert extra.allowed_values == TestEnum.set()
def main_loop(tree: Module) -> While: main = find_function("main", tree) for node in main.body: if isinstance( node, While ): # TODO more specific condition (test for True argument) return node raise SourceException("Main loop not found.")
def object_instance_from_res( tree: Module, object_name: str, object_id: str, cls_name: str, cls_type: Union[Literal["objects"], Literal["services"]]) -> None: main_body = find_function("main", tree).body last_assign_idx = -1 for body_idx, body_item in enumerate(main_body): if isinstance(body_item, (Assign, AnnAssign)): last_assign_idx = body_idx assign = AnnAssign(target=Name(id=fix_object_name(object_name), ctx=Store()), annotation=Name(id=cls_name, ctx=Load()), value=Subscript( value=get_name_attr('res', cls_type), slice=Index(value=Str(s=object_id, kind='')), ctx=Load()), simple=1) main_body.insert(last_assign_idx + 1, assign)
def object_actions(type_def: Type[Generic], tree: AST) -> Dict[str, ObjectAction]: ret: Dict[str, ObjectAction] = {} # ...inspect.ismethod does not work on un-initialized classes for method_name, method_def in iterate_over_actions(type_def): meta: ActionMetadata = method_def.__action__ # type: ignore data = ObjectAction(name=method_name, meta=meta) if method_name in type_def.CANCEL_MAPPING: meta.cancellable = True try: if not method_def.__doc__: doc = {} else: doc = parse_docstring(method_def.__doc__) doc_short = doc["short_description"] if doc_short: data.description = doc_short signature = inspect.signature(method_def) # sig.parameters is OrderedDict try: method_tree = find_function(method_name, tree) except SourceException: # function is probably defined in predecessor, will be added later continue hints = get_type_hints(method_def) # standard (unordered) dict if "an" not in signature.parameters.keys(): raise IgnoreActionException("Action is missing 'an' parameter.") try: if hints["an"] != Optional[str]: raise IgnoreActionException("Parameter 'an' has invalid type annotation.") except KeyError: raise IgnoreActionException("Parameter 'an' is missing type annotation.") parameter_names_without_self = list(signature.parameters.keys())[1:] if not parameter_names_without_self or parameter_names_without_self[-1] != "an": raise IgnoreActionException("The 'an' parameter have to be the last one.") # handle return try: return_ttype = hints["return"] except KeyError: raise IgnoreActionException("Action is missing return type annotation.") # ...just ignore NoneType for returns if return_ttype != type(None): # noqa: E721 if typing_inspect.is_tuple_type(return_ttype): for arg in typing_inspect.get_args(return_ttype): resolved_param = plugin_from_type(arg) if resolved_param is None: raise IgnoreActionException("None in return tuple is not supported.") data.returns.append(resolved_param.type_name()) else: # TODO resolving needed for e.g. enums - add possible values to action metadata somewhere? data.returns = [plugin_from_type(return_ttype).type_name()] for name in parameter_names_without_self[:-1]: # omit also an try: ttype = hints[name] except KeyError: raise IgnoreActionException(f"Parameter {name} is missing type annotation.") param_type = plugin_from_type(ttype) assert param_type is not None args = ParameterMeta(name=name, type=param_type.type_name()) try: param_type.meta(args, method_def, method_tree) except ParameterPluginException as e: raise IgnoreActionException(e) from e if name in type_def.DYNAMIC_PARAMS: args.dynamic_value = True dvp = type_def.DYNAMIC_PARAMS[name][1] if dvp: args.dynamic_value_parents = dvp def_val = signature.parameters[name].default if def_val is not inspect.Parameter.empty: args.default_value = param_type.value_to_json(def_val) try: args.description = doc["params"][name].strip() except KeyError: pass data.parameters.append(args) except Arcor2Exception as e: data.disabled = True data.problem = str(e) glob.logger.warn(f"Disabling action {method_name} of {type_def.__name__}. {str(e)}") ret[data.name] = data return ret
def object_actions(plugins: Dict[Type, Type[ParameterPlugin]], type_def: Union[Type[Generic], Type[Service]], source: str) -> ObjectActions: ret: ObjectActions = [] tree = horast.parse(source) # ...inspect.ismethod does not work on un-initialized classes for method_name, method_def in inspect.getmembers(type_def, predicate=inspect.isfunction): # TODO check also if the method has 'action' decorator (ast needed) if not hasattr(method_def, "__action__"): continue # action from ancestor, will be copied later (only if the action was not overridden) base_cls_def = type_def.__bases__[0] if hasattr(base_cls_def, method_name) and getattr(base_cls_def, method_name) == method_def: continue meta: ActionMetadata = method_def.__action__ data = ObjectAction(name=method_name, meta=meta) if method_name in type_def.CANCEL_MAPPING: meta.cancellable = True doc = parse_docstring(method_def.__doc__) doc_short = doc["short_description"] if doc_short: data.description = doc_short signature = inspect.signature(method_def) method_tree = find_function(method_name, tree) try: for name, ttype in get_type_hints(method_def).items(): try: param_type = plugins[ttype] except KeyError: for k, v in plugins.items(): if not v.EXACT_TYPE and inspect.isclass(ttype) and issubclass(ttype, k): param_type = v break else: if name == "return": # noqa: E721 # ...just ignore NoneType for returns continue # ignore action with unknown parameter type raise IgnoreActionException(f"Parameter {name} of action {method_name}" f" has unknown type {ttype}.") if name == "return": data.returns = param_type.type_name() continue args = ActionParameterMeta(name=name, type=param_type.type_name()) try: param_type.meta(args, method_def, method_tree) except ParameterPluginException as e: # TODO log exception raise IgnoreActionException(e) if name in type_def.DYNAMIC_PARAMS: args.dynamic_value = True dvp = type_def.DYNAMIC_PARAMS[name][1] if dvp: args.dynamic_value_parents = dvp def_val = signature.parameters[name].default if def_val is not inspect.Parameter.empty: args.default_value = def_val try: args.description = doc["params"][name].strip() except KeyError: pass data.parameters.append(args) except IgnoreActionException as e: data.disabled = True data.problem = str(e) # TODO log exception ret.append(data) return ret