Read about our upcoming Code of Conduct on this issue

Commit 08759bbd authored by Pierre Augier's avatar Pierre Augier
Browse files

Bug modules dict module_name

--HG--
branch : dev
parent a12d44f9b6df
......@@ -2,8 +2,16 @@
Future
------
- :code:`cachedjit` for simple methods (without assignation to attributes
and call of other methods)
0.1.4 (2018-12-06)
------------------
- :code:`pythran_def` for simple methods (without assignation to attributes
and call of other methods)
- Bugfixes
- :code:`FLUIDPYTHRAN_DIR`
0.1.3 (2018-12-04)
------------------
......
......@@ -271,38 +271,29 @@ export`.
Python classes: :code:`@pythran_def` for methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Just a NotImplemented idea! See https://bitbucket.org/fluiddyn/fluidpythran/issues/3
For simple methods only using simple attributes, if could be simple and *very*
useful to support this:
For simple methods **only using attributes**, we can write:
.. code :: python
from fluidpythran import Type, NDim, Array, pythran_def
import numpy as np
T = Type(int, np.float64)
N = NDim(1)
from fluidpythran import pythran_def, pythran_class
A1 = Array[T, N]
A2 = Array[float, N+1]
A = "float[:]"
@pythran_class
class MyClass:
arr0: A1
arr1: A1
arr2: A2
arr0: A
arr1: A
def __init__(self, n, dtype=int):
self.arr0 = np.zeros(n, dtype=dtype)
self.arr1 = np.zeros(n, dtype=dtype)
self.arr2 = np.zeros(n)
def __init__(self, n):
self.arr0 = np.zeros(n)
self.arr1 = np.zeros(n)
@pythran_def
def compute(self, alpha: int):
tmp = (self.arr0 + self.arr1).mean()
return tmp ** alpha * self.arr2
def compute(self, alpha: float):
return (self.arr0 + self.arr1).mean() ** alpha
Make the Pythran files
----------------------
......
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)
freq = 1.0
trans = Transmitter(freq)
def for_check(freq, inp):
return inp * np.exp(np.arange(len(inp)) * freq * 1j)
assert np.allclose(trans(inp), for_check(freq, inp))
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)
freq = 1.0
trans = Transmitter(freq)
def for_check(freq, inp):
return inp * np.exp(np.arange(len(inp)) * freq * 1j)
assert np.allclose(trans(inp), for_check(freq, inp))
Object oriented programming!
============================
With FluidPythran, one can easily use Pythran for methods:
.. literalinclude:: methods.py
For implementation reasons, we *need* to decorate the method and the class...
......@@ -12,6 +12,7 @@ Examples
examples/type_hints
examples/using_cachedjit
examples/blocks
examples/methods
ipynb/executed/demo_pythranize_at_import
ipynb/executed/demo_cachedjit
......
__version__ = "0.1.3"
__version__ = "0.1.4"
try:
from pyfiglet import figlet_format
......
......@@ -339,8 +339,10 @@ class FluidPythran:
self.is_compiled = hasattr(self.module_pythran, "__pythran__")
if self.is_compiled:
module = inspect.getmodule(frame[0])
module.__pythran__ = self.module_pythran.__pythran__
module.__fluidpythran__ = self.module_pythran.__fluidpythran__
# module can be None if (at least) it has been run with runpy
if module is not None:
module.__pythran__ = self.module_pythran.__pythran__
module.__fluidpythran__ = self.module_pythran.__fluidpythran__
if hasattr(self.module_pythran, "arguments_blocks"):
self.arguments_blocks = getattr(
......@@ -349,6 +351,19 @@ class FluidPythran:
modules[module_name] = self
def reload_module_pythran(self):
if self.path_extension.exists() and not self.is_compiling:
self.module_pythran = import_from_path(
self.path_extension, self.module_pythran.__name__
)
elif self.path_pythran.exists():
self.module_pythran = import_from_path(
self.path_pythran, self.module_pythran.__name__
)
else:
self.is_transpiled = False
self.is_compiled = False
def pythran_def(self, func):
"""Decorator used for functions
......@@ -379,7 +394,11 @@ class FluidPythran:
func_tmp = getattr(self.module_pythran, func.__name__)
except AttributeError:
logger.warning("Pythran file does not seem to be up-to-date.")
func_tmp = func
self.reload_module_pythran()
try:
func_tmp = getattr(self.module_pythran, func.__name__)
except AttributeError:
func_tmp = func
if self.is_compiling:
return functools.wraps(func)(CheckCompiling(self, func_tmp))
......@@ -396,6 +415,8 @@ class FluidPythran:
"""
print("pythran_def_method", func, is_transpiling, self.is_transpiled)
if is_transpiling:
func.__fluidpythran__ = "pythran_def_method"
return func
......@@ -414,7 +435,6 @@ class FluidPythran:
cls: a class
"""
if is_transpiling:
self.classes[cls.__name__] = cls
return cls
......@@ -435,6 +455,13 @@ class FluidPythran:
f"__code_new_method__{cls_name}__{func_name}"
)
if not hasattr(self.module_pythran, name_pythran_func):
print("self.module_pythran", self.module_pythran)
self.reload_module_pythran()
print("self.module_pythran", self.module_pythran)
with open(self.module_pythran.__file__, "r") as file:
print(file.read())
try:
pythran_func = getattr(self.module_pythran, name_pythran_func)
code_new_method = getattr(
......@@ -442,6 +469,7 @@ class FluidPythran:
)
except AttributeError:
logger.warning("Pythran file does not seem to be up-to-date.")
# setattr(cls, key, func)
else:
namespace = {"pythran_func": pythran_func}
exec(code_new_method, namespace)
......@@ -524,7 +552,11 @@ class FluidPythran:
class FluidPythranTemporaryMethod:
def __init__(self, func):
print("init FluidPythranTemporaryMethod", func)
self.func = func
def __call__(self, self_bis, *args, **kwargs):
raise RuntimeError("Do not call this function!")
raise RuntimeError(
"Did you forget to decorate a class using methods decorated "
"with fluidpythran? Please decorate it with @pythran_class."
)
......@@ -2,7 +2,7 @@ import numpy as np
# pythran import numpy as np
from fluidpythran import FluidPythran, pythran_def
from fluidpythran import FluidPythran, pythran_def, pythran_class
# pythran def func(int, float)
......@@ -39,3 +39,32 @@ def func1(a, b):
result = 0.0
for _ in range(n):
result += a ** 2 + b ** 3
@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)
def check_class():
inp = np.ones(2)
freq = 1.0
trans = Transmitter(freq)
def for_check(freq, inp):
return inp * np.exp(np.arange(len(inp)) * freq * 1j)
assert np.allclose(trans(inp), for_check(freq, inp))
if __name__ == "__main__":
check_class()
\ No newline at end of file
......@@ -10,12 +10,12 @@ from . import path_data_tests
dist.can_import_pythran = True
shutil.rmtree(path_data_tests / "__pythran__", ignore_errors=True)
@pytest.mark.skipif(nb_proc > 1, reason="No dist in MPI")
def test_detect_pythran_extensions():
shutil.rmtree(path_data_tests / "__pythran__", ignore_errors=True)
names = [
"block_fluidsim.py",
"class_blocks.py",
......@@ -34,6 +34,9 @@ def test_detect_pythran_extensions():
ext_names = detect_pythran_extensions(path_data_tests)
assert len(ext_names) == len(names) - 1
shutil.rmtree(path_data_tests / "__pythran__", ignore_errors=True)
@pytest.mark.skipif(nb_proc > 1, reason="No dist in MPI")
def test_modification_date():
......
......@@ -20,10 +20,11 @@ def test_not_fluidpythranized():
importlib.reload(for_test_init)
from .for_test_init import func, func1
from .for_test_init import func, func1, check_class
func(1, 3.14)
func1(1.1, 2.2)
check_class()
def test_use_pythran_false():
......
......@@ -62,10 +62,16 @@ class TestsInit(unittest.TestCase):
if self.path_pythran.exists():
self.path_pythran.unlink()
print("make_pythran_file", self.path_for_test)
make_pythran_file(self.path_for_test)
print("from . import for_test_init")
from . import for_test_init
print("importlib.reload(for_test_init)")
importlib.reload(for_test_init)
assert self.path_pythran.exists()
......@@ -73,6 +79,7 @@ class TestsInit(unittest.TestCase):
for_test_init.func(1, 3.14)
for_test_init.func1(1.1, 2.2)
for_test_init.check_class()
@unittest.skipIf(not pythran, "Pythran is required for PYTHRANIZE_AT_IMPORT")
def test_pythranize(self):
......@@ -122,3 +129,4 @@ class TestsInit(unittest.TestCase):
for_test_init.func(1, 3.14)
for_test_init.func1(1.1, 2.2)
for_test_init.check_class()
......@@ -48,7 +48,7 @@ def test_create_pythran_classic():
rmtree(path_dir_out)
path_file = path_data_tests / "classic.py"
sys.argv = f"fluidpythran -np -v {path_file}".split()
sys.argv = f"fluidpythran -np {path_file}".split()
run()
print("after first build")
......
......@@ -25,7 +25,16 @@ Internal API
"""
from tokenize import tokenize, untokenize, COMMENT, INDENT, DEDENT, STRING, NAME, OP
from tokenize import (
tokenize,
untokenize,
COMMENT,
INDENT,
DEDENT,
STRING,
NAME,
OP,
)
import os
from logging import DEBUG
......@@ -286,7 +295,9 @@ def produce_pythran_code_class_func(cls, func_name):
tokens.append((NAME, "self"))
using_self = False
else:
raise RuntimeError
raise NotImplementedError(
f"self{tokval} not supported by FluidPythran"
)
if using_self == ".":
if toknum == NAME and tokval in cls_annotations:
......@@ -295,7 +306,9 @@ def produce_pythran_code_class_func(cls, func_name):
attributes.add(tokval)
continue
else:
raise RuntimeError
raise NotImplementedError(
f"self.{tokval} used but {tokval} not in class annotations"
)
if toknum == NAME and tokval == "self":
using_self = tokval
......@@ -366,21 +379,22 @@ def make_pythran_code(path_py: Path):
code = file.read()
namespace = None
module_name = ".".join(
path_py.absolute().relative_to(os.getcwd()).with_suffix("").parts
)
if "# FLUIDPYTHRAN_NO_IMPORT" not in code:
# we have to import the module!
fluidpythran.aheadoftime.is_transpiling = True
try:
namespace = run_path(str(path_py))
except ImportError:
name_mod = ".".join(
path_py.absolute().relative_to(os.getcwd()).with_suffix("").parts
)
sys.path.insert(0, "")
try:
namespace = run_module(name_mod)
namespace = run_module(module_name)
except ImportError:
logger.error(
f"fluidpythran was unable to import module {name_mod}: "
f"fluidpythran was unable to import module {module_name}: "
"no Pythran file created. "
"You could add '# FLUIDPYTHRAN_NO_IMPORT' "
"in the module if needed..."
......@@ -416,7 +430,13 @@ imports: {imports}\n"""
if imports:
code_pythran += "\n" + "\n".join(imports) + "\n"
module_name = Path(path_py).with_suffix("").name
print(f"\n\n {path_py} \n {module_name} \n\n")
print(
"if module_name in fluidpythran.aheadoftime.modules:",
module_name in fluidpythran.aheadoftime.modules,
)
print(module_name, fluidpythran.aheadoftime.modules)
if module_name in fluidpythran.aheadoftime.modules:
fp = fluidpythran.aheadoftime.modules[module_name]
fp._make_signatures_from_annotations()
......@@ -431,6 +451,7 @@ imports: {imports}\n"""
signatures_func_all[name_func] = []
signatures_func_all[name_func].extend(signatures)
print("for cls in fp.classes.values():")
for cls in fp.classes.values():
code_pythran += produce_pythran_code_class(cls)
......
......@@ -21,5 +21,10 @@ class Transmitter:
if __name__ == "__main__":
inp = np.ones(2)
trans = Transmitter(1.0)
trans(inp)
freq = 1.0
trans = Transmitter(freq)
def for_check(freq, inp):
return inp * np.exp(np.arange(len(inp)) * freq * 1j)
assert np.allclose(trans(inp), for_check(freq, 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