Skip to content

Commit

Permalink
Merge pull request #16 from PowerGridModel/feature/python-api
Browse files Browse the repository at this point in the history
Feature/python api
  • Loading branch information
mgovers authored Dec 4, 2024
2 parents 0d1c894 + f8cebbb commit d9ec3fe
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 26 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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] {
Expand All @@ -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; }
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = [
Expand Down
98 changes: 98 additions & 0 deletions src/power_grid_model_io_native/_core/error_handling.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
#
# 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
24 changes: 21 additions & 3 deletions src/power_grid_model_io_native/_core/power_grid_model_io_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -27,6 +27,12 @@ class HandlePtr(c_void_p):
"""


class PgmVnfConverterPtr(c_void_p):
"""
Pointer to PgmVnfConverter
"""


def _load_core() -> CDLL:
"""
Expand Down Expand Up @@ -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)
Expand All @@ -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()
44 changes: 44 additions & 0 deletions src/power_grid_model_io_native/_core/vnf_converter.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
#
# 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
3 changes: 3 additions & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
#
# SPDX-License-Identifier: MPL-2.0
2 changes: 1 addition & 1 deletion tests/c_api_tests/test_c_api_vnf_converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
3 changes: 3 additions & 0 deletions tests/unit/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# SPDX-FileCopyrightText: Contributors to the Power Grid Model project <[email protected]>
#
# SPDX-License-Identifier: MPL-2.0
27 changes: 23 additions & 4 deletions tests/unit/test_vnf_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit d9ec3fe

Please sign in to comment.