Source code for langchain_experimental.utilities.python
importfunctoolsimportloggingimportmultiprocessingimportreimportsysfromioimportStringIOfromtypingimportDict,OptionalfrompydanticimportBaseModel,Fieldlogger=logging.getLogger(__name__)@functools.lru_cache(maxsize=None)defwarn_once()->None:"""Warn once about the dangers of PythonREPL."""logger.warning("Python REPL can execute arbitrary code. Use with caution.")
[docs]classPythonREPL(BaseModel):"""Simulates a standalone Python REPL."""globals:Optional[Dict]=Field(default_factory=dict,alias="_globals")# type: ignore[arg-type]locals:Optional[Dict]=Field(default_factory=dict,alias="_locals")# type: ignore[arg-type]
[docs]@staticmethoddefsanitize_input(query:str)->str:"""Sanitize input to the python REPL. Remove whitespace, backtick & python (if llm mistakes python console as terminal) Args: query: The query to sanitize Returns: str: The sanitized query """query=re.sub(r"^(\s|`)*(?i:python)?\s*","",query)query=re.sub(r"(\s|`)*$","",query)returnquery
[docs]defrun(self,command:str,timeout:Optional[int]=None)->str:"""Run command with own globals/locals and returns anything printed. Timeout after the specified number of seconds."""# Warn against dangers of PythonREPLwarn_once()queue:multiprocessing.Queue=multiprocessing.Queue()# Only use multiprocessing if we are enforcing a timeoutiftimeoutisnotNone:# create a Processp=multiprocessing.Process(target=self.worker,args=(command,self.globals,self.locals,queue))# start itp.start()# wait for the process to finish or kill it after timeout secondsp.join(timeout)ifp.is_alive():p.terminate()return"Execution timed out"else:self.worker(command,self.globals,self.locals,queue)# get the result from the worker functionreturnqueue.get()