jeudi 28 novembre 2019

Automating checkers and error handling with decorator design

I have a collection of functions which helps me asserting and testing requirements on my data_frame. For this group of functions, I want to add a common decorator error_handler, which will allows me, to customize, my error handling and creates decorators for my checkers such that if, i have a checker function with name assert_no_na my decorator would be named as assert_no_naD, like,

# Decorators.py
def assert_no_naD(error='raise'):
  def deco(fobj):
    def fwrapper(*args, **kwargs):
      try:
        return_value = fobj(*args, **kwargs)
      except (e):
        if errors == 'raise':
          raise(e)
      return return_value # wrapper returns func_result
    return fwrapper       # deco returns wrapper

  return deco

# Checkers.py
def assert_no_na(data):
  # assert all entrines in data are not null
  pass

# has_attr(), is_dtype(), all_unique(), ...

As it is obivous, if i repeat the same for all my functions in Checkers.py, i am only duplicating major chunk of my code.

I would like to build something along the lines of,

# GenDecorators.py
import functools as ft
import Checkers as Chk

# Iterate over functions in checkers
for func in dir(Chk):
  if func.startswith("__") and hasattr(func, '__call__'):
     continue # pass inbuilt functions
  # build the decorators...
  Dchk = alldecorator(Chk) 
  # how do i populate Dchk in the `Decorators` namespace with different name.

@ft.wraps(Chkerfunction)
def alldecorators(Chkerfunction):
  def err_handler(errors = 'raise'):
    def ideco(usrfunc):
      @ft.wraps(usrfunc)
      def wrapper(*args, **kwargs):
        res = None # any exception raise, will not assign this, so need a default.
        try:
          fresult = usrfunc(*args, **kwargs)
          res = Chkerfunction(fresult)
        except Exception as e:
          if errors == 'raise':
            raise e
        return fresult, res
      return wrapper
    return ideco

  return err_handler

This make my application look like,

@assert_no_naD(errors='raise')
def collect_inventory_sheet(*args, **kw):
  assert_no_na(args)
  # some collection + cal
  return inventory

I am stuck with the following problems,

  1. Collecting functions from a module. (not sure about the solution in example code)
  2. How to populate the newely created decorator in the modules namespace with different name. eg. Chkerfunction = 'assert_no_na' then Decorator = 'assert_no_naD'

Aucun commentaire:

Enregistrer un commentaire