1+ import re
2+ import ast
3+
4+ import parso
5+ import pytest
6+
7+ from pathlib import Path
8+ from redbaron import RedBaron
9+ from redbaron .utils import indent
10+
11+
12+ class SourceCode :
13+ def __init__ (self , exists , code ):
14+ self .exists = exists
15+ self .code = code
16+
17+
18+ class Parser :
19+ def __init__ (self , filename ):
20+ self .code = ""
21+ self .message = ""
22+
23+ error_message = ""
24+ error_start_pos = ""
25+
26+ if filename == "sensor" :
27+ file_path = Path .cwd () / "sensor.py"
28+ else :
29+ file_path = Path .cwd () / "sensor" / "{}.py" .format (filename )
30+
31+ grammar = parso .load_grammar ()
32+ module = grammar .parse (path = file_path .resolve ())
33+ self .success = len (grammar .iter_errors (module )) == 0
34+
35+ if self .success :
36+ with open (file_path .resolve (), "r" ) as source_code :
37+ self .code = RedBaron (source_code .read ())
38+ else :
39+ error_message = grammar .iter_errors (module )[0 ].message
40+ error_start_pos = grammar .iter_errors (module )[0 ].start_pos [0 ]
41+ self .message = "{} on or around line {} in `{}`." .format (
42+ error_message , error_start_pos , file_path .name
43+ )
44+
45+ def get_by_name (self , type , name , code = None ):
46+ if code is None :
47+ item = self .code .find_all (type , lambda node : node .name == name )
48+ else :
49+ item = code .find_all (type , lambda node : node .name == name )
50+
51+ return SourceCode (True , item [0 ]) if len (item ) > 0 else SourceCode (False , [])
52+
53+ def get_call (self , value , code ):
54+ call = code .find ("call" , lambda node : node .previous .value == value )
55+ return SourceCode (True , call ) if call is not None and len (call ) > 0 else SourceCode (False , [])
56+
57+ def get_args (self , code ):
58+ return list (
59+ code .find_all ("call_argument" ).map (
60+ lambda node : str (node .target ) + ":" + str (node .value ).replace ("'" , '"' )
61+ )
62+ )
63+
64+ def get_by_value (self , type , value , code = None ):
65+ if code is None :
66+ item = self .code .find_all (type , lambda node : str (node .target ) == value )
67+ else :
68+ item = code .find_all (type , lambda node : str (node .target ) == value )
69+ return SourceCode (True , item [0 ]) if len (item ) > 0 else SourceCode (False , [])
70+
71+ def get_imports (self ):
72+ imports = []
73+ self .code .find_all (
74+ "import" ,
75+ lambda node : node .find_all (
76+ "dotted_as_name" , lambda node : imports .append (str (node ))
77+ ),
78+ )
79+ return imports
80+
81+ def get_from_import (self , value ):
82+ imports = self .code .find_all (
83+ "from_import" ,
84+ lambda node : "" .join (list (node .value .node_list .map (lambda node : str (node ))))
85+ == value ,
86+ ).find_all ("name_as_name" )
87+ return list (imports .map (lambda node : node .value ))
88+
89+ def flatten (self , dictionary ):
90+ def _flatten (node ):
91+ trimmed = re .sub (r"\"|'" , "" , node .key .value )
92+ flattened = []
93+ if node .value .type is "list" :
94+ for item in node .value .node_list :
95+ if item .type is not "comma" :
96+ flattened .append ("{}:{}" .format (trimmed , str (item )))
97+ else :
98+ flattened .append ("{}:{}" .format (trimmed , node .value .value ))
99+
100+ return flattened
101+
102+ items = list (dictionary .find_all ("dictitem" ).map (lambda node : _flatten (node )))
103+ return [item for sublist in items for item in sublist ]
104+
105+ def get_conditional (self , values , type , nested = False ):
106+ def flat (node ):
107+ if node .type == "comparison" :
108+ return "{}:{}:{}" .format (
109+ str (node .first ).replace ("'" , '"' ),
110+ str (node .value ).replace (" " , ":" ),
111+ str (node .second ).replace ("'" , '"' ),
112+ )
113+ elif node .type == "unitary_operator" :
114+ return "{}:{}" .format (
115+ str (node .value ), str (node .target ).replace ("'" , '"' )
116+ )
117+
118+ nodes = self .code .value if nested else self .code
119+ for value in values :
120+ final_node = nodes .find_all (type ).find (
121+ ["comparison" , "unitary_operator" ], lambda node : flat (node ) == value
122+ )
123+ if final_node is not None :
124+ return final_node
125+ return None
126+
127+
128+ @pytest .fixture
129+ def parse ():
130+ def _parse (filename ):
131+ return Parser (filename )
132+
133+ return _parse
0 commit comments