5
5
# The MIT License (MIT)
6
6
#
7
7
# Copyright (c) 2020 Damien P. George
8
- # Copyright (c) 2020 Jim Mussared
8
+ # Copyright (c) 2023 Jim Mussared
9
9
#
10
10
# Permission is hereby granted, free of charge, to any person obtaining a copy
11
11
# of this software and associated documentation files (the "Software"), to deal
25
25
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26
26
# THE SOFTWARE.
27
27
28
+ # This is based on tools/codeformat.py from the main micropython/micropython
29
+ # repository but without support for .c/.h files.
30
+
28
31
import argparse
29
32
import glob
30
33
import itertools
34
37
35
38
# Relative to top-level repo dir.
36
39
PATHS = [
37
- # C
38
- "**/*.[ch]" ,
39
- # Python
40
40
"**/*.py" ,
41
41
]
42
42
45
45
# Path to repo top-level dir.
46
46
TOP = os .path .abspath (os .path .join (os .path .dirname (__file__ ), ".." ))
47
47
48
- UNCRUSTIFY_CFG = os .path .join (TOP , "tools/uncrustify.cfg" )
49
-
50
- C_EXTS = (
51
- ".c" ,
52
- ".h" ,
53
- )
54
48
PY_EXTS = (".py" ,)
55
49
56
50
57
- MAIN_BRANCH = "master"
58
- BASE_BRANCH = os .environ .get ("GITHUB_BASE_REF" , MAIN_BRANCH )
59
-
60
-
61
51
def list_files (paths , exclusions = None , prefix = "" ):
62
52
files = set ()
63
53
for pattern in paths :
@@ -67,128 +57,33 @@ def list_files(paths, exclusions=None, prefix=""):
67
57
return sorted (files )
68
58
69
59
70
- def fixup_c (filename ):
71
- # Read file.
72
- with open (filename ) as f :
73
- lines = f .readlines ()
74
-
75
- # Write out file with fixups.
76
- with open (filename , "w" , newline = "" ) as f :
77
- dedent_stack = []
78
- while lines :
79
- # Get next line.
80
- l = lines .pop (0 )
81
-
82
- # Dedent #'s to match indent of following line (not previous line).
83
- m = re .match (r"( +)#(if |ifdef |ifndef |elif |else|endif)" , l )
84
- if m :
85
- indent = len (m .group (1 ))
86
- directive = m .group (2 )
87
- if directive in ("if " , "ifdef " , "ifndef " ):
88
- l_next = lines [0 ]
89
- indent_next = len (re .match (r"( *)" , l_next ).group (1 ))
90
- if indent - 4 == indent_next and re .match (r" +(} else |case )" , l_next ):
91
- # This #-line (and all associated ones) needs dedenting by 4 spaces.
92
- l = l [4 :]
93
- dedent_stack .append (indent - 4 )
94
- else :
95
- # This #-line does not need dedenting.
96
- dedent_stack .append (- 1 )
97
- else :
98
- if dedent_stack [- 1 ] >= 0 :
99
- # This associated #-line needs dedenting to match the #if.
100
- indent_diff = indent - dedent_stack [- 1 ]
101
- assert indent_diff >= 0
102
- l = l [indent_diff :]
103
- if directive == "endif" :
104
- dedent_stack .pop ()
105
-
106
- # Write out line.
107
- f .write (l )
108
-
109
- assert not dedent_stack , filename
110
-
111
-
112
- def query_git_files (verbose ):
113
- def cmd_result_set (cmd ):
114
- ret = subprocess .run (cmd , capture_output = True ).stdout .strip ().decode ()
115
- if not ret :
116
- return set ()
117
- return {f .strip () for f in ret .split ("\n " )}
118
-
119
- def rel_paths (files , root ):
120
- return {os .path .relpath (os .path .join (root , f .strip ()), "." ) for f in files }
121
-
122
- try :
123
- ret = set ()
124
-
125
- # get path to root of repository
126
- root_dir = (
127
- subprocess .run (["git" , "rev-parse" , "--show-toplevel" ], capture_output = True )
128
- .stdout .strip ()
129
- .decode ()
130
- )
131
-
132
- # Check locally modified files
133
- status = cmd_result_set (["git" , "status" , "--porcelain" ])
134
- dirty_files = rel_paths ({line .split (" " , 1 )[- 1 ] for line in status }, root_dir )
135
- ret |= dirty_files
136
-
137
- # Current commit and branch
138
- current_commit = (
139
- subprocess .run (["git" , "rev-parse" , "HEAD" ], capture_output = True )
140
- .stdout .strip ()
141
- .decode ()
142
- )
143
- current_branches = cmd_result_set (["git" , "branch" , "--contains" , current_commit ])
144
- if MAIN_BRANCH in current_branches :
145
- if ret :
146
- if verbose :
147
- print ("Local changes detected, only scanning them." )
148
- return ret
149
-
150
- # We're on clean master, run on entire repo
151
- if verbose :
152
- print ("Scanning whole repository" )
153
- return None
154
-
155
- # List the files modified on current branch
156
- if verbose :
157
- print ("Scanning changes from current branch and any local changes" )
158
- files_on_branch = rel_paths (
159
- cmd_result_set (["git" , "diff" , "--name-only" , BASE_BRANCH ]), root_dir
160
- )
161
- ret |= files_on_branch
162
- return ret
163
- except :
164
- # Git not available, run on entire repo
165
- return None
166
-
167
-
168
60
def main ():
169
- cmd_parser = argparse .ArgumentParser (description = "Auto-format C and Python files." )
170
- cmd_parser .add_argument ("-c" , action = "store_true" , help = "Format C code only" )
171
- cmd_parser .add_argument ("-p" , action = "store_true" , help = "Format Python code only" )
61
+ cmd_parser = argparse .ArgumentParser (description = "Auto-format Python files." )
172
62
cmd_parser .add_argument ("-v" , action = "store_true" , help = "Enable verbose output" )
173
63
cmd_parser .add_argument (
174
- "files " ,
175
- nargs = "* " ,
176
- help = "Run on specific globs. If not specied current branch changes will be used " ,
64
+ "-f " ,
65
+ action = "store_true " ,
66
+ help = "Filter files provided on the command line against the default list of files to check. " ,
177
67
)
68
+ cmd_parser .add_argument ("files" , nargs = "*" , help = "Run on specific globs" )
178
69
args = cmd_parser .parse_args ()
179
70
180
- # Setting only one of -c or -p disables the other. If both or neither are set, then do both.
181
- format_c = args .c or not args .p
182
- format_py = args .p or not args .c
183
-
184
71
# Expand the globs passed on the command line, or use the default globs above.
185
72
files = []
186
73
if args .files :
187
74
files = list_files (args .files )
75
+ if args .f :
76
+ # Filter against the default list of files. This is a little fiddly
77
+ # because we need to apply both the inclusion globs given in PATHS
78
+ # as well as the EXCLUSIONS, and use absolute paths
79
+ files = set (os .path .abspath (f ) for f in files )
80
+ all_files = set (list_files (PATHS , EXCLUSIONS , TOP ))
81
+ if args .v : # In verbose mode, log any files we're skipping
82
+ for f in files - all_files :
83
+ print ("Not checking: {}" .format (f ))
84
+ files = list (files & all_files )
188
85
else :
189
- files = query_git_files (verbose = args .v )
190
- if not files :
191
- files = list_files (PATHS , EXCLUSIONS , TOP )
86
+ files = list_files (PATHS , EXCLUSIONS , TOP )
192
87
193
88
# Extract files matching a specific language.
194
89
def lang_files (exts ):
@@ -204,23 +99,13 @@ def batch(cmd, files, N=200):
204
99
break
205
100
subprocess .check_call (cmd + file_args )
206
101
207
- # Format C files with uncrustify.
208
- if format_c :
209
- command = ["uncrustify" , "-c" , UNCRUSTIFY_CFG , "-lC" , "--no-backup" ]
210
- if not args .v :
211
- command .append ("-q" )
212
- batch (command , lang_files (C_EXTS ))
213
- for file in lang_files (C_EXTS ):
214
- fixup_c (file )
215
-
216
102
# Format Python files with black.
217
- if format_py :
218
- command = ["black" , "--fast" , "--line-length=99" ]
219
- if args .v :
220
- command .append ("-v" )
221
- else :
222
- command .append ("-q" )
223
- batch (command , lang_files (PY_EXTS ))
103
+ command = ["black" , "--fast" , "--line-length=99" ]
104
+ if args .v :
105
+ command .append ("-v" )
106
+ else :
107
+ command .append ("-q" )
108
+ batch (command , lang_files (PY_EXTS ))
224
109
225
110
226
111
if __name__ == "__main__" :
0 commit comments