Skip to content

Commit 3287c9b

Browse files
committed
Added infrastructure to safely bump flake8 version
Added logic in the Flake8Task to rerun the execution with ignoring a set of rules if the first execution fails. It will rerun if the IGNORE_RULES set it non-empty. This set of rules will contain any rules/checks added between flake8 versions. For example, when we bump to flake8 3.8.0, we should update the IGNORE_RULES value to hold all rules and checks added between flake8 3.6.0 and 3.8.0. If the second run, when ignoring newly added rules, succeeds, we now emit a warning to the user stating which rules are being ignored, and which of these ignored rules failed for them. Added a method called overrideIgnoreRules() which is used for integration testings and will be used internally by LI. Integration tests were added to verify this new functionality.
1 parent 12052d2 commit 3287c9b

File tree

3 files changed

+105
-16
lines changed

3 files changed

+105
-16
lines changed

pygradle-plugin/src/integTest/groovy/com/linkedin/gradle/python/tasks/Flake8TaskIntegrationTest.groovy

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -26,17 +26,20 @@ class Flake8TaskIntegrationTest extends Specification {
2626

2727
@Rule
2828
final DefaultProjectLayoutRule testProjectDir = new DefaultProjectLayoutRule()
29+
def bazPy
2930

30-
def "a passing flake8"() {
31-
given:
31+
def setup() {
3232
testProjectDir.buildFile << """\
3333
|plugins {
3434
| id 'com.linkedin.python'
3535
|}
3636
|
3737
|${ PyGradleTestBuilder.createRepoClosure() }
3838
""".stripMargin().stripIndent()
39+
bazPy = new File(testProjectDir.root, 'foo/src/foo/baz.py')
40+
}
3941

42+
def "a passing flake8"() {
4043
when:
4144
def result = GradleRunner.create()
4245
.withProjectDir(testProjectDir.root)
@@ -46,22 +49,12 @@ class Flake8TaskIntegrationTest extends Specification {
4649
println result.output
4750

4851
then:
49-
5052
result.task(':foo:flake8').outcome == TaskOutcome.SUCCESS
5153
}
5254

5355
def "a failing flake8"() {
5456
given:
55-
testProjectDir.buildFile << """\
56-
|plugins {
57-
| id 'com.linkedin.python'
58-
|}
59-
|
60-
|${ PyGradleTestBuilder.createRepoClosure() }
61-
""".stripMargin().stripIndent()
62-
63-
def baxPy = new File(testProjectDir.root, 'foo/src/foo/baz.py')
64-
baxPy.text = '''
57+
bazPy.text = '''
6558
|import os, sys
6659
'''.stripMargin().stripIndent()
6760

@@ -77,4 +70,58 @@ class Flake8TaskIntegrationTest extends Specification {
7770
result.output.contains('baz.py:2:10: E401 multiple imports on one line')
7871
result.task(':foo:flake8').outcome == TaskOutcome.FAILED
7972
}
73+
74+
def "flake8 fails even with ignore"() {
75+
given:
76+
testProjectDir.buildFile << '''
77+
| import com.linkedin.gradle.python.tasks.Flake8Task
78+
| tasks.withType(Flake8Task) { Flake8Task task ->
79+
| task.setIgnoreRules(["E401"] as Set)
80+
| }
81+
'''.stripMargin().stripIndent()
82+
83+
bazPy.text = '''
84+
|import os, sys
85+
|'''.stripMargin().stripIndent()
86+
87+
when:
88+
def result = GradleRunner.create()
89+
.withProjectDir(testProjectDir.root)
90+
.withArguments('flake8', '-s', '-i')
91+
.withPluginClasspath()
92+
.buildAndFail()
93+
println result.output
94+
95+
then:
96+
result.output.contains("baz.py:2:1: F401 'os' imported but unused")
97+
result.task(':foo:flake8').outcome == TaskOutcome.FAILED
98+
}
99+
100+
def "warning for a newly failing flake8"() {
101+
given:
102+
testProjectDir.buildFile << '''
103+
| import com.linkedin.gradle.python.tasks.Flake8Task
104+
| tasks.withType(Flake8Task) { Flake8Task task ->
105+
| task.setIgnoreRules(["E401", "F401"] as Set)
106+
| }
107+
'''.stripMargin().stripIndent()
108+
109+
bazPy.text = '''
110+
|import os, sys
111+
|'''.stripMargin().stripIndent()
112+
113+
when:
114+
def result = GradleRunner.create()
115+
.withProjectDir(testProjectDir.root)
116+
.withArguments('flake8', '-s', '-i')
117+
.withPluginClasspath()
118+
.build()
119+
println result.output
120+
121+
then:
122+
result.output.contains('The flake8 version has been recently updated, which added the following new rules:')
123+
result.output.contains('baz.py:2:10: E401 multiple imports on one line')
124+
result.output.contains("baz.py:2:1: F401 'os' imported but unused")
125+
result.task(':foo:flake8').outcome == TaskOutcome.SUCCESS
126+
}
80127
}

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/tasks/AbstractPythonMainSourceDefaultTask.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ abstract public class AbstractPythonMainSourceDefaultTask extends DefaultTask im
5555
private List<String> subArguments = new ArrayList<>();
5656
private PythonExtension extension;
5757
private PythonDetails pythonDetails;
58-
private String output;
58+
protected String output;
5959

6060
@Input
6161
public List<String> additionalArguments = new ArrayList<>();
@@ -135,7 +135,11 @@ public void subArgs(Collection<String> args) {
135135
}
136136

137137
@TaskAction
138-
public void executePythonProcess() {
138+
public void executeTask() {
139+
executePythonProcess();
140+
}
141+
142+
void executePythonProcess() {
139143
preExecution();
140144

141145
final TeeOutputContainer container = new TeeOutputContainer(stdOut, errOut);

pygradle-plugin/src/main/groovy/com/linkedin/gradle/python/tasks/Flake8Task.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
import com.linkedin.gradle.python.PythonExtension;
1919
import com.linkedin.gradle.python.util.ExtensionUtils;
20+
import java.util.HashSet;
21+
import java.util.Set;
2022
import org.apache.commons.io.FileUtils;
2123
import org.gradle.api.logging.Logger;
2224
import org.gradle.api.logging.Logging;
@@ -32,8 +34,31 @@
3234
public class Flake8Task extends AbstractPythonMainSourceDefaultTask {
3335

3436
private static final Logger log = Logging.getLogger(Flake8Task.class);
37+
// Track whether the current run is excluding the new rules
38+
private Boolean ignoringNewRules = false;
39+
private String firstRunOutput = null;
40+
41+
// Set of flake8 rules to ignore (i.e. warn if these checks fail, rather than failing the task)
42+
private Set<String> ignoreRules = new HashSet<>();
43+
44+
private static final String IGNORED_RULES_MSG = "######################### WARNING ##########################\n"
45+
+ "The flake8 version has been recently updated, which added the following new rules:\n"
46+
+ "%s\n" // This will be replaced with the set of ignored rules
47+
+ "Your project is failing for one or more of these rules. Please address them, as they will be enforced soon.\n"
48+
+ "%s############################################################\n";
49+
50+
// Provide the ability to set the ignored rules for testing purposes,
51+
// but mainly so that users can enforce the use of a different version of flake8 in their plugins.
52+
public void setIgnoreRules(Set<String> ignoreRules) {
53+
this.ignoreRules = ignoreRules;
54+
}
55+
56+
public Set<String> getIgnoreRules() {
57+
return ignoreRules;
58+
}
3559

3660
public void preExecution() {
61+
ignoreExitValue = true;
3762
PythonExtension pythonExtension = ExtensionUtils.getPythonExtension(getProject());
3863
File flake8Exec = pythonExtension.getDetails().getVirtualEnvironment().findExecutable("flake8");
3964

@@ -77,6 +102,19 @@ public void preExecution() {
77102

78103
@Override
79104
public void processResults(ExecResult execResult) {
80-
//Not needed
105+
// If the first run of flake8 fails, trying running it again but ignoring the
106+
// rules/checks added by the previous version bump.
107+
if ((execResult.getExitValue() != 0) && !ignoringNewRules && (ignoreRules.size() > 0)) {
108+
ignoringNewRules = true;
109+
firstRunOutput = this.output;
110+
subArgs("--extend-ignore=" + String.join(",", ignoreRules));
111+
executePythonProcess();
112+
} else if ((execResult.getExitValue() == 0) && ignoringNewRules) {
113+
// The previous run failed, but flake8 succeeds when we ignore the most recent rules.
114+
// Warn the user that they are failing one or more of the new rules.
115+
log.warn(String.format(IGNORED_RULES_MSG, String.join(", ", ignoreRules), firstRunOutput));
116+
} else {
117+
execResult.assertNormalExitValue();
118+
}
81119
}
82120
}

0 commit comments

Comments
 (0)