99"""
1010
1111import asyncio
12+ import hashlib
1213import importlib .metadata
1314import json
1415import logging
2829logger = logging .getLogger (__name__ )
2930
3031
32+ class TaskProcessor :
33+ """
34+ A Caching task processor that only reloads the plugin if the config has changed.
35+ """
36+
37+ config_hash : str
38+ module_path_hash : str
39+ hook_ref : HookRef | None
40+ executor : PluginExecutor | None
41+
42+ def __init__ (self ) -> None :
43+ """Initialize defaults."""
44+ hasher = hashlib .sha256 ()
45+ hasher .update (b"" )
46+ self .config_hash = hasher .hexdigest ()
47+ self .module_path_hash = self .config_hash
48+ self .hook_ref = None
49+ self .executor = None
50+
51+ def compute_hash (self , json_config_or_module_path : str ):
52+ """Compute the hash of the supplied string"""
53+ hasher = hashlib .sha256 ()
54+ hasher .update (json_config_or_module_path .encode ())
55+ return hasher .hexdigest ()
56+
57+ def initialize (self , hook_ref : HookRef , executor : PluginExecutor , json_config : str , module_path : str ):
58+ """Assign locals, and compute hashes."""
59+ self .hook_ref = hook_ref
60+ self .executor = executor
61+ self .config_hash = self .compute_hash (json_config_or_module_path = json_config )
62+ self .module_path_hash = self .compute_hash (json_config_or_module_path = module_path )
63+
64+
3165def get_environment_info ():
3266 """Get information about current Python environment."""
3367 return {
@@ -55,7 +89,7 @@ def get_proper_config(name, module_path):
5589 return None
5690
5791
58- async def process_task (task_data ):
92+ async def process_task (task_data , tp : TaskProcessor ):
5993 """Process the task received from parent."""
6094 task_type = task_data .get ("task_type" )
6195
@@ -71,28 +105,33 @@ async def process_task(task_data):
71105 json_config = task_data .get ("config" )
72106 config_raw = json .loads (json_config )
73107 module_path : str = task_data .get ("script_path" )
74- sys .path .append (str (Path (module_path ).resolve ()))
75- config = get_proper_config (config_raw .get ("name" ), module_path )
76- hook_type = task_data .get (HOOK_TYPE )
77- cls_name : str = task_data .get ("class_name" )
78- mod_name , n_cls_name = parse_class_name (cls_name )
79- module : ModuleType = importlib .import_module (mod_name )
80- # cool, we found the module, and verified it implemented the hook type.
81- class_ = getattr (module , n_cls_name )
82- plugin_type = cast (Type [Plugin ], class_ )
83- plugin = plugin_type (config )
84- await plugin .initialize ()
85- # now invoke the hook
86- plugin_ref = PluginRef (plugin )
87- hook_ref = HookRef (hook_type , plugin_ref )
88- executor = PluginExecutor (None , 30 )
108+ if tp .module_path_hash != tp .compute_hash (module_path ) or tp .config_hash != tp .compute_hash (json_config ):
109+ sys .path .append (str (Path (module_path ).resolve ()))
110+ config = get_proper_config (config_raw .get ("name" ), module_path )
111+ hook_type = task_data .get (HOOK_TYPE )
112+ cls_name : str = task_data .get ("class_name" )
113+ mod_name , n_cls_name = parse_class_name (cls_name )
114+ module : ModuleType = importlib .import_module (mod_name )
115+ # cool, we found the module, and verified it implemented the hook type.
116+ class_ = getattr (module , n_cls_name )
117+ plugin_type = cast (Type [Plugin ], class_ )
118+ plugin = plugin_type (config )
119+ await plugin .initialize ()
120+ # now invoke the hook
121+ plugin_ref = PluginRef (plugin )
122+ hook_ref = HookRef (hook_type , plugin_ref )
123+ executor = PluginExecutor (None , 30 )
124+ tp .initialize (hook_ref = hook_ref , executor = executor , json_config = json_config , module_path = module_path )
89125 # retrieve the context
90126 context = task_data .get ("context" )
91127 plugin_context = PluginContext (
92128 state = context .get ("state" ), global_context = context .get ("global_context" ), metadata = context .get ("metadata" )
93129 )
94- result = await executor .execute_plugin (
95- hook_ref , payload = task_data .get ("payload" ), local_context = plugin_context , violations_as_exceptions = False
130+ result = await tp .executor .execute_plugin (
131+ hookref = tp .hook_ref ,
132+ payload = task_data .get ("payload" ),
133+ local_context = plugin_context ,
134+ violations_as_exceptions = False ,
96135 )
97136 return result
98137
@@ -102,6 +141,8 @@ async def main():
102141 logger .info ("Worker process started, waiting for tasks..." )
103142
104143 try :
144+ # Cache the plugin so that it only has to be initialized once
145+ tp = TaskProcessor ()
105146 # Continuously read and process tasks
106147 while True :
107148 try :
@@ -125,7 +166,7 @@ async def main():
125166 break
126167
127168 # Process the task
128- response = await process_task (task_data )
169+ response = await process_task (task_data , tp )
129170
130171 # Serialize response
131172 if response :
0 commit comments