__init__.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. """
  2. The functions within are designed to load external files and apply functions against the data
  3. The plugins are applied as
  4. - post-processing if we are reading data
  5. - and pre-processing if we are writing data
  6. The plugin will use a decorator to identify meaningful functions
  7. @TODO: This should work in tandem with loggin (otherwise we don't have visibility into what is going on)
  8. """
  9. import importlib as IL
  10. import importlib.util
  11. import sys
  12. import os
  13. import pandas as pd
  14. import time
  15. class Plugin :
  16. """
  17. Implementing function decorator for data-transport plugins (post-pre)-processing
  18. """
  19. def __init__(self,**_args):
  20. """
  21. :name name of the plugin
  22. :mode restrict to reader/writer
  23. :about tell what the function is about
  24. """
  25. self._name = _args['name'] if 'name' in _args else None
  26. self._version = _args['version'] if 'version' in _args else '0.1'
  27. self._doc = _args['doc'] if 'doc' in _args else "N/A"
  28. self._mode = _args['mode'] if 'mode' in _args else 'rw'
  29. def __call__(self,pointer,**kwargs):
  30. def wrapper(_args,**kwargs):
  31. return pointer(_args,**kwargs)
  32. #
  33. # @TODO:
  34. # add attributes to the wrapper object
  35. #
  36. self._name = pointer.__name__ if not self._name else self._name
  37. setattr(wrapper,'transport',True)
  38. setattr(wrapper,'name',self._name)
  39. setattr(wrapper,'version',self._version)
  40. setattr(wrapper,'doc',self._doc)
  41. return wrapper
  42. class PluginLoader :
  43. """
  44. This class is intended to load a plugin and make it available and assess the quality of the developed plugin
  45. """
  46. def __init__(self,**_args):
  47. """
  48. """
  49. # _names = _args['names'] if 'names' in _args else None
  50. # path = _args['path'] if 'path' in _args else None
  51. # self._names = _names if type(_names) == list else [_names]
  52. self._modules = {}
  53. self._names = []
  54. self._registry = _args['registry']
  55. pass
  56. def load (self,**_args):
  57. """
  58. This function loads a plugin
  59. """
  60. self._modules = {}
  61. self._names = []
  62. path = _args ['path']
  63. if os.path.exists(path) :
  64. _alias = path.split(os.sep)[-1]
  65. spec = importlib.util.spec_from_file_location(_alias, path)
  66. module = importlib.util.module_from_spec(spec)
  67. spec.loader.exec_module(module) #--loads it into sys.modules
  68. for _name in dir(module) :
  69. if self.isplugin(module,_name) :
  70. self._module[_name] = getattr(module,_name)
  71. # self._names [_name]
  72. def format (self,**_args):
  73. uri = _args['alias'],_args['name']
  74. # def set(self,_pointer) :
  75. def set(self,_key) :
  76. """
  77. This function will set a pointer to the list of modules to be called
  78. This should be used within the context of using the framework as a library
  79. """
  80. if type(_key).__name__ == 'function':
  81. #
  82. # The pointer is in the code provided by the user and loaded in memory
  83. #
  84. _pointer = _key
  85. _key = 'inline@'+_key.__name__
  86. # self._names.append(_key.__name__)
  87. else:
  88. _pointer = self._registry.get(key=_key)
  89. if _pointer :
  90. self._modules[_key] = _pointer
  91. self._names.append(_key)
  92. def isplugin(self,module,name):
  93. """
  94. This function determines if a module is a recognized plugin
  95. :module module object loaded from importlib
  96. :name name of the functiion of interest
  97. """
  98. p = type(getattr(module,name)).__name__ =='function'
  99. q = hasattr(getattr(module,name),'transport')
  100. #
  101. # @TODO: add a generated key, and more indepth validation
  102. return p and q
  103. def has(self,_name):
  104. """
  105. This will determine if the module name is loaded or not
  106. """
  107. return _name in self._modules
  108. def ratio (self):
  109. """
  110. This functiion determines how many modules loaded vs unloaded given the list of names
  111. """
  112. _n = len(self._names)
  113. return len(set(self._modules.keys()) & set (self._names)) / _n
  114. def apply(self,_data,_logger=[]):
  115. _input= {}
  116. for _name in self._modules :
  117. try:
  118. _input = {'action':'plugin','object':_name,'input':{'status':'PASS'}}
  119. _pointer = self._modules[_name]
  120. if type(_data) == list :
  121. _data = pd.DataFrame(_data)
  122. _brow,_bcol = list(_data.shape)
  123. #
  124. # @TODO: add exception handling
  125. _data = _pointer(_data)
  126. _input['input']['shape'] = {'rows-dropped':_brow - _data.shape[0]}
  127. except Exception as e:
  128. _input['input']['status'] = 'FAILED'
  129. print (e)
  130. time.sleep(1)
  131. if _logger:
  132. try:
  133. _logger(**_input)
  134. except Exception as e:
  135. pass
  136. return _data
  137. # def apply(self,_data,_name):
  138. # """
  139. # This function applies an external module function against the data.
  140. # The responsibility is on the plugin to properly return data, thus responsibility is offloaded
  141. # """
  142. # try:
  143. # _pointer = self._modules[_name]
  144. # _data = _pointer(_data)
  145. # except Exception as e:
  146. # pass
  147. # return _data