Package buildbot :: Package steps :: Module python
[frames] | no frames]

Source Code for Module buildbot.steps.python

  1  # This file is part of Buildbot.  Buildbot is free software: you can 
  2  # redistribute it and/or modify it under the terms of the GNU General Public 
  3  # License as published by the Free Software Foundation, version 2. 
  4  # 
  5  # This program is distributed in the hope that it will be useful, but WITHOUT 
  6  # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 
  7  # FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
  8  # details. 
  9  # 
 10  # You should have received a copy of the GNU General Public License along with 
 11  # this program; if not, write to the Free Software Foundation, Inc., 51 
 12  # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 13  # 
 14  # Copyright Buildbot Team Members 
 15   
 16   
 17  import re 
 18  from buildbot.status.results import SUCCESS, FAILURE, WARNINGS 
 19  from buildbot.steps.shell import ShellCommand 
 20  from buildbot import config 
 21   
 22  try: 
 23      import cStringIO 
 24      StringIO = cStringIO.StringIO 
 25  except ImportError: 
 26      from StringIO import StringIO 
 27   
 28   
29 -class BuildEPYDoc(ShellCommand):
30 name = "epydoc" 31 command = ["make", "epydocs"] 32 description = ["building", "epydocs"] 33 descriptionDone = ["epydoc"] 34
35 - def createSummary(self, log):
36 import_errors = 0 37 warnings = 0 38 errors = 0 39 40 for line in StringIO(log.getText()): 41 if line.startswith("Error importing "): 42 import_errors += 1 43 if line.find("Warning: ") != -1: 44 warnings += 1 45 if line.find("Error: ") != -1: 46 errors += 1 47 48 self.descriptionDone = self.descriptionDone[:] 49 if import_errors: 50 self.descriptionDone.append("ierr=%d" % import_errors) 51 if warnings: 52 self.descriptionDone.append("warn=%d" % warnings) 53 if errors: 54 self.descriptionDone.append("err=%d" % errors) 55 56 self.import_errors = import_errors 57 self.warnings = warnings 58 self.errors = errors
59
60 - def evaluateCommand(self, cmd):
61 if cmd.didFail(): 62 return FAILURE 63 if self.warnings or self.errors: 64 return WARNINGS 65 return SUCCESS
66 67
68 -class PyFlakes(ShellCommand):
69 name = "pyflakes" 70 command = ["make", "pyflakes"] 71 description = ["running", "pyflakes"] 72 descriptionDone = ["pyflakes"] 73 flunkOnFailure = False 74 flunkingIssues = ["undefined"] # any pyflakes lines like this cause FAILURE 75 76 MESSAGES = ("unused", "undefined", "redefs", "import*", "misc") 77
78 - def createSummary(self, log):
79 counts = {} 80 summaries = {} 81 for m in self.MESSAGES: 82 counts[m] = 0 83 summaries[m] = [] 84 85 first = True 86 for line in StringIO(log.getText()).readlines(): 87 # the first few lines might contain echoed commands from a 'make 88 # pyflakes' step, so don't count these as warnings. Stop ignoring 89 # the initial lines as soon as we see one with a colon. 90 if first: 91 if line.find(":") != -1: 92 # there's the colon, this is the first real line 93 first = False 94 # fall through and parse the line 95 else: 96 # skip this line, keep skipping non-colon lines 97 continue 98 if line.find("imported but unused") != -1: 99 m = "unused" 100 elif line.find("*' used; unable to detect undefined names") != -1: 101 m = "import*" 102 elif line.find("undefined name") != -1: 103 m = "undefined" 104 elif line.find("redefinition of unused") != -1: 105 m = "redefs" 106 else: 107 m = "misc" 108 summaries[m].append(line) 109 counts[m] += 1 110 111 self.descriptionDone = self.descriptionDone[:] 112 for m in self.MESSAGES: 113 if counts[m]: 114 self.descriptionDone.append("%s=%d" % (m, counts[m])) 115 self.addCompleteLog(m, "".join(summaries[m])) 116 self.setProperty("pyflakes-%s" % m, counts[m], "pyflakes") 117 self.setProperty("pyflakes-total", sum(counts.values()), "pyflakes")
118 119
120 - def evaluateCommand(self, cmd):
121 if cmd.didFail(): 122 return FAILURE 123 for m in self.flunkingIssues: 124 if self.getProperty("pyflakes-%s" % m): 125 return FAILURE 126 if self.getProperty("pyflakes-total"): 127 return WARNINGS 128 return SUCCESS
129
130 -class PyLint(ShellCommand):
131 '''A command that knows about pylint output. 132 It is a good idea to add --output-format=parseable to your 133 command, since it includes the filename in the message. 134 ''' 135 name = "pylint" 136 description = ["running", "pylint"] 137 descriptionDone = ["pylint"] 138 139 # pylint's return codes (see pylint(1) for details) 140 # 1 - 16 will be bit-ORed 141 142 RC_OK = 0 143 RC_FATAL = 1 144 RC_ERROR = 2 145 RC_WARNING = 4 146 RC_REFACTOR = 8 147 RC_CONVENTION = 16 148 RC_USAGE = 32 149 150 # Using the default text output, the message format is : 151 # MESSAGE_TYPE: LINE_NUM:[OBJECT:] MESSAGE 152 # with --output-format=parseable it is: (the outer brackets are literal) 153 # FILE_NAME:LINE_NUM: [MESSAGE_TYPE[, OBJECT]] MESSAGE 154 # message type consists of the type char and 4 digits 155 # The message types: 156 157 MESSAGES = { 158 'C': "convention", # for programming standard violation 159 'R': "refactor", # for bad code smell 160 'W': "warning", # for python specific problems 161 'E': "error", # for much probably bugs in the code 162 'F': "fatal", # error prevented pylint from further processing. 163 'I': "info", 164 } 165 166 flunkingIssues = ["F", "E"] # msg categories that cause FAILURE 167 168 _re_groupname = 'errtype' 169 _msgtypes_re_str = '(?P<%s>[%s])' % (_re_groupname, ''.join(MESSAGES.keys())) 170 _default_line_re = re.compile(r'^%s(\d{4})?: *\d+(,\d+)?:.+' % _msgtypes_re_str) 171 _parseable_line_re = re.compile(r'[^:]+:\d+: \[%s(\d{4})?[,\]] .+' % _msgtypes_re_str) 172
173 - def createSummary(self, log):
174 counts = {} 175 summaries = {} 176 for m in self.MESSAGES: 177 counts[m] = 0 178 summaries[m] = [] 179 180 line_re = None # decide after first match 181 for line in StringIO(log.getText()).readlines(): 182 if not line_re: 183 # need to test both and then decide on one 184 if self._parseable_line_re.match(line): 185 line_re = self._parseable_line_re 186 elif self._default_line_re.match(line): 187 line_re = self._default_line_re 188 else: # no match yet 189 continue 190 mo = line_re.match(line) 191 if mo: 192 msgtype = mo.group(self._re_groupname) 193 assert msgtype in self.MESSAGES 194 summaries[msgtype].append(line) 195 counts[msgtype] += 1 196 197 self.descriptionDone = self.descriptionDone[:] 198 for msg, fullmsg in self.MESSAGES.items(): 199 if counts[msg]: 200 self.descriptionDone.append("%s=%d" % (fullmsg, counts[msg])) 201 self.addCompleteLog(fullmsg, "".join(summaries[msg])) 202 self.setProperty("pylint-%s" % fullmsg, counts[msg]) 203 self.setProperty("pylint-total", sum(counts.values()))
204
205 - def evaluateCommand(self, cmd):
206 if cmd.rc & (self.RC_FATAL|self.RC_ERROR|self.RC_USAGE): 207 return FAILURE 208 for msg in self.flunkingIssues: 209 if self.getProperty("pylint-%s" % self.MESSAGES[msg]): 210 return FAILURE 211 if self.getProperty("pylint-total"): 212 return WARNINGS 213 return SUCCESS
214
215 -class Sphinx(ShellCommand):
216 ''' A Step to build sphinx documentation ''' 217 218 name = "sphinx" 219 description = ["running", "sphinx"] 220 descriptionDone = ["sphinx"] 221 222 haltOnFailure = True 223
224 - def __init__(self, sphinx_sourcedir='.', sphinx_builddir=None, 225 sphinx_builder=None, sphinx = 'sphinx-build', tags = [], 226 defines = {}, mode='incremental', **kwargs):
227 228 if sphinx_builddir is None: 229 # Who the heck is not interested in the built doc ? 230 config.error("Sphinx argument sphinx_builddir is required") 231 232 if mode not in ('incremental', 'full'): 233 config.error("Sphinx argument mode has to be 'incremental' or" + 234 "'full' is required") 235 236 self.warnings = 0 237 self.success = False 238 ShellCommand.__init__(self, **kwargs) 239 240 # build the command 241 command = [sphinx] 242 if sphinx_builder is not None: 243 command.extend(['-b', sphinx_builder]) 244 245 for tag in tags: 246 command.extend(['-t', tag]) 247 248 for key in sorted(defines): 249 if defines[key] is None: 250 command.extend(['-D', key]) 251 elif isinstance(defines[key], bool): 252 command.extend(['-D', 253 '%s=%d' % (key, defines[key] and 1 or 0)]) 254 else: 255 command.extend(['-D', '%s=%s' % (key, defines[key])]) 256 257 if mode == 'full': 258 command.extend(['-E']) # Don't use a saved environment 259 260 command.extend([sphinx_sourcedir, sphinx_builddir]) 261 self.setCommand(command)
262
263 - def createSummary(self, log):
264 265 msgs = ['WARNING', 'ERROR', 'SEVERE'] 266 267 warnings = [] 268 for line in log.getText().split('\n'): 269 if (line.startswith('build succeeded') 270 or line.startswith('no targets are out of date.')): 271 self.success = True 272 else: 273 for msg in msgs: 274 if msg in line: 275 warnings.append(line) 276 self.warnings += 1 277 if self.warnings > 0: 278 self.addCompleteLog('warnings', "\n".join(warnings)) 279 280 self.step_status.setStatistic('warnings', self.warnings)
281
282 - def evaluateCommand(self, cmd):
283 if self.success: 284 if self.warnings == 0: 285 return SUCCESS 286 else: 287 return WARNINGS 288 else: 289 return FAILURE
290
291 - def describe(self, done=False):
292 if not done: 293 return ["building"] 294 295 description = [self.name] 296 description.append('%d warnings' % self.warnings) 297 return description
298