88import ast
99from typing import Any
1010from ..tools import get_files
11+ import re
1112
1213empty_list = ast .parse ("[]" ).body [0 ].value
1314
1415
16+ ### Abstract visitor class ###
17+
18+
1519class AbstractVisitor (ast .NodeVisitor ):
16- def __init__ (self ) -> None :
20+ def __init__ (self , all_code = None ) -> None :
1721 # ((line, line_end, col_offset, end_col_offset), replace_by) NO OVERLAPS
22+ self .all_code = all_code
1823 self .change_todo = []
1924
2025 def post_process (self , all_code : str , file : str ) -> str :
@@ -46,6 +51,73 @@ def add_change(self, old_node: ast.AST, new_node: ast.AST | str):
4651 self .change_todo .append ((position , ast .unparse (new_node )))
4752
4853
54+ def apply_visitor_scripts (logger , filename ):
55+ with open (filename , mode = "rt" ) as file :
56+ new_all = all_code = file .read ()
57+ if ".read_group(" in all_code or "._read_group(" in all_code :
58+ for Step in Steps_visitor :
59+ visitor = Step ()
60+ try :
61+ visitor .visit (ast .parse (new_all ))
62+ except Exception :
63+ logger .error (
64+ f"ERROR in { filename } at step { visitor .__class__ } : \n { new_all } "
65+ )
66+ raise
67+ new_all = visitor .post_process (new_all , filename )
68+ if new_all == all_code :
69+ logger .warning (
70+ "read_group detected but not changed in file %s" % filename
71+ )
72+ else :
73+ logger .info ("Script read_group replace applied in file %s" % filename )
74+ with open (filename , mode = "wt" ) as file :
75+ file .write (new_all )
76+
77+ if "def name_get(" in all_code :
78+ visitor = VisitorNameGet (all_code )
79+ try :
80+ visitor .visit (ast .parse (new_all ))
81+ except Exception :
82+ logger .error (
83+ f"ERROR in { filename } at step { visitor .__class__ } : \n { new_all } "
84+ )
85+ raise
86+ new_all = visitor .post_process (new_all , filename )
87+ if new_all == all_code :
88+ logger .warning (
89+ "name_get detected but not changed in file: %s" % filename
90+ )
91+ else :
92+ logger .warning (
93+ "Script name_get replace applied in file %s. "
94+ "You probably need to add manually some dependencies to the new computed method."
95+ % filename
96+ )
97+ with open (filename , mode = "wt" ) as file :
98+ file .write (new_all )
99+
100+
101+ def _reformat_files (
102+ logger , module_path , module_name , manifest_path , migration_steps , tools
103+ ):
104+ """Reformat read_group method in py files."""
105+
106+ reformat_file_ext = [".py" ]
107+ file_paths = get_files (module_path , reformat_file_ext )
108+ logger .debug (f"{ reformat_file_ext } files found:\n " f"{ list (map (str , file_paths ))} " )
109+
110+ reformatted_files = list ()
111+ for file_path in file_paths :
112+ reformatted_file = apply_visitor_scripts (logger , file_path )
113+ if reformatted_file :
114+ reformatted_files .append (reformatted_file )
115+ logger .debug ("Reformatted files:\n " f"{ list (reformatted_files )} " )
116+
117+
118+ ### Refactor _read_group script ###
119+
120+
49121class VisitorToPrivateReadGroup (AbstractVisitor ):
50122 def post_process (self , all_code : str , file : str ) -> str :
51123 all_lines = all_code .split ("\n " )
@@ -214,29 +286,7 @@ def visit_Call(self, node: ast.Call) -> Any:
214286]
215287
216288
217- def replace_read_group_signature (logger , filename ):
218- with open (filename , mode = "rt" ) as file :
219- new_all = all_code = file .read ()
220- if ".read_group(" in all_code or "._read_group(" in all_code :
221- for Step in Steps_visitor :
222- visitor = Step ()
223- try :
224- visitor .visit (ast .parse (new_all ))
225- except Exception :
226- logger .info (
227- f"ERROR in { filename } at step { visitor .__class__ } : \n { new_all } "
228- )
229- raise
230- new_all = visitor .post_process (new_all , filename )
231- if new_all == all_code :
232- logger .info ("read_group detected but not changed in file %s" % filename )
233-
234- if new_all != all_code :
235- logger .info ("Script read_group replace applied in file %s" % filename )
236- with open (filename , mode = "wt" ) as file :
237- file .write (new_all )
238-
239-
289+ ### Warning open_form_view ###
240290
241291
242292def _check_open_form_view (logger , file_path : Path ):
@@ -258,31 +308,72 @@ def _check_open_form_view(logger, file_path: Path):
258308def _check_open_form (
259309 logger , module_path , module_name , manifest_path , migration_steps , tools
260310):
261- reformat_file_ext = ( ".xml" )
311+ reformat_file_ext = [ ".xml" ]
262312 file_paths = get_files (module_path , reformat_file_ext )
263313 logger .debug (f"{ reformat_file_ext } files found:\n " f"{ list (map (str , file_paths ))} " )
264314
265315 for file_path in file_paths :
266316 _check_open_form_view (logger , file_path )
267317
268318
269- def _reformat_read_group (
270- logger , module_path , module_name , manifest_path , migration_steps , tools
271- ):
272- """Reformat read_group method in py files."""
319+ ### name_get script ###
273320
274- reformat_file_ext = (".py" )
275- file_paths = get_files (module_path , reformat_file_ext )
276- logger .debug (f"{ reformat_file_ext } files found:\n " f"{ list (map (str , file_paths ))} " )
321+ list_comprehension_re = re .compile (
322+ r"""def name_get\(self\):(.*)
323+ return \[\s*\(.*id,\s*(.+)\s*\)\s*for\s*(\w+)\s*in\s*self\s*\]""" ,
324+ re .S ,
325+ )
326+ list_comprehension_into = r"""@api.depends('')
327+ def _compute_display_name(self):\g<1>
328+ for \g<3> in self:
329+ \g<3>.display_name = \g<2>"""
277330
278- reformatted_files = list ()
279- for file_path in file_paths :
280- reformatted_file = replace_read_group_signature (logger , file_path )
281- if reformatted_file :
282- reformatted_files .append (reformatted_file )
283- logger .debug ("Reformatted files:\n " f"{ list (reformatted_files )} " )
331+ list_append_re = re .compile (
332+ r"""def name_get\(self\):.+
333+ return ([A-Za-z_]+)""" ,
334+ re .S ,
335+ )
336+
337+
338+ class VisitorNameGet (AbstractVisitor ):
339+ def post_process (self , all_code : str , file : str ) -> str :
340+ for (old_str , new_str ) in self .change_todo :
341+ all_code = all_code .replace (old_str , new_str )
342+ return all_code
343+
344+ def add_change (self , old_str , new_str ):
345+ self .change_todo .append ((old_str , new_str ))
346+
347+ def visit_FunctionDef (self , node : ast .FunctionDef ) -> Any :
348+ if node .name == "name_get" :
349+ # Replace fields by aggregate and orderby by order
350+ code = ast .get_source_segment (self .all_code , node )
351+ new_code = list_comprehension_re .sub (list_comprehension_into , code , 1 )
352+
353+ if new_code != code :
354+ self .add_change (code , new_code )
355+ elif groups := list_append_re .fullmatch (new_code ):
356+ variable_to_remove = groups .group (1 )
357+ new_code = new_code .replace (f"\n { variable_to_remove } = []" , "" )
358+ new_code = new_code .replace (
359+ "def name_get(self):" , "def _compute_display_name(self):"
360+ )
361+ new_code = new_code .replace (
362+ "super().name_get()" , "super()._compute_display_name()"
363+ )
364+ new_code = new_code .replace (
365+ f"\n return { variable_to_remove } " , ""
366+ )
367+ new_code = re .sub (
368+ variable_to_remove + r".append\(\((.+).id, (.*),?\)\)" ,
369+ r"\g<1>.display_name = \g<2>" ,
370+ new_code ,
371+ )
372+ self .add_change (code , new_code )
373+ else :
374+ print (f"Cannot find a way to convert:\n { code } " )
284375
285376
286377class MigrationScript (BaseMigrationScript ):
287378
288- _GLOBAL_FUNCTIONS = [_check_open_form , _reformat_read_group ]
379+ _GLOBAL_FUNCTIONS = [_check_open_form , _reformat_files ]
0 commit comments