@@ -45,21 +45,24 @@ enum MessageType {
4545
4646enum FormatCheck {
4747 clang,
48+ gn,
4849 java,
50+ python,
4951 whitespace,
50- gn,
5152}
5253
5354FormatCheck nameToFormatCheck (String name) {
5455 switch (name) {
5556 case 'clang' :
5657 return FormatCheck .clang;
58+ case 'gn' :
59+ return FormatCheck .gn;
5760 case 'java' :
5861 return FormatCheck .java;
62+ case 'python' :
63+ return FormatCheck .python;
5964 case 'whitespace' :
6065 return FormatCheck .whitespace;
61- case 'gn' :
62- return FormatCheck .gn;
6366 default :
6467 throw FormattingException ('Unknown FormatCheck type $name ' );
6568 }
@@ -69,12 +72,14 @@ String formatCheckToName(FormatCheck check) {
6972 switch (check) {
7073 case FormatCheck .clang:
7174 return 'C++/ObjC' ;
75+ case FormatCheck .gn:
76+ return 'GN' ;
7277 case FormatCheck .java:
7378 return 'Java' ;
79+ case FormatCheck .python:
80+ return 'Python' ;
7481 case FormatCheck .whitespace:
7582 return 'Trailing whitespace' ;
76- case FormatCheck .gn:
77- return 'GN' ;
7883 }
7984}
8085
@@ -140,6 +145,15 @@ abstract class FormatChecker {
140145 allFiles: allFiles,
141146 messageCallback: messageCallback,
142147 );
148+ case FormatCheck .gn:
149+ return GnFormatChecker (
150+ processManager: processManager,
151+ baseGitRef: baseGitRef,
152+ repoDir: repoDir,
153+ srcDir: srcDir,
154+ allFiles: allFiles,
155+ messageCallback: messageCallback,
156+ );
143157 case FormatCheck .java:
144158 return JavaFormatChecker (
145159 processManager: processManager,
@@ -149,17 +163,17 @@ abstract class FormatChecker {
149163 allFiles: allFiles,
150164 messageCallback: messageCallback,
151165 );
152- case FormatCheck .whitespace :
153- return WhitespaceFormatChecker (
166+ case FormatCheck .python :
167+ return PythonFormatChecker (
154168 processManager: processManager,
155169 baseGitRef: baseGitRef,
156170 repoDir: repoDir,
157171 srcDir: srcDir,
158172 allFiles: allFiles,
159173 messageCallback: messageCallback,
160174 );
161- case FormatCheck .gn :
162- return GnFormatChecker (
175+ case FormatCheck .whitespace :
176+ return WhitespaceFormatChecker (
163177 processManager: processManager,
164178 baseGitRef: baseGitRef,
165179 repoDir: repoDir,
@@ -665,6 +679,93 @@ class GnFormatChecker extends FormatChecker {
665679 }
666680}
667681
682+ /// Checks the format of any .py files using the "yapf" command.
683+ class PythonFormatChecker extends FormatChecker {
684+ PythonFormatChecker ({
685+ ProcessManager processManager = const LocalProcessManager (),
686+ required String baseGitRef,
687+ required Directory repoDir,
688+ required Directory srcDir,
689+ bool allFiles = false ,
690+ MessageCallback ? messageCallback,
691+ }) : super (
692+ processManager: processManager,
693+ baseGitRef: baseGitRef,
694+ repoDir: repoDir,
695+ srcDir: srcDir,
696+ allFiles: allFiles,
697+ messageCallback: messageCallback,
698+ ) {
699+ yapfBin = File (path.join (
700+ repoDir.absolute.path,
701+ 'tools' ,
702+ 'yapf.sh' ,
703+ ));
704+ _yapfStyle = File (path.join (
705+ repoDir.absolute.path,
706+ '.style.yapf' ,
707+ ));
708+ }
709+
710+ late final File yapfBin;
711+ late final File _yapfStyle;
712+
713+ @override
714+ Future <bool > checkFormatting () async {
715+ message ('Checking Python formatting...' );
716+ return (await _runYapfCheck (fixing: false )) == 0 ;
717+ }
718+
719+ @override
720+ Future <bool > fixFormatting () async {
721+ message ('Fixing Python formatting...' );
722+ await _runYapfCheck (fixing: true );
723+ // The yapf script shouldn't fail when fixing errors.
724+ return true ;
725+ }
726+
727+ Future <int > _runYapfCheck ({required bool fixing}) async {
728+ final List <String > filesToCheck = await getFileList (< String > ['*.py' ]);
729+
730+ final List <String > cmd = < String > [
731+ yapfBin.path,
732+ '--style' , _yapfStyle.path,
733+ if (! fixing) '--diff' ,
734+ if (fixing) '--in-place' ,
735+ ];
736+ final List <WorkerJob > jobs = < WorkerJob > [];
737+ for (final String file in filesToCheck) {
738+ jobs.add (WorkerJob (< String > [...cmd, file]));
739+ }
740+ final ProcessPool yapfPool = ProcessPool (
741+ processRunner: _processRunner,
742+ printReport: namedReport ('python format' ),
743+ );
744+ final List <WorkerJob > completedJobs = await yapfPool.runToCompletion (jobs);
745+ reportDone ();
746+ final List <String > incorrect = < String > [];
747+ for (final WorkerJob job in completedJobs) {
748+ if (job.result.exitCode == 1 ) {
749+ incorrect.add (' ${job .command .last }\n ${job .result .output }' );
750+ }
751+ }
752+ if (incorrect.isNotEmpty) {
753+ final bool plural = incorrect.length > 1 ;
754+ if (fixing) {
755+ message ('Fixed ${incorrect .length } python file${plural ? 's' : '' }'
756+ ' which ${plural ? 'were' : 'was' } formatted incorrectly.' );
757+ } else {
758+ error ('Found ${incorrect .length } python file${plural ? 's' : '' }'
759+ ' which ${plural ? 'were' : 'was' } formatted incorrectly:' );
760+ incorrect.forEach (stderr.writeln);
761+ }
762+ } else {
763+ message ('All python files formatted correctly.' );
764+ }
765+ return incorrect.length;
766+ }
767+ }
768+
668769@immutable
669770class _GrepResult {
670771 const _GrepResult (this .file, [this .hits = const < String > [], this .lineNumbers = const < int > []]);
0 commit comments