11from __future__ import annotations
22
33import ast
4- import re
54from copy import copy
65from keyword import kwlist
76from pathlib import Path
1514 rewrite_changed_nodes ,
1615)
1716
18- CAMEL_CASE_SUB_PATTERN = re .compile (r"(?<!^)(?=[A-Z])" )
19-
2017
2118@click .command ()
2219@click .argument ("paths" , nargs = - 1 , type = click .Path (exists = True ))
2320def rewrite_props (paths : list [str ]) -> None :
24- """Rewrite snake_case to props to camelCase"""
25-
21+ """Rewrite snake_case props to camelCase in the specified paths"""
2622 for p in map (Path , paths ):
23+ # Process each file or recursively process each Python file in directories
2724 for f in [p ] if p .is_file () else p .rglob ("*.py" ):
2825 result = generate_rewrite (file = f , source = f .read_text (encoding = "utf-8" ))
2926 if result is not None :
3027 f .write_text (result )
3128
3229
3330def generate_rewrite (file : Path , source : str ) -> str | None :
34- tree = ast .parse (source )
31+ """Generate the rewritten source code if changes are detected"""
32+ tree = ast .parse (source ) # Parse the source code into an AST
3533
36- changed = find_nodes_to_change (tree )
34+ changed = find_nodes_to_change (tree ) # Find nodes that need to be changed
3735 if not changed :
38- return None
36+ return None # Return None if no changes are needed
3937
40- new = rewrite_changed_nodes (file , source , tree , changed )
38+ new = rewrite_changed_nodes (
39+ file , source , tree , changed
40+ ) # Rewrite the changed nodes
4141 return new
4242
4343
4444def find_nodes_to_change (tree : ast .AST ) -> list [ChangedNode ]:
45+ """Find nodes in the AST that need to be changed"""
4546 changed : list [ChangedNode ] = []
4647 for el_info in find_element_constructor_usages (tree ):
48+ # Check if the props need to be rewritten
4749 if _rewrite_props (el_info .props , _construct_prop_item ):
50+ # Add the changed node to the list
4851 changed .append (ChangedNode (el_info .call , el_info .parents ))
4952 return changed
5053
5154
5255def conv_attr_name (name : str ) -> str :
56+ """Convert snake_case attribute name to camelCase"""
5357 if name in kwlist :
54- return name
58+ return name # Return the name as is if it's a Python keyword
59+ # Convert snake_case to CamelCase
5560 result = name .replace ("_" , " " ).title ().replace (" " , "" )
61+ # Ensure the first letter is lowercase
5662 result = name [0 ].lower () + name [1 :]
5763 return result
5864
5965
6066def _construct_prop_item (key : str , value : ast .expr ) -> tuple [str , ast .expr ]:
67+ """Construct a new prop item with the converted key and possibly modified value"""
6168 if key == "style" and isinstance (value , (ast .Dict , ast .Call )):
69+ # Create a copy of the value to avoid modifying the original
6270 new_value = copy (value )
6371 if _rewrite_props (
6472 new_value ,
6573 lambda k , v : (
6674 (k , v )
67- # avoid infinite recursion
75+ # Avoid infinite recursion
6876 if k == "style"
6977 else _construct_prop_item (k , v )
7078 ),
7179 ):
80+ # Update the value if changes were made
7281 value = new_value
7382 else :
83+ # Convert the key to camelCase
7484 key = conv_attr_name (key )
7585 return key , value
7686
@@ -79,12 +89,15 @@ def _rewrite_props(
7989 props_node : ast .Dict | ast .Call ,
8090 constructor : Callable [[str , ast .expr ], tuple [str , ast .expr ]],
8191) -> bool :
92+ """Rewrite the props in the given AST node using the provided constructor"""
93+ did_change = False
8294 if isinstance (props_node , ast .Dict ):
83- did_change = False
8495 keys : list [ast .expr | None ] = []
8596 values : list [ast .expr ] = []
97+ # Iterate over the keys and values in the dictionary
8698 for k , v in zip (props_node .keys , props_node .values ):
8799 if isinstance (k , ast .Constant ) and isinstance (k .value , str ):
100+ # Construct the new key and value
88101 k_value , new_v = constructor (k .value , v )
89102 if k_value != k .value or new_v is not v :
90103 did_change = True
@@ -93,20 +106,22 @@ def _rewrite_props(
93106 keys .append (k )
94107 values .append (v )
95108 if not did_change :
96- return False
109+ return False # Return False if no changes were made
97110 props_node .keys = keys
98111 props_node .values = values
99112 else :
100113 did_change = False
101114 keywords : list [ast .keyword ] = []
115+ # Iterate over the keywords in the call
102116 for kw in props_node .keywords :
103117 if kw .arg is not None :
118+ # Construct the new keyword argument and value
104119 kw_arg , kw_value = constructor (kw .arg , kw .value )
105120 if kw_arg != kw .arg or kw_value is not kw .value :
106121 did_change = True
107122 kw = ast .keyword (arg = kw_arg , value = kw_value )
108123 keywords .append (kw )
109124 if not did_change :
110- return False
125+ return False # Return False if no changes were made
111126 props_node .keywords = keywords
112127 return True
0 commit comments