33
44import libcst as cst
55from libcst import matchers
6+ from libcst .codemod import CodemodContext
67
78from codemodder .codemods .libcst_transformer import (
89 LibcstResultTransformer ,
910 LibcstTransformerPipeline ,
1011)
1112from codemodder .codemods .utils_mixin import NameAndAncestorResolutionMixin
13+ from codemodder .file_context import FileContext
14+ from codemodder .result import Result , same_line
1215from codemodder .utils .utils import clean_simplestring
1316from core_codemods .api import CoreCodemod , Metadata , Reference , ReviewGuidance
1417
@@ -19,41 +22,83 @@ class TempfileMktempTransformer(
1922 change_description = "Replaces `tempfile.mktemp` with `tempfile.mkstemp`."
2023 _module_name = "tempfile"
2124
22- def leave_SimpleStatementLine (self , original_node , updated_node ):
25+ def __init__ (
26+ self ,
27+ context : CodemodContext ,
28+ results : list [Result ] | None ,
29+ file_context : FileContext ,
30+ _transformer : bool = False ,
31+ ):
32+ self .mktemp_calls : set [cst .Call ] = set ()
33+ super ().__init__ (context , results , file_context , _transformer )
34+
35+ def visit_Call (self , node : cst .Call ) -> None :
36+ if self ._is_mktemp_call (node ):
37+ self .mktemp_calls .add (node )
38+
39+ def leave_SimpleStatementLine (
40+ self ,
41+ original_node : cst .SimpleStatementLine ,
42+ updated_node : cst .SimpleStatementLine ,
43+ ) -> cst .SimpleStatementLine | cst .FlattenSentinel :
44+ if not self .node_is_selected (original_node ):
45+ return updated_node
46+
2347 match original_node :
2448 case cst .SimpleStatementLine (body = [bsstmt ]):
25- return self .check_mktemp (original_node , bsstmt )
49+ if maybe_tuple := self ._is_assigned_to_mktemp (bsstmt ):
50+ assign_name , call = maybe_tuple
51+ return self .report_and_change (
52+ call , assign_name , original_node .leading_lines
53+ )
54+ if maybe_tuple := self ._mktemp_is_sink (bsstmt ):
55+ wrapper_func_name , call = maybe_tuple
56+ return self .report_and_change (
57+ call ,
58+ wrapper_func_name ,
59+ original_node .leading_lines ,
60+ assignment = False ,
61+ )
62+
63+ # If we get here it's because there is a mktemp call but we haven't fixed it yet.
64+ for unfixed in self .mktemp_calls :
65+ self .report_unfixed (unfixed , reason = "Pixee does not yet support this fix." )
66+ self .mktemp_calls .clear ()
2667 return updated_node
2768
28- def check_mktemp (
29- self , original_node : cst .SimpleStatementLine , bsstmt : cst .BaseSmallStatement
30- ) -> cst .SimpleStatementLine | cst .FlattenSentinel :
31- if maybe_tuple := self ._is_assigned_to_mktemp (bsstmt ): # type: ignore
32- assign_name , call = maybe_tuple
33- return self .report_and_change (call , assign_name )
34- if maybe_tuple := self ._mktemp_is_sink (bsstmt ):
35- wrapper_func_name , call = maybe_tuple
36- return self .report_and_change (call , wrapper_func_name , assignment = False )
37- return original_node
69+ def filter_by_result (self , node ) -> bool :
70+ match node :
71+ case cst .SimpleStatementLine ():
72+ pos_to_match = self .node_position (node )
73+ return self .results is None or any (
74+ self .match_location (pos_to_match , result )
75+ for result in self .results or []
76+ )
77+ return False
78+
79+ def match_location (self , pos , result ):
80+ return any (same_line (pos , location ) for location in result .locations )
3881
3982 def report_and_change (
40- self , node : cst .Call , name : cst .Name , assignment = True
83+ self , node : cst .Call , name : cst .Name , leading_lines : tuple , assignment = True
4184 ) -> cst .FlattenSentinel :
85+ self .mktemp_calls .clear ()
4286 self .report_change (node )
4387 self .add_needed_import (self ._module_name )
4488 self .remove_unused_import (node )
4589 with_block = (
4690 f"{ name .value } = tf.name" if assignment else f"{ name .value } (tf.name)"
4791 )
4892 new_stmt = dedent (
49- f"""
93+ f"""\
5094 with tempfile.NamedTemporaryFile({ self ._make_args (node )} ) as tf:
5195 { with_block }
5296 """
5397 ).rstrip ()
98+
5499 return cst .FlattenSentinel (
55100 [
56- cst .parse_statement (new_stmt ),
101+ cst .parse_statement (new_stmt ). with_changes ( leading_lines = leading_lines ) ,
57102 ]
58103 )
59104
0 commit comments