From c620d0b0143d45e362f9b680e570f78123e405db Mon Sep 17 00:00:00 2001 From: Yuyao Huang Date: Mon, 22 Apr 2024 16:43:21 +0800 Subject: [PATCH] check_support; Attribute() --- experiment.py | 5 +++-- tests/test_control_flow.py | 13 +++++++++++- tests/test_definitions.py | 2 +- tests/test_expressions.py | 1 + tests/test_variables.py | 1 + trace_commentor/commentor.py | 28 ++++++++++++++++++++++--- trace_commentor/flags.py | 7 +++++++ trace_commentor/formatters/__init__.py | 8 ++++++- trace_commentor/handlers/__init__.py | 2 +- trace_commentor/handlers/expressions.py | 5 +++++ trace_commentor/utils.py | 11 ++++++++++ 11 files changed, 74 insertions(+), 9 deletions(-) diff --git a/experiment.py b/experiment.py index 25660d0..990cccd 100644 --- a/experiment.py +++ b/experiment.py @@ -1,9 +1,10 @@ from tests.test_utils import * - +from trace_commentor.check import Check def test(): - @Commentor() + # @Commentor() + @Check() def target(a, d=1, *b, c, k=1): return a + k diff --git a/tests/test_control_flow.py b/tests/test_control_flow.py index 82cca0c..7013ca1 100644 --- a/tests/test_control_flow.py +++ b/tests/test_control_flow.py @@ -13,6 +13,7 @@ def test_constant(): x = 2 print(x == 2) """ + : print True : x == 2 None : print(x == 2) """ @@ -64,7 +65,7 @@ def test_if(): def test_for(): with closing(StringIO()) as f: - @Commentor(f) + @Commentor(f, _exit=False, check=False) def target(): odds = [] # return only odd numbers - 1,3,5,7,9 @@ -101,6 +102,8 @@ def test_for(): # continue # skipped # odds.append(x) """ + [] : odds + : odds.append 1 : x None : odds.append(x) """ @@ -125,6 +128,8 @@ def test_for(): # continue # skipped # odds.append(x) """ + [1] : odds + : odds.append 3 : x None : odds.append(x) """ @@ -149,6 +154,8 @@ def test_for(): # continue # skipped # odds.append(x) """ + [1, 3] : odds + : odds.append 5 : x None : odds.append(x) """ @@ -173,6 +180,8 @@ def test_for(): # continue # skipped # odds.append(x) """ + [1, 3, 5] : odds + : odds.append 7 : x None : odds.append(x) """ @@ -197,6 +206,8 @@ def test_for(): continue # skipped odds.append(x) """ + [1, 3, 5, 7] : odds + : odds.append 9 : x None : odds.append(x) """ diff --git a/tests/test_definitions.py b/tests/test_definitions.py index a78551d..ac46b57 100644 --- a/tests/test_definitions.py +++ b/tests/test_definitions.py @@ -17,7 +17,7 @@ def test_return(): with closing(StringIO()) as f: - @Commentor(f) + @Commentor(f, _exit=False) def target(): a = 1 return a + 1 diff --git a/tests/test_expressions.py b/tests/test_expressions.py index 7ef6de8..3588c22 100644 --- a/tests/test_expressions.py +++ b/tests/test_expressions.py @@ -46,6 +46,7 @@ def test_call_print(): def target(): print('This line will be printed.') """ + : print None : print('This line will be printed.') """ ''') diff --git a/tests/test_variables.py b/tests/test_variables.py index 422f2f5..0f82584 100644 --- a/tests/test_variables.py +++ b/tests/test_variables.py @@ -14,6 +14,7 @@ def test_assign(): myint = 7 print(myint) """ + : print 7 : myint None : print(myint) """ diff --git a/trace_commentor/commentor.py b/trace_commentor/commentor.py index 9dce81c..8bb036b 100644 --- a/trace_commentor/commentor.py +++ b/trace_commentor/commentor.py @@ -9,12 +9,12 @@ from functools import wraps from . import handlers from . import formatters 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): - def __init__(self, output="", _globals=dict(), fmt=[]) -> None: + def __init__(self, output="", _globals=dict(), fmt=[], check=True, _exit=True) -> None: self._locals = dict() self._globals = dict().update(_globals) self._return = None @@ -25,6 +25,8 @@ class Commentor(object): self.state = flags.SOURCE self.file = output self._stack_event = flags.NORMAL + self._exit = _exit + self._check = check def __call__(self, func): @@ -32,6 +34,8 @@ class Commentor(object): self.indent = len(raw_lines[0]) - len(raw_lines[0].lstrip()) unindented_source = ''.join([l[self.indent:] for l in raw_lines]) self.root = ast.parse(unindented_source).body[0] + if self._check: self.check_support() + pt = getfullargspec(func) if flags.DEBUG: @@ -71,6 +75,8 @@ class Commentor(object): # output { comments = "\n".join(self._lines) if comment_to_file(comments, file=self.file): + if self._exit: + exit(0) return self._return else: return comments @@ -102,7 +108,9 @@ class Commentor(object): def get_formatter(self, obj): 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 else: return repr @@ -128,3 +136,17 @@ class Commentor(object): self.__append('"""') if line is not None: 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) diff --git a/trace_commentor/flags.py b/trace_commentor/flags.py index 506a7f0..be4769e 100644 --- a/trace_commentor/flags.py +++ b/trace_commentor/flags.py @@ -21,3 +21,10 @@ APPEND_SOURCE_BY_THEMSELVES = [ ASSIGN_SILENT = [ 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, +] diff --git a/trace_commentor/formatters/__init__.py b/trace_commentor/formatters/__init__.py index 540235b..b28efd2 100644 --- a/trace_commentor/formatters/__init__.py +++ b/trace_commentor/formatters/__init__.py @@ -1 +1,7 @@ -LIST = [] +def fmt_callable(fn): + return "" + + +LIST = [ + (callable, fmt_callable), +] diff --git a/trace_commentor/handlers/__init__.py b/trace_commentor/handlers/__init__.py index 6612cfe..df80451 100644 --- a/trace_commentor/handlers/__init__.py +++ b/trace_commentor/handlers/__init__.py @@ -1,6 +1,6 @@ from .definitions import FunctionDef, Return 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 .variables import Name from .control_flow import If, For, Continue, Break diff --git a/trace_commentor/handlers/expressions.py b/trace_commentor/handlers/expressions.py index 8113cc6..6bedc5e 100644 --- a/trace_commentor/handlers/expressions.py +++ b/trace_commentor/handlers/expressions.py @@ -12,8 +12,13 @@ def Compare(self, cmtor): cmtor.append_comment(cmtor.eval(self)) def Call(self, cmtor): + cmtor.process(self.func) for arg in self.args: cmtor.process(arg) for kwarg in self.keywords: cmtor.process(kwarg) cmtor.append_comment(cmtor.eval(self)) + +def Attribute(self, cmtor): + cmtor.process(self.value) + cmtor.append_comment(cmtor.eval(self)) diff --git a/trace_commentor/utils.py b/trace_commentor/utils.py index dd42b9b..c41e448 100644 --- a/trace_commentor/utils.py +++ b/trace_commentor/utils.py @@ -26,6 +26,10 @@ def to_source(node): return src +def dump(node, file=sys.stderr): + print(ast.dump(node, indent=4), file=file) + + def comment_to_file(code, file: str) -> bool: if file == "": return False @@ -49,3 +53,10 @@ def comment_to_file(code, file: str) -> bool: else: raise NotImplementedError(f"Unknown file protocal {file}") return True + + +def isinstance_noexcept(_obj, _class_or_tuple): + try: + return isinstance(_obj, _class_or_tuple) + except TypeError: + return False