Skip to content

Commit eda78aa

Browse files
committed
Trying to figure out how to get python file install path right
1 parent 1af97d7 commit eda78aa

10 files changed

Lines changed: 610 additions & 6 deletions

File tree

meson.build

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ project(
1212
],
1313
)
1414

15+
# https://mesonbuild.com/Fs-module.html
16+
fs = import('fs')
17+
1518
# Some useful constants
1619
pyprojectwheelbuild_enabled = get_option('pyprojectwheelbuild').enabled()
1720

@@ -68,8 +71,15 @@ if pyprojectwheelbuild_enabled
6871
# Injected with `script/inject-srcs-into-meson-build.py`
6972
python_srcs = files(
7073
'src/example_fgen_basic/__init__.py',
74+
'src/example_fgen_basic/error_v/__init__.py',
75+
'src/example_fgen_basic/error_v/creation.py',
76+
'src/example_fgen_basic/error_v/error_v.py',
7177
'src/example_fgen_basic/exceptions.py',
7278
'src/example_fgen_basic/get_wavelength.py',
79+
'src/example_fgen_basic/pyfgen_runtime/__init__.py',
80+
'src/example_fgen_basic/pyfgen_runtime/base_finalisable.py',
81+
'src/example_fgen_basic/pyfgen_runtime/exceptions.py',
82+
'src/example_fgen_basic/pyfgen_runtime/formatting.py',
7383
'src/example_fgen_basic/runtime_helpers.py',
7484
)
7585

@@ -137,9 +147,12 @@ if pyprojectwheelbuild_enabled
137147
# this is where that operation happens.
138148
# Files get copied to <python directory>/site-packages/<subdir>
139149
foreach python_src : python_srcs
150+
message(python_src)
151+
message(python_project_name)
152+
message(fs.relative_to(python_src, python_project_name))
140153
py.install_sources(
141154
python_src,
142-
subdir: python_project_name,
155+
subdir: python_project_name / fs.relative_to(python_src, python_project_name),
143156
pure: false,
144157
)
145158

pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,14 @@ requires = [
130130
# https://mesonbuild.com/meson-python/how-to-guides/meson-args.html
131131
[tool.meson-python.args]
132132
setup = [
133-
'--default-library=static',
134-
'-Dpyprojectwheelbuild=enabled',
135-
]
133+
# '-v',
134+
'--default-library=static',
135+
'-Dpyprojectwheelbuild=enabled',
136+
]
136137
install = [
137-
'--skip-subprojects',
138+
'--skip-subprojects',
138139
]
140+
compile = ['-v']
139141

