44from types import SimpleNamespace
55from typing import Any
66
7- from cmem_plugin_base .dataintegration .context import ExecutionContext
7+ from cmem .cmempy .workspace .python import list_packages
8+ from cmem_plugin_base .dataintegration .context import ExecutionContext , PluginContext
89from cmem_plugin_base .dataintegration .description import Icon , Plugin , PluginAction , PluginParameter
910from cmem_plugin_base .dataintegration .entity import Entities
1011from cmem_plugin_base .dataintegration .parameter .code import PythonCode
1112from cmem_plugin_base .dataintegration .plugins import WorkflowPlugin
12- from pkg_resources import working_set
13+ from cmem_plugin_base .dataintegration .utils import setup_cmempy_user_access
14+
15+ from cmem_plugin_python .package_management import install_missing_packages
1316
1417docu_links = SimpleNamespace ()
1518docu_links .entities = (
202205 label = "List Packages" ,
203206 description = "Show installed python packages with version." ,
204207 ),
208+ PluginAction (
209+ name = "install_missing_packages_action" ,
210+ label = "Install missing dependencies" ,
211+ description = "Install missing dependency packages." ,
212+ ),
205213 ],
206214 parameters = [
207215 PluginParameter (
214222 label = "Python source code for the execution phase" ,
215223 default_value = "" ,
216224 ),
225+ PluginParameter (
226+ name = "dependencies" ,
227+ label = "Dependencies" ,
228+ description = "Comma-separated list of package names, e.g. 'pandas'." ,
229+ default_value = "" ,
230+ ),
217231 ],
218232)
219233class PythonCodeWorkflowPlugin (WorkflowPlugin ):
@@ -222,36 +236,26 @@ class PythonCodeWorkflowPlugin(WorkflowPlugin):
222236 init_code : str
223237 execute_code : str
224238 data : dict
239+ dependencies : list [str ]
225240
226- def do_init (self ) -> dict [str , Any ]:
227- """Run initialization phase"""
228- scope : dict [str , Any ] = {"data" : {}}
229- exec (str (self .init_code ), scope ) # nosec # noqa: S102
230- return scope
231-
232- def do_execute (
233- self , inputs : Sequence [Entities ], context : ExecutionContext | None , data : dict
234- ) -> dict [str , Any ]:
235- """Run execution phase"""
236- scope : dict [str , Any ] = {"inputs" : inputs , "context" : context , "data" : data }
237- exec (str (self .execute_code ), scope ) # nosec # noqa: S102
238- return scope
239-
240- def __init__ (self , init_code : PythonCode , execute_code : PythonCode ):
241+ def __init__ (self , init_code : PythonCode , execute_code : PythonCode , dependencies : str = "" ):
241242 self .init_code = str (init_code )
242243 self .execute_code = str (execute_code )
244+ self .dependencies = (
245+ [] if dependencies .strip () == "" else [_ .strip () for _ in dependencies .split ("," )]
246+ )
243247 scope = self .do_init ()
244248 if "input_ports" in scope :
245249 self .input_ports = scope ["input_ports" ]
246250 if "output_port" in scope :
247251 self .output_port = scope ["output_port" ]
248252 self .data = scope .get ("data" , {})
249253
250- def execute (self , inputs : Sequence [ Entities ], context : ExecutionContext ) -> Entities | None :
251- """Start the plugin in workflow context. """
252- self . log . info ( "Start doing bad things with custom code." )
253- scope = self .do_execute ( inputs , context , self . data )
254- return scope . get ( "result" )
254+ def do_init (self ) -> dict [ str , Any ] :
255+ """Run initialization phase """
256+ scope : dict [ str , Any ] = { "data" : {}}
257+ exec ( str ( self .init_code ), scope ) # nosec # noqa: S102
258+ return scope
255259
256260 def validate_init_action (self ) -> str :
257261 """Run the init code and report results."""
@@ -284,6 +288,14 @@ def validate_init_action(self) -> str:
284288 output += "No data provided.\n "
285289 return output
286290
291+ def do_execute (
292+ self , inputs : Sequence [Entities ], context : ExecutionContext | None , data : dict
293+ ) -> dict [str , Any ]:
294+ """Run execution phase"""
295+ scope : dict [str , Any ] = {"inputs" : inputs , "context" : context , "data" : data }
296+ exec (str (self .execute_code ), scope ) # nosec # noqa: S102
297+ return scope
298+
287299 def validate_execute_action (self ) -> str :
288300 """Run the execute code and report results."""
289301 init_scope = self .do_init ()
@@ -304,7 +316,28 @@ def validate_execute_action(self) -> str:
304316 output = "No result provided.\n "
305317 return output
306318
307- def list_packages_action (self ) -> str :
319+ def list_packages_action (self , context : PluginContext ) -> str :
308320 """List Packages action"""
309- packages = sorted ([f"- { i .key } ({ i .version } )" for i in working_set ])
310- return "\n " .join (packages )
321+ setup_cmempy_user_access (context = context .user )
322+ packages : list [dict [str , str ]] = list_packages ()
323+ output = [f"- { package ['name' ]} ({ package ['version' ]} )" for package in packages ]
324+ return "\n " .join (output )
325+
326+ def install_missing_packages_action (self , context : PluginContext ) -> str :
327+ """Install Missing Packages action"""
328+ results = install_missing_packages (package_specs = self .dependencies , context = context .user )
329+ output = []
330+ for package , result in results .items ():
331+ output .append (f"# { package } \n \n " )
332+ output .append (f"{ result .output } \n \n " )
333+ if len (output ) == 0 :
334+ output .append ("No packages installed." )
335+ return "\n " .join (output )
336+
337+ def execute (self , inputs : Sequence [Entities ], context : ExecutionContext ) -> Entities | None :
338+ """Start the plugin in workflow context."""
339+ self .log .info ("Start doing bad things with custom code." )
340+ if self .dependencies :
341+ install_missing_packages (package_specs = self .dependencies , context = context .user )
342+ scope = self .do_execute (inputs , context , self .data )
343+ return scope .get ("result" )
0 commit comments