-
Notifications
You must be signed in to change notification settings - Fork 1
/
FileParser.py
185 lines (160 loc) · 7.16 KB
/
FileParser.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
import Borland
import EmbarcaderoClang
import VisualStudio
import Gcc
import Cpplint
from pathlib import Path
import util
import fnmatch
from chardet.universaldetector import UniversalDetector
#Used for parsing an entire file. The file parser just loads the correct type of line parser,
#and uses it to iterate over an entire file.
class FileParser(object):
supportedflavors = ["gcc", "borland", "visualstudio","cpplint","embarcaderoclang"]
parsers = [Gcc.GccLineParser, Borland.BorlandLineParser, VisualStudio.VisualStudioLineParser,
Cpplint.CpplintLineParser, EmbarcaderoClang.EmbarcaderoClangLineParser]
all_warnings = [Gcc.all_warnings, Borland.all_warnings, VisualStudio.all_warnings,
Cpplint.all_warnings, EmbarcaderoClang.all_warnings]
totalwarninglines = 0
discoveredwarnings = None
warninghashes = None
flavor = None
#create the File parser, given the 'flavor' of warnings expected
#this is basically a line parser factory pattern.
def __init__(self, flavor):
if flavor in self.supportedflavors:
self.flavor = flavor
self.discoveredwarnings = set()
self.warninghashes = list()
else:
raise ValueError
#read a logfile given by filename
#make discovered paths relative to the folder given by the 'relativeto' argument
def readFile(self, filename, relativeto = None):
if (self.flavor) == None:
raise ValueError
lineparser = dict(zip(self.supportedflavors, self.parsers))
#determine file encoding
detector = UniversalDetector()
with open(filename, 'rb') as f:
for line in f:
detector.feed(line)
if detector.done:
break
detector.close()
#open logfile and start looking for warnings
with open(filename, 'r', encoding=detector.result['encoding']) as f:
for line in f:
parser = lineparser[self.flavor]() #here is where the line parser is called based on flavor
parser.setLine(line)
parser.parseLine()
w = parser.getWarningObject()
if w != None:
if relativeto != None:
w.fullpath = util.getpathfrom(w.fullpath, relativeto)
self.totalwarninglines += 1
self.discoveredwarnings.add(w)
# Open each file once for all the warnings that come from the file.
for file in self.getUniqueFiles():
self.getWarningLines(file)
#update all warnings from one particular file in case of error
def updateWarningsInFile(self, filename:Path, message):
warnings_in_this_file = self.getWarningsByFullpath(filename)
for warn in warnings_in_this_file:
warn.warningline = message
warn.fileopened = False
#get lines of code from the files that had warnings
def getWarningLines(self, filename):
warn = None
f = None
try:
f = open(Path(filename),'r', encoding='utf-8', errors='ignore')
except FileNotFoundError:
self.updateWarningsInFile(Path(filename), "warning_scraper error: could not open file")
return
#read file, should not be decoding errors since we are ignoring them in the open() call
lines = f.readlines()
f.close()
#get lines of code from file
warnings_in_this_file = self.getWarningsByFullpath(filename)
for warn in warnings_in_this_file:
try:
warn.warningline = lines[warn.linenumber - 1].strip() #the line of code, with leading and trailing whitespace stripped
warn.fileopened = True
#this means we could not get the line number we wanted
except IndexError:
if warn != None:
warn.warningline = "warning_scraper error: line not found in file"
warn.fileopened = False
#get a list of unique warning IDs. With this list plus getWarningsByWarningId() you can get a list of warnings by ID
def getUniqueWarningIds(self):
warningids = set()
for warning in self.discoveredwarnings:
warningids.add(warning.warningid)
result = list(warningids)
result.sort()
return result
#given all files that were found, show only unique files. With this list plus getWarningsByFullpath() you can get a list of warnings by file
def getUniqueFiles(self):
files = set()
for warning in self.discoveredwarnings:
files.add(warning.fullpath)
result = list(files)
result.sort()
return result
#given the filename only, find all warnings for that file
def getWarningsByFilename(self, filename:Path):
results = list()
for warning in self.discoveredwarnings:
if warning.fullpath.name.lower() == filename.name.lower():
results.append(warning)
results.sort()
return results
#given the full path to a file, find all warnings for that file
def getWarningsByFullpath(self, filename:Path):
results = list()
for warning in self.discoveredwarnings:
try:
if warning.fullpath == filename:
results.append(warning)
except AttributeError:
print("AttributeError --> debugme")
results.sort()
return results
#given a warning ID, get all warnings with that ID
def getWarningsByWarningId(self, warningid):
results = list()
for warning in self.discoveredwarnings:
if warning.warningid == warningid:
results.append(warning)
results.sort()
return results
#given a warning ID, get the official warning description
def getWarningDescriptionFromId(self, warningid):
result = "(no warning description available)"
flavor_to_warningdict = dict(zip(self.supportedflavors, self.all_warnings))
warningdict = flavor_to_warningdict[self.flavor]
if warningid in warningdict.keys():
result = warningdict[warningid].description
return result
def getWarningSeverityFromId(self, warningid):
result = "(no warning description available)"
flavor_to_warningdict = dict(zip(self.supportedflavors, self.all_warnings))
warningdict = flavor_to_warningdict[self.flavor]
if warningid in warningdict.keys():
result = warningdict[warningid].severity.name
return result
def removeExcludedWarnings(self, excludedFile = None):
if excludedFile == None:
return
else:
with open(Path(excludedFile), 'r') as f:
warningstoremove = set()
lines = f.read().splitlines()
for warning in self.discoveredwarnings:
for line in lines:
if fnmatch.fnmatch(warning.fullpath, line):
warningstoremove.add(warning)
for warning in warningstoremove:
self.discoveredwarnings.remove(warning)
return