fastpython-native 0.2.2__py3-none-any.whl → 0.2.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastpython/__init__.py +1 -1
- fastpython/cli.py +21 -2
- fastpython/codegen/transpiler.py +13 -0
- fastpython/importer.py +93 -42
- {fastpython_native-0.2.2.dist-info → fastpython_native-0.2.3.dist-info}/METADATA +1 -1
- {fastpython_native-0.2.2.dist-info → fastpython_native-0.2.3.dist-info}/RECORD +9 -9
- {fastpython_native-0.2.2.dist-info → fastpython_native-0.2.3.dist-info}/WHEEL +0 -0
- {fastpython_native-0.2.2.dist-info → fastpython_native-0.2.3.dist-info}/entry_points.txt +0 -0
- {fastpython_native-0.2.2.dist-info → fastpython_native-0.2.3.dist-info}/top_level.txt +0 -0
fastpython/__init__.py
CHANGED
fastpython/cli.py
CHANGED
|
@@ -76,8 +76,27 @@ def main():
|
|
|
76
76
|
sys.argv = [str(target_script)] + args.script_args
|
|
77
77
|
sys.path.insert(0, str(target_script.parent))
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
from .importer import auto_accelerate, transform_source
|
|
80
|
+
auto_accelerate(project_dir=str(target_script.parent), verbose=True)
|
|
81
|
+
|
|
82
|
+
with open(target_script, "r", encoding="utf-8") as f:
|
|
83
|
+
src = f.read()
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
tree, candidates = transform_source(src, str(target_script), verbose=True)
|
|
87
|
+
if candidates:
|
|
88
|
+
cpu = get_cpu_info()
|
|
89
|
+
print(f"[FastPython.Run] Pre-accelerated {len(candidates)} functions in '{target_script.name}': {', '.join(candidates)}")
|
|
90
|
+
code = compile(tree, str(target_script), "exec")
|
|
91
|
+
globs = {
|
|
92
|
+
"__name__": "__main__",
|
|
93
|
+
"__file__": str(target_script),
|
|
94
|
+
"__builtins__": __builtins__,
|
|
95
|
+
"_FASTPYTHON_MAIN_ACCELERATED": True
|
|
96
|
+
}
|
|
97
|
+
exec(code, globs)
|
|
98
|
+
except Exception:
|
|
99
|
+
runpy.run_path(str(target_script), run_name="__main__")
|
|
81
100
|
|
|
82
101
|
|
|
83
102
|
if __name__ == "__main__":
|
fastpython/codegen/transpiler.py
CHANGED
|
@@ -89,6 +89,12 @@ class PythonToCTranspiler(ast.NodeVisitor):
|
|
|
89
89
|
# Check return annotation
|
|
90
90
|
if node.returns:
|
|
91
91
|
self.return_type = self._resolve_type_annotation(node.returns)
|
|
92
|
+
elif "return" not in self.type_hints:
|
|
93
|
+
# Infer return type from return statements if possible
|
|
94
|
+
for stmt in ast.walk(node):
|
|
95
|
+
if isinstance(stmt, ast.Return) and stmt.value is not None:
|
|
96
|
+
self.return_type = self._infer_expr_type(stmt.value)
|
|
97
|
+
break
|
|
92
98
|
|
|
93
99
|
# Process function body
|
|
94
100
|
for stmt in node.body:
|
|
@@ -260,6 +266,13 @@ class PythonToCTranspiler(ast.NodeVisitor):
|
|
|
260
266
|
left = self.visit(node.left)
|
|
261
267
|
right = self.visit(node.right)
|
|
262
268
|
if isinstance(node.op, ast.Pow):
|
|
269
|
+
if isinstance(node.right, ast.Constant):
|
|
270
|
+
if node.right.value == 2:
|
|
271
|
+
return f"(({left}) * ({left}))"
|
|
272
|
+
elif node.right.value == 3:
|
|
273
|
+
return f"(({left}) * ({left}) * ({left}))"
|
|
274
|
+
elif node.right.value == 0.5:
|
|
275
|
+
return f"sqrt((double)({left}))"
|
|
263
276
|
return f"pow((double)({left}), (double)({right}))"
|
|
264
277
|
elif isinstance(node.op, ast.FloorDiv):
|
|
265
278
|
return f"((int64_t)({left}) / (int64_t)({right}))"
|
fastpython/importer.py
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Automatic Project Importer & AST Hook for FastPython.
|
|
3
|
-
Intercepts imports of project modules, detects computational functions,
|
|
3
|
+
Intercepts imports of project modules, detects computational functions (at root or nested),
|
|
4
4
|
and compiles them Ahead-of-Time (AOT) to hardware-native machine code.
|
|
5
|
+
Also auto-accelerates the main script (__main__) when imported at the top of the entry point.
|
|
5
6
|
"""
|
|
6
7
|
|
|
7
8
|
import sys
|
|
@@ -10,7 +11,7 @@ import ast
|
|
|
10
11
|
from pathlib import Path
|
|
11
12
|
from importlib.abc import MetaPathFinder, Loader
|
|
12
13
|
from importlib.machinery import ModuleSpec
|
|
13
|
-
from typing import Optional, List, Set
|
|
14
|
+
from typing import Optional, List, Set, Tuple
|
|
14
15
|
|
|
15
16
|
from .hardware.cpu import get_cpu_info
|
|
16
17
|
|
|
@@ -35,6 +36,57 @@ def is_acceleration_candidate(node: ast.FunctionDef) -> bool:
|
|
|
35
36
|
return has_loop or (has_arithmetic and len(node.body) > 1)
|
|
36
37
|
|
|
37
38
|
|
|
39
|
+
class AutoAccelerateTransformer(ast.NodeTransformer):
|
|
40
|
+
"""
|
|
41
|
+
AST transformer that walks the entire syntax tree (including nested blocks,
|
|
42
|
+
if __name__ == '__main__': blocks, and classes) and decorates eligible
|
|
43
|
+
computational functions with FastPython native compilation.
|
|
44
|
+
"""
|
|
45
|
+
def __init__(self, verbose: bool = True):
|
|
46
|
+
self.candidates: List[str] = []
|
|
47
|
+
self.verbose = verbose
|
|
48
|
+
|
|
49
|
+
def visit_FunctionDef(self, node: ast.FunctionDef):
|
|
50
|
+
self.generic_visit(node)
|
|
51
|
+
|
|
52
|
+
already_decorated = False
|
|
53
|
+
for dec in node.decorator_list:
|
|
54
|
+
if isinstance(dec, ast.Name) and "compile" in dec.id.lower():
|
|
55
|
+
already_decorated = True
|
|
56
|
+
elif isinstance(dec, ast.Call) and isinstance(dec.func, ast.Name) and "compile" in dec.func.id.lower():
|
|
57
|
+
already_decorated = True
|
|
58
|
+
|
|
59
|
+
if not already_decorated and is_acceleration_candidate(node):
|
|
60
|
+
self.candidates.append(node.name)
|
|
61
|
+
call_node = ast.Call(
|
|
62
|
+
func=ast.Name(id="_fastpython_compile", ctx=ast.Load()),
|
|
63
|
+
args=[],
|
|
64
|
+
keywords=[
|
|
65
|
+
ast.keyword(arg="fastmath", value=ast.Constant(value=True)),
|
|
66
|
+
ast.keyword(arg="verbose", value=ast.Constant(value=self.verbose)),
|
|
67
|
+
]
|
|
68
|
+
)
|
|
69
|
+
node.decorator_list.insert(0, call_node)
|
|
70
|
+
|
|
71
|
+
return node
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def transform_source(source: str, filename: str, verbose: bool = True) -> Tuple[ast.AST, List[str]]:
|
|
75
|
+
"""Parses Python source, applies AutoAccelerateTransformer across all scopes, and injects imports."""
|
|
76
|
+
tree = ast.parse(source, filename=filename)
|
|
77
|
+
transformer = AutoAccelerateTransformer(verbose=verbose)
|
|
78
|
+
transformer.visit(tree)
|
|
79
|
+
if transformer.candidates:
|
|
80
|
+
import_stmt = ast.ImportFrom(
|
|
81
|
+
module="fastpython",
|
|
82
|
+
names=[ast.alias(name="compile_native", asname="_fastpython_compile")],
|
|
83
|
+
level=0
|
|
84
|
+
)
|
|
85
|
+
tree.body.insert(0, import_stmt)
|
|
86
|
+
ast.fix_missing_locations(tree)
|
|
87
|
+
return tree, transformer.candidates
|
|
88
|
+
|
|
89
|
+
|
|
38
90
|
class FastPythonLoader(Loader):
|
|
39
91
|
def __init__(self, fullname: str, filepath: str, verbose: bool = True):
|
|
40
92
|
self.fullname = fullname
|
|
@@ -51,40 +103,14 @@ class FastPythonLoader(Loader):
|
|
|
51
103
|
source = f.read()
|
|
52
104
|
|
|
53
105
|
try:
|
|
54
|
-
tree =
|
|
106
|
+
tree, candidates = transform_source(source, self.filepath, verbose=self.verbose)
|
|
55
107
|
except Exception:
|
|
56
|
-
# Fall back to standard execution if parsing fails
|
|
57
108
|
exec(compile(source, self.filepath, "exec"), module.__dict__)
|
|
58
109
|
return
|
|
59
110
|
|
|
60
|
-
candidates
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
candidates.append(node.name)
|
|
64
|
-
# Inject decorator: @_fastpython_compile(fastmath=True)
|
|
65
|
-
call_node = ast.Call(
|
|
66
|
-
func=ast.Name(id="_fastpython_compile", ctx=ast.Load()),
|
|
67
|
-
args=[],
|
|
68
|
-
keywords=[
|
|
69
|
-
ast.keyword(arg="fastmath", value=ast.Constant(value=True)),
|
|
70
|
-
ast.keyword(arg="verbose", value=ast.Constant(value=self.verbose)),
|
|
71
|
-
]
|
|
72
|
-
)
|
|
73
|
-
node.decorator_list.insert(0, call_node)
|
|
74
|
-
|
|
75
|
-
if candidates:
|
|
76
|
-
if self.verbose:
|
|
77
|
-
cpu = get_cpu_info()
|
|
78
|
-
print(f"[FastPython.Auto] Accelerating module '{self.fullname}' ({len(candidates)} functions: {', '.join(candidates)}) for {cpu.brand.strip()}...")
|
|
79
|
-
|
|
80
|
-
# Inject import of compile_native at top of module
|
|
81
|
-
import_stmt = ast.ImportFrom(
|
|
82
|
-
module="fastpython",
|
|
83
|
-
names=[ast.alias(name="compile_native", asname="_fastpython_compile")],
|
|
84
|
-
level=0
|
|
85
|
-
)
|
|
86
|
-
tree.body.insert(0, import_stmt)
|
|
87
|
-
ast.fix_missing_locations(tree)
|
|
111
|
+
if candidates and self.verbose:
|
|
112
|
+
cpu = get_cpu_info()
|
|
113
|
+
print(f"[FastPython.Auto] Accelerating module '{self.fullname}' ({len(candidates)} functions: {', '.join(candidates)}) for {cpu.brand.strip()}...")
|
|
88
114
|
|
|
89
115
|
code_obj = compile(tree, filename=self.filepath, mode="exec")
|
|
90
116
|
exec(code_obj, module.__dict__)
|
|
@@ -176,17 +202,42 @@ _HOOK_INSTALLED = False
|
|
|
176
202
|
def auto_accelerate(project_dir: Optional[str] = None, verbose: bool = True):
|
|
177
203
|
"""
|
|
178
204
|
Installs the FastPython automatic compilation hook.
|
|
179
|
-
Every module imported in the project
|
|
180
|
-
hardware-native machine code before execution.
|
|
205
|
+
Every module imported in the project, as well as the main script entry point,
|
|
206
|
+
will be analyzed and compiled AOT into hardware-native machine code before execution.
|
|
181
207
|
"""
|
|
182
208
|
global _HOOK_INSTALLED
|
|
183
|
-
if _HOOK_INSTALLED:
|
|
184
|
-
return
|
|
185
209
|
|
|
186
210
|
root = project_dir or os.getcwd()
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
211
|
+
|
|
212
|
+
if not _HOOK_INSTALLED:
|
|
213
|
+
finder = FastPythonFinder(project_root=root, verbose=verbose)
|
|
214
|
+
sys.meta_path.insert(0, finder)
|
|
215
|
+
_HOOK_INSTALLED = True
|
|
216
|
+
if verbose:
|
|
217
|
+
cpu = get_cpu_info()
|
|
218
|
+
print(f"[FastPython.Auto] Hook active for project '{root}'. Hardware: {cpu.summary()}")
|
|
219
|
+
|
|
220
|
+
# Check if imported directly from __main__ entry point script
|
|
221
|
+
try:
|
|
222
|
+
f = sys._getframe(1)
|
|
223
|
+
while f:
|
|
224
|
+
globs = f.f_globals
|
|
225
|
+
if globs.get("__name__") == "__main__":
|
|
226
|
+
main_file = globs.get("__file__")
|
|
227
|
+
if main_file and os.path.isfile(main_file) and not globs.get("_FASTPYTHON_MAIN_ACCELERATED"):
|
|
228
|
+
globs["_FASTPYTHON_MAIN_ACCELERATED"] = True
|
|
229
|
+
with open(main_file, "r", encoding="utf-8") as fp:
|
|
230
|
+
main_source = fp.read()
|
|
231
|
+
tree, candidates = transform_source(main_source, main_file, verbose=verbose)
|
|
232
|
+
if candidates:
|
|
233
|
+
if verbose:
|
|
234
|
+
cpu = get_cpu_info()
|
|
235
|
+
print(f"[FastPython.Auto] Accelerating main script '{Path(main_file).name}' ({len(candidates)} functions: {', '.join(candidates)}) for {cpu.brand.strip()}...")
|
|
236
|
+
code_obj = compile(tree, filename=main_file, mode="exec")
|
|
237
|
+
exec(code_obj, globs)
|
|
238
|
+
sys.exit(0)
|
|
239
|
+
break
|
|
240
|
+
f = f.f_back
|
|
241
|
+
except Exception as e:
|
|
242
|
+
import traceback
|
|
243
|
+
traceback.print_exc()
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
fastpython/__init__.py,sha256=
|
|
1
|
+
fastpython/__init__.py,sha256=ZLYZPgE1RLUPl7djtVM2llzhMITq5CmLMkxqvL377QM,583
|
|
2
2
|
fastpython/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
|
|
3
3
|
fastpython/api.py,sha256=Urm-aEzv_ohMmEHBbgpKpvsb3NPFf2k1-lvN8dWfz-U,6930
|
|
4
4
|
fastpython/auto.py,sha256=aHFcXX8gijt01sYlbZymyS4wSzXFZHLLeGvtB1u30OM,283
|
|
5
|
-
fastpython/cli.py,sha256=
|
|
6
|
-
fastpython/importer.py,sha256=
|
|
5
|
+
fastpython/cli.py,sha256=BRLhrNabnlSY9Xsod6Ji3gboWhFGDXtFRO1zHmOkZlY,4037
|
|
6
|
+
fastpython/importer.py,sha256=8dHjkPMgSs0lCHo06ReHArx-N6xzeSj1lo4hf-PSTPc,9650
|
|
7
7
|
fastpython/jit.py,sha256=3LyZ-HZvs5uS_rssvSrvKz5aArplz8ZiaRQ1sKcFRDU,922
|
|
8
8
|
fastpython/accelerators/fast_raytracer.c,sha256=r5Uf3aOIL72c8MzDRRAfTG08q8uGRLvEX_7taHRY1N4,10854
|
|
9
9
|
fastpython/accelerators/raytracer.py,sha256=IAPf2luONvkSj3rWET1vSBC6t6mizMoYoDCdNiSuQMI,5648
|
|
10
10
|
fastpython/codegen/__init__.py,sha256=fqqGp_3A9TwzdnC8vWKyfe3zwN6YLPSt_ZnG_DmtzPA,173
|
|
11
|
-
fastpython/codegen/transpiler.py,sha256=
|
|
11
|
+
fastpython/codegen/transpiler.py,sha256=KXRVJgOyFelrNuEm7XaI-DWby5JYdfXwoA51UGl4Gf0,21011
|
|
12
12
|
fastpython/compiler/__init__.py,sha256=oQfnYP3KTfl6Nyr8rBWmTwUGCPqvjIVgo-qzbsiI_r0,158
|
|
13
13
|
fastpython/compiler/cache.py,sha256=l-7CMjWzqaxfKEKdYhgzVBmWjoRVkf0cnr_iP-zNmS0,2269
|
|
14
14
|
fastpython/compiler/toolchain.py,sha256=LDVH4LyoRW3r5ooTaYntGUJRz2pPbbK3treOk3CAxFM,5037
|
|
@@ -22,8 +22,8 @@ fastpython/server/native_server.c,sha256=LupO6CkSy1f01297ILWzf_eN7lOWwtiY1ACn1Kt
|
|
|
22
22
|
fastpython/simd/__init__.py,sha256=ID-MWvL0UYooW-DnZLdPNv6IzXxNlfNCxWHVbCD-ln4,474
|
|
23
23
|
fastpython/simd/fast_json.py,sha256=lr7Y9XMJDF7_4R5nBuiJ641_yfBRXwxHt-KDchhrjPI,2423
|
|
24
24
|
fastpython/simd/fast_simd.c,sha256=zltkJBr-Jx1kWFWfmLBuzhkizYOhYGRxpVtACnV5csc,9446
|
|
25
|
-
fastpython_native-0.2.
|
|
26
|
-
fastpython_native-0.2.
|
|
27
|
-
fastpython_native-0.2.
|
|
28
|
-
fastpython_native-0.2.
|
|
29
|
-
fastpython_native-0.2.
|
|
25
|
+
fastpython_native-0.2.3.dist-info/METADATA,sha256=0Dy7C2TGqEoaM3svPiKAUrLXB3tue8RDOhFBYWF4e6Y,10729
|
|
26
|
+
fastpython_native-0.2.3.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
27
|
+
fastpython_native-0.2.3.dist-info/entry_points.txt,sha256=IGbP7VbTQmM-57joccriVqB2ieX5uU4u0K5k49HvhE0,51
|
|
28
|
+
fastpython_native-0.2.3.dist-info/top_level.txt,sha256=XjyQFL6KqwXjTwH8faHiYGaSiNuJMpZ2ChIn64covz4,11
|
|
29
|
+
fastpython_native-0.2.3.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|