From d6d0317534f6648b6fb6fabafcb5c584ca858475 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Fri, 30 Jan 2026 14:53:24 +0100 Subject: [PATCH 1/7] Handle connection error gracefully --- codeplain_REST_api.py | 18 +++++++++++++++++- plain2code.py | 6 ++++++ plain2code_exceptions.py | 6 ++++++ tui/plain2code_tui.py | 17 +++++++++-------- 4 files changed, 38 insertions(+), 9 deletions(-) diff --git a/codeplain_REST_api.py b/codeplain_REST_api.py index 51c502e..b6d41c1 100644 --- a/codeplain_REST_api.py +++ b/codeplain_REST_api.py @@ -2,6 +2,7 @@ from typing import Optional import requests +from requests.exceptions import ConnectionError, Timeout import plain2code_exceptions from plain2code_state import RunState @@ -9,7 +10,6 @@ MAX_RETRIES = 4 RETRY_DELAY = 3 -# TODO: Handle connection errors RETRY_ERROR_CODES = [ "LLMInternalError", ] @@ -84,7 +84,23 @@ def post_request( response.raise_for_status() return response_json + except (ConnectionError, Timeout) as e: + # Network-related errors should always be retried + if attempt < MAX_RETRIES: + self.console.info(f"Network error on attempt {attempt + 1}/{MAX_RETRIES + 1}: {e}") + self.console.info(f"Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + # Exponential backoff + retry_delay *= 2 + else: + self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Network connection failed.") + self.console.error(f"Last error: {str(e)}") + raise plain2code_exceptions.NetworkConnectionError( + f"Failed to connect to API server after {MAX_RETRIES + 1} attempts. " + ) + except Exception as e: + # For other errors, check if they should be retried if response_json is not None and "error_code" in response_json: if response_json["error_code"] not in RETRY_ERROR_CODES: raise e diff --git a/plain2code.py b/plain2code.py index ed980ac..5e6d5b1 100644 --- a/plain2code.py +++ b/plain2code.py @@ -27,6 +27,7 @@ MissingAPIKey, MissingPreviousFunctionalitiesError, MissingResource, + NetworkConnectionError, OutdatedClientVersion, PlainSyntaxError, UnexpectedState, @@ -316,6 +317,11 @@ def main(): # noqa: C901 exc_info = sys.exc_info() console.error(f"Missing resource: {str(e)}\n") console.debug(f"Render ID: {run_state.render_id}") + except NetworkConnectionError as e: + exc_info = sys.exc_info() + console.error(f"Connection error: {str(e)}\n") + console.error("Please check that your internet connection is working.") + console.debug(f"Render ID: {run_state.render_id}") except Exception as e: exc_info = sys.exc_info() console.error(f"Error rendering plain code: {str(e)}\n") diff --git a/plain2code_exceptions.py b/plain2code_exceptions.py index f0e4535..4f2e06c 100644 --- a/plain2code_exceptions.py +++ b/plain2code_exceptions.py @@ -67,3 +67,9 @@ class MissingPreviousFunctionalitiesError(Exception): """Raised when trying to render from a FRID but previous FRID commits are missing.""" pass + + +class NetworkConnectionError(Exception): + """Raised when there is a network connectivity issue with the API server.""" + + pass diff --git a/tui/plain2code_tui.py b/tui/plain2code_tui.py index 8985ab5..34798ad 100644 --- a/tui/plain2code_tui.py +++ b/tui/plain2code_tui.py @@ -19,7 +19,7 @@ RenderModuleStarted, RenderStateUpdated, ) -from plain2code_exceptions import InternalServerError +from plain2code_exceptions import InternalServerError, NetworkConnectionError from render_machine.states import States from tui.widget_helpers import log_to_widget @@ -139,13 +139,14 @@ def on_worker_state_changed(self, event: Worker.StateChanged) -> None: error = event.worker.error original_error = error.__cause__ if isinstance(error, WorkerFailed) and error.__cause__ else error - # Every error in worker thread gets converted to InternalServerError so it's handled by the common call to - # action in plain2code.py - internal_error = InternalServerError(str(original_error)) - internal_error.__cause__ = original_error - - # Exit the TUI and return the wrapped exception - self.exit(result=internal_error) + # Connection-related errors are propagated as-is to show helpful error messages + # All other errors are wrapped in InternalServerError for consistent handling + if isinstance(original_error, NetworkConnectionError): + self.exit(result=original_error) + else: + internal_error = InternalServerError(str(original_error)) + internal_error.__cause__ = original_error + self.exit(result=internal_error) def _handle_exception(self, error: Exception) -> None: """Override Textual's exception handler to suppress console tracebacks for worker errors. From 8af38bc4e08f6c298325cf1bd070641287768371 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Fri, 30 Jan 2026 15:38:17 +0100 Subject: [PATCH 2/7] Refactor and simplify retry mechanism --- codeplain_REST_api.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/codeplain_REST_api.py b/codeplain_REST_api.py index b6d41c1..46ddca8 100644 --- a/codeplain_REST_api.py +++ b/codeplain_REST_api.py @@ -44,6 +44,32 @@ def _extend_payload_with_run_state(self, payload: dict, run_state: RunState): run_state.increment_call_count() payload["render_state"] = run_state.to_dict() + def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception, error_type: str = "Error") -> int: + """ + Handles retry logic with exponential backoff. + + Args: + attempt: Current attempt number + retry_delay: Current retry delay in seconds + error: The exception that occurred + error_type: Type of error for logging (e.g., "Network error", "Error") + + Returns: + Updated retry_delay for next attempt + + Raises: + The original exception if max retries exceeded + """ + if attempt < MAX_RETRIES: + self.console.info(f"{error_type} on attempt {attempt + 1}/{MAX_RETRIES + 1}: {error}") + self.console.info(f"Retrying in {retry_delay} seconds...") + time.sleep(retry_delay) + # Exponential backoff + return retry_delay * 2 + else: + self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Last error: {error}") + raise error + def _raise_for_error_code(self, response_json): """Raise appropriate exception based on error code in response.""" error_code = response_json.get("error_code") @@ -87,11 +113,7 @@ def post_request( except (ConnectionError, Timeout) as e: # Network-related errors should always be retried if attempt < MAX_RETRIES: - self.console.info(f"Network error on attempt {attempt + 1}/{MAX_RETRIES + 1}: {e}") - self.console.info(f"Retrying in {retry_delay} seconds...") - time.sleep(retry_delay) - # Exponential backoff - retry_delay *= 2 + retry_delay = self._handle_retry_logic(attempt, retry_delay, e, "Network error") else: self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Network connection failed.") self.console.error(f"Last error: {str(e)}") @@ -105,14 +127,7 @@ def post_request( if response_json["error_code"] not in RETRY_ERROR_CODES: raise e - if attempt < num_retries: - self.console.info(f"Error on attempt {attempt + 1}/{num_retries + 1}: {e}") - self.console.info(f"Retrying in {retry_delay} seconds...") - time.sleep(retry_delay) - retry_delay *= 2 # Exponential backoff - else: - self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {e}") - raise e + retry_delay = self._handle_retry_logic(attempt, retry_delay, e) def connection_check(self, client_version): endpoint_url = f"{self.api_url}/connection_check" From 5e0e8fff7f2a98d48ae996a6410a882edf10ee19 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Fri, 30 Jan 2026 16:39:08 +0100 Subject: [PATCH 3/7] Simplify handling of retry logic --- codeplain_REST_api.py | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/codeplain_REST_api.py b/codeplain_REST_api.py index 46ddca8..db37301 100644 --- a/codeplain_REST_api.py +++ b/codeplain_REST_api.py @@ -44,7 +44,7 @@ def _extend_payload_with_run_state(self, payload: dict, run_state: RunState): run_state.increment_call_count() payload["render_state"] = run_state.to_dict() - def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception, error_type: str = "Error") -> int: + def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception) -> int: """ Handles retry logic with exponential backoff. @@ -60,15 +60,20 @@ def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception, Raises: The original exception if max retries exceeded """ + is_connection_error = isinstance(error, ConnectionError) or isinstance(error, Timeout) + connection_error_type = "Network error" if is_connection_error else "Error" if attempt < MAX_RETRIES: - self.console.info(f"{error_type} on attempt {attempt + 1}/{MAX_RETRIES + 1}: {error}") + self.console.info(f"{connection_error_type} on attempt {attempt + 1}/{MAX_RETRIES + 1}: {error}") self.console.info(f"Retrying in {retry_delay} seconds...") time.sleep(retry_delay) # Exponential backoff return retry_delay * 2 else: self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Last error: {error}") - raise error + if is_connection_error: + raise plain2code_exceptions.NetworkConnectionError("Failed to connect to API server.") + else: + raise error def _raise_for_error_code(self, response_json): """Raise appropriate exception based on error code in response.""" @@ -110,17 +115,6 @@ def post_request( response.raise_for_status() return response_json - except (ConnectionError, Timeout) as e: - # Network-related errors should always be retried - if attempt < MAX_RETRIES: - retry_delay = self._handle_retry_logic(attempt, retry_delay, e, "Network error") - else: - self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Network connection failed.") - self.console.error(f"Last error: {str(e)}") - raise plain2code_exceptions.NetworkConnectionError( - f"Failed to connect to API server after {MAX_RETRIES + 1} attempts. " - ) - except Exception as e: # For other errors, check if they should be retried if response_json is not None and "error_code" in response_json: From 5412557a86ef3eaeb3e6283db1ceed6989c41292 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Tue, 3 Feb 2026 11:17:54 +0100 Subject: [PATCH 4/7] Fix error propagation in TUI --- plain2code.py | 4 ++-- plain2code_exceptions.py | 2 +- .../actions/analyze_specification_ambiguity.py | 3 ++- render_machine/actions/fix_conformance_test.py | 4 ++-- render_machine/actions/fix_unit_tests.py | 4 ++-- render_machine/conformance_tests.py | 3 ++- tui/plain2code_tui.py | 11 +---------- 7 files changed, 12 insertions(+), 19 deletions(-) diff --git a/plain2code.py b/plain2code.py index 5e6d5b1..0c5c27b 100644 --- a/plain2code.py +++ b/plain2code.py @@ -20,6 +20,7 @@ from plain2code_exceptions import ( ConflictingRequirements, CreditBalanceTooLow, + InternalClientError, InternalServerError, InvalidAPIKey, InvalidFridArgument, @@ -30,7 +31,6 @@ NetworkConnectionError, OutdatedClientVersion, PlainSyntaxError, - UnexpectedState, ) from plain2code_logger import ( CrashLogHandler, @@ -295,7 +295,7 @@ def main(): # noqa: C901 console.error(f"Invalid API key: {str(e)}\n") except OutdatedClientVersion as e: console.error(f"Outdated client version: {str(e)}\n") - except (InternalServerError, UnexpectedState): + except (InternalServerError, InternalClientError): exc_info = sys.exc_info() console.error( f"Internal server error.\n\nPlease report the error to support@codeplain.ai with the attached {args.log_file_name} file." diff --git a/plain2code_exceptions.py b/plain2code_exceptions.py index 4f2e06c..3a307ed 100644 --- a/plain2code_exceptions.py +++ b/plain2code_exceptions.py @@ -25,7 +25,7 @@ class PlainSyntaxError(Exception): pass -class UnexpectedState(Exception): +class InternalClientError(Exception): pass diff --git a/render_machine/actions/analyze_specification_ambiguity.py b/render_machine/actions/analyze_specification_ambiguity.py index f14375c..052a038 100644 --- a/render_machine/actions/analyze_specification_ambiguity.py +++ b/render_machine/actions/analyze_specification_ambiguity.py @@ -4,6 +4,7 @@ import git_utils import plain_spec from plain2code_console import console +from plain2code_exceptions import InternalClientError from plain2code_utils import AMBIGUITY_CAUSES from render_machine.actions.base_action import BaseAction from render_machine.render_context import RenderContext @@ -17,7 +18,7 @@ def execute(self, render_context: RenderContext, _previous_action_payload: Any | render_context.build_folder, render_context.frid_context.frid ) if fixed_implementation_code_diff is None: - raise Exception( + raise InternalClientError( "Fixes to the implementation code found during conformance testing are not committed to git." ) previous_frid = plain_spec.get_previous_frid(render_context.plain_source_tree, render_context.frid_context.frid) diff --git a/render_machine/actions/fix_conformance_test.py b/render_machine/actions/fix_conformance_test.py index d0048d2..4d0b046 100644 --- a/render_machine/actions/fix_conformance_test.py +++ b/render_machine/actions/fix_conformance_test.py @@ -5,7 +5,7 @@ import plain_spec from memory_management import MemoryManager from plain2code_console import console -from plain2code_exceptions import UnexpectedState +from plain2code_exceptions import InternalClientError from render_machine.actions.base_action import BaseAction from render_machine.implementation_code_helpers import ImplementationCodeHelpers from render_machine.render_context import RenderContext @@ -21,7 +21,7 @@ def execute(self, render_context: RenderContext, previous_action_payload: Any | ) if not previous_action_payload.get("previous_conformance_tests_issue"): - raise UnexpectedState("Previous action payload does not contain previous conformance tests issue.") + raise InternalClientError("Previous action payload does not contain previous conformance tests issue.") previous_conformance_tests_issue = previous_action_payload["previous_conformance_tests_issue"] render_context.conformance_tests_running_context.previous_conformance_tests_issue_old = ( diff --git a/render_machine/actions/fix_unit_tests.py b/render_machine/actions/fix_unit_tests.py index 691ea31..190301e 100644 --- a/render_machine/actions/fix_unit_tests.py +++ b/render_machine/actions/fix_unit_tests.py @@ -3,7 +3,7 @@ import file_utils import render_machine.render_utils as render_utils from plain2code_console import console -from plain2code_exceptions import UnexpectedState +from plain2code_exceptions import InternalClientError from render_machine.actions.base_action import BaseAction from render_machine.implementation_code_helpers import ImplementationCodeHelpers from render_machine.render_context import RenderContext @@ -16,7 +16,7 @@ class FixUnitTests(BaseAction): def execute(self, render_context: RenderContext, previous_action_payload: Any | None): if not previous_action_payload.get("previous_unittests_issue"): - raise UnexpectedState("Previous action payload does not contain previous unit tests issue.") + raise InternalClientError("Previous action payload does not contain previous unit tests issue.") previous_unittests_issue = previous_action_payload["previous_unittests_issue"] if previous_unittests_issue and len(previous_unittests_issue) > MAX_ISSUE_LENGTH: diff --git a/render_machine/conformance_tests.py b/render_machine/conformance_tests.py index 961add5..410c5be 100644 --- a/render_machine/conformance_tests.py +++ b/render_machine/conformance_tests.py @@ -3,6 +3,7 @@ import file_utils from plain2code_console import console +from plain2code_exceptions import InternalClientError from plain_modules import PlainModule CONFORMANCE_TESTS_DEFINITION_FILE_NAME = "conformance_tests.json" @@ -69,7 +70,7 @@ def get_source_conformance_test_folder_name( ) -> tuple[str, str]: original_prefix = self.get_module_conformance_tests_folder(current_testing_module_name) if not original_conformance_test_folder_name.startswith(original_prefix): - raise Exception( + raise InternalClientError( f"Unexpected conformance test folder name prefix {original_prefix} for {original_conformance_test_folder_name}!" ) diff --git a/tui/plain2code_tui.py b/tui/plain2code_tui.py index 34798ad..d7a8dde 100644 --- a/tui/plain2code_tui.py +++ b/tui/plain2code_tui.py @@ -19,7 +19,6 @@ RenderModuleStarted, RenderStateUpdated, ) -from plain2code_exceptions import InternalServerError, NetworkConnectionError from render_machine.states import States from tui.widget_helpers import log_to_widget @@ -138,15 +137,7 @@ def on_worker_state_changed(self, event: Worker.StateChanged) -> None: # Extract the original exception from WorkerFailed wrapper error = event.worker.error original_error = error.__cause__ if isinstance(error, WorkerFailed) and error.__cause__ else error - - # Connection-related errors are propagated as-is to show helpful error messages - # All other errors are wrapped in InternalServerError for consistent handling - if isinstance(original_error, NetworkConnectionError): - self.exit(result=original_error) - else: - internal_error = InternalServerError(str(original_error)) - internal_error.__cause__ = original_error - self.exit(result=internal_error) + self.exit(result=original_error) def _handle_exception(self, error: Exception) -> None: """Override Textual's exception handler to suppress console tracebacks for worker errors. From 3996d15ceb8b94731a27ffa0a79f63f19999d4cd Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Tue, 3 Feb 2026 12:10:23 +0100 Subject: [PATCH 5/7] Remove propagating file as an argument --- plain2code.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plain2code.py b/plain2code.py index 0c5c27b..d02c13c 100644 --- a/plain2code.py +++ b/plain2code.py @@ -166,7 +166,7 @@ def setup_logging( root_logger.info(f"Render ID: {render_id}") # Ensure render ID is logged in to codeplain.log file -def _check_connection(codeplainAPI): +def _check_connection(codeplainAPI: codeplain_api.CodeplainAPI): """Check API connectivity and validate API key and client version.""" response = codeplainAPI.connection_check(system_config.client_version) @@ -185,7 +185,7 @@ def _check_connection(codeplainAPI): ) -def render(args, run_state: RunState, codeplain_api, event_bus: EventBus): # noqa: C901 +def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901 # Check system requirements before proceeding system_config.verify_requirements() @@ -262,7 +262,7 @@ def main(): # noqa: C901 ) console.debug(f"Render ID: {run_state.render_id}") # Ensure render ID is logged to the console - render(args, run_state, codeplain_api, event_bus) + render(args, run_state, event_bus) except InvalidFridArgument as e: exc_info = sys.exc_info() console.error(f"Invalid FRID argument: {str(e)}.\n") From 17713a527e619b87c33a5804105ffeb85fd98805 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Tue, 3 Feb 2026 12:11:19 +0100 Subject: [PATCH 6/7] Fix handling max retries and optionally silence logging --- codeplain_REST_api.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/codeplain_REST_api.py b/codeplain_REST_api.py index db37301..d034189 100644 --- a/codeplain_REST_api.py +++ b/codeplain_REST_api.py @@ -44,7 +44,9 @@ def _extend_payload_with_run_state(self, payload: dict, run_state: RunState): run_state.increment_call_count() payload["render_state"] = run_state.to_dict() - def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception) -> int: + def _handle_retry_logic( + self, attempt: int, retry_delay: int, num_retries: int, error: Exception, silent: bool + ) -> int: """ Handles retry logic with exponential backoff. @@ -52,6 +54,7 @@ def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception) attempt: Current attempt number retry_delay: Current retry delay in seconds error: The exception that occurred + num_retries: Number of retries allowed error_type: Type of error for logging (e.g., "Network error", "Error") Returns: @@ -62,14 +65,16 @@ def _handle_retry_logic(self, attempt: int, retry_delay: int, error: Exception) """ is_connection_error = isinstance(error, ConnectionError) or isinstance(error, Timeout) connection_error_type = "Network error" if is_connection_error else "Error" - if attempt < MAX_RETRIES: - self.console.info(f"{connection_error_type} on attempt {attempt + 1}/{MAX_RETRIES + 1}: {error}") - self.console.info(f"Retrying in {retry_delay} seconds...") + if attempt < num_retries: + if not silent: + self.console.info(f"{connection_error_type} on attempt {attempt + 1}/{num_retries + 1}: {error}") + self.console.info(f"Retrying in {retry_delay} seconds...") time.sleep(retry_delay) # Exponential backoff return retry_delay * 2 else: - self.console.error(f"Max retries ({MAX_RETRIES}) exceeded. Last error: {error}") + if not silent: + self.console.error(f"Max retries ({num_retries}) exceeded. Last error: {error}") if is_connection_error: raise plain2code_exceptions.NetworkConnectionError("Failed to connect to API server.") else: @@ -91,7 +96,13 @@ def _raise_for_error_code(self, response_json): raise exception_class(message) def post_request( - self, endpoint_url, headers, payload, run_state: Optional[RunState], num_retries: int = MAX_RETRIES + self, + endpoint_url, + headers, + payload, + run_state: Optional[RunState], + num_retries: int = MAX_RETRIES, + silent: bool = False, ): if run_state is not None: self._extend_payload_with_run_state(payload, run_state) @@ -121,7 +132,7 @@ def post_request( if response_json["error_code"] not in RETRY_ERROR_CODES: raise e - retry_delay = self._handle_retry_logic(attempt, retry_delay, e) + retry_delay = self._handle_retry_logic(attempt, retry_delay, num_retries, e, silent) def connection_check(self, client_version): endpoint_url = f"{self.api_url}/connection_check" @@ -130,7 +141,7 @@ def connection_check(self, client_version): "api_key": self.api_key, "client_version": client_version, } - return self.post_request(endpoint_url, headers, payload, None, num_retries=0) + return self.post_request(endpoint_url, headers, payload, None, num_retries=0, silent=True) def render_functional_requirement( self, From ff58f46512790bb20deae942393c3ac9f8be1ec1 Mon Sep 17 00:00:00 2001 From: Tjaz Erzen Date: Mon, 2 Feb 2026 16:55:39 +0100 Subject: [PATCH 7/7] Fix propagating module does not exist error --- plain2code.py | 5 +++++ plain_file.py | 4 ++-- tests/test_requires.py | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/plain2code.py b/plain2code.py index d02c13c..f0505f7 100644 --- a/plain2code.py +++ b/plain2code.py @@ -28,6 +28,7 @@ MissingAPIKey, MissingPreviousFunctionalitiesError, MissingResource, + ModuleDoesNotExistError, NetworkConnectionError, OutdatedClientVersion, PlainSyntaxError, @@ -317,6 +318,10 @@ def main(): # noqa: C901 exc_info = sys.exc_info() console.error(f"Missing resource: {str(e)}\n") console.debug(f"Render ID: {run_state.render_id}") + except ModuleDoesNotExistError as e: + exc_info = sys.exc_info() + console.error(f"Module does not exist: {str(e)}\n") + console.debug(f"Render ID: {run_state.render_id}") except NetworkConnectionError as e: exc_info = sys.exc_info() console.error(f"Connection error: {str(e)}\n") diff --git a/plain_file.py b/plain_file.py index 4762827..1ce11f0 100644 --- a/plain_file.py +++ b/plain_file.py @@ -17,7 +17,7 @@ import concept_utils import file_utils import plain_spec -from plain2code_exceptions import PlainSyntaxError +from plain2code_exceptions import ModuleDoesNotExistError, PlainSyntaxError from plain2code_nodes import Plain2CodeIncludeTag, Plain2CodeLoaderMixin RESOURCE_MARKER = "[resource]" @@ -513,7 +513,7 @@ def parse_plain_source( # noqa: C901 def read_module_plain_source(module_name: str, template_dirs: list[str]) -> str: plain_source_text = file_utils.open_from(template_dirs, module_name + PLAIN_SOURCE_FILE_EXTENSION) if plain_source_text is None: - raise PlainSyntaxError(f"Module does not exist ({module_name}).") + raise ModuleDoesNotExistError(f"Module does not exist ({module_name}).") return plain_source_text diff --git a/tests/test_requires.py b/tests/test_requires.py index 8cd72ee..9540a66 100644 --- a/tests/test_requires.py +++ b/tests/test_requires.py @@ -1,11 +1,11 @@ import pytest import plain_file -from plain2code_exceptions import PlainSyntaxError +from plain2code_exceptions import ModuleDoesNotExistError def test_non_existent_require(get_test_data_path): - with pytest.raises(PlainSyntaxError, match="Module does not exist"): + with pytest.raises(ModuleDoesNotExistError, match="Module does not exist"): plain_file.plain_file_parser("non_existent_require.plain", [get_test_data_path("data/requires")])