fastpython-native 0.1.0__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 ADDED
@@ -0,0 +1,19 @@
1
+ from .api import compile_native, accelerate, AcceleratedFunction
2
+ from .hardware.cpu import get_cpu_info, CPUFeatures
3
+ from .importer import auto_accelerate
4
+ from .server import FastWebServer
5
+ from . import simd
6
+ from . import memory
7
+
8
+ __all__ = [
9
+ "compile_native",
10
+ "accelerate",
11
+ "AcceleratedFunction",
12
+ "get_cpu_info",
13
+ "CPUFeatures",
14
+ "auto_accelerate",
15
+ "FastWebServer",
16
+ "simd",
17
+ "memory",
18
+ ]
19
+ __version__ = "0.2.0"
fastpython/__main__.py ADDED
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()
fastpython/api.py ADDED
@@ -0,0 +1,185 @@
1
+ """
2
+ Public API for FastPython.
3
+ Provides decorators and pre-start compilation helpers to turn Python functions
4
+ into hardware-optimized machine code.
5
+ """
6
+
7
+ import functools
8
+ import inspect
9
+ import time
10
+ from typing import Callable, Dict, Any, Optional
11
+
12
+ from .hardware.cpu import get_cpu_info
13
+ from .compiler.toolchain import CompilerToolchain
14
+ from .compiler.cache import ModuleCache
15
+ from .codegen.transpiler import generate_c_extension_source
16
+
17
+
18
+ _GLOBAL_TOOLCHAIN: Optional[CompilerToolchain] = None
19
+ _GLOBAL_CACHE: Optional[ModuleCache] = None
20
+
21
+
22
+ def get_toolchain() -> CompilerToolchain:
23
+ global _GLOBAL_TOOLCHAIN
24
+ if _GLOBAL_TOOLCHAIN is None:
25
+ _GLOBAL_TOOLCHAIN = CompilerToolchain()
26
+ return _GLOBAL_TOOLCHAIN
27
+
28
+
29
+ def get_cache() -> ModuleCache:
30
+ global _GLOBAL_CACHE
31
+ if _GLOBAL_CACHE is None:
32
+ _GLOBAL_CACHE = ModuleCache()
33
+ return _GLOBAL_CACHE
34
+
35
+
36
+ class AcceleratedFunction:
37
+ """Wrapper that holds both the original Python function and the compiled native function."""
38
+ def __init__(
39
+ self,
40
+ py_func: Callable[..., Any],
41
+ type_hints: Optional[Dict[str, Any]] = None,
42
+ parallel: bool = False,
43
+ fastmath: bool = True,
44
+ verbose: bool = True,
45
+ eager: bool = True,
46
+ source_code: Optional[str] = None
47
+ ):
48
+ self.py_func = py_func
49
+ self.func_name = getattr(py_func, "__name__", "anonymous")
50
+ self.type_hints = type_hints or getattr(py_func, "__annotations__", {})
51
+ self.parallel = parallel
52
+ self.fastmath = fastmath
53
+ self.verbose = verbose
54
+ self.source_code = source_code
55
+ self.native_func: Optional[Callable[..., Any]] = None
56
+ self.compilation_time_ms: float = 0.0
57
+ self.is_compiled: bool = False
58
+
59
+ if eager:
60
+ self._compile_or_load()
61
+
62
+ def _compile_or_load(self, sample_args: Optional[tuple] = None):
63
+ if self.is_compiled:
64
+ return
65
+
66
+ toolchain = get_toolchain()
67
+ cache = get_cache()
68
+ cpu = get_cpu_info()
69
+
70
+ # Update type hints from sample_args if not specified
71
+ if sample_args and not self.type_hints:
72
+ sig = inspect.signature(self.py_func)
73
+ inferred = {}
74
+ for param, val in zip(sig.parameters.values(), sample_args):
75
+ if isinstance(val, int):
76
+ inferred[param.name] = "int64_t"
77
+ elif isinstance(val, float):
78
+ inferred[param.name] = "double"
79
+ elif isinstance(val, bool):
80
+ inferred[param.name] = "bool"
81
+ elif hasattr(val, "__len__") and hasattr(val, "__getitem__"):
82
+ inferred[param.name] = "double* __restrict"
83
+ self.type_hints.update(inferred)
84
+
85
+ t0 = time.perf_counter()
86
+
87
+ try:
88
+ # Get source code
89
+ if self.source_code:
90
+ raw_source = self.source_code
91
+ else:
92
+ try:
93
+ raw_source = inspect.getsource(self.py_func)
94
+ except Exception:
95
+ raw_source = ""
96
+
97
+ if not raw_source:
98
+ raise ValueError("Could not determine source code for function")
99
+
100
+ # Compute hash from source + settings first
101
+ meta = f"parallel={self.parallel}_fastmath={self.fastmath}_hints={self.type_hints}"
102
+ code_hash = cache.compute_hash(raw_source, meta)
103
+ mod_name = f"native_{self.func_name}_{code_hash}"
104
+ c_path, pyd_path = cache.get_module_paths(mod_name, code_hash)
105
+
106
+ if not cache.is_cached(pyd_path):
107
+ if self.verbose:
108
+ print(f"[FastPython] Compiling '{self.func_name}' for host: {cpu.summary()}...")
109
+
110
+ # Generate C source with matching module name
111
+ c_source = generate_c_extension_source(
112
+ self.py_func,
113
+ module_name=mod_name,
114
+ type_hints=self.type_hints,
115
+ parallel=self.parallel,
116
+ fastmath=self.fastmath,
117
+ source_code=raw_source
118
+ )
119
+
120
+ with open(c_path, "w", encoding="utf-8") as f:
121
+ f.write(c_source)
122
+
123
+ toolchain.compile_extension(
124
+ c_source_path=c_path,
125
+ output_pyd_path=pyd_path,
126
+ fastmath=self.fastmath,
127
+ parallel=self.parallel
128
+ )
129
+ elapsed = (time.perf_counter() - t0) * 1000
130
+ self.compilation_time_ms = elapsed
131
+ if self.verbose:
132
+ print(f"[FastPython] '{self.func_name}' successfully compiled in {elapsed:.1f}ms! Ready for execution.")
133
+ else:
134
+ if self.verbose:
135
+ print(f"[FastPython] Loading cached native binary for '{self.func_name}' [hash={code_hash}]")
136
+
137
+ self.native_func = cache.load_extension(mod_name, pyd_path, self.func_name)
138
+ except Exception as e:
139
+ if self.verbose:
140
+ print(f"[FastPython] Native compilation fallback for '{self.func_name}': {e}. Using pure Python.")
141
+ self.native_func = None
142
+
143
+ self.is_compiled = True
144
+
145
+ def __call__(self, *args, **kwargs):
146
+ if not self.is_compiled:
147
+ self._compile_or_load(sample_args=args)
148
+
149
+ if self.native_func:
150
+ return self.native_func(*args, **kwargs)
151
+ return self.py_func(*args, **kwargs)
152
+
153
+
154
+ def compile_native(
155
+ func: Optional[Callable[..., Any]] = None,
156
+ *,
157
+ types: Optional[Dict[str, Any]] = None,
158
+ parallel: bool = False,
159
+ fastmath: bool = True,
160
+ verbose: bool = True,
161
+ eager: bool = True,
162
+ source_code: Optional[str] = None
163
+ ):
164
+ """
165
+ Decorator that compiles a Python function into host-native machine code before execution.
166
+ """
167
+ def decorator(fn: Callable[..., Any]) -> AcceleratedFunction:
168
+ wrapper = AcceleratedFunction(
169
+ py_func=fn,
170
+ type_hints=types,
171
+ parallel=parallel,
172
+ fastmath=fastmath,
173
+ verbose=verbose,
174
+ eager=eager,
175
+ source_code=source_code
176
+ )
177
+ functools.update_wrapper(wrapper, fn)
178
+ return wrapper
179
+
180
+ if func is not None:
181
+ return decorator(func)
182
+ return decorator
183
+
184
+
185
+ accelerate = compile_native
fastpython/auto.py ADDED
@@ -0,0 +1,10 @@
1
+ """
2
+ FastPython Auto-Import Module.
3
+ Simply writing 'import fastpython.auto' at the top of your main script
4
+ automatically accelerates and AOT-compiles all project modules upon import.
5
+ """
6
+
7
+ from .importer import auto_accelerate
8
+
9
+ # Activate automatically when imported
10
+ auto_accelerate()
fastpython/cli.py ADDED
@@ -0,0 +1,84 @@
1
+ """
2
+ Command Line Interface for FastPython.
3
+ Allows users to:
4
+ - Inspect host hardware CPU features: python -m fastpython info
5
+ - Run benchmarks: python -m fastpython benchmark
6
+ - Execute accelerated scripts: python -m fastpython run <script.py>
7
+ """
8
+
9
+ import sys
10
+ import os
11
+ import argparse
12
+ import runpy
13
+ from pathlib import Path
14
+ from .hardware.cpu import get_cpu_info
15
+
16
+
17
+ def main():
18
+ parser = argparse.ArgumentParser(
19
+ prog="fastpython",
20
+ description="FastPython: AOT Hardware-Native Machine Code Acceleration for Python"
21
+ )
22
+ subparsers = parser.add_subparsers(dest="command", help="Available subcommands")
23
+
24
+ # Command: info
25
+ subparsers.add_parser("info", help="Display host CPU architecture, features, and optimization flags")
26
+
27
+ # Command: benchmark
28
+ subparsers.add_parser("benchmark", help="Run the multi-domain FastPython benchmark suite")
29
+
30
+ # Command: run
31
+ run_parser = subparsers.add_parser("run", help="Execute a Python script with FastPython AOT pre-compilation")
32
+ run_parser.add_argument("script", help="Path to Python script to execute")
33
+ run_parser.add_argument("script_args", nargs=argparse.REMAINDER, help="Arguments to pass to script")
34
+
35
+ # Command: clear-cache
36
+ subparsers.add_parser("clear-cache", help="Clear the local .fastpython_cache directory")
37
+
38
+ args = parser.parse_args()
39
+
40
+ if args.command == "info" or args.command is None and len(sys.argv) == 1:
41
+ cpu = get_cpu_info()
42
+ print("=" * 65)
43
+ print(" FastPython Hardware Profiler")
44
+ print("=" * 65)
45
+ print(f" CPU Model: {cpu.brand}")
46
+ print(f" Vendor: {cpu.vendor}")
47
+ print(f" Architecture: {cpu.architecture}")
48
+ print(f" Threads / Cores: {cpu.logical_cores} logical / {cpu.physical_cores} physical")
49
+ print(f" AVX Support: AVX={cpu.has_avx}, AVX2={cpu.has_avx2}, AVX-512={cpu.has_avx512f}")
50
+ print(f" Math Extensions: FMA3={cpu.has_fma}, SSE4.2={cpu.has_sse42}, BMI2={cpu.has_bmi2}")
51
+ print("-" * 65)
52
+ print(f" MSVC Flags: {' '.join(cpu.get_msvc_flags())}")
53
+ print(f" GCC Flags: {' '.join(cpu.get_gcc_flags())}")
54
+ print("=" * 65)
55
+
56
+ elif args.command == "benchmark":
57
+ bench_script = Path(__file__).resolve().parent.parent / "benchmarks" / "run_all_benchmarks.py"
58
+ runpy.run_path(str(bench_script), run_name="__main__")
59
+
60
+ elif args.command == "clear-cache":
61
+ cache_dir = Path.cwd() / ".fastpython_cache"
62
+ if cache_dir.exists():
63
+ import shutil
64
+ shutil.rmtree(cache_dir)
65
+ print(f"Cleared cache at: {cache_dir}")
66
+ else:
67
+ print("No cache directory found.")
68
+
69
+ elif args.command == "run":
70
+ target_script = Path(args.script).resolve()
71
+ if not target_script.exists():
72
+ print(f"Error: Script '{args.script}' not found.")
73
+ sys.exit(1)
74
+
75
+ # Set sys.argv for the target script
76
+ sys.argv = [str(target_script)] + args.script_args
77
+ sys.path.insert(0, str(target_script.parent))
78
+
79
+ # Execute script
80
+ runpy.run_path(str(target_script), run_name="__main__")
81
+
82
+
83
+ if __name__ == "__main__":
84
+ main()
@@ -0,0 +1,3 @@
1
+ from .transpiler import PythonToCTranspiler, generate_c_extension_source, TranspileError
2
+
3
+ __all__ = ["PythonToCTranspiler", "generate_c_extension_source", "TranspileError"]