Exemple #1
0
 def scan_code(self, code: str) -> Sequence[ast.AST]:
     try:
         # horast can parse and preserve comments
         tree = horast.parse(code)
     except (ValueError, TypeError):
         # fallback to regular typed ast
         tree = ast.parse(code)
     return self.scan_ast(tree)
Exemple #2
0
def transform_code(code: str, xformer: "ASTMigrator") -> str:
    """Apply a transformer to a given chunk of source code

    This will parse the code using the AST and find the expressions that are interesting according to xformer.

    If those are found the resulting statements will be rewritten and merged into the final source code
    """

    line_ends = list(accumulate([len(x) for x in code.splitlines(keepends=True)]))
    line_starts = [0] + [x for x in line_ends[:-1]]

    try:
        tree = horast.parse(code)
    except Exception as e_horast:
        # fallback to regular typed ast
        try:
            tree = ast.parse(code)
        except Exception as e:
            raise CantParseException(str(e), code)

    matched = list(xformer.scan_ast(tree))

    astmonkey.transformers.ParentChildNodeTransformer().visit(tree)

    def node_to_code_offset(node, use_col_offset=True):
        return line_starts[node.lineno - 1] + use_col_offset * node.col_offset

    # Replace the matched patterns in reverse line order
    for match in sorted(
        matched, key=lambda node: (node.lineno, node.col_offset), reverse=True
    ):
        xformer.transform_match(match)

        parent_statement = find_parent_statement(match)
        next_statement = find_next_sibling(parent_statement)

        code_start = node_to_code_offset(parent_statement)
        if next_statement:
            code_end = node_to_code_offset(next_statement, use_col_offset=False)
        else:
            code_end = len(code)

        new_code = horast.unparse(parent_statement)
        new_code = new_code.strip()

        code = code[:code_start] + new_code + "\n" + code[code_end:]

    return code
Exemple #3
0
async def get_robot_meta(robot_type: Union[Type[Robot], Type[RobotService]], source: str) -> None:

    # TODO use inspect.getsource(robot_type) instead of source parameters
    #  once we will get rid of type_def_from_source / temp. module

    meta = robot.RobotMeta(robot_type.__name__)
    meta.features.focus = hasattr(robot_type, "focus")  # TODO more sophisticated test? (attr(s) and return value?)

    tree = parse(source)
    meta.features.move_to_pose = feature(tree, robot_type, Robot.move_to_pose.__name__)
    meta.features.move_to_joints = feature(tree, robot_type, Robot.move_to_joints.__name__)
    meta.features.stop = feature(tree, robot_type, Robot.stop.__name__)

    if issubclass(robot_type, Robot) and robot_type.urdf_package_path:
        meta.urdf_package_filename = os.path.split(robot_type.urdf_package_path)[1]

    glob.ROBOT_META[robot_type.__name__] = meta
Exemple #4
0
        def visit_FunctionDef(self, node: FunctionDef) -> FunctionDef:

            if node.name == 'main':

                kw = []

                if kwargs:
                    for k, v in kwargs.items():
                        kw.append(keyword(arg=k, value=v))

                if kwargs2parse:
                    for k, v in kwargs2parse.items():
                        kw.append(keyword(arg=k, value=parse(v)))

                node.body.insert(
                    0,
                    Assign(targets=[Name(id=name, ctx=Store())],
                           value=Call(func=Name(id=cls, ctx=Load()),
                                      args=[],
                                      keywords=kw)))

            return node
Exemple #5
0
def get_logic_from_source(source_code: str, project: Project) -> None:

    tree = parse(source_code)

    assert isinstance(tree, Module)

    try:
        actions_cache, _, _ = get_actions_cache(project)
    except ProjectException as e:
        raise SourceException(e)
    # objects_cache = get_objects_cache(project, id_to_var=True)

    found_actions: Set[str] = set()

    loop = main_loop_body(tree)

    last_action: Union[None, Action] = None

    for node_idx, node in enumerate(loop):

        # simple checks for expected 'syntax' of action calls (e.g. 'robot.move_to(**res.MoveToBoxIN)')
        if not isinstance(node, Expr) or not isinstance(
                node.value, Call) or not isinstance(node.value.func,
                                                    Attribute):
            raise SourceException("Unexpected content.")

        try:
            val = node.value
            obj_id = val.func.value.id  # type: ignore
            method = val.func.attr  # type: ignore
        except (AttributeError, IndexError) as e:
            print(e)
            raise SourceException("Script has unexpected content.")
        """
        Support for both:
            robot.move_to(res.MoveToBoxIN)  # args
        ...as well as
            robot.move_to(**res.MoveToBoxIN)  # kwargs
        """
        if len(val.args) == 1 and not val.keywords:
            action_id = val.args[0].attr  # type: ignore
        elif len(val.keywords) == 1 and not val.args:
            action_id = val.keywords[0].value.attr  # type: ignore
        else:
            raise SourceException("Unexpected argument(s) to the action.")

        if action_id in found_actions:
            raise SourceException(f"Duplicate action: {action_id}.")
        found_actions.add(action_id)

        # TODO test if object instance exists
        # raise GenerateSourceException(f"Unknown object id {obj_id}.")

        try:
            action = actions_cache[action_id]
        except KeyError:
            raise SourceException(f"Unknown action {action_id}.")

        at_obj, at_method = action.type.split("/")
        at_obj = camel_case_to_snake_case(
            at_obj)  # convert obj id into script variable name

        if at_obj != obj_id or at_method != method:
            raise SourceException(
                f"Action type {action.type} does not correspond to source, where it is"
                f" {obj_id}/{method}.")

        action.inputs.clear()
        action.outputs.clear()

        if node_idx == 0:
            action.inputs.append(ActionIO(ActionIOEnum.FIRST.value))
        else:
            assert last_action is not None
            action.inputs.append(ActionIO(last_action.id))

        if node_idx > 0:
            assert last_action is not None
            actions_cache[last_action.id].outputs.append(ActionIO(action.id))

        if node_idx == len(loop) - 1:
            action.outputs.append(ActionIO(ActionIOEnum.LAST.value))

        last_action = action
