-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
274 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,5 @@ | ||
"""Code optimization module.""" | ||
|
||
from .optimizer import CodeOptimizer | ||
|
||
__all__ = ["CodeOptimizer"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
"""Optimizer Module.""" | ||
|
||
import os | ||
from typing import Any, Dict, Optional | ||
|
||
from langchain_core.runnables.base import RunnableSerializable | ||
|
||
|
||
class CodeOptimizer: | ||
"""Code Explainer class that extracts and explains code from a given file.""" | ||
|
||
def __init__(self, chain: RunnableSerializable[Dict, Any]): | ||
""" | ||
Initialize the CodeExplainer class with a runnable chain. | ||
:param chain: A RunnableSerializable object capable of executing tasks. | ||
""" | ||
self.chain = chain | ||
|
||
def optimize( | ||
self, | ||
filename: str, | ||
function: Optional[str] = None, | ||
classname: Optional[str] = None, | ||
overwrite: bool = False, | ||
): | ||
""" | ||
Optimize the the code by invoking the runnable chain. | ||
:param path: The path to the code file to be explained. | ||
:param function: The name of the function to explain. Default is None. | ||
:param classname: The name of the class to explain. Default is None. | ||
""" | ||
with open(filename, "r") as source_file: | ||
source_code = source_file.read() | ||
if function: | ||
response = self.chain.invoke( | ||
{ | ||
"input": f"Optimize, comment and add sphinx docstrings" | ||
f" to the function '{function}' in \n\n```\n{source_code}\n```" | ||
"Also explain the optimization ina systematic way." | ||
} | ||
) | ||
elif classname: | ||
response = self.chain.invoke( | ||
{ | ||
"input": f"Optimize, comment and add sphinx docstrings" | ||
f" to the class '{classname}' in \n\n```\n{source_code}\n```" | ||
"Also explain the optimization ina systematic way." | ||
} | ||
) | ||
else: | ||
# Explain full code | ||
response = self.chain.invoke( | ||
{ | ||
"input": f"Optimize, comment and add sphinx style docstrings" | ||
f" to the following code: \n\n```\n{source_code}\n```" | ||
"Also explain the optimization ina systematic way." | ||
} | ||
) | ||
optimized_code = response.content | ||
new_filename = filename | ||
if not overwrite: | ||
# Create a new filename with the _updated suffix | ||
base, ext = os.path.splitext(filename) | ||
new_filename = f"{base}_updated{ext}" | ||
|
||
# Write the commented code to the new file | ||
with open(new_filename, "w") as updated_file: | ||
updated_file.write(optimized_code) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
"""Optimizer tests.""" | ||
|
||
import unittest | ||
from unittest.mock import MagicMock, mock_open, patch | ||
|
||
from codergpt import CodeOptimizer | ||
|
||
from tests.test_constants import TEST_INPUT_DIR, TEST_OUTPUT_DIR | ||
|
||
|
||
class TestCodeOptimizer(unittest.TestCase): | ||
"""Test the CodeOptimizer class.""" | ||
|
||
def setUp(self): | ||
""" | ||
Set up the test case. | ||
Done by creating a mock chain that can be used to simulate the behavior of the actual chain. | ||
""" | ||
self.mock_chain = MagicMock() | ||
self.code_optimizer = CodeOptimizer(chain=self.mock_chain) | ||
self.bad_python = TEST_INPUT_DIR / "bad_python.py" | ||
self.expected_bad_python = TEST_OUTPUT_DIR / "expected_bad_python.py" | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="def foo(): pass") | ||
def test_optimize_with_function(self, mock_file): | ||
"""Test the optimize method with a function.""" | ||
# Setup | ||
filename = "test.py" | ||
function = "foo" | ||
expected_response = MagicMock(content="def foo():\n # Optimized code\n pass") | ||
self.mock_chain.invoke.return_value = expected_response | ||
|
||
# Exercise | ||
self.code_optimizer.optimize(filename, function=function) | ||
|
||
# Verify | ||
self.mock_chain.invoke.assert_called_once() | ||
mock_file.assert_called_with(filename.replace(".py", "_updated.py"), "w") | ||
mock_file().write.assert_called_once_with(expected_response.content) | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="class Bar: pass") | ||
def test_optimize_with_classname(self, mock_file): | ||
""" | ||
Test the optimize method with a classname. | ||
:param self: The current instance of the class | ||
""" | ||
# Setup | ||
filename = "test.py" | ||
classname = "Bar" | ||
expected_response = MagicMock(content="class Bar:\n # Optimized code\n pass") | ||
self.mock_chain.invoke.return_value = expected_response | ||
|
||
# Exercise | ||
self.code_optimizer.optimize(filename, classname=classname) | ||
|
||
# Verify | ||
self.mock_chain.invoke.assert_called_once() | ||
mock_file.assert_called_with(filename.replace(".py", "_updated.py"), "w") | ||
mock_file().write.assert_called_once_with(expected_response.content) | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="def foo(): pass\nclass Bar: pass") | ||
def test_optimize_full_code(self, mock_file): | ||
""" | ||
Test the optimize method with full code. | ||
:param self: The current instance of the class | ||
""" | ||
# Setup | ||
filename = "test.py" | ||
expected_response = MagicMock( | ||
content="def foo():\n # Optimized function\n pass\nclass Bar:\n # Optimized class\n pass" | ||
) | ||
self.mock_chain.invoke.return_value = expected_response | ||
|
||
# Exercise | ||
self.code_optimizer.optimize(filename) | ||
|
||
# Verify | ||
self.mock_chain.invoke.assert_called_once() | ||
mock_file.assert_called_with(filename.replace(".py", "_updated.py"), "w") | ||
mock_file().write.assert_called_once_with(expected_response.content) | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="def foo(): pass") | ||
def test_optimize_overwrite_false(self, mock_file): | ||
""" | ||
Test that when overwrite is True, the original file is overwritten. | ||
:param self: The current instance of the class | ||
""" | ||
""" | ||
Test that when overwrite is True, the original file is overwritten. | ||
""" | ||
# Setup | ||
filename = "test.py" | ||
expected_response = MagicMock(content="def foo():\n # Optimized function\n pass") | ||
self.mock_chain.invoke.return_value = expected_response | ||
|
||
# Exercise | ||
self.code_optimizer.optimize(filename) | ||
|
||
# Verify | ||
self.mock_chain.invoke.assert_called_once() | ||
mock_file.assert_called_with(filename.replace(".py", "_updated.py"), "w") | ||
mock_file().write.assert_called_once_with(expected_response.content) | ||
|
||
@patch("builtins.open", new_callable=mock_open, read_data="def foo(): pass") | ||
def test_optimize_overwrite_true(self, mock_file): | ||
""" | ||
Test that when an invalid language is provided, the appropriate error is raised or handled. | ||
:param self: The current instance of the class | ||
""" | ||
# Setup | ||
filename = "test.py" | ||
expected_response = MagicMock(content="def foo():\n # Optimized function\n pass") | ||
self.mock_chain.invoke.return_value = expected_response | ||
|
||
# Exercise | ||
self.code_optimizer.optimize(filename, overwrite=True) | ||
|
||
# Verify | ||
self.mock_chain.invoke.assert_called_once() | ||
mock_file.assert_called_with(filename, "w") | ||
mock_file().write.assert_called_once_with(expected_response.content) | ||
|
||
# TODO: Fix this test | ||
# @patch("builtins.open", new_callable=mock_open, read_data="def foo(): pass") | ||
# def test_optimize_invalid_function(self, mock_file): | ||
# """ | ||
# Test that when an invalid function is provided, the appropriate error is raised or handled. | ||
|
||
# :param self: The current instance of the class | ||
# """ | ||
# # Setup | ||
# filename = "test.py" | ||
# function = "invalid_function" | ||
# expected_error_message = "Function not found in the source code." | ||
|
||
# # Exercise & Verify | ||
# with self.assertRaises(ValueError) as context: | ||
# self.code_optimizer.optimize(filename, function=function) | ||
# self.assertEqual(str(context.exception), expected_error_message) | ||
|
||
|
||
if __name__ == "__main__": | ||
unittest.main() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters