Read about our upcoming Code of Conduct on this issue

Commit d6c1bd62 authored by Pierre Augier's avatar Pierre Augier
Browse files

transpiler for methods

--HG--
branch : dev
parent 6c4c9b2c0ce1
......@@ -6,6 +6,8 @@ User API
.. autofunction:: pythran_def
.. autofunction:: pythran_class
.. autofunction:: make_signature
.. autoclass:: FluidPythran
......@@ -128,7 +130,6 @@ def pythran_class(cls):
return fp.pythran_class(cls)
def make_signature(func, **kwargs):
"""Create signature for a function with values for the template types
......@@ -430,11 +431,15 @@ class FluidPythran:
func_name = func.__name__
name_pythran_func = f"__for_method__{cls_name}__{func_name}"
name_var_code_new_method = f"__code_new_method__{cls_name}__{func_name}"
name_var_code_new_method = (
f"__code_new_method__{cls_name}__{func_name}"
)
try:
pythran_func = getattr(self.module_pythran, name_pythran_func)
code_new_method = getattr(self.module_pythran, name_var_code_new_method)
code_new_method = getattr(
self.module_pythran, name_var_code_new_method
)
except AttributeError:
logger.warning("Pythran file does not seem to be up-to-date.")
else:
......@@ -522,4 +527,4 @@ class FluidPythranTemporaryMethod:
self.func = func
def __call__(self, self_bis, *args, **kwargs):
raise RuntimeError("Do not call this function!")
\ No newline at end of file
raise RuntimeError("Do not call this function!")
......@@ -154,8 +154,6 @@ class SchedulerPopen:
cwd = str(cwd)
words_command = [str(word) for word in words_command]
# FIXME lock file ?
self.block_until_avail(parallel)
process = None
......
......@@ -25,12 +25,13 @@ Internal API
"""
from tokenize import tokenize, untokenize, COMMENT, INDENT, DEDENT, STRING, NAME
from tokenize import tokenize, untokenize, COMMENT, INDENT, DEDENT, STRING, NAME, OP
import os
from logging import DEBUG
from token import tok_name
import inspect
from io import BytesIO
from runpy import run_module, run_path
import sys
......@@ -245,6 +246,119 @@ def make_pythran_code_functions(
return code_pythran
def produce_pythran_code_class(cls):
pythran_code = ""
for key, value in cls.__dict__.items():
if (
hasattr(value, "__fluidpythran__")
and value.__fluidpythran__ == "pythran_def_method"
):
pythran_code += produce_pythran_code_class_func(cls, key)
return pythran_code
def produce_pythran_code_class_func(cls, func_name):
cls_name = cls.__name__
cls_annotations = cls.__annotations__
func = cls.__dict__[func_name]
signature = inspect.signature(func)
types_func = [param.annotation for param in signature.parameters.values()][1:]
args_func = list(signature.parameters.keys())[1:]
src = get_source_without_decorator(func)
tokens = []
attributes = set()
using_self = False
g = tokenize(BytesIO(src.encode("utf-8")).readline)
for toknum, tokval, a, b, c in g:
logger.debug((tok_name[toknum], tokval))
if using_self == "self":
if toknum == OP and tokval == ".":
using_self = tokval
continue
elif toknum == OP and tokval == ",":
tokens.append((NAME, "self"))
using_self = False
else:
raise RuntimeError
if using_self == ".":
if toknum == NAME and tokval in cls_annotations:
using_self = False
tokens.append((NAME, "self_" + tokval))
attributes.add(tokval)
continue
else:
raise RuntimeError
if toknum == NAME and tokval == "self":
using_self = tokval
continue
tokens.append((toknum, tokval))
attributes = sorted(attributes)
types_attrs = [cls_annotations[attr] for attr in attributes]
attributes_self = ["self_" + attr for attr in attributes]
index_self = tokens.index((NAME, "self"))
tokens_attr = []
for ind, attr in enumerate(attributes_self):
tokens_attr.append((NAME, attr))
tokens_attr.append((OP, ","))
tokens = tokens[:index_self] + tokens_attr + tokens[index_self + 2 :]
index_func_name = tokens.index((NAME, func_name))
name_pythran_func = f"__for_method__{cls_name}__{func_name}"
tokens[index_func_name] = (NAME, name_pythran_func)
new_code = untokenize(tokens).decode("utf-8")
new_code = black.format_str(new_code, line_length=82)
# args_pythran = attributes + args_func
types_pythran = types_attrs + types_func
pythran_signatures = "\n"
for types_string_signature in compute_pythran_types_from_valued_types(
types_pythran
):
pythran_signatures += (
"# pythran export "
+ name_pythran_func
+ "("
+ ", ".join(types_string_signature)
+ ")\n"
)
pythran_code = pythran_signatures + "\n" + new_code
name_var_code_new_method = f"__code_new_method__{cls_name}__{func_name}"
str_self_dot_attributes = ", ".join("self." + attr for attr in attributes)
str_args_func = ", ".join(args_func)
pythran_code += (
f"\n# pythran export {name_var_code_new_method}\n"
f'\n{name_var_code_new_method} = """\n\n'
f"def new_method(self, {str_args_func}):\n"
f" return pythran_func({str_self_dot_attributes}, {str_args_func})"
'\n\n"""\n'
)
return pythran_code
def make_pythran_code(path_py: Path):
"""Create a pythran code from a Python file"""
......@@ -316,6 +430,10 @@ imports: {imports}\n"""
if name_func not in signatures_func_all:
signatures_func_all[name_func] = []
signatures_func_all[name_func].extend(signatures)
for cls in fp.classes.values():
code_pythran += produce_pythran_code_class(cls)
else:
codes_functions = get_code_functions(code, functions)
signatures_func_all = signatures_func
......
......@@ -25,3 +25,4 @@ check("try0", original)
check("try1", original)
check("try2", original)
check("try3", original)
check("try4", original)
import numpy as np
# pythran import numpy as np
from fluidpythran import pythran_class, pythran_def
@pythran_class
class Transmitter:
freq: float
def __init__(self, freq):
self.freq = float(freq)
@pythran_def
def __call__(self, inp: "float[]"):
"""My docstring"""
return inp * np.exp(np.arange(len(inp)) * self.freq * 1j)
if __name__ == "__main__":
inp = np.ones(2)
trans = Transmitter(1.0)
trans(inp)
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment