11import ast
22import inspect
3+ import os
34import re
45import warnings
56from pathlib import Path
1011CWD = Path ('.' ).resolve ()
1112
1213
14+ def env_true (var_name , alt = 'FALSE' ):
15+ return os .getenv (var_name , alt ).upper () in {'1' , 'TRUE' }
16+
17+
1318class DebugArgument :
1419 __slots__ = 'value' , 'name' , 'extra'
1520
@@ -64,14 +69,20 @@ def __repr__(self) -> str:
6469
6570class Debug :
6671 output_class = DebugOutput
67- # 50 lines should be enough to make sure we always get the entire function definition
68- frame_context_length = 50
6972 complex_nodes = (
7073 ast .Call , ast .Attribute ,
7174 ast .IfExp , ast .BoolOp , ast .BinOp , ast .Compare ,
7275 ast .DictComp , ast .ListComp , ast .SetComp , ast .GeneratorExp
7376 )
7477
78+ def __init__ (self , * , warnings : Optional [bool ]= None , frame_context_length : int = 50 ):
79+ if warnings is None :
80+ self ._warnings = env_true ('PY_DEVTOOLS_WARNINGS' , 'TRUE' )
81+ else :
82+ self ._warnings = warnings
83+ # 50 lines should be enough to make sure we always get the entire function definition
84+ self ._frame_context_length = frame_context_length
85+
7586 def __call__ (self , * args , ** kwargs ):
7687 print (self ._process (args , kwargs , r'debug *\(' ), flush = True )
7788
@@ -80,7 +91,7 @@ def format(self, *args, **kwargs):
8091
8192 def _process (self , args , kwargs , func_regex ):
8293 curframe = inspect .currentframe ()
83- frames = inspect .getouterframes (curframe , context = self .frame_context_length )
94+ frames = inspect .getouterframes (curframe , context = self ._frame_context_length )
8495 # BEWARE: this must be call by a method which in turn is called "directly" for the frame to be correct
8596 call_frame = frames [2 ]
8697
@@ -102,7 +113,8 @@ def _process(self, args, kwargs, func_regex):
102113 arguments = list (self ._args_inspection_failed (args , kwargs ))
103114 else :
104115 lineno = call_frame .lineno
105- warnings .warn ('no code context for debug call, code inspection impossible' , RuntimeWarning )
116+ if self ._warnings :
117+ warnings .warn ('no code context for debug call, code inspection impossible' , RuntimeWarning )
106118 arguments = list (self ._args_inspection_failed (args , kwargs ))
107119
108120 return self .output_class (
@@ -182,9 +194,15 @@ def _parse_code(self, call_frame, func_regex, filename) -> Tuple[Optional[ast.AS
182194 break
183195
184196 if not func_ast :
185- warnings .warn ('error passing code:\n "{}"\n Error: {}' .format (original_code , e1 ), SyntaxWarning )
197+ if self ._warnings :
198+ warnings .warn ('error passing code:\n "{}"\n Error: {}' .format (original_code , e1 ), SyntaxWarning )
186199 return None , None , lineno
187200
201+ if not isinstance (func_ast , ast .Call ):
202+ if self ._warnings :
203+ warnings .warn ('error passing code, found {} not Call' .format (func_ast .__class__ ), SyntaxWarning )
204+ return None , None , lineno
205+
188206 code_lines = [l for l in code .split ('\n ' ) if l ]
189207 # this removes the trailing bracket from the lines of code meaning it doesn't appear in the
190208 # representation of the last argument
0 commit comments