check_support; Attribute()

This commit is contained in:
Yuyao Huang 2024-04-22 16:43:21 +08:00
parent 0281d68cb4
commit c620d0b014
11 changed files with 74 additions and 9 deletions

View File

@ -1,9 +1,10 @@
from tests.test_utils import * from tests.test_utils import *
from trace_commentor.check import Check
def test(): def test():
@Commentor() # @Commentor()
@Check()
def target(a, d=1, *b, c, k=1): def target(a, d=1, *b, c, k=1):
return a + k return a + k

View File

@ -13,6 +13,7 @@ def test_constant():
x = 2 x = 2
print(x == 2) print(x == 2)
""" """
<callable> : print
True : x == 2 True : x == 2
None : print(x == 2) None : print(x == 2)
""" """
@ -64,7 +65,7 @@ def test_if():
def test_for(): def test_for():
with closing(StringIO()) as f: with closing(StringIO()) as f:
@Commentor(f) @Commentor(f, _exit=False, check=False)
def target(): def target():
odds = [] odds = []
# return only odd numbers - 1,3,5,7,9 # return only odd numbers - 1,3,5,7,9
@ -101,6 +102,8 @@ def test_for():
# continue # skipped # continue # skipped
# odds.append(x) # odds.append(x)
""" """
[] : odds
<callable> : odds.append
1 : x 1 : x
None : odds.append(x) None : odds.append(x)
""" """
@ -125,6 +128,8 @@ def test_for():
# continue # skipped # continue # skipped
# odds.append(x) # odds.append(x)
""" """
[1] : odds
<callable> : odds.append
3 : x 3 : x
None : odds.append(x) None : odds.append(x)
""" """
@ -149,6 +154,8 @@ def test_for():
# continue # skipped # continue # skipped
# odds.append(x) # odds.append(x)
""" """
[1, 3] : odds
<callable> : odds.append
5 : x 5 : x
None : odds.append(x) None : odds.append(x)
""" """
@ -173,6 +180,8 @@ def test_for():
# continue # skipped # continue # skipped
# odds.append(x) # odds.append(x)
""" """
[1, 3, 5] : odds
<callable> : odds.append
7 : x 7 : x
None : odds.append(x) None : odds.append(x)
""" """
@ -197,6 +206,8 @@ def test_for():
continue # skipped continue # skipped
odds.append(x) odds.append(x)
""" """
[1, 3, 5, 7] : odds
<callable> : odds.append
9 : x 9 : x
None : odds.append(x) None : odds.append(x)
""" """

View File

@ -17,7 +17,7 @@ def test_return():
with closing(StringIO()) as f: with closing(StringIO()) as f:
@Commentor(f) @Commentor(f, _exit=False)
def target(): def target():
a = 1 a = 1
return a + 1 return a + 1

View File

@ -46,6 +46,7 @@ def test_call_print():
def target(): def target():
print('This line will be printed.') print('This line will be printed.')
""" """
<callable> : print
None : print('This line will be printed.') None : print('This line will be printed.')
""" """
''') ''')

View File

@ -14,6 +14,7 @@ def test_assign():
myint = 7 myint = 7
print(myint) print(myint)
""" """
<callable> : print
7 : myint 7 : myint
None : print(myint) None : print(myint)
""" """

View File

