11package codacy .pylint
22
3- import java .nio .file .{Files , Path , Paths }
3+ import java .nio .file .{Files , Path }
44
55import codacy .dockerApi ._
6- import codacy .dockerApi .utils .{FileHelper , CommandRunner , ToolHelper }
6+ import codacy .dockerApi .utils .{CommandRunner , FileHelper , ToolHelper }
77import play .api .libs .json ._
88
9- import scala .collection .immutable .Iterable
109import scala .sys .process ._
1110import scala .util .{Properties , Success , Try }
1211
1312object Pylint extends Tool {
1413
15- override def apply (path : Path , conf : Option [List [PatternDef ]], files : Option [Set [Path ]])(implicit spec : Spec ): Try [List [Result ]] = {
14+ override def apply (path : Path , conf : Option [List [PatternDef ]], files : Option [Set [Path ]])(implicit spec : Spec ): Try [List [Result ]] = {
1615 val completeConf = ToolHelper .getPatternsToLint(conf)
1716
1817 def isEnabled (issue : Result ) = {
19- issue match {
20- case Issue (_, _, patternId, _) => completeConf.map(item => item.exists(_.patternId == patternId)).getOrElse(true )
21- case _ => true
22- }
18+ issue match {
19+ case Issue (_, _, patternId, _) => completeConf.map(item => item.exists(_.patternId == patternId)).getOrElse(true )
20+ case _ => true
21+ }
2322 }
2423
2524 def buildFileCommands (files : Map [String , Array [String ]]) = {
26- files.map { case (key, values) => commandFor(key, path, completeConf, values)}
27- .flatMap( item => item.toOption).toList
25+ files.map { case (key, values) => commandFor(key, path, completeConf, values) }
26+ .flatMap(item => item.toOption).toList
2827 }
2928
3029 def getStdout (command : List [String ]): Try [List [String ]] = {
3130 Try {
32- CommandRunner .exec(command) match {
33- case Right (resultFromTool) =>
34- resultFromTool.stdout
35- case Left (failure) => {
36- throw failure}
31+ CommandRunner .exec(command, Some (path.toFile) ) match {
32+ case Right (resultFromTool) =>
33+ resultFromTool.stdout
34+ case Left (failure) => {
35+ throw failure
3736 }
37+ }
3838 }
3939 }
4040
@@ -44,78 +44,83 @@ object Pylint extends Tool {
4444 val lines_iterable = commands.map { item => item.map(getStdout) }
4545 val lines = lines_iterable.map {
4646 case iterable => iterable.flatMap {
47- case item => item.toOption
47+ case item => item.toOption
4848 }.flatten
4949 }
50- lines.map { case line => line.flatMap(parseLine).flatten.filter(isEnabled)}
50+ lines.map { case line => line.flatMap(parseLine).flatten.filter(isEnabled) }
5151 }
5252
5353
5454 private implicit lazy val writer = Json .reads[Issue ]
5555
5656 private def parseLine (line : String ) = {
57- val LineRegex = """ (.*?)###(.*?)###(.*?)###(.*?)""" .r
58- line match {
59- case LineRegex (filename, lineNumber, message, patternId) if message.contains(" invalid syntax" ) =>
60- val fileError = FileError (SourcePath (filename),
61- Option (ErrorMessage (message)))
62- val issue = Issue (SourcePath (filename),
63- ResultMessage (message),
64- PatternId (patternId),
65- ResultLine (lineNumber.toInt))
66- Option (List (fileError, issue))
67- case LineRegex (filename, lineNumber, message, patternId) =>
68- Option (List (Issue (SourcePath (filename),
69- ResultMessage (message),
70- PatternId (patternId),
71- ResultLine (lineNumber.toInt))))
72- case _ =>
73- Option .empty
74- }
57+ val LineRegex = """ (.*?)###(\d*?)###(.*?)###(.*?)""" .r
58+
59+ def createIssue (filename : String , lineNumber : String , message : String , patternId : String ) = {
60+ // If the pylint returns no line put the issue in the first line
61+ val issueLine = if (lineNumber.nonEmpty) lineNumber.toInt else 1
62+ Issue (SourcePath (filename),
63+ ResultMessage (message),
64+ PatternId (patternId),
65+ ResultLine (issueLine))
66+ }
67+
68+ line match {
69+ case LineRegex (filename, lineNumber, message, patternId) if message.contains(" invalid syntax" ) =>
70+ val fileError = FileError (SourcePath (filename),
71+ Option (ErrorMessage (message)))
72+ val issue = createIssue(filename, lineNumber, message, patternId)
73+ Option (List (fileError, issue))
74+ case LineRegex (filename, lineNumber, message, patternId) =>
75+ Option (List (createIssue(filename, lineNumber, message, patternId)))
76+ case _ =>
77+ Option .empty
78+ }
7579 }
7680
7781 private val msgTemplate = " {path}###{line}###{msg}###{msg_id}"
78- private val classifyScript = s """
79- |import os
80- |import sys
81- |import ast
82- |current = sys.version_info[0]
83- |other = 2 if current == 3 else 3
84- |def _classify_file(path):
85- | try:
86- | with open(path, 'r') as stream:
87- | try:
88- | ast.parse(stream.read())
89- | except (ValueError, TypeError, UnicodeError):
90- | # Assume it's the current interpreter.
91- | return current
92- | except SyntaxError:
93- | # the other version or an actual syntax error on current interpreter
94- | return other
95- | else:
96- | return current
97- | except Exception:
98- | # Shouldn't happen, but if it does, just assume there's
99- | # something inherently wrong with the file.
100- | return current
101- |def classify_file(path):
102- | interpreter = _classify_file(path)
103- | return path + "###" + str(interpreter)
104- |def flatten_files(folder):
105- | for path, _, files in os.walk(folder):
106- | for file in files:
107- | if file.endswith(".py"):
108- | yield os.path.join(path, file)
109- |def walk_items(items):
110- | for item in items:
111- | if os.path.isfile(item): yield item
112- | elif os.path.isdir(item):
113- | for file in flatten_files(item): yield file
114- |def classify(items):
115- | for file in walk_items(items):
116- | print(classify_file(file))
117- |items = filter(None, sys.argv[1].split("###"))
118- |classify(items)
82+ private val classifyScript =
83+ s """
84+ |import os
85+ |import sys
86+ |import ast
87+ |current = sys.version_info[0]
88+ |other = 2 if current == 3 else 3
89+ |def _classify_file(path):
90+ | try:
91+ | with open(path, 'r') as stream:
92+ | try:
93+ | ast.parse(stream.read())
94+ | except (ValueError, TypeError, UnicodeError):
95+ | # Assume it's the current interpreter.
96+ | return current
97+ | except SyntaxError:
98+ | # the other version or an actual syntax error on current interpreter
99+ | return other
100+ | else:
101+ | return current
102+ | except Exception:
103+ | # Shouldn't happen, but if it does, just assume there's
104+ | # something inherently wrong with the file.
105+ | return current
106+ |def classify_file(path):
107+ | interpreter = _classify_file(path)
108+ | return path + "###" + str(interpreter)
109+ |def flatten_files(folder):
110+ | for path, _, files in os.walk(folder):
111+ | for file in files:
112+ | if file.endswith(".py"):
113+ | yield os.path.join(path, file)
114+ |def walk_items(items):
115+ | for item in items:
116+ | if os.path.isfile(item): yield item
117+ | elif os.path.isdir(item):
118+ | for file in flatten_files(item): yield file
119+ |def classify(items):
120+ | for file in walk_items(items):
121+ | print(classify_file(file))
122+ |items = filter(None, sys.argv[1].split("###"))
123+ |classify(items)
119124 """ .stripMargin
120125
121126 private def collectFiles (files : Option [Set [Path ]], path : Path ) = {
@@ -132,16 +137,16 @@ object Pylint extends Tool {
132137 }
133138
134139 private def classifyFiles (files : List [String ]) = {
135- Try {
136- val output = generateClassification(files)
137- val lines = output.split(System .lineSeparator())
138- val parsed = lines.map { case line =>
139- val splitted = line.split(" ###" )
140- (splitted(0 ), splitted(1 ))
141- }
142- parsed.groupBy { case (path, version) => version}
143- .map { case (key, pairs) => (key, pairs map { case (file, version) => file})}
140+ Try {
141+ val output = generateClassification(files)
142+ val lines = output.split(System .lineSeparator())
143+ val parsed = lines.map { case line =>
144+ val splitted = line.split(" ###" )
145+ (splitted(0 ), splitted(1 ))
144146 }
147+ parsed.groupBy { case (path, version) => version }
148+ .map { case (key, pairs) => (key, pairs map { case (file, version) => file }) }
149+ }
145150 }
146151
147152 private def commandFor (interpreter : String , path : Path , conf : Option [List [PatternDef ]], files : Array [String ])(implicit spec : Spec ): Try [List [String ]] = {
@@ -160,14 +165,14 @@ object Pylint extends Tool {
160165
161166 // Additional plugins
162167 val django = Seq (" --load-plugins=pylint_django" ,
163- " --disable=django-installed-checker,django-model-checker" )
168+ " --disable=django-installed-checker,django-model-checker" )
164169 val flask = Seq (" --load-plugins=pylint_flask" )
165170 val additionalPlugins = django ++ flask
166171
167172 configPart.map { configPart =>
168173 List (" python" + interpreter, " -m" , " pylint" ) ++
169- configPart ++ List (s " --msg-template= $msgTemplate" ) ++
170- rulesPart ++ additionalPlugins ++ files
174+ configPart ++ List (s " --msg-template= $msgTemplate" ) ++
175+ rulesPart ++ additionalPlugins ++ files
171176 }
172177 }
173178
0 commit comments