diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6fda0029d..a94a51169 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -15,13 +15,36 @@ on: jobs: pytest: - name: Python ${{ matrix.python-version }} + name: pytest - runs-on: ubuntu-20.04 strategy: fail-fast: false # false: try to complete all jobs matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] + name: + - linux python-3.8 + - linux python-3.9 + - linux python-3.10 + - linux python-3.11 + - macos python-3.11 + + include: + - name: linux python-3.8 + os: ubuntu-20.04 + python-version: '3.8' + - name: linux python-3.9 + os: ubuntu-20.04 + python-version: '3.9' + - name: linux python-3.10 + os: ubuntu-20.04 + python-version: '3.10' + - name: linux python-3.11 + os: ubuntu-20.04 + python-version: '3.11' + - name: macos python-3.11 + os: macos-14 + python-version: '3.11' + + runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -34,21 +57,35 @@ jobs: python-version: ${{ matrix.python-version }} cache: 'pip' - - name: Refresh package lists + - name: Refresh package lists and install Loki dependencies run: | - sudo apt-get -o Acquire::Retries=3 update || true - - - name: Install Loki dependencies - run: | - sudo apt-get -o Acquire::Retries=3 install -y graphviz gfortran - - - name: Install OMNI + CLAW dependencies + if [[ "${{ matrix.os }}" =~ macos ]]; then + brew install gcc@13 graphviz + else + sudo apt-get -o Acquire::Retries=3 update || true + sudo apt-get -o Acquire::Retries=3 install -y graphviz gfortran + fi + + - name: Install OMNI + CLAW dependencies on Linux + if: ${{ ! startsWith(matrix.os, 'macos') }} run: | sudo apt-get -o Acquire::Retries=3 install -y byacc flex openjdk-11-jdk cmake ant - name: Install Loki run: | - ./install --with-claw --with-ofp --with-examples --with-tests --with-dace + if [[ "${{ matrix.os }}" =~ macos ]]; then + export CC=gcc-13 CXX=g++-13 FC=gfortran-13 + ./install --with-examples --with-tests --with-dace + echo "export PATH=$(brew --prefix)/opt/python@3.11/libexec/bin:$(brew --prefix)/bin:${PATH}" | cat - loki-activate > loki-activate.tmp + mv loki-activate.tmp loki-activate + echo "export CC=gcc-13" >> loki-activate + echo "export CXX=g++-13" >> loki-activate + echo "export FC=gfortran-13" >> loki-activate + echo "export F90=gfortran-13" >> loki-activate + echo "export LD=gfortran-13" >> loki-activate + else + ./install --with-claw --with-ofp --with-examples --with-tests --with-dace + fi - name: Install up-to-date CMake run: | @@ -62,6 +99,7 @@ jobs: - name: Upload loki coverage report to Codecov uses: codecov/codecov-action@v4 + if: ${{ ! startsWith(matrix.os, 'macos') }} with: flags: loki files: ./coverage.xml @@ -75,6 +113,7 @@ jobs: - name: Upload lint_rules coverage report to Codecov uses: codecov/codecov-action@v4 + if: ${{ ! startsWith(matrix.os, 'macos') }} with: flags: lint_rules files: ./coverage.xml diff --git a/CMakeLists.txt b/CMakeLists.txt index 44ae9a39b..d2c3033f8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -34,8 +34,9 @@ # Version 3.12 required to use FindPython # Version 3.15 officially required to use Python3_FIND_VIRTUALENV (not working on 3.15.3, # though, and use 3.17 for conda support anyway) -cmake_minimum_required( VERSION 3.17 FATAL_ERROR ) -find_package( ecbuild 3.4 REQUIRED HINTS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild ) +# Version 3.19 for support of find_package version range and file(CHMOD) +cmake_minimum_required( VERSION 3.19 FATAL_ERROR ) +find_package( ecbuild 3.7 REQUIRED ) # Specify project and configuration options project( loki LANGUAGES NONE ) @@ -100,7 +101,8 @@ if( NOT HAVE_NO_INSTALL ) # Setup Python virtual environment include( python_venv ) set( loki_VENV_PATH ${CMAKE_CURRENT_BINARY_DIR}/loki_env ) - setup_python_venv( ${loki_VENV_PATH} ) + set( PYTHON_VERSION 3.8...<3.12 ) + setup_python_venv( ${loki_VENV_PATH} ${PYTHON_VERSION} ) install( DIRECTORY ${loki_VENV_PATH} DESTINATION . USE_SOURCE_PERMISSIONS ) # Enable Pytest tests as ecbuild/ctest targets @@ -171,13 +173,13 @@ if( NOT HAVE_NO_INSTALL ) # We install Loki at configure time (for now), since bulk-transformation planning # requires configure time execution to allow injection with CMake targets. - message( STATUS "Install Loki in virtual environment" ) + ecbuild_info( "Install Loki in virtual environment" ) execute_process( COMMAND ${Python3_EXECUTABLE} -m pip install ${PIP_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}${_INSTALL_OPTIONS} ) - message( STATUS "Install Loki in virtual environment - done" ) + ecbuild_info( "Install Loki in virtual environment - done" ) - message( STATUS "Install Loki lint_rules in virtual environment" ) + ecbuild_info( "Install Loki lint_rules in virtual environment" ) execute_process( COMMAND ${Python3_EXECUTABLE} -m pip install ${PIP_OPTIONS} ${CMAKE_CURRENT_SOURCE_DIR}/lint_rules ) - message( STATUS "Install Loki lint_rules in virtual environment - done" ) + ecbuild_info( "Install Loki lint_rules in virtual environment - done" ) # Make the shebang in Python scripts use relative paths list( diff --git a/INSTALL.md b/INSTALL.md index 0946fd7cc..4a58e9390 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -19,14 +19,19 @@ do this automatically. ## Requirements - Python 3.8+ with virtualenv and pip -- For OpenFortranParser/OMNI+CLAW: - - JDK 1.8+ with ant (can be installed using the install script or ecbuild) - - libxml2 (with headers) -- For graphical output from the scheduler: graphviz +- For graphical output of Scheduler dependency graphs: graphviz -## Installation via `pip install` +### Optional requirements -The easiest way to obtain a useable installation of Loki with the fparser frontend: +The following is required to use the Open Fortran Parser (OFP) or OMNI frontend or the CLAW compiler: + +- JDK 1.8+ with ant (can be installed using the install script or ecbuild) +- libxml2 (with headers) + +## Installation without prior download + +The easiest way to obtain a useable installation of Loki with the fparser frontend does not +require downloading the source code. Simply run the following commands: ```bash python3 -m venv loki_env # Create a virtual environment @@ -35,17 +40,50 @@ source loki_env/bin/activate # Activate the virtual environment # Installation of the Loki core library pip install "loki @ git+https://github.com/ecmwf-ifs/loki.git" -# Optional: Installation of additional transformations -pip install "transformations @ git+https://github.com/ecmwf-ifs/loki.git#subdirectory=transformations" - -# Optional: Installation of rules for the use as a linter +# Optional: Installation of IFS lint rules for the use as a linter pip install "lint_rules @ git+https://github.com/ecmwf-ifs/loki.git#subdirectory=lint_rules" ``` This makes the Python package available and installs the scripts `loki-transform.py` and `loki-lint.py` on the PATH. -## Installation using install script +## Installation from source + +After downloading the source code, e.g., via + +```bash +git clone https://github.com/ecmwf-ifs/loki.git +``` + +enter the created source directory and choose one of the following installation methods. + +### Installation with pip + +This yields an installation that uses the fparser frontend and is suitable for +development of transformations and working with the example notebooks: + +```bash +python3 -m venv loki_env # Create a virtual environment +source loki_env/bin/activate # Activate the virtual environment + +# Installation of the Loki core library +# Optional: +# * Add `-e` to obtain an editable install that allows modifying the +# source files without having to re-install the package +# * Enable the following options by providing them as a comma-separated +# list in square brackets behind the `.`: +# * tests - allows running the Loki test suite +# * examples - installs dependencies to run the example notebooks +# * docs - installs dependencies required to generate the Sphinx documentation +# * dace - installs DaCe +pip install . + +# Optional: Installation of IFS lint rules for the use as a linter +# (again optionally with `-e` for an editable install) +pip install ./lint_rules +``` + +### Installation using install script The provided `install` script can be used to install Loki with selected dependencies inside a local virtual environment `loki_env`. This is the @@ -85,7 +123,7 @@ will install only the Fparser2 frontend. After completion, this script writes a `loki-activate` file that can be sourced to bring up the virtual environment and set paths for the external dependencies. -### Examples: +#### Examples: The default command on ECMWF's Atos HPC facility for a full stack installation is @@ -105,7 +143,7 @@ To update the installation (e.g., to add JDK), the existing virtual environment ./install --with-claw --with-jdk --with-ant --use-venv=loki_env --with-ofp ``` -## Installation using CMake/ecbuild +### Installation using CMake/ecbuild Loki and dependencies (excluding OpenFortranParser) can be installed using [ecbuild](https://github.com/ecmwf/ecbuild) (a set of CMake macros and a wrapper @@ -167,6 +205,40 @@ need to take care of PATH handling on the user side. See the [CLOUDSC dwarf](https://github.com/ecmwf-ifs/dwarf-p-cloudsc) for an example how this can be used. +## Installation on MacOS + +Although tailored to the Linux environment commonly found on HPC systems, Loki +can also be installed on MacOS. + +This requires installing some additional dependencies using +[Brew](https://brew.sh) to allow running the Loki test suite: + + +```bash +# Install dependencies with brew +brew install gcc@13 graphviz python@3.11 + +# Install Loki using the install script +# NB: we explicitly select Python 3.11 (in case a newer version is the default) +# by adding it in first place to the search path +PATH="$(brew --prefix)/opt/python@3.11/libexec/bin:$PATH" \ + CC=gcc-13 CXX=g++-13 FC=gfortran-13 \ + ./install --with-examples --with-tests --with-dace + +# Amend the Loki environment with correct compiler variables +echo "export PATH=$(brew --prefix)/opt/python@3.11/libexec/bin:$(brew --prefix)/bin:${PATH}" | cat - loki-activate > loki-activate.tmp +mv loki-activate.tmp loki-activate +echo "export CC=gcc-13" >> loki-activate +echo "export CXX=g++-13" >> loki-activate +echo "export FC=gfortran-13" >> loki-activate +echo "export F90=gfortran-13" >> loki-activate +echo "export LD=gfortran-13" >> loki-activate + +# Activate the virtual environment to run the tests +source loki-activate +pytest --pyargs loki +``` + ## Installation as part of an ecbundle bundle Loki being installable by CMake/ecbuild makes it easy to integrate with @@ -217,7 +289,6 @@ that has support for editable installs using `pyproject.toml`. ```bash pushd loki pip install -e .[tests,examples] -pip install -e ./transformations pip install -e ./lint_rules popd ``` diff --git a/cmake/python_venv.cmake b/cmake/python_venv.cmake index 285612b09..ff649cdda 100644 --- a/cmake/python_venv.cmake +++ b/cmake/python_venv.cmake @@ -22,6 +22,7 @@ # ------- # # :VENV_PATH: The path to the virtual environment +# :PYTHON_VERSION: Permissible Python versions for find_package # # Output variables # ---------------- @@ -33,7 +34,7 @@ # ############################################################################## -function( find_python_venv VENV_PATH ) +function( find_python_venv VENV_PATH PYTHON_VERSION ) # Update the environment with VIRTUAL_ENV variable (mimic the activate script) set( ENV{VIRTUAL_ENV} ${VENV_PATH} ) @@ -49,7 +50,7 @@ function( find_python_venv VENV_PATH ) set( Python3_ROOT_DIR "${VENV_PATH}" ) # Launch a new search - find_package( Python3 COMPONENTS Interpreter Development REQUIRED ) + find_package( Python3 ${PYTHON_VERSION} COMPONENTS Interpreter Development REQUIRED ) # Find the binary directory of the virtual environment execute_process( @@ -83,25 +84,36 @@ endfunction() # ------- # # :VENV_PATH: The path to use for the virtual environment +# :PYTHON_VERSION: Permissible Python versions for find_package # ############################################################################## -function( create_python_venv VENV_PATH ) +function( create_python_venv VENV_PATH PYTHON_VERSION ) # Discover only system install Python 3 set( Python3_FIND_VIRTUALENV STANDARD ) - find_package( Python3 COMPONENTS Interpreter REQUIRED ) + find_package( Python3 ${PYTHON_VERSION} COMPONENTS Interpreter REQUIRED ) + + # Ensure the activate script is writable in case the venv exists already + if( EXISTS "${VENV_PATH}/bin/activate" ) + file( CHMOD "${VENV_PATH}/bin/activate" FILE_PERMISSIONS OWNER_READ OWNER_WRITE ) + endif() # Create a loki virtualenv - message( STATUS "Create Python virtual environment ${VENV_PATH}" ) + ecbuild_info( "Create Python virtual environment ${VENV_PATH}" ) execute_process( COMMAND ${Python3_EXECUTABLE} -m venv --copies "${VENV_PATH}" ) # Make the virtualenv portable by automatically deducing the VIRTUAL_ENV path from # the 'activate' script's location in the filesystem - execute_process( - COMMAND - sed -i "s/^VIRTUAL_ENV=\".*\"$/VIRTUAL_ENV=\"$(cd \"$(dirname \"$(dirname \"\${BASH_SOURCE[0]}\" )\")\" \\&\\& pwd)\"/" "${VENV_PATH}/bin/activate" + file( READ "${VENV_PATH}/bin/activate" FILE_CONTENT ) + string( + REGEX REPLACE + "\nVIRTUAL_ENV=\".*\"\nexport VIRTUAL_ENV" + "\nVIRTUAL_ENV=\"$(readlink -f \"$(dirname \"$(dirname \"\${BASH_SOURCE[0]}\")\")\")\"\nexport VIRTUAL_ENV" + FILE_CONTENT + "${FILE_CONTENT}" ) + file( WRITE "${VENV_PATH}/bin/activate" "${FILE_CONTENT}" ) endfunction() @@ -121,6 +133,7 @@ endfunction() # ------- # # :VENV_PATH: The path to use for the virtual environment +# :PYTHON_VERSION: Permissible Python versions for find_package # # Output variables # ---------------- @@ -132,13 +145,13 @@ endfunction() # ############################################################################## -macro( setup_python_venv VENV_PATH ) +macro( setup_python_venv VENV_PATH PYTHON_VERSION ) # Create the virtual environment - create_python_venv( ${VENV_PATH} ) + create_python_venv( ${VENV_PATH} ${PYTHON_VERSION} ) # Discover Python in the virtual environment and set-up variables - find_python_venv( ${VENV_PATH} ) + find_python_venv( ${VENV_PATH} ${PYTHON_VERSION} ) endmacro() @@ -171,11 +184,15 @@ function( update_python_shebang ) # ''' ecbuild_debug( "Update shebang for ${_exe}" ) - - execute_process( - COMMAND - sed -i "1s/^.*$/#\\!\\/bin\\/sh\\n\\\"true\\\" '''\\\\'\\nexec \\\"$(dirname \\\"$(readlink -f \\\"\\$0\\\")\\\")\\\"\\/python \\\"\\$0\\\" \\\"\\$@\\\"\\n'''/" ${_exe} + file( READ "${_exe}" FILE_CONTENT ) + string( + REGEX REPLACE + "#!/.*\n#" + "#!/bin/sh\n\"true\" '''\\\\'\nexec \"$(dirname \"$(readlink -f \"\$0\")\")/python\" \"\$0\" \"\$@\"\n'''\n#" + FILE_CONTENT + "${FILE_CONTENT}" ) + file( WRITE "${_exe}" "${FILE_CONTENT}" ) endforeach() diff --git a/install b/install index 4b18dd494..158768d33 100755 --- a/install +++ b/install @@ -513,5 +513,7 @@ echo "Activate the Loki environment with" echo echo " source loki-activate" echo -echo "You can test the Loki installation by running 'py.test lint_rules loki'" +echo "You can then test the Loki installation by running" +echo +echo " pytest --pyargs loki lint_rules" echo diff --git a/loki-post-import.cmake.in b/loki-post-import.cmake.in index 0e289af51..2c0267aa5 100644 --- a/loki-post-import.cmake.in +++ b/loki-post-import.cmake.in @@ -28,7 +28,7 @@ set( LOKI_EXECUTABLES @LOKI_EXECUTABLES@ ) if( NOT ${loki_HAVE_NO_INSTALL} ) # Detect the installed virtual environment include( python_venv ) - find_python_venv( ${loki_VENV_PATH} ) + find_python_venv( ${loki_VENV_PATH} @PYTHON_VERSION@ ) endif() # Discover Loki executables and make available as CMake targets diff --git a/loki/batch/tests/test_batch.py b/loki/batch/tests/test_batch.py index 299d8d72c..a402d25b3 100644 --- a/loki/batch/tests/test_batch.py +++ b/loki/batch/tests/test_batch.py @@ -249,7 +249,7 @@ def test_file_item2(testdir): ) # Files don't have dependencies (direct dependencies, anyway) - assert item.dependencies is () + assert isinstance(item.dependencies, tuple) and not item.dependencies def test_file_item3(testdir): @@ -371,7 +371,7 @@ def test_procedure_item1(testdir): assert item == '#comp1' assert str(item) == 'loki.batch.ProcedureItem<#comp1>' assert item.ir is item.source['comp1'] - assert item.definitions is () + assert isinstance(item.definitions, tuple) and not item.definitions assert not item.create_definition_items(item_factory=ItemFactory()) @@ -421,7 +421,7 @@ def test_procedure_item2(testdir): item = get_item(ProcedureItem, proj/'source/comp2.f90', '#comp2', RegexParserClass.ProgramUnitClass) assert item.name == '#comp2' assert item.ir is item.source['comp2'] - assert item.definitions is () + assert isinstance(item.definitions, tuple) and not item.definitions item_factory = ItemFactory() assert not item.create_definition_items(item_factory=item_factory) @@ -816,7 +816,7 @@ def test_procedure_binding_item1(testdir): assert item.name == 't_mod#t%proc' assert str(item) == 'loki.batch.ProcedureBindingItem' assert item.ir is item.source['t'].variable_map['proc'] - assert item.definitions is () + assert isinstance(item.definitions, tuple) and not item.definitions assert not item.create_definition_items(item_factory=ItemFactory()) assert item.dependencies == as_tuple(item.source['t_proc']) @@ -838,7 +838,7 @@ def test_procedure_binding_item2(testdir, default_config): item = get_item(ProcedureBindingItem, proj/'module/t_mod.F90', 't_mod#t%no%way', parser_classes) assert item.name == 't_mod#t%no%way' assert isinstance(item.ir, Scalar) - assert item.definitions is () + assert isinstance(item.definitions, tuple) and not item.definitions assert not item.create_definition_items(item_factory=ItemFactory()) assert item.dependencies == ('no%way',) @@ -879,7 +879,7 @@ def test_procedure_binding_item3(testdir): item = get_item(ProcedureBindingItem, proj/'module/t_mod.F90', 't_mod#t%yay%proc', parser_classes) assert item.name == 't_mod#t%yay%proc' assert isinstance(item.ir, Scalar) - assert item.definitions is () + assert isinstance(item.definitions, tuple) and not item.definitions assert not item.create_definition_items(item_factory=ItemFactory()) assert item.dependencies == ('yay%proc',) diff --git a/loki/tests/test_cmake.py b/loki/tests/test_cmake.py index 6197d40ae..2a9f01bfa 100644 --- a/loki/tests/test_cmake.py +++ b/loki/tests/test_cmake.py @@ -34,6 +34,17 @@ def check_cmake(): pytest.mark.skipif(not check_cmake(), reason='CMake not available') +@pytest.fixture(scope='module', name='tmp_dir') +def fixture_tmp_dir(): + """Return a test module lifetime tmp directory""" + tmp_dir = gettempdir()/'test_cmake' + if tmp_dir.exists(): + shutil.rmtree(tmp_dir) + tmp_dir.mkdir() + yield tmp_dir + if tmp_dir.exists(): + shutil.rmtree(tmp_dir) + @pytest.fixture(scope='module', name='here') def fixture_here(): @@ -54,7 +65,7 @@ def fixture_srcdir(here): @pytest.fixture(scope='module', name='config') -def fixture_config(here): +def fixture_config(tmp_dir): """ Write default configuration as a temporary file and return the file path @@ -65,52 +76,43 @@ def fixture_config(here): 'driverB': {'role': 'driver'}, } } - filepath = here/'test_cmake_loki.config' + filepath = tmp_dir/'test_cmake_loki.config' filepath.write_text(toml.dumps(default_config)) yield filepath filepath.unlink() @pytest.fixture(scope='module', name='ecbuild') -def fixture_ecbuild(): +def fixture_ecbuild(tmp_dir): """ - Download and install ecbuild + Download ecbuild """ - srcdir = gettempdir()/'ecbuild_tmp' - if srcdir.exists(): - shutil.rmtree(srcdir) - ecbuilddir = gettempdir()/'ecbuild' + ecbuilddir = tmp_dir/'ecbuild' if ecbuilddir.exists(): shutil.rmtree(ecbuilddir) - - execute(['git', 'clone', 'https://github.com/ecmwf/ecbuild.git', str(srcdir)]) - (srcdir/'bootstrap').mkdir() - execute(['cmake', '..'], cwd=srcdir/'bootstrap') - execute(['cmake', '--install', '.', '--prefix', str(ecbuilddir)], cwd=srcdir/'bootstrap') - - shutil.rmtree(srcdir) + execute(['git', 'clone', 'https://github.com/ecmwf/ecbuild.git', str(ecbuilddir)]) yield ecbuilddir shutil.rmtree(ecbuilddir) @pytest.fixture(scope='module', name='loki_install', params=[True, False]) -def fixture_loki_install(here, ecbuild, silent, request): +def fixture_loki_install(here, tmp_dir, ecbuild, silent, request): """ Install Loki using CMake into an install directory """ - builddir = gettempdir()/'loki_bootstrap' + builddir = tmp_dir/'loki_bootstrap' if builddir.exists(): shutil.rmtree(builddir) builddir.mkdir() - cmd = [f'{ecbuild}/bin/ecbuild', str(here.parent.parent), '-DENABLE_CLAW=OFF'] + cmd = ['cmake', '-DENABLE_CLAW=OFF', f'-DCMAKE_MODULE_PATH={ecbuild}/cmake', str(here.parent.parent)] if request.param: cmd += ['-DENABLE_EDITABLE=ON'] else: cmd += ['-DENABLE_EDITABLE=OFF'] - + execute(cmd, silent=silent, cwd=builddir) - lokidir = gettempdir()/'loki' + lokidir = tmp_dir/'loki' if lokidir.exists(): shutil.rmtree(lokidir) execute( @@ -126,11 +128,11 @@ def fixture_loki_install(here, ecbuild, silent, request): @contextmanager -def clean_builddir(name): +def clean_builddir(builddir): """ - Create a build directory in the temp directory + Clean the build directory in the temp directory """ - builddir = gettempdir()/str(name) + builddir = Path(builddir) if builddir.exists(): shutil.rmtree(builddir) builddir.mkdir() @@ -180,7 +182,7 @@ def fixture_cmake_project(here, config, srcdir): (srcdir/'loki').unlink() -def test_cmake_plan(srcdir, config, cmake_project, loki_install, ecbuild, silent): +def test_cmake_plan(srcdir, tmp_dir, config, cmake_project, loki_install, ecbuild, silent): """ Test the `loki_transform_plan` CMake function with a single task graph spanning two projects @@ -195,7 +197,7 @@ def test_cmake_plan(srcdir, config, cmake_project, loki_install, ecbuild, silent assert cmake_project.exists() for loki_root in loki_install: - with clean_builddir('test_cmake_plan') as builddir: + with clean_builddir(tmp_dir/'test_cmake_plan') as builddir: execute( [f'{ecbuild}/bin/ecbuild', str(srcdir), f'-Dloki_ROOT={loki_root}'], cwd=builddir, silent=silent diff --git a/loki/tests/test_modules.py b/loki/tests/test_modules.py index 11782398e..d48eeb504 100644 --- a/loki/tests/test_modules.py +++ b/loki/tests/test_modules.py @@ -597,8 +597,8 @@ def test_module_access_spec_none(frontend, tmp_path): # Check module properties assert module.default_access_spec is None - assert module.public_access_spec is () - assert module.private_access_spec is () + assert isinstance(module.public_access_spec, tuple) and not module.public_access_spec + assert isinstance(module.private_access_spec, tuple) and not module.private_access_spec # Check backend output code = module.to_fortran().upper() diff --git a/loki/tools/tests/test_tools.py b/loki/tools/tests/test_tools.py index 8ac46c860..c1e82e7b8 100644 --- a/loki/tools/tests/test_tools.py +++ b/loki/tools/tests/test_tools.py @@ -316,14 +316,14 @@ def test_timeout(): with timeout(5): sleep(.3) stop = perf_counter() - assert .2 < stop - start < .4 + assert .2 < stop - start < .45 # Timeout disabled: start = perf_counter() with timeout(0): sleep(.3) stop = perf_counter() - assert .2 < stop - start < .4 + assert .2 < stop - start < .45 # Default exception with pytest.raises(RuntimeError) as exc: @@ -331,7 +331,7 @@ def test_timeout(): with timeout(1): sleep(5) stop = perf_counter() - assert .9 < stop - start < 1.1 + assert .9 < stop - start < 1.15 assert "Timeout reached after 2 second(s)" in str(exc.value) # Custom message @@ -340,7 +340,7 @@ def test_timeout(): with timeout(1, message="My message"): sleep(5) stop = perf_counter() - assert .9 < stop - start < 1.1 + assert .9 < stop - start < 1.15 assert "My message" in str(exc.value) diff --git a/loki/transformations/tests/test_array_indexing.py b/loki/transformations/tests/test_array_indexing.py index 0fe32f954..d544daf2e 100644 --- a/loki/transformations/tests/test_array_indexing.py +++ b/loki/transformations/tests/test_array_indexing.py @@ -5,6 +5,7 @@ # granted to it by virtue of its status as an intergovernmental organisation # nor does it submit to any jurisdiction. +import platform import pytest import numpy as np @@ -318,6 +319,8 @@ def test_transform_demote_dimension_arguments(tmp_path, frontend): assert np.all(vec2 == 2) and np.sum(vec2) == 6 assert np.all(matrix == 16) and np.sum(matrix) == 32 + +@pytest.mark.skipif(platform.system() == 'Darwin', reason='Unclear issue causing problems on MacOS (#352)') @pytest.mark.parametrize('frontend', available_frontends()) @pytest.mark.parametrize('start_index', (0, 1, 5)) def test_transform_normalize_array_shape_and_access(tmp_path, frontend, start_index):