Exemple #6
0
#the ast module is f*****g useless, because it doesnt include comments
#same with inspect, because it only gives the source, not a tree

with open("../vm.py") as f:
    text = f.read()

from horast import parse, unparse
#print("Parsing...")
tree = parse(text)
#print(unparse(tree))
#print(tree)
"""
class Visitor(RecursiveAstVisitor[typed_ast.ast3]):
    def visit_node(self, node):
        if not only_localizable or hasattr(node, 'lineno') and hasattr(node, 'col_offset'):
            print(node)
visitor = Visitor()
visitor.visit(tree)
"""

from typed_ast import _ast3 as ast
from horast.nodes import Comment

node = tree.body[2].body[-1].body[-2]

print("| Instruction | Description |")
print("|-------------|-------------|")

while len(node.orelse) > 0:
    #print(type(node.orelse[0]))
    if type(node.orelse[0]) in [ast.Break]:
Exemple #7
0
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
Exemple #8
0
def _publish(project_id: str, package_name: str):

    with tempfile.TemporaryDirectory() as tmpdirname:

        try:
            project = ps.get_project(project_id)
            scene = ps.get_scene(project.scene_id)

            project_dir = os.path.join(tmpdirname, "arcor2_project")

            data_path = os.path.join(project_dir, "data")
            ot_path = os.path.join(project_dir, "object_types")
            srv_path = os.path.join(project_dir, "services")

            os.makedirs(data_path)
            os.makedirs(ot_path)
            os.makedirs(srv_path)

            with open(os.path.join(ot_path, "__init__.py"), "w"):
                pass

            with open(os.path.join(srv_path, "__init__.py"), "w"):
                pass

            with open(os.path.join(data_path, "project.json"), "w") as project_file:
                project_file.write(project.to_json())

            with open(os.path.join(data_path, "scene.json"), "w") as scene_file:
                scene_file.write(scene.to_json())

            obj_types_with_models: Set[str] = set()

            for scene_obj in scene.objects:  # TODO handle inheritance

                obj_type = ps.get_object_type(scene_obj.type)

                if obj_type.model and obj_type.id not in obj_types_with_models:
                    obj_types_with_models.add(obj_type.id)

                    model = ps.get_model(obj_type.model.id, obj_type.model.type)
                    obj_model = ObjectModel(obj_type.model.type, **{model.type().value.lower(): model})

                    with open(os.path.join(data_path, camel_case_to_snake_case(obj_type.id) + ".json"), "w")\
                            as model_file:
                        model_file.write(obj_model.to_json())

                with open(os.path.join(ot_path, camel_case_to_snake_case(obj_type.id)) + ".py", "w") as obj_file:
                    obj_file.write(obj_type.source)

            for scene_srv in scene.services:
                srv = ps.get_service_type(scene_srv.type)
                with open(os.path.join(srv_path, camel_case_to_snake_case(srv.id)) + ".py", "w") as srv_file:
                    srv_file.write(srv.source)

        except ps.PersistentStorageException as e:
            logger.exception("Failed to get something from the project service.")
            return str(e), 404

        try:

            with open(os.path.join(project_dir, 'script.py'), "w") as script_file:

                if project.has_logic:
                    script_file.write(program_src(project, scene, built_in_types_names(), True))
                else:
                    try:
                        script = ps.get_project_sources(project.id).script

                        # check if it is a valid Python code
                        try:
                            horast.parse(script)
                        except SyntaxError:
                            logger.exception("Failed to parse code of the uploaded script.")
                            return "Invalid code.", 501

                        script_file.write(script)

                    except ps.PersistentStorageException:

                        logger.info("Script not found on project service, creating one from scratch.")

                        # write script without the main loop
                        script_file.write(program_src(project, scene, built_in_types_names(), False))

            with open(os.path.join(project_dir, 'resources.py'), "w") as res:
                res.write(derived_resources_class(project))

            with open(os.path.join(project_dir, 'actions.py'), "w") as act:
                act.write(global_actions_class(project))

            with open(os.path.join(project_dir, 'action_points.py'), "w") as aps:
                aps.write(global_action_points_class(project))

            with open(os.path.join(project_dir, 'package.json'), "w") as pkg:
                pkg.write(PackageMeta(package_name, datetime.now(tz=timezone.utc)).to_json())

        except SourceException as e:
            logger.exception("Failed to generate script.")
            return str(e), 501

        archive_path = os.path.join(tmpdirname, "arcor2_project")
        shutil.make_archive(archive_path, 'zip',  project_dir)
        return send_file(archive_path + ".zip", as_attachment=True, cache_timeout=0)
Exemple #9
0
import inspect
import sys

from horast import parse

from arcor2.source.utils import function_implemented

module_tree = parse(inspect.getsource(sys.modules[__name__]))


def func_1() -> None:
    """
    Test subject
    :return:
    """
    return None


def func_2() -> None:
    """
    Test subject
    :return:
    """

    raise NotImplementedError("This function is not implemented.")


def func_3() -> None:

    var = 1 + 2
    raise NotImplementedError(f"{var}")