Skip to content

Commit 0ccf977

Browse files
committed
Passing before error bubble-up
1 parent da8421d commit 0ccf977

10 files changed

Lines changed: 102 additions & 43 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ test: ## run the tests (re-installs the package every time so you might want to
5353
uv run --no-sync python -c 'from pathlib import Path; import example_fgen_basic' || ( echo "Run make virtual-environment first" && false )
5454
COV_DIR=$$(uv run --no-sync python -c 'from pathlib import Path; import example_fgen_basic; print(Path(example_fgen_basic.__file__).parent)'); \
5555
uv run --no-editable --reinstall-package example-fgen-basic pytest -s -r a -v tests src --doctest-modules --doctest-report ndiff --cov=$$COV_DIR
56-
# uv run --no-editable --reinstall-package example-fgen-basic pytest -s -r a -v tests/unit/test_get_square_root.py src --doctest-modules --doctest-report ndiff --cov=$$COV_DIR
56+
# uv run --no-editable --reinstall-package example-fgen-basic pytest -s -r a -v tests/unit/test_get_square_root.py src --doctest-modules --doctest-report ndiff --cov=$$COV_DIR
5757

5858
# Note on code coverage and testing:
5959
# You must specify cov=src.

fortitude.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
select = [ "C", "E", "S", "PORT" ]
66
#Ignoring:
77
# C003: 'implicit none' missing 'external' [f2py does not recognize the syntax implicit none(type, external)]
8-
ignore = ["C003","C072","S221"]
8+
# ignore = ["C003","C072","S221"]
9+
ignore = ["C003"]
910
line-length = 120

src/example_fgen_basic/error_v/error_v.f90

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ module m_error_v
3030
! (means you can stop but also unwind errors and traceback along the way)
3131

3232
! TODO: think about adding trace (might be simpler than compiling with traceback)
33-
! class(ErrorV), allocatable :: cause
34-
type(ErrorV), pointer :: cause => null()
33+
! class(ErrorV), allocatable :: cause
34+
type(ErrorV), pointer :: cause => null()
3535

3636
contains
3737

@@ -141,6 +141,41 @@ subroutine build(self, code, message, cause)
141141
end if
142142

143143
end subroutine build
144+
! subroutine build(self, code, message, cause)
145+
! !! Build instance
146+
!
147+
! class(ErrorV), intent(out) :: self
148+
! ! Hopefully can leave without docstring (like Python)
149+
!
150+
! integer, intent(in) :: code
151+
! !! Error code
152+
! !!
153+
! !! Use [TODO: figure out xref] `NO_ERROR_CODE` if there is no error
154+
!
155+
! character(len=*), optional, intent(in) :: message
156+
! !! Error message
157+
! type(ErrorV), target, optional, intent(in) :: cause
158+
!
159+
! self % code = code
160+
!
161+
! if (present(cause)) then
162+
! ! self % cause => cause
163+
! ! allocate(self % cause)
164+
! ! call self%cause%build(cause%code, cause%message, cause%cause)
165+
! ! self%cause = cause
166+
! if (present(message)) then
167+
! self % message = adjustl(trim(message)) // " --> Cause: " // cause % message
168+
! else
169+
! self % message = " --> Cause: " // cause % message
170+
! end if
171+
!
172+
! else
173+
! if (present(message)) then
174+
! self % message = adjustl(trim(message))
175+
! end if
176+
! end if
177+
!
178+
! end subroutine build
144179

145180
subroutine finalise(self)
146181
!! Finalise the instance (i.e. free/deallocate)
@@ -153,7 +188,8 @@ subroutine finalise(self)
153188
if (allocated(self%message)) deallocate(self%message)
154189
! MZ when the object is finalized or goes out of scope, its pointer components are destroyed.
155190
! Hopefully no shared ownership??
156-
if (associated(self%cause)) nullify(self%cause)
191+
if (associated(self%cause)) nullify(self%cause)
192+
! if (allocated(self%cause)) deallocate(self%cause)
157193

158194
end subroutine finalise
159195

src/example_fgen_basic/get_square_root.f90

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ function get_square_root(inv) result(res)
1717

1818
real(kind=dp), intent(in) :: inv
1919
!! Frequency
20+
character(len=:), allocatable :: msg
21+
character(len=10) :: input_char
2022

