Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ if pyprojectwheelbuild_enabled
'src/example_fgen_basic/error_v/creation_wrapper.f90',
'src/example_fgen_basic/error_v/error_v_wrapper.f90',
'src/example_fgen_basic/error_v/passing_wrapper.f90',
'src/example_fgen_basic/get_square_root_wrapper.f90',
'src/example_fgen_basic/get_wavelength_wrapper.f90',
'src/example_fgen_basic/result/result_dp_wrapper.f90',
)

# Specify all the other source Fortran files (original files and managers)
Expand All @@ -66,8 +68,15 @@ if pyprojectwheelbuild_enabled
'src/example_fgen_basic/error_v/passing.f90',
'src/example_fgen_basic/fpyfgen/base_finalisable.f90',
'src/example_fgen_basic/fpyfgen/derived_type_manager_helpers.f90',
'src/example_fgen_basic/get_square_root.f90',
'src/example_fgen_basic/get_wavelength.f90',
'src/example_fgen_basic/kind_parameters.f90',
'src/example_fgen_basic/result/result.f90',
'src/example_fgen_basic/result/result_dp.f90',
'src/example_fgen_basic/result/result_dp_manager.f90',
'src/example_fgen_basic/result/result_int.f90',
'src/example_fgen_basic/result/result_int1D.f90',
'src/example_fgen_basic/result/result_none.f90',
)

# All Python files (wrappers and otherwise)
Expand All @@ -79,9 +88,12 @@ if pyprojectwheelbuild_enabled
'src/example_fgen_basic/error_v/error_v.py',
'src/example_fgen_basic/error_v/passing.py',
'src/example_fgen_basic/exceptions.py',
'src/example_fgen_basic/get_square_root.py',
'src/example_fgen_basic/get_wavelength.py',
'src/example_fgen_basic/pyfgen_runtime/__init__.py',
'src/example_fgen_basic/pyfgen_runtime/exceptions.py',
'src/example_fgen_basic/result/__init__.py',
'src/example_fgen_basic/result/result_dp.py',
'src/example_fgen_basic/typing.py',
)

Expand Down
1 change: 1 addition & 0 deletions scripts/inject-srcs-into-meson-build.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ def main():
meson_variable, sorted(src_paths), REPO_ROOT
)

# TODO: something wrong in here
meson_build_out = re.sub(pattern, substitution, meson_build_out)

with open(REPO_ROOT / "meson.build", "w") as fh:
Expand Down
2 changes: 1 addition & 1 deletion src/example_fgen_basic/error_v/creation.f90
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module m_error_v_creation

use m_error_v, only: ErrorV, NO_ERROR_CODE

implicit none (type, external)
implicit none(type, external)
private

public :: create_error, create_errors
Expand Down
4 changes: 3 additions & 1 deletion src/example_fgen_basic/error_v/creation.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,9 @@ def create_errors(invs: NP_ARRAY_OF_INT) -> tuple[ErrorV, ...]:
Created errors
"""
# Get the result, but receiving an instance index rather than the object itself
instance_indexes: NP_ARRAY_OF_INT = m_error_v_creation_w.create_errors(invs)
instance_indexes: NP_ARRAY_OF_INT = m_error_v_creation_w.create_errors(
invs, len(invs)
)

# Initialise the result from the received index
res = tuple(ErrorV.from_instance_index(i) for i in instance_indexes)
Expand Down
2 changes: 1 addition & 1 deletion src/example_fgen_basic/error_v/creation_wrapper.f90
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ module m_error_v_creation_w
error_v_manager_set_instance_index_to => set_instance_index_to, &
error_v_manager_ensure_instance_array_size_is_at_least => ensure_instance_array_size_is_at_least

implicit none (type, external)
implicit none(type, external)
private

public :: create_error, create_errors
Expand Down
22 changes: 19 additions & 3 deletions src/example_fgen_basic/error_v/error_v.f90
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
!>
!> Fortran doesn't have a null value.
!> As a result, we introduce this derived type
!> with the convention that a code of 0 indicates no error.
!> with the convention that a code of `NO_ERROR_CODE` (0)
!> indicates no error (i.e. is our equivalent of a null value).
module m_error_v

implicit none (type, external)
implicit none(type, external)
private

integer, parameter, public :: NO_ERROR_CODE = 0
Expand All @@ -34,7 +35,9 @@ module m_error_v

private

procedure, public :: build, finalise
procedure, public :: build
procedure, public :: finalise
final :: finalise_auto
! get_res sort of not needed (?)
! get_err sort of not needed (?)

Expand Down Expand Up @@ -92,4 +95,17 @@ subroutine finalise(self)

end subroutine finalise

subroutine finalise_auto(self)
!! Finalise the instance (i.e. free/deallocate)
!!
!! This method is expected to be called automatically
!! by clever clean up, which is why it differs from [TODO x-ref] `finalise`

type(ErrorV), intent(inout) :: self
! Hopefully can leave without docstring (like Python)

call self % finalise()

end subroutine finalise_auto

end module m_error_v
38 changes: 31 additions & 7 deletions src/example_fgen_basic/error_v/error_v_manager.f90
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module m_error_v_manager

use m_error_v, only: ErrorV

implicit none (type, external)
implicit none(type, external)
private

type(ErrorV), dimension(:), allocatable :: instance_array
Expand Down Expand Up @@ -68,13 +68,17 @@ subroutine get_available_instance_index(available_instance_index)

instance_available(i) = .false.
available_instance_index = i
! TODO: switch to returning a Result type
! res = ResultInt(data=i)
return

end if

end do

! TODO: switch to returning a Result type with an error set
! res = ResultInt(ErrorV(code=1, message="No available instances"))
print *, "print"
error stop 1

end subroutine get_available_instance_index
Expand Down Expand Up @@ -114,17 +118,37 @@ subroutine check_index_claimed(instance_index)
!! Instance index to check

if (instance_available(instance_index)) then
! TODO: switch to errors here - will require some thinking
! TODO: Switch to using Result here
! Use `ResultNone` which is a Result type
! that doesn't have a `data` attribute
! (i.e. if this succeeds, there is no data to check,
! if it fails, the error_v attribute will be set).
! So the code would be something like
! res = ResultNone(ErrorV(code=1, message="Index ", instance_index, " has not been claimed"))
print *, "Index ", instance_index, " has not been claimed"
error stop 1
end if

if (instance_index < 1) then
! TODO: switch to errors here - will require some thinking
! TODO: Switch to using Result here
! Use `ResultNone` which is a Result type
! that doesn't have a `data` attribute
! (i.e. if this succeeds, there is no data to check,
! if it fails, the error_v attribute will be set).
! So the code would be something like
! res = ResultNone(ErrorV(code=2, message="Requested index is ", instance_index, " which is less than 1"))
print *, "Requested index is ", instance_index, " which is less than 1"
error stop 1
end if

! ! Here, result becomes
! ! Now that I've thought about this, it's also clear
! ! that we will only use functions
! ! or subroutines with a result type that has `intent(out)`.
! ! We will no longer have subroutines that return nothing
! ! (like this one currently does).
! res = ResultNone()

end subroutine check_index_claimed

subroutine ensure_instance_array_size_is_at_least(n)
Expand All @@ -137,19 +161,19 @@ subroutine ensure_instance_array_size_is_at_least(n)

if (.not. allocated(instance_array)) then

allocate(instance_array(n))
allocate (instance_array(n))

allocate(instance_available(n))
allocate (instance_available(n))
! Race conditions ?
instance_available = .true.

else if (size(instance_available) < n) then

allocate(tmp_instances(n))
allocate (tmp_instances(n))
tmp_instances(1:size(instance_array)) = instance_array
call move_alloc(tmp_instances, instance_array)

allocate(tmp_available(n))
allocate (tmp_available(n))
tmp_available(1:size(instance_available)) = instance_available
tmp_available(size(instance_available) + 1:size(tmp_available)) = .true.
call move_alloc(tmp_available, instance_available)
Expand Down
2 changes: 1 addition & 1 deletion src/example_fgen_basic/error_v/error_v_wrapper.f90
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ module m_error_v_w
error_v_manager_get_instance => get_instance, &
error_v_manager_ensure_instance_array_size_is_at_least => ensure_instance_array_size_is_at_least

implicit none (type, external)
implicit none(type, external)
private

public :: build_instance, finalise_instance, finalise_instances, &
Expand Down
2 changes: 1 addition & 1 deletion src/example_fgen_basic/error_v/passing.f90
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ module m_error_v_passing

use m_error_v, only: ErrorV, NO_ERROR_CODE

implicit none (type, external)
implicit none(type, external)
private

public :: pass_error, pass_errors
Expand Down
4 changes: 2 additions & 2 deletions src/example_fgen_basic/error_v/passing_wrapper.f90
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ module m_error_v_passing_w

! The manager module, which makes this all work
use m_error_v_manager, only: &
error_v_manager_get_instance => get_instance
error_v_manager_get_instance => get_instance
! error_v_manager_get_available_instance_index => get_available_instance_index, &
! error_v_manager_set_instance_index_to => set_instance_index_to, &
! error_v_manager_ensure_instance_array_size_is_at_least => ensure_instance_array_size_is_at_least

implicit none (type, external)
implicit none(type, external)
private

public :: pass_error, pass_errors
Expand Down
4 changes: 2 additions & 2 deletions src/example_fgen_basic/fpyfgen/base_finalisable.f90
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
!> across the Python-Fortran interface.
module fpyfgen_base_finalisable

implicit none (type, external)
implicit none(type, external)
private

integer, parameter, public :: INVALID_INSTANCE_INDEX = -1
Expand Down Expand Up @@ -38,7 +38,7 @@ subroutine derived_type_finalise(self)

import :: BaseFinalisable

implicit none (type, external)
implicit none(type, external)

class(BaseFinalisable), intent(inout) :: self

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module fpyfgen_derived_type_manager_helpers

use fpyfgen_base_finalisable, only: BaseFinalisable, invalid_instance_index

implicit none (type, external)
implicit none(type, external)
private

public :: get_derived_type_free_instance_number, &
Expand Down
36 changes: 36 additions & 0 deletions src/example_fgen_basic/get_square_root.f90
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
!> Get square root of a number
module m_get_square_root

use kind_parameters, only: dp
use m_error_v, only: ErrorV
use m_result_dp, only: ResultDP

implicit none(type, external)
private

public :: get_square_root

contains

function get_square_root(inv) result(res)
!! Get square root of a number

real(kind=dp), intent(in) :: inv
!! Frequency

type(ResultDP) :: res
!! Result
!!
!! Square root if the number is positive or zero.
!! Error otherwise.

if (inv >= 0) then
res = ResultDP(data_v=sqrt(inv))
else
! TODO: include input value in the message
res = ResultDP(error_v=ErrorV(code=1, message="Input value was negative"))
end if

end function get_square_root

end module m_get_square_root
72 changes: 72 additions & 0 deletions src/example_fgen_basic/get_square_root.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""
Get square root of a number
"""

