import re
from mako import exceptions
from mako import pyparser
class PythonCode:
def __init__(self, code, **exception_kwargs):
self.code = code
self.declared_identifiers = set()
self.undeclared_identifiers = set()
if isinstance(code, str):
expr = pyparser.parse(code.lstrip(), "exec", **exception_kwargs)
else:
expr = code
f = pyparser.FindIdentifiers(self, **exception_kwargs)
f.visit(expr)
class ArgumentList:
def __init__(self, code, **exception_kwargs):
self.codeargs = []
self.args = []
self.declared_identifiers = set()
self.undeclared_identifiers = set()
if isinstance(code, str):
if re.match(r"\S", code) and not re.match(r",\s*$", code):
code += ","
expr = pyparser.parse(code, "exec", **exception_kwargs)
else:
expr = code
f = pyparser.FindTuple(self, PythonCode, **exception_kwargs)
f.visit(expr)
class PythonFragment(PythonCode):
def __init__(self, code, **exception_kwargs):
m = re.match(r"^(\w+)(?:\s+(.*?))?:\s*(#|$)", code.strip(), re.S)
if not m:
raise exceptions.CompileException(
"Fragment '%s' is not a partial control statement" % code,
**exception_kwargs,
)
if m.group(3):
code = code[: m.start(3)]
(keyword, expr) = m.group(1, 2)
if keyword in ["for", "if", "while"]:
code = code + "pass"
elif keyword == "try":
code = code + "pass\nexcept:pass"
elif keyword in ["elif", "else"]:
code = "if False:pass\n" + code + "pass"
elif keyword == "except":
code = "try:pass\n" + code + "pass"
elif keyword == "with":
code = code + "pass"
else:
raise exceptions.CompileException(
"Unsupported control keyword: '%s'" % keyword,
**exception_kwargs,
)
super().__init__(code, **exception_kwargs)
class FunctionDecl:
def __init__(self, code, allow_kwargs=True, **exception_kwargs):
self.code = code
expr = pyparser.parse(code, "exec", **exception_kwargs)
f = pyparser.ParseFunc(self, **exception_kwargs)
f.visit(expr)
if not hasattr(self, "funcname"):
raise exceptions.CompileException(
"Code '%s' is not a function declaration" % code,
**exception_kwargs,
)
if not allow_kwargs and self.kwargs:
raise exceptions.CompileException(
"'**%s' keyword argument not allowed here"
% self.kwargnames[-1],
**exception_kwargs,
)
def get_argument_expressions(self, as_call=False):
namedecls = []
argnames = self.argnames[::-1]
kwargnames = self.kwargnames[::-1]
defaults = self.defaults[::-1]
kwdefaults = self.kwdefaults[::-1]
if self.kwargs:
namedecls.append("**" + kwargnames.pop(0))
for name in kwargnames:
if as_call:
namedecls.append("%s=%s" % (name, name))
elif kwdefaults:
default = kwdefaults.pop(0)
if default is None:
namedecls.append(name)
else:
namedecls.append(
"%s=%s"
% (name, pyparser.ExpressionGenerator(default).value())
)
else:
namedecls.append(name)
if self.varargs:
namedecls.append("*" + argnames.pop(0))
for name in argnames:
if as_call or not defaults:
namedecls.append(name)
else:
default = defaults.pop(0)
namedecls.append(
"%s=%s"
% (name, pyparser.ExpressionGenerator(default).value())
)
namedecls.reverse()
return namedecls
@property
def allargnames(self):
return tuple(self.argnames) + tuple(self.kwargnames)
class FunctionArgs(FunctionDecl):
def __init__(self, code, **kwargs):
super().__init__("def ANON(%s):pass" % code, **kwargs)