@ -9,12 +9,12 @@ from functools import wraps
from . import handlers from . import handlers
from . import formatters from . import formatters
from . import flags from . import flags
from .utils import sign, to_source, comment_to_file from .utils import sign, to_source, comment_to_file, isinstance_noexcept
class Commentor(object): class Commentor(object):
def __init__(self, output="<stderr>", _globals=dict(), fmt=[]) -> None: def __init__(self, output="<stderr>", _globals=dict(), fmt=[], check=True, _exit=True) -> None:
self._locals = dict() self._locals = dict()
self._globals = dict().update(_globals) self._globals = dict().update(_globals)
self._return = None self._return = None
@ -25,6 +25,8 @@ class Commentor(object):
self.state = flags.SOURCE self.state = flags.SOURCE
self.file = output self.file = output
self._stack_event = flags.NORMAL self._stack_event = flags.NORMAL
self._exit = _exit
self._check = check
def __call__(self, func): def __call__(self, func):
@ -32,6 +34,8 @@ class Commentor(object):
self.indent = len(raw_lines[0]) - len(raw_lines[0].lstrip()) self.indent = len(raw_lines[0]) - len(raw_lines[0].lstrip())
unindented_source = ''.join([l[self.indent:] for l in raw_lines]) unindented_source = ''.join([l[self.indent:] for l in raw_lines])
self.root = ast.parse(unindented_source).body[0] self.root = ast.parse(unindented_source).body[0]
if self._check: self.check_support()
pt = getfullargspec(func) pt = getfullargspec(func)
if flags.DEBUG: if flags.DEBUG:
@ -71,6 +75,8 @@ class Commentor(object):
# output { # output {
comments = "\n".join(self._lines) comments = "\n".join(self._lines)
if comment_to_file(comments, file=self.file): if comment_to_file(comments, file=self.file):
if self._exit:
exit(0)
return self._return return self._return
else: else:
return comments return comments
@ -102,7 +108,9 @@ class Commentor(object):
def get_formatter(self, obj): def get_formatter(self, obj):
for typ, fmt in self._formatters: for typ, fmt in self._formatters:
if isinstance(obj, typ): if isinstance_noexcept(obj, typ):
return fmt
elif callable(typ) and typ(obj):
return fmt return fmt
else: else:
return repr return repr
@ -128,3 +136,17 @@ class Commentor(object):
self.__append('"""') self.__append('"""')
if line is not None: if line is not None:
return self.__append(sign(line, 2)) return self.__append(sign(line, 2))
def check_support(self):
unimpl = []
for node in ast.walk(self.root):
if node.__class__ in flags.HANDLER_FREE_NODES:
continue
node_type = node.__class__.__name__
handler = getattr(handlers, node_type, None)
if handler is None:
unimpl.append(node_type)
if unimpl:
print("Unsupported nodes: ", ", ".join(unimpl))
exit(0)

View File

@ -21,3 +21,10 @@ APPEND_SOURCE_BY_THEMSELVES = [
ASSIGN_SILENT = [ ASSIGN_SILENT = [
ast.Constant, ast.List ast.Constant, ast.List
] ]
HANDLER_FREE_NODES = [
ast.Add, ast.Sub, ast.Mult, ast.Div, ast.FloorDiv, ast.Mod, ast.LShift, ast.RShift, ast.BitOr, ast.BitXor, ast.BitAnd, ast.MatMult,
ast.Eq, ast.NotEq, ast.Lt, ast.LtE, ast.Gt, ast.GtE, ast.Is, ast.IsNot, ast.In, ast.NotIn,
ast.Load, ast.Store,
ast.arg, ast.arguments, ast.keyword,
]

View File

@ -1 +1,7 @@
LIST = [] def fmt_callable(fn):
return "<callable>"
LIST = [
(callable, fmt_callable),
]

View File

@ -1,6 +1,6 @@
from .definitions import FunctionDef, Return from .definitions import FunctionDef, Return
from .statements import Pass, Assign from .statements import Pass, Assign
from .expressions import Expr, BinOp, Call, Compare from .expressions import Expr, BinOp, Call, Compare, Attribute
from .literals import Constant, Tuple, List from .literals import Constant, Tuple, List
from .variables import Name from .variables import Name
from .control_flow import If, For, Continue, Break from .control_flow import If, For, Continue, Break

View File

@ -12,8 +12,13 @@ def Compare(self, cmtor):
cmtor.append_comment(cmtor.eval(self)) cmtor.append_comment(cmtor.eval(self))
def Call(self, cmtor): def Call(self, cmtor):
cmtor.process(self.func)
for arg in self.args: for arg in self.args:
cmtor.process(arg) cmtor.process(arg)
for kwarg in self.keywords: for kwarg in self.keywords:
cmtor.process(kwarg) cmtor.process(kwarg)
cmtor.append_comment(cmtor.eval(self)) cmtor.append_comment(cmtor.eval(self))
def Attribute(self, cmtor):
cmtor.process(self.value)
cmtor.append_comment(cmtor.eval(self))

View File

@ -26,6 +26,10 @@ def to_source(node):
return src return src
def dump(node, file=sys.stderr):
print(ast.dump(node, indent=4), file=file)
def comment_to_file(code, file: str) -> bool: def comment_to_file(code, file: str) -> bool:
if file == "<return>": if file == "<return>":
return False return False
@ -49,3 +53,10 @@ def comment_to_file(code, file: str) -> bool:
else: else:
raise NotImplementedError(f"Unknown file protocal {file}") raise NotImplementedError(f"Unknown file protocal {file}")
return True return True
def isinstance_noexcept(_obj, _class_or_tuple):
try:
return isinstance(_obj, _class_or_tuple)
except TypeError:
return False