from __future__ import annotations

from example_fgen_basic.pyfgen_runtime.exceptions import (
CompiledExtensionNotFoundError,
FortranError,
)
from example_fgen_basic.result import ResultDP

try:
from example_fgen_basic._lib import m_get_square_root_w # type: ignore
except (ModuleNotFoundError, ImportError) as exc: # pragma: no cover
raise CompiledExtensionNotFoundError(
"example_fgen_basic._lib.m_get_square_root_w"
) from exc

try:
from example_fgen_basic._lib import m_result_dp_w
except (ModuleNotFoundError, ImportError) as exc: # pragma: no cover
raise CompiledExtensionNotFoundError(
"example_fgen_basic._lib.m_result_dp_w"
) from exc


def get_square_root(inv: float) -> float:
"""
Get square root

Parameters
----------
inv
Value for which to get the square root

Returns
-------
:
Square root of `inv`

Raises
------
FortranError
`inv` is negative

TODO: use a more specific error
"""
result_instance_index: int = m_get_square_root_w.get_square_root(inv)
result = ResultDP.from_instance_index(result_instance_index)

if result.error_v is not None:
# TODO: be more specific
raise FortranError(result.error_v.message)
# raise LessThanZeroError(result.error_v.message)

if result.data_v is None:
raise AssertionError

res = result.data_v

# TODO: think
# I like the clarity of finalising result_instance_index here
# by having an explicit call
# (so you can see creation and finalisation in same place).
# (Probably the above is my preferred right now, but we should think about it.)
# I like the safety of finalising in `from_instance_index`.
# if not finalised(result_instance_index):
# finalise(result_instance_index)
m_result_dp_w.finalise_instance(result_instance_index)

return res
Loading
Loading