Exemple #1
0
    def __init__(self, backend_name: str, frame=None):

        self.backend_name = backend_name
        if frame is None:
            frame = get_frame(1)

        self.filename = _get_filename_from_frame(frame)
        if any(
                self.filename.startswith(start)
                for start in ("<ipython-", "/tmp/ipykernel_")):
            self.is_dummy_file = True
            self._ipython_src, self.filename = get_info_from_ipython()
            self.module_name = self.filename
        else:
            self.is_dummy_file = False
            self.module_name = get_module_name(frame)
        modules_backends[backend_name][self.module_name] = self
        self.used_functions = {}
        self.jit_functions = {}

        (
            jitted_dicts,
            codes_dependance,
            codes_dependance_classes,
            code_ext,
            special,
        ) = analysis_jit(self.get_source(), self.filename, backend_name)

        self.info_analysis = {
            "jitted_dicts": jitted_dicts,
            "codes_dependance": codes_dependance,
            "codes_dependance_classes": codes_dependance_classes,
            "special": special,
        }

        self.backend = backend = backends[backend_name]
        path_jit = mpi.Path(backend.jit.path_base)
        path_jit_class = mpi.Path(backend.jit.path_class)

        # TODO: check if these files have to be written here...
        # Write exterior code for functions
        for file_name, code in code_ext["function"].items():
            path_ext = path_jit / self.module_name.replace(".", os.path.sep)
            path_ext_file = path_ext / (file_name + ".py")
            write_if_has_to_write(path_ext_file, format_str(code), logger.info)

        # Write exterior code for classes
        for file_name, code in code_ext["class"].items():
            path_ext = path_jit_class / self.module_name.replace(
                ".", os.path.sep)
            path_ext_file = path_ext / (file_name + ".py")
            write_if_has_to_write(path_ext_file, format_str(code), logger.info)
Exemple #2
0
    def compile_extension(
        self,
        path_backend,
        name_ext_file=None,
        native=False,
        xsimd=False,
        openmp=False,
        str_accelerator_flags: Optional[str] = None,
        parallel=True,
        force=True,
    ):
        if name_ext_file is None:
            name_ext_file = self.name_ext_from_path_backend(path_backend)

        with open(path_backend) as file:
            source = file.read()

        source = source.replace("# __protected__ ", "")

        with open(path_backend.with_name(name_ext_file), "w") as file:
            file.write(format_str(source))

        compiling = False
        process = None
        return compiling, process
Exemple #3
0
def add_numba_comments(code):
    """Add Numba code in Python comments"""
    mod = parse(code)
    new_body = [CommentLine("# __protected__ from numba import njit")]

    for node in mod.body:
        if isinstance(node, gast.FunctionDef):
            new_body.append(CommentLine("# __protected__ @njit(cache=True, fastmath=True)"))
        new_body.append(node)

    mod.body = new_body
    return format_str(unparse(mod))
Exemple #4
0
    def _make_backend_code(self, path_py, analyse):
        """Create a backend code from a Python file"""

        boosted_dicts, code_dependance, annotations, blocks, codes_ext = analyse

        boosted_dicts = {
            key: value[self.name]
            for key, value in boosted_dicts.items()
        }

        lines_code = ["\n" + code_dependance + "\n"]
        lines_header = self._make_first_lines_header()
        # Deal with functions
        for fdef in boosted_dicts["functions"].values():
            signatures_func = self._make_header_1_function(fdef, annotations)
            if signatures_func:
                lines_header.extend(signatures_func)
            code_function = self._make_code_from_fdef_node(fdef)
            lines_code.append(code_function)

        # Deal with methods
        signatures, code_for_meths = self._make_code_methods(
            boosted_dicts, annotations, path_py)
        lines_code.extend(code_for_meths)
        if signatures:
            lines_header.extend(signatures)

        # Deal with blocks
        signatures, code_blocks = self._make_code_blocks(blocks)
        lines_code.extend(code_blocks)
        if signatures:
            lines_header.extend(signatures)

        code = "\n".join(lines_code).strip()

        if code:
            code = self._make_beginning_code() + code
            self._append_line_header_variable(lines_header, "__transonic__")
            code += f"\n\n__transonic__ = ('{transonic.__version__}',)"

        return format_str(
            code), codes_ext, "\n".join(lines_header).strip() + "\n"
Exemple #5
0
    def make_backend_file(self,
                          path_py: Path,
                          analyse=None,
                          force=False,
                          log_level=None):
        """Create a Python file from a Python file (if necessary)"""

        if log_level is not None:
            logger.set_level(log_level)

        path_py = Path(path_py)

        if not path_py.exists():
            raise FileNotFoundError(f"Input file {path_py} not found")

        if path_py.absolute().parent.name == f"__{self.name}__":
            logger.debug(f"skip file {path_py}")
            return None, None, None
        if not path_py.name.endswith(".py"):
            raise ValueError(
                f"transonic only processes Python file. Cannot process {path_py}"
            )

        path_dir = path_py.parent / str(f"__{self.name}__")
        path_backend = (path_dir / path_py.name).with_suffix(
            self.suffix_backend)

        if not has_to_build(path_backend, path_py) and not force:
            logger.warning(f"File {path_backend} already up-to-date.")
            return None, None, None

        if path_dir is None:
            return

        if not analyse:
            with open(path_py) as file:
                code = file.read()
            analyse = analyse_aot(code, path_py, self.name)

        code_backend, codes_ext, code_header = self._make_backend_code(
            path_py, analyse)
        if not code_backend:
            return
        logger.debug(f"code_{self.name}:\n{code_backend}")

        for file_name, code in codes_ext["function"].items():
            path_ext_file = path_dir / (file_name + ".py")
            write_if_has_to_write(path_ext_file, format_str(code), logger.info,
                                  force)

        for file_name, code in codes_ext["class"].items():
            path_ext_file = (path_dir.parent / f"__{self.name}__" /
                             (file_name + ".py"))
            write_if_has_to_write(path_ext_file, format_str(code), logger.info,
                                  force)

        written = write_if_has_to_write(path_backend, code_backend,
                                        logger.info, force)

        if not written:
            logger.warning(f"Code in file {path_backend} already up-to-date.")
            return

        if self.suffix_header:
            path_header = (path_dir / path_py.name).with_suffix(
                self.suffix_header)
            write_if_has_to_write(path_header, code_header, logger.info, force)

        logger.info(f"File {path_backend} updated")

        return path_backend
