

def handle(exception, callback):
	def inner(wrapped):
		def f(self, *args, **kwargs):
			try:
				return wrapped(self, *args, **kwargs)
			except exception as exc:
				callback(self)
				self.rollback()
				return 'retry' # enum action result retry value
		return f
	return inner


def print_action(msg, action):
	print(msg.format(action=action.__class__.__name__))


class Action:
	
	def run(self):
		pass
	
	def rollback(self):
		print_action('Rolling back {action}', self)


class A1(Action):
	
	def run(self):
		print_action('Running {action}', self)


class A2(A1):
	
	@handle(
		RuntimeError,
		lambda action: print_action('Handle RuntimeError for action {action}', action)
	)
	def run(self):
		raise RuntimeError()

class A3(A2):
	
	@handle(
		ValueError,
		lambda action: print_action('Handle ValueError for action {action}', action)
	)
	@handle(
		IndexError,
		lambda action: print_action('Handle IndexError for action {action}', action)
	)
	def run(self):
		raise ValueError()


a1 = A1()
a1.run()

a2 = A2()
a2.run()

a3 = A3()
a3.run()

