From 16da4cfa1a96d4114f86eced15cea4b42284252d Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 19:38:29 -0500 Subject: [PATCH 1/6] Change name from LocalCommandlineCodeExecutor to LocalCommandLineCodeExecutor --- autogen/coding/factory.py | 4 +- .../coding/local_commandline_code_executor.py | 74 +++++++++++++++++-- test/coding/test_commandline_code_executor.py | 22 +++--- 3 files changed, 81 insertions(+), 19 deletions(-) diff --git a/autogen/coding/factory.py b/autogen/coding/factory.py index e4ff09c568f..c95d64460b5 100644 --- a/autogen/coding/factory.py +++ b/autogen/coding/factory.py @@ -34,8 +34,8 @@ def create(code_execution_config: Dict[str, Any]) -> CodeExecutor: return EmbeddedIPythonCodeExecutor(**code_execution_config.get("ipython-embedded", {})) elif executor == "commandline-local": - from .local_commandline_code_executor import LocalCommandlineCodeExecutor + from .local_commandline_code_executor import LocalCommandLineCodeExecutor - return LocalCommandlineCodeExecutor(**code_execution_config.get("commandline-local", {})) + return LocalCommandLineCodeExecutor(**code_execution_config.get("commandline-local", {})) else: raise ValueError(f"Unknown code executor {executor}") diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index 0ed4ce1ae82..fcc7d1bebec 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -13,7 +13,8 @@ __all__ = ( - "LocalCommandlineCodeExecutor", + "LocalCommandLineCodeExecutor", + "LocalCommandLineCodeExecutor", "CommandlineCodeResult", ) @@ -27,7 +28,7 @@ class CommandlineCodeResult(CodeResult): ) -class LocalCommandlineCodeExecutor(BaseModel): +class LocalCommandLineCodeExecutor(BaseModel): """(Experimental) A code executor class that executes code through a local command line environment. @@ -51,7 +52,7 @@ class LocalCommandlineCodeExecutor(BaseModel): directory is the current directory ".". system_message_update (str): The system message update for agent that produces code to run on this executor. - Default is `LocalCommandlineCodeExecutor.DEFAULT_SYSTEM_MESSAGE_UPDATE`. + Default is `LocalCommandLineCodeExecutor.DEFAULT_SYSTEM_MESSAGE_UPDATE`. """ DEFAULT_SYSTEM_MESSAGE_UPDATE: ClassVar[ @@ -94,10 +95,10 @@ def _check_work_dir(cls, v: str) -> str: raise ValueError(f"Working directory {v} does not exist.") @property - def user_capability(self) -> "LocalCommandlineCodeExecutor.UserCapability": + def user_capability(self) -> "LocalCommandLineCodeExecutor.UserCapability": """Export a user capability for this executor that can be added to an agent that produces code to be executed by this executor.""" - return LocalCommandlineCodeExecutor.UserCapability(self.system_message_update) + return LocalCommandLineCodeExecutor.UserCapability(self.system_message_update) @property def code_extractor(self) -> CodeExtractor: @@ -138,7 +139,7 @@ def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeRe for i, code_block in enumerate(code_blocks): lang, code = code_block.language, code_block.code - LocalCommandlineCodeExecutor.sanitize_command(lang, code) + LocalCommandLineCodeExecutor.sanitize_command(lang, code) print( colored( @@ -181,3 +182,64 @@ def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeRe def restart(self) -> None: """(Experimental) Restart the code executor.""" warnings.warn("Restarting local command line code executor is not supported. No action is taken.") + +# From stack overflow: https://stackoverflow.com/a/52087847/2214524 +class _DeprecatedClassMeta(type): + def __new__(cls, name, bases, classdict, *args, **kwargs): + alias = classdict.get("_DeprecatedClassMeta__alias") + + if alias is not None: + + def new(cls, *args, **kwargs): + alias = getattr(cls, "_DeprecatedClassMeta__alias") + + if alias is not None: + warnings.warn( + "{} has been renamed to {}, the alias will be " + "removed in the future".format(cls.__name__, alias.__name__), + DeprecationWarning, + stacklevel=2, + ) + + return alias(*args, **kwargs) + + classdict["__new__"] = new + classdict["_DeprecatedClassMeta__alias"] = alias + + fixed_bases = [] + + for b in bases: + alias = getattr(b, "_DeprecatedClassMeta__alias", None) + + if alias is not None: + warnings.warn( + "{} has been renamed to {}, the alias will be " + "removed in the future".format(b.__name__, alias.__name__), + DeprecationWarning, + stacklevel=2, + ) + + # Avoid duplicate base classes. + b = alias or b + if b not in fixed_bases: + fixed_bases.append(b) + + fixed_bases = tuple(fixed_bases) + + return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs) + + def __instancecheck__(cls, instance): + return any( + cls.__subclasscheck__(c) for c in {type(instance), instance.__class__} + ) + + def __subclasscheck__(cls, subclass): + if subclass is cls: + return True + else: + return issubclass(subclass, getattr(cls, "_DeprecatedClassMeta__alias")) + +class LocalCommandLineCodeExecutor(metaclass=_DeprecatedClassMeta): + """LocalCommandLineCodeExecutor renamed to LocalCommandLineCodeExecutor""" + + _DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor diff --git a/test/coding/test_commandline_code_executor.py b/test/coding/test_commandline_code_executor.py index 5a9931890f1..6dec5c574a3 100644 --- a/test/coding/test_commandline_code_executor.py +++ b/test/coding/test_commandline_code_executor.py @@ -4,7 +4,7 @@ from autogen.agentchat.conversable_agent import ConversableAgent from autogen.coding.base import CodeBlock, CodeExecutor from autogen.coding.factory import CodeExecutorFactory -from autogen.coding.local_commandline_code_executor import LocalCommandlineCodeExecutor +from autogen.coding.local_commandline_code_executor import LocalCommandLineCodeExecutor from autogen.oai.openai_utils import config_list_from_json from conftest import MOCK_OPEN_AI_API_KEY, skip_openai @@ -13,25 +13,25 @@ def test_create() -> None: config = {"executor": "commandline-local"} executor = CodeExecutorFactory.create(config) - assert isinstance(executor, LocalCommandlineCodeExecutor) + assert isinstance(executor, LocalCommandLineCodeExecutor) - config = {"executor": LocalCommandlineCodeExecutor()} + config = {"executor": LocalCommandLineCodeExecutor()} executor = CodeExecutorFactory.create(config) assert executor is config["executor"] def test_local_commandline_executor_init() -> None: - executor = LocalCommandlineCodeExecutor(timeout=10, work_dir=".") + executor = LocalCommandLineCodeExecutor(timeout=10, work_dir=".") assert executor.timeout == 10 and executor.work_dir == "." # Try invalid working directory. with pytest.raises(ValueError, match="Working directory .* does not exist."): - executor = LocalCommandlineCodeExecutor(timeout=111, work_dir="/invalid/directory") + executor = LocalCommandLineCodeExecutor(timeout=111, work_dir="/invalid/directory") def test_local_commandline_executor_execute_code() -> None: with tempfile.TemporaryDirectory() as temp_dir: - executor = LocalCommandlineCodeExecutor(work_dir=temp_dir) + executor = LocalCommandLineCodeExecutor(work_dir=temp_dir) _test_execute_code(executor=executor) @@ -81,7 +81,7 @@ def _test_execute_code(executor: CodeExecutor) -> None: @pytest.mark.skipif(sys.platform in ["win32"], reason="do not run on windows") def test_local_commandline_code_executor_timeout() -> None: with tempfile.TemporaryDirectory() as temp_dir: - executor = LocalCommandlineCodeExecutor(timeout=1, work_dir=temp_dir) + executor = LocalCommandLineCodeExecutor(timeout=1, work_dir=temp_dir) _test_timeout(executor) @@ -92,7 +92,7 @@ def _test_timeout(executor: CodeExecutor) -> None: def test_local_commandline_code_executor_restart() -> None: - executor = LocalCommandlineCodeExecutor() + executor = LocalCommandLineCodeExecutor() _test_restart(executor) @@ -105,7 +105,7 @@ def _test_restart(executor: CodeExecutor) -> None: @pytest.mark.skipif(skip_openai, reason="requested to skip openai tests") def test_local_commandline_executor_conversable_agent_capability() -> None: with tempfile.TemporaryDirectory() as temp_dir: - executor = LocalCommandlineCodeExecutor(work_dir=temp_dir) + executor = LocalCommandLineCodeExecutor(work_dir=temp_dir) _test_conversable_agent_capability(executor=executor) @@ -150,7 +150,7 @@ def _test_conversable_agent_capability(executor: CodeExecutor) -> None: def test_local_commandline_executor_conversable_agent_code_execution() -> None: with tempfile.TemporaryDirectory() as temp_dir: - executor = LocalCommandlineCodeExecutor(work_dir=temp_dir) + executor = LocalCommandLineCodeExecutor(work_dir=temp_dir) with pytest.MonkeyPatch.context() as mp: mp.setenv("OPENAI_API_KEY", MOCK_OPEN_AI_API_KEY) _test_conversable_agent_code_execution(executor) @@ -192,7 +192,7 @@ def _test_conversable_agent_code_execution(executor: CodeExecutor) -> None: ) def test_dangerous_commands(lang, code, expected_message): with pytest.raises(ValueError) as exc_info: - LocalCommandlineCodeExecutor.sanitize_command(lang, code) + LocalCommandLineCodeExecutor.sanitize_command(lang, code) assert expected_message in str( exc_info.value ), f"Expected message '{expected_message}' not found in '{str(exc_info.value)}'" From 21adbd4b337e8060fd8a3bce83a1e9e71d4632a4 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 19:39:20 -0500 Subject: [PATCH 2/6] formatting --- autogen/coding/local_commandline_code_executor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index fcc7d1bebec..b2ed9d6b34d 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -183,6 +183,7 @@ def restart(self) -> None: """(Experimental) Restart the code executor.""" warnings.warn("Restarting local command line code executor is not supported. No action is taken.") + # From stack overflow: https://stackoverflow.com/a/52087847/2214524 class _DeprecatedClassMeta(type): def __new__(cls, name, bases, classdict, *args, **kwargs): @@ -229,9 +230,7 @@ def new(cls, *args, **kwargs): return super().__new__(cls, name, fixed_bases, classdict, *args, **kwargs) def __instancecheck__(cls, instance): - return any( - cls.__subclasscheck__(c) for c in {type(instance), instance.__class__} - ) + return any(cls.__subclasscheck__(c) for c in {type(instance), instance.__class__}) def __subclasscheck__(cls, subclass): if subclass is cls: @@ -239,6 +238,7 @@ def __subclasscheck__(cls, subclass): else: return issubclass(subclass, getattr(cls, "_DeprecatedClassMeta__alias")) + class LocalCommandLineCodeExecutor(metaclass=_DeprecatedClassMeta): """LocalCommandLineCodeExecutor renamed to LocalCommandLineCodeExecutor""" From 2d7f2501c53d1ab43a81e4fcb42d706c417f44bc Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 19:42:32 -0500 Subject: [PATCH 3/6] name --- autogen/coding/local_commandline_code_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index b2ed9d6b34d..0ddd67d150d 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -239,7 +239,7 @@ def __subclasscheck__(cls, subclass): return issubclass(subclass, getattr(cls, "_DeprecatedClassMeta__alias")) -class LocalCommandLineCodeExecutor(metaclass=_DeprecatedClassMeta): +class LocalCommandlineCodeExecutor(metaclass=_DeprecatedClassMeta): """LocalCommandLineCodeExecutor renamed to LocalCommandLineCodeExecutor""" _DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor From 26698fb552b5ee3d2dbbf10d8c70a8df9e14b878 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 19:44:11 -0500 Subject: [PATCH 4/6] name --- autogen/coding/local_commandline_code_executor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index 0ddd67d150d..afbcb176537 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -240,6 +240,6 @@ def __subclasscheck__(cls, subclass): class LocalCommandlineCodeExecutor(metaclass=_DeprecatedClassMeta): - """LocalCommandLineCodeExecutor renamed to LocalCommandLineCodeExecutor""" + """LocalCommandlineCodeExecutor renamed to LocalCommandLineCodeExecutor""" _DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor From da49d53db3b307979a796de33d86f6e8f18d3057 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 20:09:03 -0500 Subject: [PATCH 5/6] CommandLineCodeResult rename too --- .../coding/local_commandline_code_executor.py | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index afbcb176537..f9117a016f0 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -14,12 +14,11 @@ __all__ = ( "LocalCommandLineCodeExecutor", - "LocalCommandLineCodeExecutor", - "CommandlineCodeResult", + "CommandLineCodeResult", ) -class CommandlineCodeResult(CodeResult): +class CommandLineCodeResult(CodeResult): """(Experimental) A code result class for command line code executor.""" code_file: Optional[str] = Field( @@ -127,14 +126,14 @@ def sanitize_command(lang: str, code: str) -> None: if re.search(pattern, code): raise ValueError(f"Potentially dangerous command detected: {message}") - def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeResult: + def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandLineCodeResult: """(Experimental) Execute the code blocks and return the result. Args: code_blocks (List[CodeBlock]): The code blocks to execute. Returns: - CommandlineCodeResult: The result of the code execution.""" + CommandLineCodeResult: The result of the code execution.""" logs_all = "" for i, code_block in enumerate(code_blocks): lang, code = code_block.language, code_block.code @@ -177,7 +176,7 @@ def execute_code_blocks(self, code_blocks: List[CodeBlock]) -> CommandlineCodeRe if exitcode != 0: break code_filename = os.path.join(self.work_dir, filename) if filename is not None else None - return CommandlineCodeResult(exit_code=exitcode, output=logs_all, code_file=code_filename) + return CommandLineCodeResult(exit_code=exitcode, output=logs_all, code_file=code_filename) def restart(self) -> None: """(Experimental) Restart the code executor.""" @@ -243,3 +242,8 @@ class LocalCommandlineCodeExecutor(metaclass=_DeprecatedClassMeta): """LocalCommandlineCodeExecutor renamed to LocalCommandLineCodeExecutor""" _DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor + +class CommandlineCodeResult(metaclass=_DeprecatedClassMeta): + """CommandlineCodeResult renamed to CommandLineCodeResult""" + + _DeprecatedClassMeta__alias = CommandLineCodeResult From 40afc4e897f296279efa431592defb9039f43e54 Mon Sep 17 00:00:00 2001 From: Jack Gerrits Date: Mon, 4 Mar 2024 20:12:21 -0500 Subject: [PATCH 6/6] formatting --- autogen/coding/local_commandline_code_executor.py | 1 + 1 file changed, 1 insertion(+) diff --git a/autogen/coding/local_commandline_code_executor.py b/autogen/coding/local_commandline_code_executor.py index f9117a016f0..e9bbb616a08 100644 --- a/autogen/coding/local_commandline_code_executor.py +++ b/autogen/coding/local_commandline_code_executor.py @@ -243,6 +243,7 @@ class LocalCommandlineCodeExecutor(metaclass=_DeprecatedClassMeta): _DeprecatedClassMeta__alias = LocalCommandLineCodeExecutor + class CommandlineCodeResult(metaclass=_DeprecatedClassMeta): """CommandlineCodeResult renamed to CommandLineCodeResult"""