Skip to content

Commit 6bbf866

Browse files
authored
Merge branch 'develop' into feature/convert-core-edge-to-plasma-ids
2 parents 2a3c973 + 0de0782 commit 6bbf866

23 files changed

Lines changed: 105 additions & 186 deletions

conftest.py

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
import functools
1010
import logging
1111
import os
12-
import sys
1312
from copy import deepcopy
1413
from pathlib import Path
1514

@@ -22,7 +21,6 @@
2221
import pytest
2322
from packaging.version import Version
2423

25-
from imas.backends.imas_core.imas_interface import has_imas as _has_imas
2624
from imas.backends.imas_core.imas_interface import ll_interface, lowlevel
2725
from imas.dd_zip import dd_etree, dd_xml_versions, latest_dd_version
2826
from imas.ids_defs import (
@@ -39,17 +37,7 @@
3937

4038
os.environ["IMAS_AL_DISABLE_VALIDATE"] = "1"
4139

42-
43-
try:
44-
import imas # noqa
45-
except ImportError:
46-
47-
class SkipOnIMASAccess:
48-
def __getattr__(self, attr):
49-
pytest.skip("This test requires the `imas` HLI, which is not available.")
50-
51-
# Any test that tries to access an attribute from the `imas` package will be skipped
52-
sys.modules["imas"] = SkipOnIMASAccess()
40+
import imas # noqa
5341

5442

5543
def pytest_addoption(parser):
@@ -78,7 +66,6 @@ def pytest_addoption(parser):
7866
if "not available" in str(iex.message):
7967
_BACKENDS.pop("mdsplus")
8068

81-
8269
try:
8370
import pytest_xdist
8471
except ImportError:
@@ -91,28 +78,11 @@ def worker_id():
9178
@pytest.fixture(params=_BACKENDS)
9279
def backend(pytestconfig: pytest.Config, request: pytest.FixtureRequest):
9380
backends_provided = any(map(pytestconfig.getoption, _BACKENDS))
94-
if not _has_imas:
95-
if backends_provided:
96-
raise RuntimeError(
97-
"Explicit backends are provided, but IMAS is not available."
98-
)
99-
pytest.skip("No IMAS available, skip tests using a backend")
10081
if backends_provided and not pytestconfig.getoption(request.param):
10182
pytest.skip(f"Tests for {request.param} backend are skipped.")
10283
return _BACKENDS[request.param]
10384

10485

105-
@pytest.fixture()
106-
def has_imas():
107-
return _has_imas
108-
109-
110-
@pytest.fixture()
111-
def requires_imas():
112-
if not _has_imas:
113-
pytest.skip("No IMAS available")
114-
115-
11686
def pytest_generate_tests(metafunc):
11787
if "ids_name" in metafunc.fixturenames:
11888
if metafunc.config.getoption("ids"):
@@ -214,7 +184,7 @@ def wrapper(*args, **kwargs):
214184

215185

216186
@pytest.fixture
217-
def log_lowlevel_calls(monkeypatch, requires_imas):
187+
def log_lowlevel_calls(monkeypatch):
218188
"""Debugging fixture to log calls to the imas lowlevel module."""
219189
for al_function in dir(lowlevel):
220190
if al_function.startswith("ual_") or al_function.startswith("al"):

imas/backends/db_entry_impl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def get(
7878
destination: IDSToplevel,
7979
lazy: bool,
8080
nbc_map: Optional[NBCPathMap],
81-
) -> None:
81+
) -> IDSToplevel:
8282
"""Implement DBEntry.get/get_slice/get_sample. Load data from the data source.
8383
8484
Args:

imas/backends/imas_core/db_entry_al.py

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838

3939
from .al_context import ALContext, LazyALContext
4040
from .db_entry_helpers import delete_children, get_children, put_children
41-
from .imas_interface import LLInterfaceError, has_imas, ll_interface
41+
from .imas_interface import LLInterfaceError, ll_interface
4242
from .mdsplus_model import mdsplus_model_dir
4343
from .uda_support import extract_idsdef, get_dd_version_from_idsdef_xml
4444

@@ -52,14 +52,6 @@
5252
logger = logging.getLogger(__name__)
5353

5454

55-
def require_imas_available():
56-
if not has_imas:
57-
raise RuntimeError(
58-
"The IMAS Core library is not available. Please install 'imas_core', "
59-
"or load a supported IMAS module if you use an HPC environment."
60-
)
61-
62-
6355
class ALDBEntryImpl(DBEntryImpl):
6456
"""DBEntry implementation using imas_core as a backend."""
6557

@@ -86,7 +78,6 @@ def __init__(self, uri: str, mode: int, factory: IDSFactory):
8678

8779
@classmethod
8880
def from_uri(cls, uri: str, mode: str, factory: IDSFactory) -> "ALDBEntryImpl":
89-
require_imas_available()
9081
if mode not in _OPEN_MODES:
9182
modes = list(_OPEN_MODES)
9283
raise ValueError(f"Unknown mode {mode!r}, was expecting any of {modes}")
@@ -105,8 +96,6 @@ def from_pulse_run(
10596
options: Any,
10697
factory: IDSFactory,
10798
) -> "ALDBEntryImpl":
108-
# Raise an error if imas is not available
109-
require_imas_available()
11099

111100
# Set defaults
112101
user_name = user_name or getpass.getuser()

imas/backends/imas_core/imas_interface.py

Lines changed: 9 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,17 @@
1212

1313
from packaging.version import Version
1414

15-
logger = logging.getLogger(__name__)
15+
# Import the Access Layer module
16+
# First try to import imas_core, which is available since AL 5.2
17+
from imas_core import _al_lowlevel as lowlevel
18+
from imas_core import imasdef # noqa: F401
1619

20+
logger = logging.getLogger(__name__)
1721

18-
# Import the Access Layer module
19-
has_imas = True
20-
try:
21-
# First try to import imas_core, which is available since AL 5.2
22-
from imas_core import _al_lowlevel as lowlevel
23-
from imas_core import imasdef
24-
25-
# Enable throwing exceptions from the _al_lowlevel interface
26-
enable_exceptions = getattr(lowlevel, "imas_core_config_enable_exceptions", None)
27-
if enable_exceptions:
28-
enable_exceptions()
29-
30-
except ImportError as exc:
31-
imas = None
32-
has_imas = False
33-
imasdef = None
34-
lowlevel = None
35-
logger.warning(
36-
"Could not import 'imas_core': %s. Some functionality is not available.",
37-
exc,
38-
)
22+
# Enable throwing exceptions from the _al_lowlevel interface
23+
enable_exceptions = getattr(lowlevel, "imas_core_config_enable_exceptions", None)
24+
if enable_exceptions:
25+
enable_exceptions()
3926

4027

4128
class LLInterfaceError(RuntimeError):

imas/backends/netcdf/nc2ids.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ def run(self, lazy: bool) -> None:
157157
for index, node in indexed_tree_iter(self.ids, target_metadata):
158158
value = data[index]
159159
if value != getattr(var, "_FillValue", None):
160+
if isinstance(value, np.generic):
161+
value = value.item()
160162
# NOTE: bypassing IDSPrimitive.value.setter logic
161163
node._IDSPrimitive__value = value
162164

@@ -166,10 +168,16 @@ def run(self, lazy: bool) -> None:
166168
# here, we'll let IDSPrimitive.value.setter take care of it:
167169
self.ids[target_metadata.path].value = data
168170

169-
else:
171+
# We need to unpack 0D ints, floats and complex numbers. For better
172+
# performance this check is done outside the for-loop:
173+
elif metadata.ndim or metadata.data_type is IDSDataType.STR:
170174
for index, node in indexed_tree_iter(self.ids, target_metadata):
171175
# NOTE: bypassing IDSPrimitive.value.setter logic
172176
node._IDSPrimitive__value = data[index]
177+
else:
178+
for index, node in indexed_tree_iter(self.ids, target_metadata):
179+
# NOTE: bypassing IDSPrimitive.value.setter logic
180+
node._IDSPrimitive__value = data[index].item() # Unpack 0D value
173181

174182
def validate_variables(self) -> None:
175183
"""Validate that all variables in the netCDF Group exist and match the DD."""
@@ -365,7 +373,7 @@ def get_child(self, child):
365373
value = var[self.index]
366374

367375
if value is not None:
368-
if isinstance(value, np.ndarray):
376+
if isinstance(value, (np.ndarray, np.generic)):
369377
if value.ndim == 0: # Unpack 0D numpy arrays:
370378
value = value.item()
371379
else:

imas/db_entry.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ def __init__(
160160
legacy = True
161161
except TypeError as exc2:
162162
raise TypeError(
163-
f"Incorrect arguments to {__class__.__name__}.__init__(): "
163+
"Incorrect arguments to DBEntry.__init__(): "
164164
f"{exc1.args[0]}, {exc2.args[0]}"
165165
) from None
166166

@@ -561,7 +561,7 @@ def _get(
561561
raise RuntimeError("Database entry is not open.")
562562
if lazy and destination:
563563
raise ValueError("Cannot supply a destination IDS when lazy loading.")
564-
if not self._ids_factory.exists(ids_name) and autoconvert:
564+
if autoconvert and not self._ids_factory.exists(ids_name):
565565
raise IDSNameError(ids_name, self._ids_factory)
566566

567567
# Note: this will raise an exception when the ids/occurrence is not filled:
@@ -577,7 +577,7 @@ def _get(
577577
ids_name,
578578
occurrence,
579579
)
580-
elif dd_version != self.dd_version and dd_version not in dd_xml_versions():
580+
elif dd_version not in dd_xml_versions() and dd_version != self.dd_version:
581581
# We don't know the DD version that this IDS was written with
582582
if ignore_unknown_dd_version:
583583
# User chooses to ignore this problem, load as if it was stored with

imas/exception.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,8 @@
2020

2121

2222
# Expose ALException, which may be thrown by the lowlevel
23-
if _imas_interface.has_imas:
24-
ALException = _imas_interface.lowlevel.ALException
25-
else:
26-
ALException = None
23+
24+
ALException = _imas_interface.lowlevel.ALException
2725

2826

2927
class IDSNameError(ValueError):

imas/ids_defs.py

Lines changed: 37 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -86,86 +86,46 @@
8686
Identifier for the default serialization protocol.
8787
"""
8888

