1#!/usr/bin/env python 2""" 3 4 exceptions.py 5 6""" 7 8################################################################################ 9# 10# exceptions.py 11# 12# 13# Copyright (c) 10/9/2009 Leo Goodstadt 14# 15# Permission is hereby granted, free of charge, to any person obtaining a copy 16# of this software and associated documentation files (the "Software"), to deal 17# in the Software without restriction, including without limitation the rights 18# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19# copies of the Software, and to permit persons to whom the Software is 20# furnished to do so, subject to the following conditions: 21# 22# The above copyright notice and this permission notice shall be included in 23# all copies or substantial portions of the Software. 24# 25# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31# THE SOFTWARE. 32################################################################################# 33 34import sys 35import os 36from collections import defaultdict 37 38 39# 88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 40 41# Exceptions 42 43 44# 88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 45 46# if __name__ != '__main__': 47# import task 48 49class error_task(Exception): 50 def __init__(self, *errmsg): 51 Exception.__init__(self, *errmsg) 52 53 # list of associated tasks 54 self.tasks = set() 55 56 # error message 57 self.main_msg = "" 58 59 def get_main_msg(self): 60 """ 61 Make main message with lists of task names 62 Prefix with new lines for added emphasis 63 """ 64 # turn tasks names into 'def xxx(...): format 65 task_names = "\n".join("task = %r" % t._name for t in self.tasks) 66 if len(self.main_msg): 67 return "\n\n" + self.main_msg + " for\n\n%s\n" % task_names 68 else: 69 return "\n\n%s\n" % task_names 70 71 def __str__(self): 72 # indent 73 msg = self.get_main_msg() + " ".join(map(str, self.args)) 74 return " " + msg.replace("\n", "\n ") 75 76 def specify_task(self, task, main_msg): 77 self.tasks.add(task) 78 self.main_msg = main_msg 79 return self 80 81 82class error_task_contruction(error_task): 83 """ 84 Exceptions when contructing pipeline tasks 85 """ 86 87 def __init__(self, task, main_msg, *errmsg): 88 error_task.__init__(self, *errmsg) 89 self.specify_task(task, main_msg) 90 91 92class RethrownJobError(error_task): 93 """ 94 Wrap up one or more exceptions rethrown across process boundaries 95 96 See multiprocessor.Server.handle_request/serve_client for an analogous function 97 """ 98 99 def __init__(self, job_exceptions=[]): 100 error_task.__init__(self) 101 self.job_exceptions = list(job_exceptions) 102 103 def __len__(self): 104 return len(self.job_exceptions) 105 106 def append(self, job_exception): 107 self.job_exceptions.append(job_exception) 108 109 def task_to_func_name(self, task_name): 110 if "mkdir " in task_name: 111 return task_name 112 113 return "def %s(...):" % task_name.replace("__main__.", "") 114 115 def get_nth_exception_str(self, nn=-1): 116 if nn == -1: 117 nn = len(self.job_exceptions) - 1 118 task_name, job_name, exception_name, exception_value, exception_stack = self.job_exceptions[ 119 nn] 120 message = "\nException #%d\n" % (nn + 1) 121 message += " '%s%s' raised in ...\n" % (exception_name, exception_value) 122 if task_name: 123 message += " Task = %s\n %s\n\n" % (self.task_to_func_name(task_name), 124 job_name) 125 message += "%s\n" % (exception_stack, ) 126 return message.replace("\n", "\n ") 127 128 def __str__(self): 129 message = ["\nOriginal exception%s:\n" % 130 ("s" if len(self.job_exceptions) > 1 else "")] 131 for ii in range(len(self.job_exceptions)): 132 message += self.get_nth_exception_str(ii) 133 # 134 # For each exception: 135 # turn original exception stack message into an indented string 136 # 137 return (self.get_main_msg()).replace("\n", "\n ") + "".join(message) 138 139 140class error_input_file_does_not_match(error_task): 141 pass 142 143 144class fatal_error_input_file_does_not_match(error_task): 145 pass 146 147 148class task_FilesArgumentsError(error_task): 149 pass 150 151 152class task_FilesreArgumentsError(error_task): 153 pass 154 155 156class MissingInputFileError(error_task): 157 pass 158 159 160class JobSignalledBreak(error_task): 161 pass 162 163 164class JobSignalledSuspend(error_task): 165 pass 166 167 168class JobSignalledResume(error_task): 169 pass 170 171 172class JobFailed(error_task): 173 pass 174 175 176class PostTaskArgumentError(error_task): 177 pass 178 179 180class JobsLimitArgumentError(error_task): 181 pass 182 183 184class error_task_get_output(error_task_contruction): 185 pass 186 187 188class error_task_transform_inputs_multiple_args(error_task_contruction): 189 pass 190 191 192class error_task_transform(error_task_contruction): 193 pass 194 195 196class error_task_product(error_task_contruction): 197 pass 198 199 200class error_task_mkdir(error_task_contruction): 201 pass 202 203 204class error_task_permutations(error_task_contruction): 205 pass 206 207 208class error_task_combinations(error_task_contruction): 209 pass 210 211 212class error_task_combinations_with_replacement(error_task_contruction): 213 pass 214 215 216class error_task_merge(error_task_contruction): 217 pass 218 219 220class error_task_subdivide(error_task_contruction): 221 pass 222 223 224class error_task_originate(error_task_contruction): 225 pass 226 227 228class error_task_collate(error_task_contruction): 229 pass 230 231 232class error_task_collate_inputs_multiple_args(error_task_contruction): 233 pass 234 235 236class error_task_split(error_task_contruction): 237 pass 238 239 240class error_task_files_re(error_task_contruction): 241 pass 242 243 244class error_task_files(error_task_contruction): 245 pass 246 247 248class error_task_parallel(error_task_contruction): 249 pass 250 251 252class error_making_directory(error_task): 253 pass 254 255 256class error_duplicate_task_name(error_task): 257 pass 258 259 260class error_decorator_args(error_task): 261 pass 262 263 264class error_task_name_lookup_failed(error_task): 265 pass 266 267 268class error_task_decorator_takes_no_args(error_task): 269 pass 270 271 272class error_function_is_not_a_task(error_task): 273 pass 274 275 276class error_ambiguous_task(error_task): 277 pass 278 279 280class error_not_a_pipeline(error_task): 281 pass 282 283 284class error_circular_dependencies(error_task): 285 pass 286 287 288class error_not_a_directory(error_task): 289 pass 290 291 292class error_missing_output(error_task): 293 pass 294 295 296class error_job_signalled_interrupt(error_task): 297 pass 298 299 300class error_node_not_task(error_task): 301 pass 302 303 304class error_missing_runtime_parameter(error_task): 305 pass 306 307 308class error_unescaped_regular_expression_forms(error_task): 309 pass 310 311 312class error_checksum_level(error_task): 313 pass 314 315 316class error_missing_args(error_task): 317 pass 318 319 320class error_too_many_args(error_task): 321 pass 322 323 324class error_inputs_multiple_args(error_task): 325 pass 326 327 328class error_set_input(error_task): 329 pass 330 331 332class error_set_output(error_task): 333 pass 334 335 336class error_no_head_tasks(error_task): 337 pass 338 339 340class error_no_tail_tasks(error_task): 341 pass 342 343 344class error_executable_str(error_task): 345 pass 346 347 348class error_extras_wrong_type(error_task): 349 pass 350 351 352# 88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 353 354# Testing 355# 88888888888888888888888888888888888888888888888888888888888888888888888888888888888888888 356if __name__ == '__main__': 357 import unittest 358 359 # 360 # minimal task object to test exceptions 361 # 362 class task: 363 class Task (object): 364 """ 365 dummy task 366 """ 367 _action_mkdir = 1 368 369 def __init__(self, _name, _action_type=0): 370 self._action_type = _action_type 371 self._name = _name 372 373 class Test_exceptions(unittest.TestCase): 374 375 # self.assertEqual(self.seq, range(10)) 376 # self.assert_(element in self.seq) 377 # self.assertRaises(ValueError, random.sample, self.seq, 20) 378 379 def test_error_task(self): 380 """ 381 test 382 """ 383 fake_task1 = task.Task("task1") 384 fake_task2 = task.Task("task2") 385 fake_mkdir_task3 = task.Task("task3", task.Task._action_mkdir) 386 fake_mkdir_task4 = task.Task("task4", task.Task._action_mkdir) 387 e = error_task() 388 e.specify_task(fake_task1, "Some message 0") 389 e.specify_task(fake_task2, "Some message 1") 390 e.specify_task(fake_mkdir_task3, "Some message 2") 391 e.specify_task(fake_mkdir_task4, "Some message 3") 392 self.assertEqual(str(e), 393 """ 394 395 Some message 3 for 396 397 'def task1(...):' 398 'def task2(...):' 399 task3 400 task4 401 """) 402 403 def test_RethrownJobError(self): 404 """ 405 test 406 """ 407 #job_name, exception_name, exception_value, exception_stack 408 exception_data = [ 409 [ 410 "task1", 411 "[[temp_branching_dir/a.2, a.1] -> temp_branching_dir/a.3]", 412 "ruffus.task.MissingInputFileError", 413 "(instance value)", 414 "Traceback (most recent call last):\n File \"what.file.py\", line 333, in some_func\n somecode(sfasf)\n" 415 ], 416 [ 417 "task1", 418 "[None -> [temp_branching_dir/a.1, temp_branching_dir/b.1, temp_branching_dir/c.1]]", 419 "exceptions.ZeroDivisionError:", 420 "(1)", 421 "Traceback (most recent call last):\n File \"anotherfile.py\", line 345, in other_func\n badcode(rotten)\n" 422 ] 423 424 ] 425 e = RethrownJobError(exception_data) 426 fake_task1 = task.Task("task1") 427 fake_task2 = task.Task("task2") 428 fake_mkdir_task3 = task.Task("task3", task.Task._action_mkdir) 429 fake_mkdir_task4 = task.Task("task4", task.Task._action_mkdir) 430 e.specify_task(fake_task1, "Exceptions running jobs") 431 e.specify_task(fake_task2, "Exceptions running jobs") 432 e.specify_task(fake_mkdir_task3, "Exceptions running jobs") 433 e.specify_task(fake_mkdir_task4, "Exceptions running jobs") 434 self.assertEqual(str(e), 435 """ 436 437 Exceptions running jobs for 438 439 'def task1(...):' 440 'def task2(...):' 441 task3 442 task4 443 444 Original exceptions: 445 446 Exception #1 447 ruffus.task.MissingInputFileError(instance value): 448 for task1.[[temp_branching_dir/a.2, a.1] -> temp_branching_dir/a.3] 449 450 Traceback (most recent call last): 451 File "what.file.py", line 333, in some_func 452 somecode(sfasf) 453 454 455 Exception #2 456 exceptions.ZeroDivisionError:(1): 457 for task1.[None -> [temp_branching_dir/a.1, temp_branching_dir/b.1, temp_branching_dir/c.1]] 458 459 Traceback (most recent call last): 460 File "anotherfile.py", line 345, in other_func 461 badcode(rotten) 462 463 """) 464 465 466# 467# debug code not run if called as a module 468# 469if __name__ == '__main__': 470 if sys.argv.count("--debug"): 471 sys.argv.remove("--debug") 472 unittest.main() 473