Exemple #6
0
 def _make_code_from_fdef_node(self, fdef):
     transformed = TypeHintRemover().visit(fdef)
     # convert the AST back to source code
     code = extast.unparse(transformed)
     return format_str(code)
Exemple #7
0
    def _make_code_method(self, class_name, fdef, meth_name, annotations,
                          boosted_dicts):
        class_def = boosted_dicts["classes"][class_name]

        if class_name in annotations["classes"]:
            annotations_class = annotations["classes"][class_name]
        else:
            annotations_class = {}

        if (class_name, meth_name) in annotations["methods"]:
            annotations_meth = annotations["methods"][(class_name, meth_name)]
        else:
            annotations_meth = {}

        meth_name = fdef.name
        python_code, attributes, _ = make_new_code_method_from_nodes(
            class_def, fdef)

        for attr in attributes:
            if attr not in annotations_class:
                raise NotImplementedError(
                    f"self.{attr} used but {attr} not in class annotations")
        types_attrs = {
            "self_" + attr: annotations_class[attr]
            for attr in attributes
        }
        types_pythran = {**types_attrs, **annotations_meth}

        # TODO: locals_types for methods
        locals_types = None
        signatures_method = self._make_header_from_fdef_annotations(
            extast.parse(python_code).body[0], [types_pythran], locals_types)

        str_self_dot_attributes = ", ".join("self." + attr
                                            for attr in attributes)
        args_func = [arg.id for arg in fdef.args.args[1:]]
        str_args_func = ", ".join(args_func)

        defaults = fdef.args.defaults
        nb_defaults = len(defaults)
        nb_args = len(fdef.args.args)
        nb_no_defaults = nb_args - nb_defaults - 1

        str_args_value_func = []
        ind_default = 0
        for ind, arg in enumerate(fdef.args.args[1:]):
            name = arg.id
            if ind < nb_no_defaults:
                str_args_value_func.append(f"{name}")
            else:
                default = extast.unparse(defaults[ind_default]).strip()
                str_args_value_func.append(f"{name}={default}")
                ind_default += 1

        str_args_value_func = ", ".join(str_args_value_func)

        if str_self_dot_attributes:
            str_args_backend_func = ", ".join(
                (str_self_dot_attributes, str_args_func))
        else:
            str_args_backend_func = str_args_func

        name_var_code_new_method = f"__code_new_method__{class_name}__{meth_name}"

        self._append_line_header_variable(signatures_method,
                                          name_var_code_new_method)
        python_code += (f'\n{name_var_code_new_method} = """\n\n'
                        f"def new_method(self, {str_args_value_func}):\n"
                        f"    return backend_func({str_args_backend_func})"
                        '\n\n"""\n')

        return signatures_method, format_str(python_code)
Exemple #8
0
def make_code_method_jit(cls, func_name):

    func = cls.__dict__[func_name]
    func = func.func

    new_code, attributes, name_new_func = make_new_code_method_from_objects(
        cls, func)

    try:
        cls_annotations = cls.__annotations__
    except AttributeError:
        cls_annotations = {}

    types_attrs = [
        cls_annotations[attr] for attr in attributes if attr in cls_annotations
    ]

    signature = inspect.signature(func)
    types_func = [param.annotation
                  for param in signature.parameters.values()][1:]
    types_pythran = types_attrs + types_func

    transonic_signatures = "\n"

    try:
        signatures_as_lists_strings = compute_signatures_from_typeobjects(
            types_pythran, base_type_formatter)
    except ValueError:
        signatures_as_lists_strings = []

    for signature_as_strings in signatures_as_lists_strings:
        transonic_signatures += (f"# transonic def {name_new_func}(" +
                                 ", ".join(signature_as_strings) + ")\n")

    new_code = "from transonic import jit\n\n@jit\n" + new_code

    python_code = transonic_signatures + "\n" + new_code

    str_self_dot_attributes = ", ".join("self." + attr for attr in attributes)
    args_func = list(signature.parameters.keys())[1:]
    str_args_func = ", ".join(args_func)

    str_args_value_func = ""
    for param, value in signature.parameters.items():
        if param == "self":
            continue
        elif value.default is value.empty:
            str_args_value_func += f"{param}, "
        else:
            str_args_value_func += f"{param}={value.default}, "

    str_args_value_func = str_args_value_func.rstrip(", ")

    name_new_method = f"__new_method__{cls.__name__}__{func_name}"
    python_code += (
        f"\ndef {name_new_method}"
        f"(self, {str_args_value_func}):\n"
        f"    return {name_new_func}({str_self_dot_attributes}, {str_args_func})"
        "\n")

    python_code = format_str(python_code)

    return python_code