diff --git a/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/basics.h b/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/basics.h index 209b899..d565c59 100644 --- a/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/basics.h +++ b/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/basics.h @@ -68,7 +68,7 @@ typedef int32_t PGM_IO_ID; * @brief Opaque struct for the VnfConverter class. * */ -typedef struct PGM_IO_VnfConverter PGM_IO_VnfConverter; +typedef struct PGM_IO_VnfPgmConverter PGM_IO_VnfPgmConverter; /** * @brief Opaque struct for the handle class. diff --git a/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/vnf_pgm_converter.h b/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/vnf_pgm_converter.h index b11e85e..cf72762 100644 --- a/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/vnf_pgm_converter.h +++ b/power_grid_model_io_native_c/power_grid_model_io_native_c/include/power_grid_model_io_native_c/vnf_pgm_converter.h @@ -13,29 +13,29 @@ extern "C" { #endif /** - * @brief Create the PGM_IO_VnfConverter + * @brief Create the PGM_IO_VnfPgmConverter * @param handle * @param file_buffer A pointer to the null-terminated C string. - * @return The pointer to a PGM_IO_VnfConverter instance. The instance must be freed by + * @return The pointer to a PGM_IO_VnfPgmConverter instance. The instance must be freed by * PGM_IO_destroy_vnf_converter. */ -PGM_IO_API PGM_IO_VnfConverter* PGM_IO_create_vnf_converter(PGM_IO_Handle* handle, char const* file_buffer, - PGM_IO_ExperimentalFeatures experimental_features); +PGM_IO_API PGM_IO_VnfPgmConverter* PGM_IO_create_vnf_converter(PGM_IO_Handle* handle, char const* file_buffer, + PGM_IO_ExperimentalFeatures experimental_features); /** * @brief Retrieve the transformed input data from .vnf format to PGM format * @param handle - * @param converter_ptr A pointer to a PGM_IO_VnfConverter instace. - * @param dataset A pointer to the const dataset supplied by the user. - * @return The pointer to the const dataset instance supplied by the user which has been filled in. + * @param converter_ptr A pointer to a PGM_IO_VnfPgmConverter instace. + * @return The pointer to the json string instance that holds data in PGM format. */ -PGM_IO_API char const* PGM_IO_get_vnf_input_data(PGM_IO_Handle* handle, PGM_IO_VnfConverter* converter_ptr); +PGM_IO_API char const* PGM_IO_vnf_pgm_converter_get_input_data(PGM_IO_Handle* handle, + PGM_IO_VnfPgmConverter* converter_ptr); /** - * @brief Destroy the PGM_IO_VnfConverter and free up the memory that was dedicated to it. - * @param converter_ptr A pointer to a PGM_IO_VnfConverter instance. + * @brief Destroy the PGM_IO_VnfPgmConverter and free up the memory that was dedicated to it. + * @param converter_ptr A pointer to a PGM_IO_VnfPgmConverter instance. */ -PGM_IO_API void PGM_IO_destroy_vnf_converter(PGM_IO_VnfConverter* converter_ptr); +PGM_IO_API void PGM_IO_destroy_vnf_converter(PGM_IO_VnfPgmConverter* converter_ptr); #ifdef __cplusplus } diff --git a/power_grid_model_io_native_c/power_grid_model_io_native_c/src/vnf_pgm_converter.cpp b/power_grid_model_io_native_c/power_grid_model_io_native_c/src/vnf_pgm_converter.cpp index b8065ed..52f3c2c 100644 --- a/power_grid_model_io_native_c/power_grid_model_io_native_c/src/vnf_pgm_converter.cpp +++ b/power_grid_model_io_native_c/power_grid_model_io_native_c/src/vnf_pgm_converter.cpp @@ -16,12 +16,12 @@ namespace pgm_io = power_grid_model_io_native; -struct PGM_IO_VnfConverter : public pgm_io::PgmVnfConverter { +struct PGM_IO_VnfPgmConverter : public pgm_io::PgmVnfConverter { using PgmVnfConverter::PgmVnfConverter; }; -PGM_IO_VnfConverter* PGM_IO_create_vnf_converter(PGM_IO_Handle* handle, char const* file_buffer, - PGM_IO_ExperimentalFeatures experimental_features) { +PGM_IO_VnfPgmConverter* PGM_IO_create_vnf_converter(PGM_IO_Handle* handle, char const* file_buffer, + PGM_IO_ExperimentalFeatures experimental_features) { return call_with_catch( handle, [file_buffer, experimental_features] { @@ -37,16 +37,16 @@ PGM_IO_VnfConverter* PGM_IO_create_vnf_converter(PGM_IO_Handle* handle, char con default: throw power_grid_model::MissingCaseForEnumError{"PGM_IO_create_vnf_converter", experimental_features}; } - auto* converter = new PGM_IO_VnfConverter(file_buffer, experimental_feature); + auto* converter = new PGM_IO_VnfPgmConverter(file_buffer, experimental_feature); parse_vnf_file_wrapper(converter); return converter; }, PGM_IO_regular_error); } -char const* PGM_IO_get_vnf_input_data(PGM_IO_Handle* handle, PGM_IO_VnfConverter* converter_ptr) { +char const* PGM_IO_vnf_pgm_converter_get_input_data(PGM_IO_Handle* handle, PGM_IO_VnfPgmConverter* converter_ptr) { return call_with_catch( handle, [converter_ptr] { return convert_input_wrapper(converter_ptr).c_str(); }, PGM_IO_regular_error); } -void PGM_IO_destroy_vnf_converter(PGM_IO_VnfConverter* converter_ptr) { delete converter_ptr; } +void PGM_IO_destroy_vnf_converter(PGM_IO_VnfPgmConverter* converter_ptr) { delete converter_ptr; } diff --git a/setup.py b/setup.py index 03927ce..f52707d 100644 --- a/setup.py +++ b/setup.py @@ -178,6 +178,7 @@ def generate_build_ext(pkg_dir: Path, pkg_name: str): libraries: list[str] = [] sources = [ str(pgm_io_c / pgm_io_c / "src" / "handle.cpp"), + str(pgm_io_c / pgm_io_c / "src" / "vnf_pgm_converter.cpp"), ] # macro define_macros = [ diff --git a/src/power_grid_model_io_native/_core/error_handling.py b/src/power_grid_model_io_native/_core/error_handling.py new file mode 100644 index 0000000..ace9fc2 --- /dev/null +++ b/src/power_grid_model_io_native/_core/error_handling.py @@ -0,0 +1,98 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +""" +Error handling +""" + +from power_grid_model._core.error_handling import ( + PGM_NO_ERROR, + PGM_REGULAR_ERROR, + PGM_SERIALIZATION_ERROR, + VALIDATOR_MSG, + PowerGridBatchError, + PowerGridSerializationError, + _interpret_error, +) + +from power_grid_model_io_native._core.power_grid_model_io_core import pgm_io_core as pgmic + +# def _interpret_error_pgm_io(message: str, decode_error: bool = True) -> PowerGridError: +# if decode_error: +# for pattern, type_ in _ERROR_MESSAGE_PATTERNS.items(): +# if pattern.search(message) is not None: +# return type_(message) + +# return PowerGridError(message) + + +def find_error(batch_size: int = 1, decode_error: bool = True) -> RuntimeError | None: + """ + Check if there is an error and return it + + Args: + batch_size: (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True + + Returns: error object, can be none + + """ + _ = batch_size + error_code: int = pgmic.error_code() + if error_code == PGM_NO_ERROR: + return None + if error_code == PGM_REGULAR_ERROR: + error_message = pgmic.error_message() + error_message += VALIDATOR_MSG + return _interpret_error(error_message, decode_error=decode_error) + if error_code == PGM_SERIALIZATION_ERROR: + return PowerGridSerializationError(pgmic.error_message()) + return RuntimeError("Unknown error!") + + +def assert_no_error(batch_size: int = 1, decode_error: bool = True): + """ + Assert there is no error in the last operation + If there is an error, raise it + + Args: + batch_size (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True + + Returns: + + """ + error = find_error(batch_size=batch_size, decode_error=decode_error) + if error is not None: + raise error + + +def handle_errors( + continue_on_batch_error: bool, batch_size: int = 1, decode_error: bool = True +) -> PowerGridBatchError | None: + """ + Handle any errors in the way that is specified. + + Args: + continue_on_batch_error (bool): Return the error when the error type is a batch error instead of reraising it. + batch_size (int, optional): Size of batch. Defaults to 1. + decode_error (bool, optional): Decode the error message(s) to derived error classes. Defaults to True + + Raises: + error: Any errors previously encountered, unless it was a batch error and continue_on_batch_error was True. + + Returns: + PowerGridBatchError | None: None if there were no errors, or the previously encountered + error if it was a batch error and continue_on_batch_error was True. + """ + error: RuntimeError | None = find_error(batch_size=batch_size, decode_error=decode_error) + if error is None: + return None + + if continue_on_batch_error and isinstance(error, PowerGridBatchError): + # continue on batch error + return error + + # raise normal error + raise error diff --git a/src/power_grid_model_io_native/_core/power_grid_model_io_core.py b/src/power_grid_model_io_native/_core/power_grid_model_io_core.py index 56c74ed..920360b 100644 --- a/src/power_grid_model_io_native/_core/power_grid_model_io_core.py +++ b/src/power_grid_model_io_native/_core/power_grid_model_io_core.py @@ -9,7 +9,7 @@ from inspect import signature from itertools import chain from pathlib import Path -from typing import Callable, Optional +from typing import Callable from power_grid_model._core.power_grid_core import CharPtr, CStr, IdxC @@ -27,6 +27,12 @@ class HandlePtr(c_void_p): """ +class PgmVnfConverterPtr(c_void_p): + """ + Pointer to PgmVnfConverter + """ + + def _load_core() -> CDLL: """ @@ -112,9 +118,9 @@ class PowerGridModelIoCore: """ _handle: HandlePtr - _instance: Optional["PowerGridModelIoCore"] = None + _instance: "PowerGridModelIoCore | None" = None - # singleton of power grid core + # singleton of power grid model io core def __new__(cls, *args, **kwargs): if cls._instance is None: cls._instance = super().__new__(cls, *args, **kwargs) @@ -139,6 +145,18 @@ def error_code(self) -> int: # type: ignore[empty-body] def error_message(self) -> str: # type: ignore[empty-body] pass # pragma: no cover + @make_c_binding + def create_vnf_converter(self, data: str, experim_feature: int) -> PgmVnfConverterPtr: # type: ignore[empty-body] + pass # pragma: no cover + + @make_c_binding + def vnf_pgm_converter_get_input_data(self, pgmvnfconverter: PgmVnfConverterPtr) -> str: # type: ignore[empty-body] + pass # pragma: no cover + + @make_c_binding + def destroy_vnf_converter(self, pgmvnfconverter: PgmVnfConverterPtr) -> None: # type: ignore[empty-body] + pass # pragma: no cover + # make one instance pgm_io_core = PowerGridModelIoCore() diff --git a/src/power_grid_model_io_native/_core/vnf_converter.py b/src/power_grid_model_io_native/_core/vnf_converter.py new file mode 100644 index 0000000..17b4337 --- /dev/null +++ b/src/power_grid_model_io_native/_core/vnf_converter.py @@ -0,0 +1,44 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 + +""" +Power grid model io native converter for vnf files +""" + +from power_grid_model_io_native._core.error_handling import assert_no_error +from power_grid_model_io_native._core.power_grid_model_io_core import PgmVnfConverterPtr, pgm_io_core as pgmic + + +class PgmVnfConverter: + """A converter class which will convert a given string representation of .vnf data to the PowerGridModel format""" + + _pgm_vnf_converter: PgmVnfConverterPtr + _serialized_data: str + + def __new__( + cls, + string_buffer: str, + experimental_feature: int, + ): + instance = super().__new__(cls) + + instance._pgm_vnf_converter = pgmic.create_vnf_converter(string_buffer, experimental_feature) + assert_no_error() + + return instance + + def __del__(self): + if hasattr(self, "_pgm_vnf_converter"): + pgmic.destroy_vnf_converter(self._pgm_vnf_converter) + + def get_pgm_input_data(self): + """A function of the PgmVnfConverter class which will convert and return the data in PGM format + + Returns: + str: json data in PGM format + """ + pgm_data = pgmic.vnf_pgm_converter_get_input_data(self._pgm_vnf_converter) + assert_no_error() + self._serialized_data = pgm_data + return self._serialized_data diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..1296dc4 --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 diff --git a/tests/c_api_tests/test_c_api_vnf_converter.cpp b/tests/c_api_tests/test_c_api_vnf_converter.cpp index 00ac5a4..d7d14be 100644 --- a/tests/c_api_tests/test_c_api_vnf_converter.cpp +++ b/tests/c_api_tests/test_c_api_vnf_converter.cpp @@ -45,7 +45,7 @@ TEST_CASE("Test PGM_IO_get_vnf_input_data") { auto converter = PGM_IO_create_vnf_converter(handle, "", experimental_feature_flag); CHECK(converter != nullptr); - auto json_result = PGM_IO_get_vnf_input_data(handle, converter); + auto json_result = PGM_IO_vnf_pgm_converter_get_input_data(handle, converter); std::string_view json_string = R"({"version":"1.0","type":"input","is_batch":false,"attributes":{},"data":{}})"; CHECK(json_string == json_result); diff --git a/tests/unit/__init__.py b/tests/unit/__init__.py new file mode 100644 index 0000000..1296dc4 --- /dev/null +++ b/tests/unit/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: Contributors to the Power Grid Model project +# +# SPDX-License-Identifier: MPL-2.0 diff --git a/tests/unit/test_vnf_converter.py b/tests/unit/test_vnf_converter.py index f464f29..ff9fddc 100644 --- a/tests/unit/test_vnf_converter.py +++ b/tests/unit/test_vnf_converter.py @@ -2,9 +2,28 @@ # # SPDX-License-Identifier: MPL-2.0 -from power_grid_model_io_native._core.power_grid_model_io_core import pgm_io_core +import pytest +from power_grid_model._core.error_handling import InvalidArguments +from power_grid_model_io_native._core.error_handling import assert_no_error +from power_grid_model_io_native._core.vnf_converter import PgmVnfConverter -def test_nothing(): - assert pgm_io_core.error_code() == 0 - assert pgm_io_core.error_message() == "" + +def test_pgmvnfconverter_constructor_without_experimental_features(): + """A test case for creating pgmvnfconverter without experimental features""" + with pytest.raises(InvalidArguments): + _ = PgmVnfConverter("", 0) + + +def test_pgmvnfconverter_constructor_with_experimental_features(): + """A test case for creating pgmvnfconverter with experimental features""" + _ = PgmVnfConverter("", 1) + assert_no_error() + + +def test_get_pgm_input_data(): + """A test case for obtaining the data in PGM format from pgmvnfconverter""" + converter = PgmVnfConverter("", 1) + result_buffer = converter.get_pgm_input_data() + json_output = '{"version":"1.0","type":"input","is_batch":false,"attributes":{},"data":{}}' + assert result_buffer == json_output