3838import concurrent .futures
3939import multiprocessing
4040
41+ from pip ._internal import main as pip_main
42+ from pip ._internal .commands .show import search_packages_info
43+
4144from walkoff_app_sdk .app_base import AppBase
4245
4346class Tools (AppBase ):
@@ -54,8 +57,58 @@ def __init__(self, redis, logger, console_logger=None):
5457 :param console_logger:
5558 """
5659 super ().__init__ (redis , logger , console_logger )
60+
61+ def get_missing_packages (required_packages : list ) -> list :
62+ """
63+ Returns a list of packages that aren't currently installed.
64+
65+ Args:
66+ required_packages: List of package names (can include version specs)
67+
68+ Returns:
69+ List of package names that aren't installed
70+ """
71+ missing = []
72+ for package in required_packages :
73+ # Remove version specifiers if present (e.g., 'pandas>=1.0.0' -> 'pandas')
74+ package_name = package .split ('==' )[0 ].split ('>=' )[0 ].split ('<=' )[0 ].split ('>' )[0 ].split ('<' )[0 ].strip ()
75+
76+ # Check if package exists in environment
77+ if not list (search_packages_info ([package_name ])):
78+ missing .append (package )
79+
80+ return missing
81+
82+ def install_packages (self , packages = []) -> None :
83+ """
84+ Install Python packages using pip's Python interface.
85+
86+ Args:
87+ packages: List of package names to install
88+ """
89+
90+ packages_not_found = self .get_missing_packages (packages )
91+
92+ for package in packages_not_found :
93+ try :
94+ pip_main (['install' , package ])
95+ print (f"Successfully installed { package } " )
96+ except Exception as e :
97+ print (f"Failed to install { package } : { str (e )} " )
5798
58- def execute_python (self , code ):
99+ def execute_python (self , code , packages = []) -> dict :
100+ if os .getenv ("ALLOW_PACKAGE_INSTALL" ) == "true" :
101+ allow_package_install = True
102+
103+ if packages :
104+ if allow_package_install :
105+ self .install_packages (packages )
106+ else :
107+ return {
108+ "success" : False ,
109+ "message" : "Package installation is disabled in this environment" ,
110+ }
111+
59112 if len (code ) == 36 and "-" in code :
60113 filedata = self .get_file (code )
61114 if filedata ["success" ] == False :
0 commit comments