-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathepython.py
More file actions
111 lines (89 loc) · 3.14 KB
/
epython.py
File metadata and controls
111 lines (89 loc) · 3.14 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
"""
EPython is a code-transformer that translates a statically typed subset of
Python syntax into an extension of Python for a particular backend.
The .epy file is first compiled into an AST
The AST is validated to ensure it uses only the allowed subset of Python
The AST is then fed to a transformer specific to the backend.
"""
import argparse
import ast
import os.path
from epython import __version__
from .validate import validate
# See https://greentreesnakes.readthedocs.io/en/latest/nodes.html
_registry = {}
def register_func(name_or_func):
if isinstance(name_or_func, str):
name = name_or_func
func = None
else:
func = name_or_func
name = func.__name__
if func is None:
def decorator(new_func):
_registry[name] = new_func
return new_func
return decorator
else:
_registry[name] = func
return func
# A transformation function needs to take as agruments
# ast: the validated ast of the code
# filename: the name to generate the artefacts
#
# It returns the PATH (or URL) of the created artefact
@register_func('cpython')
def transform(ast, name):
return name + '.so'
# @register_func
# def pypy(mine):
# return mine
def main():
find_backends()
parser = argparse.ArgumentParser(prog='epython',
description="Compile statically typed subset of Python to a backend.")
parser.add_argument("file")
parser.add_argument("--backend", default="cpython")
parser.add_argument("--name", default="none")
parser.add_argument("--version", action='version',
version='%(prog)s ' + __version__)
args = parser.parse_args()
if args.name == 'none':
name = os.path.splitext(args.file)[0]
else:
name = args.name
with open(args.file) as fi:
source = fi.read()
code = ast.parse(source, name, 'exec', type_comments=True)
result = validate(code)
if result is not None:
raise result[0](result[1])
try:
transformer = _registry[args.backend]
except KeyError:
raise RuntimeError(f"There is no epython backend registered for {args.backend}.")
output = transformer(code, name)
from .cython_backend import CythonGenerator
translator = CythonGenerator()
print(translator.visit(code))
# importing the backend should be sufficient to call the decorator(s)
# that registers the function in _registry which is why the
# dictionary created here is not returned or seemingly unused.
def find_backends():
import importlib
import pkgutil
# importing the module registers the function.
discovered_plugins = {
name: importlib.import_module(name)
for finder, name, ispkg in pkgutil.iter_modules()
if name.startswith('epython-')
}
if len(discovered_plugins) > len(_registry):
print("Registry: ")
print(_registry)
print("\n\nPlugin Modules Found: ")
print(discovered_plugins)
raise (ValueError, "The number of Plugin Modules Found is larger "
"than the number of transformations successfully registered.")
if __name__ == "__main__":
code = main()