2123
type(ResultGen) :: res
2224
!! Result
@@ -25,10 +27,12 @@ function get_square_root(inv) result(res)
2527
!! Error otherwise.
2628

2729
if (inv >= 0) then
28-
res = ResultGen(tag=T_DP,data_dp=sqrt(inv))
30+
call res % build(tag=T_DP,data_dp=sqrt(inv))
2931
else
30-
! TODO: include input value in the message
31-
res = ResultGen(tag=T_ERR,error_v=ErrorV(code=1, message="Input value was negative"))
32+
write(input_char, "(F9.3)") inv
33+
msg = adjustl(trim("Error: Negative Input -> "// adjustl(trim(input_char))))
34+
35+
call res % build(tag=T_ERR,error_v=ErrorV(code=1, message=msg))
3236
end if
3337

3438
end function get_square_root

src/example_fgen_basic/get_square_root.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,11 @@ def get_square_root(inv: float) -> float:
4747
TODO: use a more specific error
4848
"""
4949
result_instance_index: int = m_get_square_root_w.get_square_root(inv)
50-
5150
result = ResultGen.from_instance_index(result_instance_index)
5251

5352
if result.error_v is not None:
5453
# TODO: be more specific
55-
m_result_w.finalise_instance(result_instance_index)
54+
# m_result_w.finalise_instance(result_instance_index)
5655
raise FortranError(result.error_v.message)
5756
# raise LessThanZeroError(result.error_v.message)
5857

src/example_fgen_basic/get_square_root_wrapper.f90

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
!> Wrapper for interfacing `m_get_square_root` with python
22
module m_get_square_root_w
33

4-
use m_result_gen, only: ResultGen
4+
use m_result_gen, only: ResultGen,T_ERR
55

66
use m_get_square_root, only: o_get_square_root => get_square_root
77

@@ -50,6 +50,12 @@ function get_square_root(inv) result(res_instance_index)
5050
! ready for its attributes to be retrieved from Python.
5151
! MZ it would be probably good to check "res_chk" for errors
5252
! res_chk = result_dp_manager_set_instance_index_to(res_instance_index, res)
53+
if (res%tag == T_ERR) then
54+
call result_manager_set_instance_index_to(instance_index=res_instance_index,&
55+
error_v=res%error_v, res_check = res_chk)
56+
return
57+
end if
58+
5359
call result_manager_set_instance_index_to(instance_index=res_instance_index,&
5460
data_dp=res%data_dp, res_check = res_chk)
5561

src/example_fgen_basic/result/result_gen.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,36 +29,39 @@ class ResultGen:
2929
error_v: ErrorV | None
3030
"""Error"""
3131

32+
"""
33+
Parameters:
34+
"""
35+
__fs_int = m_result_w.s_int
36+
__fs_dp = m_result_w.s_dp
37+
__fs_err = m_result_w.s_err
38+
3239
@classmethod
3340
def from_instance_index(cls, instance_index: int) -> ResultGen:
3441
"""
3542
Initialise from an instance index received from Fortran
3643
3744
Parameters
3845
----------
39-
intance_index
46+
instance_index
4047
Instance index received form Fortran
4148
4249
Returns
4350
-------
4451
:
4552
Initalised index
4653
"""
47-
T_INT = 1
48-
T_DP = 2
49-
T_ERR = 3
50-
5154
tag = m_result_w.get_instance_tag(instance_index)
5255

53-
if tag == T_INT:
56+
if tag == cls.__fs_int:
5457
data_v: int | None = m_result_w.get_data_int(instance_index)
5558
error_v = None
5659

57-
elif tag == T_DP:
60+
elif tag == cls.__fs_dp:
5861
data_v: float | None = m_result_w.get_data_dp(instance_index)
5962
error_v = None
6063

61-
elif tag == T_ERR:
64+
elif tag == cls.__fs_err:
6265
data_v = None
6366
error_tuple: tuple[int | None, str | None] = m_result_w.get_error(
6467
instance_index

src/example_fgen_basic/result/result_manager.f90

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module m_result_manager
1616

1717
contains
1818

19-
subroutine build_instance(tag, data_int, data_dp, error_v, instance_index,res_check)
19+
subroutine build_instance(tag, data_int, data_dp, error_v, instance_index, res_check)
2020

2121
integer, intent(in) :: tag
2222
integer(kind=i8),optional, intent(in) :: data_int

src/example_fgen_basic/result/result_wrapper.f90

Lines changed: 25 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ module m_result_w
2424
private
2525

2626
public :: build_instance_int, build_instance_dp, build_instance_err,&
27-
get_instance_tag, get_data_int, get_data_dp, &
28-
finalise_instance, finalise_instances
27+
finalise_instance, finalise_instances, &
28+
get_instance_tag, get_data_int, get_data_dp, get_error
29+
30+
integer, parameter, public :: s_claimed=T_CLAIM, s_none=T_NONE, s_int=T_INT, s_dp=T_DP, s_err=T_ERR
2931

3032
contains
3133

@@ -94,6 +96,9 @@ function build_instance_err(error_v_instance_index) result(instance_index)
9496

9597
type(ErrorV) :: error_v
9698
type(ResultGen) :: res_check
99+
! integer :: code
100+
! character(len=10) :: int2char
101+
! character(len=:), allocatable :: message
97102

98103
if (error_v_instance_index > 0) then
99104

@@ -109,8 +114,13 @@ function build_instance_err(error_v_instance_index) result(instance_index)
109114

110115
else
111116

112-
! maybe generate an error
113-
print *, "Provided code does NOT match any ERROR type"
117+
call error_v % build(code = 1, message = "Provided code does NOT match any ERROR type")
118+
call result_manager_build_instance(&
119+
tag = T_ERR, &
120+
error_v = error_v, &
121+
instance_index= instance_index,&
122+
res_check = res_check &
123+
)
114124

115125
end if
116126

@@ -150,12 +160,6 @@ function get_data_int(instance_index) result(data_int)
150160

151161
res_stored = result_manager_get_instance(instance_index)
152162

153-
if(res_stored % tag /= T_INT) then
154-
! ERROR in a smarter way
155-
print *, "TAG type does not match the expected type"
156-
return
157-
end if
158-
159163
data_int = res_stored % data_int
160164

161165
end function get_data_int
@@ -170,14 +174,7 @@ function get_data_dp(instance_index) result(data_dp)
170174

171175
res_stored = result_manager_get_instance(instance_index)
172176

173-
! Think if it is worth checking
174-
if(res_stored % tag /= T_DP) then
175-
! ERROR in a smarter way
176-
print *, "TAG type does not match the expected type"
177-
return
178-
end if
179-
180-
data_dp = res_stored% data_dp
177+
data_dp = res_stored % data_dp
181178

182179
end function get_data_dp
183180

@@ -186,15 +183,21 @@ subroutine get_error(instance_index,code,message)
186183

187184
integer, intent(in) :: instance_index
188185
integer, intent(out) :: code
189-
character(len=*), intent(out) :: message
186+
! MZ: How to avoid long fixed length??
187+
character(len=1000), intent(out) :: message
188+
character(len=10) :: int2char
190189
type(ResultGen) :: res_stored
191190

192191
res_stored = result_manager_get_instance(instance_index)
193192

194-
! Think if it is worth checking
193+
! Think if it is worth checking as the Python side should already deal with it. Should be built an error?
195194
if(res_stored % tag /= T_ERR) then
196-
! ERROR in a smarter way
197-
print *, "TAG type does not match the expected type"
195+
! ERROR in a smarter way?
196+
code = 1
197+
write(int2char,"(I0)") instance_index
198+
message = "TAG mismatch! Expected -> ERROR but index: " // adjustl(trim(int2char))
199+
write(int2char,"(I0)") res_stored % tag
200+
message = adjustl(trim(message)) // " has TAG = " // adjustl(trim(int2char))
198201
return
199202
end if
200203

tests/unit/test_get_square_root.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,20 @@
55
import pytest
66

77
from example_fgen_basic.get_square_root import get_square_root
8+
from example_fgen_basic.pyfgen_runtime.exceptions import (
9+
FortranError,
10+
)
811

912

1013
@pytest.mark.parametrize(
1114
"inv, exp, exp_error",
1215
[
1316
(4.0, 2.0, None),
14-
# (-4.0, None, pytest.raises(FortranError, match="Input value was negative")),
17+
(
18+
-4.0,
19+
None,
20+
pytest.raises(FortranError, match="Error: Negative Input -> -4.000"),
21+
),
1522
],
1623
)
1724
def test_basic(inv, exp, exp_error):

0 commit comments

Comments
 (0)