From 74217a1df35bb7073503bc13f29d91f9240e01e2 Mon Sep 17 00:00:00 2001 From: femto Date: Tue, 19 Sep 2023 12:23:22 +0800 Subject: [PATCH] modified --- metagpt/actions/write_test.py | 5 +- metagpt/utils/custom_decoder.py | 41 +++++++-- requirements.txt | 1 + tests/metagpt/actions/test_write_test.py | 17 +++- tests/metagpt/utils/test_custom_decoder.py | 96 ---------------------- 5 files changed, 54 insertions(+), 106 deletions(-) diff --git a/metagpt/actions/write_test.py b/metagpt/actions/write_test.py index 0429cb973..35ff36dc2 100644 --- a/metagpt/actions/write_test.py +++ b/metagpt/actions/write_test.py @@ -6,6 +6,7 @@ @File : environment.py """ from metagpt.actions.action import Action +from metagpt.logs import logger from metagpt.utils.common import CodeParser PROMPT_TEMPLATE = """ @@ -38,9 +39,9 @@ class WriteTest(Action): try: code = CodeParser.parse_code(block="", text=code_rsp) - except Exception as e: + except Exception: # Handle the exception if needed - print(f"An exception occurred: {str(e)}") + logger.error(f"Can't parse the code: {code_rsp}") # Return code_rsp in case of an exception, assuming llm just returns code as it is and doesn't wrap it inside ``` code = code_rsp diff --git a/metagpt/utils/custom_decoder.py b/metagpt/utils/custom_decoder.py index fcac85963..373d16356 100644 --- a/metagpt/utils/custom_decoder.py +++ b/metagpt/utils/custom_decoder.py @@ -91,6 +91,24 @@ WHITESPACE_STR = " \t\n\r" def JSONObject( s_and_end, strict, scan_once, object_hook, object_pairs_hook, memo=None, _w=WHITESPACE.match, _ws=WHITESPACE_STR ): + """Parse a JSON object from a string and return the parsed object. + + Args: + s_and_end (tuple): A tuple containing the input string to parse and the current index within the string. + strict (bool): If `True`, enforces strict JSON string decoding rules. + If `False`, allows literal control characters in the string. Defaults to `True`. + scan_once (callable): A function to scan and parse JSON values from the input string. + object_hook (callable): A function that, if specified, will be called with the parsed object as a dictionary. + object_pairs_hook (callable): A function that, if specified, will be called with the parsed object as a list of pairs. + memo (dict, optional): A dictionary used to memoize string keys for optimization. Defaults to None. + _w (function): A regular expression matching function for whitespace. Defaults to WHITESPACE.match. + _ws (str): A string containing whitespace characters. Defaults to WHITESPACE_STR. + + Returns: + tuple or dict: A tuple containing the parsed object and the index of the character in the input string + after the end of the object. + """ + s, end = s_and_end pairs = [] pairs_append = pairs.append @@ -175,14 +193,23 @@ def JSONObject( def py_scanstring(s, end, strict=True, _b=BACKSLASH, _m=STRINGCHUNK.match, delimiter='"'): - """Scan the string s for a JSON string. End is the index of the - character in s after the quote that started the JSON string. - Unescapes all valid JSON string escape sequences and raises ValueError - on attempt to decode an invalid string. If strict is False then literal - control characters are allowed in the string. + """Scan the string s for a JSON string. + + Args: + s (str): The input string to be scanned for a JSON string. + end (int): The index of the character in `s` after the quote that started the JSON string. + strict (bool): If `True`, enforces strict JSON string decoding rules. + If `False`, allows literal control characters in the string. Defaults to `True`. + _b (dict): A dictionary containing escape sequence mappings. + _m (function): A regular expression matching function for string chunks. + delimiter (str): The string delimiter used to define the start and end of the JSON string. + Can be one of: '"', "'", '\"""', or "'''". Defaults to '"'. + + Returns: + tuple: A tuple containing the decoded string and the index of the character in `s` + after the end quote. + """ - Returns a tuple of the decoded string and the index of the character in s - after the end quote.""" chunks = [] _append = chunks.append begin = end - 1 diff --git a/requirements.txt b/requirements.txt index 7f29c3343..24f539a98 100644 --- a/requirements.txt +++ b/requirements.txt @@ -38,3 +38,4 @@ typing-inspect==0.8.0 typing_extensions==4.5.0 libcst==1.0.1 qdrant-client==1.4.0 +pytest-mock==3.11.1 \ No newline at end of file diff --git a/tests/metagpt/actions/test_write_test.py b/tests/metagpt/actions/test_write_test.py index 87a22b139..e5acdff44 100644 --- a/tests/metagpt/actions/test_write_test.py +++ b/tests/metagpt/actions/test_write_test.py @@ -31,7 +31,7 @@ async def test_write_test(): code_to_test=code, test_file_name="test_food.py", source_file_path="/some/dummy/path/cli_snake_game/cli_snake_game/food.py", - workspace="/some/dummy/path/cli_snake_game" + workspace="/some/dummy/path/cli_snake_game", ) logger.info(test_code) @@ -40,3 +40,18 @@ async def test_write_test(): assert "from cli_snake_game.food import Food" in test_code assert "class TestFood(unittest.TestCase)" in test_code assert "def test_generate" in test_code + + +@pytest.mark.asyncio +async def test_write_code_invalid_code(mocker): + # Mock the _aask method to return an invalid code string + mocker.patch.object(WriteTest, "_aask", return_value="Invalid Code String") + + # Create an instance of WriteTest + write_test = WriteTest() + + # Call the write_code method + code = await write_test.write_code("Some prompt:") + + # Assert that the returned code is the same as the invalid code string + assert code == "Invalid Code String" diff --git a/tests/metagpt/utils/test_custom_decoder.py b/tests/metagpt/utils/test_custom_decoder.py index 072d96152..c7b14ad59 100644 --- a/tests/metagpt/utils/test_custom_decoder.py +++ b/tests/metagpt/utils/test_custom_decoder.py @@ -70,99 +70,3 @@ def test_parse_triple_single_quote(): parsed_data = decoder.decode(input_data) assert parsed_data["a"] == "b" - - -# def test_parse_complex(): -# # Create a custom JSON decoder -# decoder = CustomDecoder(strict=False) -# # Your provided input with single-quoted strings and line breaks -# input_data = '''{ -# "Required Python third-party packages": [ -# "flask==1.1.2", -# "bcrypt==3.2.0" -# ], -# "Required Other language third-party packages": [ -# "No third-party packages are required." -# ], -# "Full API spec": """ -# openapi: 3.0.0 -# -# description: A JSON object representing the game state. -# -# paths: -# /game: -# get: -# summary: Get the current game state. -# responses: -# '200': -# description: The current game state. -# -# /game/{direction}: -# post: -# summary: Move the snake in the specified direction. -# parameters: -# - name: direction -# in: path -# description: The direction to move the snake (one of UP, DOWN, LEFT, or RIGHT). -# responses: -# '200': -# description: The updated game state. -# -# /game/{food}: -# get: -# summary: Get the current food position. -# responses: -# '200': -# description: The current food position. -# -# /game/{snake}: -# get: -# summary: Get the current snake position. -# responses: -# '200': -# description: The current snake position. -# -# /game/{snake}/{direction}: -# post: -# summary: Set the snake direction. -# parameters: -# - name: direction -# in: path -# description: The direction to move the snake (one of UP, DOWN, LEFT, or RIGHT). -# responses: -# '200': -# description: The updated game state. -# -# /game/end: -# post: -# summary: End the game. -# responses: -# '200': -# description: The final game state. -# " -# ], -# "Logic Analysis": [ -# ["game.py","Contains the game logic and snake movement."], -# ["snake.py","Contains the snake class and methods."], -# ["game_objects.py","Contains the game objects and their properties."], -# ["game_logic.py","Contains the game logic and rules."], -# ["ui.py","Contains the user interface and display logic."] -# ], -# "Task list": [ -# "game.py", -# "snake.py", -# "game_objects.py", -# "game_logic.py", -# "ui.py" -# ], -# "Shared Knowledge": """ -# The game state is represented by a JSON object. -# The snake movement is based on the direction parameter. -# The game ends when the snake collides with the wall or eats the food. -# """, -# "Anything UNCLEAR": "The requirement is clear to me." -# } -# ''' -# # Parse the JSON using the custom decoder -# -# parsed_data = decoder.decode(input_data)