89-
import functools
9089
import logging
9190

92-
from imas.backends.imas_core.imas_interface import has_imas, imasdef
91+
from imas.backends.imas_core.imas_interface import imasdef
9392

9493
logger = logging.getLogger(__name__)
9594

9695

97-
if has_imas:
98-
ASCII_BACKEND = imasdef.ASCII_BACKEND
99-
CHAR_DATA = imasdef.CHAR_DATA
100-
CLOSE_PULSE = imasdef.CLOSE_PULSE
101-
CLOSEST_INTERP = imasdef.CLOSEST_INTERP
102-
CREATE_PULSE = imasdef.CREATE_PULSE
103-
DOUBLE_DATA = imasdef.DOUBLE_DATA
104-
COMPLEX_DATA = imasdef.COMPLEX_DATA
105-
EMPTY_COMPLEX = imasdef.EMPTY_COMPLEX
106-
EMPTY_FLOAT = imasdef.EMPTY_FLOAT
107-
EMPTY_INT = imasdef.EMPTY_INT
108-
ERASE_PULSE = imasdef.ERASE_PULSE
109-
FORCE_CREATE_PULSE = imasdef.FORCE_CREATE_PULSE
110-
FORCE_OPEN_PULSE = imasdef.FORCE_OPEN_PULSE
111-
HDF5_BACKEND = imasdef.HDF5_BACKEND
112-
IDS_TIME_MODE_HETEROGENEOUS = imasdef.IDS_TIME_MODE_HETEROGENEOUS
113-
IDS_TIME_MODE_HOMOGENEOUS = imasdef.IDS_TIME_MODE_HOMOGENEOUS
114-
IDS_TIME_MODE_INDEPENDENT = imasdef.IDS_TIME_MODE_INDEPENDENT
115-
IDS_TIME_MODE_UNKNOWN = imasdef.IDS_TIME_MODE_UNKNOWN
116-
IDS_TIME_MODES = imasdef.IDS_TIME_MODES
117-
INTEGER_DATA = imasdef.INTEGER_DATA
118-
LINEAR_INTERP = imasdef.LINEAR_INTERP
119-
MDSPLUS_BACKEND = imasdef.MDSPLUS_BACKEND
120-
MEMORY_BACKEND = imasdef.MEMORY_BACKEND
121-
NODE_TYPE_STRUCTURE = imasdef.NODE_TYPE_STRUCTURE
122-
OPEN_PULSE = imasdef.OPEN_PULSE
123-
PREVIOUS_INTERP = imasdef.PREVIOUS_INTERP
124-
READ_OP = imasdef.READ_OP
125-
UDA_BACKEND = imasdef.UDA_BACKEND
126-
UNDEFINED_INTERP = imasdef.UNDEFINED_INTERP
127-
UNDEFINED_TIME = imasdef.UNDEFINED_TIME
128-
WRITE_OP = imasdef.WRITE_OP
129-
ASCII_SERIALIZER_PROTOCOL = getattr(imasdef, "ASCII_SERIALIZER_PROTOCOL", 60)
130-
FLEXBUFFERS_SERIALIZER_PROTOCOL = getattr(
131-
imasdef, "FLEXBUFFERS_SERIALIZER_PROTOCOL", None
132-
)
133-
DEFAULT_SERIALIZER_PROTOCOL = getattr(imasdef, "DEFAULT_SERIALIZER_PROTOCOL", 60)
134-
135-
else:
136-
# Preset some constants which are used elsewhere
137-
# this is a bit ugly, perhaps reuse the list of imports from above?
138-
# it seems no problem to use None, since the use of the values should not
139-
# be allowed, they are only used in operations which use the backend,
140-
# which we (should) gate
141-
ASCII_BACKEND = CHAR_DATA = CLOSE_PULSE = CLOSEST_INTERP = DOUBLE_DATA = None
142-
FORCE_OPEN_PULSE = CREATE_PULSE = ERASE_PULSE = None
143-
COMPLEX_DATA = FORCE_CREATE_PULSE = HDF5_BACKEND = None
144-
INTEGER_DATA = LINEAR_INTERP = MDSPLUS_BACKEND = MEMORY_BACKEND = None
145-
NODE_TYPE_STRUCTURE = OPEN_PULSE = PREVIOUS_INTERP = READ_OP = None
146-
UDA_BACKEND = UNDEFINED_INTERP = UNDEFINED_TIME = WRITE_OP = None
147-
# These constants are also useful when not working with the AL
148-
EMPTY_FLOAT = -9e40
149-
EMPTY_INT = -999_999_999
150-
EMPTY_COMPLEX = complex(EMPTY_FLOAT, EMPTY_FLOAT)
151-
IDS_TIME_MODE_UNKNOWN = EMPTY_INT
152-
IDS_TIME_MODE_HETEROGENEOUS = 0
153-
IDS_TIME_MODE_HOMOGENEOUS = 1
154-
IDS_TIME_MODE_INDEPENDENT = 2
155-
IDS_TIME_MODES = [0, 1, 2]
156-
ASCII_SERIALIZER_PROTOCOL = 60
157-
FLEXBUFFERS_SERIALIZER_PROTOCOL = None
158-
DEFAULT_SERIALIZER_PROTOCOL = 60
159-
160-
161-
def needs_imas(func):
162-
if has_imas:
163-
return func
164-
165-
@functools.wraps(func)
166-
def wrapper(*args, **kwargs):
167-
raise RuntimeError(
168-
f"Function {func.__name__} requires IMAS, but IMAS is not available."
169-
)
170-
171-
return wrapper
96+
ASCII_BACKEND = imasdef.ASCII_BACKEND
97+
CHAR_DATA = imasdef.CHAR_DATA
98+
CLOSE_PULSE = imasdef.CLOSE_PULSE
99+
CLOSEST_INTERP = imasdef.CLOSEST_INTERP
100+
CREATE_PULSE = imasdef.CREATE_PULSE
101+
DOUBLE_DATA = imasdef.DOUBLE_DATA
102+
COMPLEX_DATA = imasdef.COMPLEX_DATA
103+
EMPTY_COMPLEX = imasdef.EMPTY_COMPLEX
104+
EMPTY_FLOAT = imasdef.EMPTY_FLOAT
105+
EMPTY_INT = imasdef.EMPTY_INT
106+
ERASE_PULSE = imasdef.ERASE_PULSE
107+
FORCE_CREATE_PULSE = imasdef.FORCE_CREATE_PULSE
108+
FORCE_OPEN_PULSE = imasdef.FORCE_OPEN_PULSE
109+
HDF5_BACKEND = imasdef.HDF5_BACKEND
110+
IDS_TIME_MODE_HETEROGENEOUS = imasdef.IDS_TIME_MODE_HETEROGENEOUS
111+
IDS_TIME_MODE_HOMOGENEOUS = imasdef.IDS_TIME_MODE_HOMOGENEOUS
112+
IDS_TIME_MODE_INDEPENDENT = imasdef.IDS_TIME_MODE_INDEPENDENT
113+
IDS_TIME_MODE_UNKNOWN = imasdef.IDS_TIME_MODE_UNKNOWN
114+
IDS_TIME_MODES = imasdef.IDS_TIME_MODES
115+
INTEGER_DATA = imasdef.INTEGER_DATA
116+
LINEAR_INTERP = imasdef.LINEAR_INTERP
117+
MDSPLUS_BACKEND = imasdef.MDSPLUS_BACKEND
118+
MEMORY_BACKEND = imasdef.MEMORY_BACKEND
119+
NODE_TYPE_STRUCTURE = imasdef.NODE_TYPE_STRUCTURE
120+
OPEN_PULSE = imasdef.OPEN_PULSE
121+
PREVIOUS_INTERP = imasdef.PREVIOUS_INTERP
122+
READ_OP = imasdef.READ_OP
123+
UDA_BACKEND = imasdef.UDA_BACKEND
124+
UNDEFINED_INTERP = imasdef.UNDEFINED_INTERP
125+
UNDEFINED_TIME = imasdef.UNDEFINED_TIME
126+
WRITE_OP = imasdef.WRITE_OP
127+
ASCII_SERIALIZER_PROTOCOL = getattr(imasdef, "ASCII_SERIALIZER_PROTOCOL", 60)
128+
FLEXBUFFERS_SERIALIZER_PROTOCOL = getattr(
129+
imasdef, "FLEXBUFFERS_SERIALIZER_PROTOCOL", None
130+
)
131+
DEFAULT_SERIALIZER_PROTOCOL = getattr(imasdef, "DEFAULT_SERIALIZER_PROTOCOL", 60)

0 commit comments

Comments
 (0)