140142
[tool.coverage.run]
141143
source = [
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""
2+
Definition of an error value
3+
"""
4+
5+
from example_fgen_basic.error_v.error_v import ErrorV
6+
7+
__all__ = ["ErrorV"]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
Demonstration of how to return an error (i.e. derived type)
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from example_fgen_basic.error_v.error_v import ErrorV
8+
from example_fgen_basic.pyfgen_runtime.exceptions import CompiledExtensionNotFoundError
9+
10+
try:
11+
from example._lib import ( # type: ignore
12+
m_error_v_creation_w,
13+
)
14+
except (ModuleNotFoundError, ImportError) as exc:
15+
raise CompiledExtensionNotFoundError("example._lib.m_error_v_creation_w") from exc
16+
17+
18+
def create_error(inv: int) -> ErrorV:
19+
"""
20+
Create an instance of error (a wrapper around our Fortran derived type)
21+
"""
22+
instance_index = m_error_v_creation_w.create_error(inv)
23+
24+
error = ErrorV(instance_index)
25+
26+
return error
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""
2+
Wrapper of the Fortran :class:`ErrorV`
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from attrs import define
8+
9+
from example_fgen_basic.pyfgen_runtime.base_finalisable import (
10+
FinalisableWrapperBase,
11+
check_initialised,
12+
)
13+
from example_fgen_basic.pyfgen_runtime.exceptions import CompiledExtensionNotFoundError
14+
15+
try:
16+
from example._lib import ( # type: ignore
17+
m_error_v_creation_w,
18+
)
19+
except (ModuleNotFoundError, ImportError) as exc:
20+
raise CompiledExtensionNotFoundError("example._lib.m_error_v_creation_w") from exc
21+
22+
23+
@define
24+
class ErrorV(FinalisableWrapperBase):
25+
"""
26+
TODO: auto docstring e.g. "Wrapper around the Fortran :class:`ErrorV`"
27+
"""
28+
29+
@property
30+
def exposed_attributes(self) -> tuple[str, ...]:
31+
"""
32+
Attributes exposed by this wrapper
33+
"""
34+
return ("code", "message")
35+
36+
# TODO: from_build_args, from_new_connection, context manager, finalise
37+
38+
@property
39+
@check_initialised
40+
def code(self) -> int:
41+
"""
42+
Error code
43+
44+
Returns
45+
-------
46+
:
47+
Error code, retrieved from Fortran
48+
"""
49+
code: int = m_error_v_creation_w.iget_code(instance_index=self.instance_index)
50+
51+
return code
52+
53+
@property
54+
@check_initialised
55+
def message(self) -> str:
56+
"""
57+
Error message
58+
59+
Returns
60+
-------
61+
:
62+
Error message, retrieved from Fortran
63+
"""
64+
message: str = m_error_v_creation_w.iget_message(
65+
instance_index=self.instance_index
66+
).decode()
67+
68+
return message

src/example_fgen_basic/get_wavelength.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@
1818
try:
1919
from example_fgen_basic._lib import m_get_wavelength_w # type: ignore
2020
except (ModuleNotFoundError, ImportError) as exc: # pragma: no cover
21-
raise CompiledExtensionNotFoundError("example_fgen_basic._lib") from exc
21+
raise CompiledExtensionNotFoundError(
22+
"example_fgen_basic._lib.m_get_wavelength_w"
23+
) from exc
2224

2325

2426
def get_wavelength_plain(frequency: float) -> float:
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
"""
2+
Runtime helpers for code generated with pyfgen
3+
4+
Code that will eventually get moved into a standalone package
5+
"""
6+
7+
from __future__ import annotations
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
"""
2+
Runtime helper to support wrapping of Fortran derived types
3+
"""
4+
5+
from __future__ import annotations
6+
7+
from abc import ABC, abstractmethod
8+
from functools import wraps
9+
from typing import Any, Callable, Concatenate, ParamSpec, TypeVar
10+
11+
import attrs
12+
from attrs import define, field
13+
14+
from example_fgen_basic.pyfgen_runtime.exceptions import (
15+
NotInitialisedError,
16+
)
17+
from example_fgen_basic.pyfgen_runtime.formatting import to_html, to_pretty, to_str
18+
19+
# Might be needed for Python 3.9
20+
# from typing_extensions import Concatenate, ParamSpec
21+
22+
23+
INVALID_INSTANCE_INDEX: int = -1
24+
"""
25+
Value used to denote an invalid `instance_index`.
26+
27+
This can occur value when a wrapper class
28+
has not yet been initialised (connected to a Fortran instance).
29+
"""
30+
31+
32+
@define
33+
class FinalisableWrapperBase(ABC):
34+
"""
35+
Base class for Fortran derived type wrappers
36+
"""
37+
38+
instance_index: int = field(
39+
validator=attrs.validators.instance_of(int),
40+
default=INVALID_INSTANCE_INDEX,
41+
)
42+
"""
43+
Model index of wrapper Fortran instance
44+
"""
45+
46+
def __str__(self) -> str:
47+
"""
48+
Get string representation of self
49+
"""
50+
return to_str(
51+
self,
52+
self.exposed_attributes,
53+
)
54+
55+
def _repr_pretty_(self, p: Any, cycle: bool) -> None:
56+
"""
57+
Get pretty representation of self
58+
59+
Used by IPython notebooks and other tools
60+
"""
61+
to_pretty(
62+
self,
63+
self.exposed_attributes,
64+
p=p,
65+
cycle=cycle,
66+
)
67+
68+
def _repr_html_(self) -> str:
69+
"""
70+
Get html representation of self
71+
72+
Used by IPython notebooks and other tools
73+
"""
74+
return to_html(
75+
self,
76+
self.exposed_attributes,
77+
)
78+
79+
@property
80+
def initialized(self) -> bool:
81+
"""
82+
Is the instance initialised, i.e. connected to a Fortran instance?
83+
"""
84+
return self.instance_index != INVALID_INSTANCE_INDEX
85+
86+
@property
87+
@abstractmethod
88+
def exposed_attributes(self) -> tuple[str, ...]:
89+
"""
90+
Attributes exposed by this wrapper
91+
"""
92+
...
93+
94+
# TODO: consider whether we need these
95+
# @classmethod
96+
# @abstractmethod
97+
# def from_new_connection(cls) -> FinalisableWrapperBase:
98+
# """
99+
# Initialise by establishing a new connection with the Fortran module
100+
#
101+
# This requests a new model index from the Fortran module and then
102+
# initialises a class instance
103+
#
104+
# Returns
105+
# -------
106+
# New class instance
107+
# """
108+
# ...
109+
#
110+
# @abstractmethod
111+
# def finalize(self) -> None:
112+
# """
113+
# Finalise the Fortran instance and set self back to being uninitialised
114+
#
115+
# This method resets ``self.instance_index`` back to
116+
# ``_UNINITIALISED_instance_index``
117+
#
118+
# Should be decorated with :func:`check_initialised`
119+
# """
120+
# # call to Fortran module goes here when implementing
121+
# self._uninitialise_instance_index()
122+
123+
def _uninitialise_instance_index(self) -> None:
124+
self.instance_index = INVALID_INSTANCE_INDEX
125+
126+
127+
P = ParamSpec("P")
128+
T = TypeVar("T")
129+
Wrapper = TypeVar("Wrapper", bound=FinalisableWrapperBase)
130+
131+
132+
def check_initialised(
133+
method: Callable[Concatenate[Wrapper, P], T],
134+
) -> Callable[Concatenate[Wrapper, P], T]:
135+
"""
136+
Check that the wrapper object has been initialised before executing the method
137+
138+
Parameters
139+
----------
140+
method
141+
Method to wrap
142+
143+
Returns
144+
-------
145+
:
146+
Wrapped method
147+
148+
Raises
149+
------
150+
InitialisationError
151+
Wrapper is not initialised
152+
"""
153+
154+
@wraps(method)
155+
def checked(
156+
ref: Wrapper,
157+
*args: P.args,
158+
**kwargs: P.kwargs,
159+
) -> Any:
160+
if not ref.initialized:
161+
raise NotInitialisedError(ref, method)
162+
163+
return method(ref, *args, **kwargs)
164+
165+
return checked # type: ignore

0 commit comments

Comments
 (0)