From 891d699733183fe0a35c578f1462536698387de4 Mon Sep 17 00:00:00 2001 From: Graeme Watt Date: Thu, 11 Apr 2024 09:12:41 +0100 Subject: [PATCH] Add `try`/`except` around ROOT import (#259) * Add try/except around ROOT import * Print out an error message if ROOT cannot be imported. * Add some explanation at the end of the README.md file. * Correct a typo in the link to reading_scikithep_histogram.ipynb. * Allow CI to run subset of tests without ROOT * Add a custom marker to test functions requiring ROOT. * Run subset of tests using "pytest -m 'not needs_root'". --- .github/workflows/tests.yml | 29 ++++++++++++++++++++++++++--- README.md | 9 +++++++-- docs/dev.rst | 6 ++++++ hepdata_lib/c_file_reader.py | 5 ++++- hepdata_lib/root_utils.py | 5 ++++- pyproject.toml | 3 +++ setup.py | 1 + tests/test_cfilereader.py | 2 ++ tests/test_notebooks.py | 8 ++++++++ tests/test_rootfilereader.py | 7 ++++++- tests/test_utilities.py | 6 ++++-- 11 files changed, 71 insertions(+), 10 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 251d2767..b5605949 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [ubuntu-latest] - root-version: ["6.24", "6.26", "6.28", "6.30"] + root-version: ["", "6.24", "6.26", "6.28", "6.30"] python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] exclude: - root-version: "6.24" @@ -55,6 +55,7 @@ jobs: - uses: actions/checkout@v4 - name: Setup Micromamba environment + if: ${{ matrix.root-version }} uses: mamba-org/setup-micromamba@v1 with: environment-name: ci @@ -68,7 +69,22 @@ jobs: channels: - conda-forge + - name: Setup Micromamba environment without ROOT + if: ${{ !matrix.root-version }} + uses: mamba-org/setup-micromamba@v1 + with: + environment-name: ci + create-args: >- + python=${{ matrix.python-version }} + imagemagick + ghostscript + pip + condarc: | + channels: + - conda-forge + - name: ROOT info + if: ${{ matrix.root-version }} run: | root-config --version root-config --python-version @@ -94,23 +110,30 @@ jobs: # Use python -m pytest to add current working dir as src/ dir layout not used - name: Run pytest + if: ${{ matrix.root-version }} run: | python -m pytest tests + - name: Run pytest without ROOT + if: ${{ !matrix.root-version }} + run: | + python -m pytest tests -m 'not needs_root' + - name: Report coverage with Codecov + if: ${{ matrix.root-version }} uses: codecov/codecov-action@v3 with: files: ./coverage.xml flags: unittests-${{ matrix.python-version }} - name: Save notebooks - if: ${{ always() }} + if: ${{ always() && matrix.root-version }} run: | python -m jupyter kernelspec list python -m jupyter nbconvert --ExecutePreprocessor.timeout=600 --ExecutePreprocessor.allow_errors=True --to html --execute examples/*.ipynb - name: Upload notebooks - if: ${{ always() }} + if: ${{ always() && matrix.root-version }} uses: actions/upload-artifact@v4 with: name: notebooks-${{ matrix.root-version }}-${{ matrix.python-version }}-${{ matrix.os }} py3-${{ matrix.root-version }}-${{ matrix.python-version }}-${{ matrix.os }} diff --git a/README.md b/README.md index 6f9ba74e..4b716ce1 100644 --- a/README.md +++ b/README.md @@ -72,8 +72,8 @@ There are a few more examples available that can directly be run using the [bind - [Reading TGraph and TGraphError from '.C' files](https://github.com/HEPData/hepdata_lib/blob/main/examples/read_c_file.ipynb) [![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/HEPData/hepdata_lib/main?filepath=examples/read_c_file.ipynb)

-- [Preparing scikit-hep histograms](https://github.com/HEPData/hepdata_lib/blob/main/examples/reading_scikithep_histogram.ipynb) -[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/HEPData/hepdata_lib/main?filepath=examples/reading_scikihep_histogram.ipynb) +- [Preparing scikit-hep histograms](https://github.com/HEPData/hepdata_lib/blob/main/examples/reading_scikithep_histograms.ipynb) +[![Binder](https://mybinder.org/badge.svg)](https://mybinder.org/v2/gh/HEPData/hepdata_lib/main?filepath=examples/reading_scikihep_histograms.ipynb)

## External dependencies @@ -82,3 +82,8 @@ There are a few more examples available that can directly be run using the [bind - [ImageMagick](https://www.imagemagick.org) Make sure that you have `ROOT` in your `$PYTHONPATH` and that the `convert` command is available by adding its location to your `$PATH` if needed. + +A ROOT installation is not strictly required if your input data is not in a ROOT format, for example, if +your input data is provided as text files or `scikit-hep/hist` histograms. Most of the `hepdata_lib` +functionality can be used without a ROOT installation, other than the `RootFileReader` and `CFileReader` classes, +and other functions of the `hepdata_lib.root_utils` module. \ No newline at end of file diff --git a/docs/dev.rst b/docs/dev.rst index 86e1f5f9..e3a5e1b5 100644 --- a/docs/dev.rst +++ b/docs/dev.rst @@ -15,6 +15,12 @@ To run the tests, move into the ``hepdata_lib`` directory while in your virtual It is a good idea to run the tests manually to ensure that your changes do not cause any issues. +If you don't have a working ROOT installation, a subset of the tests can still be run without ROOT: + +:: + + pytest tests -m "not needs_root" + Definition of test cases ++++++++++++++++++++++++ diff --git a/hepdata_lib/c_file_reader.py b/hepdata_lib/c_file_reader.py index a07c7148..2c957e39 100644 --- a/hepdata_lib/c_file_reader.py +++ b/hepdata_lib/c_file_reader.py @@ -2,7 +2,10 @@ import io from array import array from future.utils import raise_from -from ROOT import TGraph, TGraphErrors # pylint: disable=no-name-in-module +try: + from ROOT import TGraph, TGraphErrors # pylint: disable=no-name-in-module +except ImportError as e: # pragma: no cover + print(f'Cannot import ROOT: {str(e)}') import hepdata_lib.root_utils as ru from hepdata_lib.helpers import check_file_existence diff --git a/hepdata_lib/root_utils.py b/hepdata_lib/root_utils.py index c6d0269a..ff1dd288 100644 --- a/hepdata_lib/root_utils.py +++ b/hepdata_lib/root_utils.py @@ -3,7 +3,10 @@ import ctypes from future.utils import raise_from import numpy as np -import ROOT as r +try: + import ROOT as r +except ImportError as e: # pragma: no cover + print(f'Cannot import ROOT: {str(e)}') from hepdata_lib.helpers import check_file_existence class RootFileReader: diff --git a/pyproject.toml b/pyproject.toml index 456e710a..afbb9505 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,3 +14,6 @@ addopts = [ ] log_cli_level = "info" testpaths = "tests" +markers = [ + "needs_root: requires a ROOT installation" +] \ No newline at end of file diff --git a/setup.py b/setup.py index 088f89d6..70911595 100644 --- a/setup.py +++ b/setup.py @@ -34,6 +34,7 @@ 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], keywords='HEPData physics OpenData', packages=find_packages(exclude=['contrib', 'docs', 'tests']), diff --git a/tests/test_cfilereader.py b/tests/test_cfilereader.py index b77511f6..d19c47ab 100644 --- a/tests/test_cfilereader.py +++ b/tests/test_cfilereader.py @@ -3,8 +3,10 @@ from unittest import TestCase import os import numpy as np +import pytest from hepdata_lib.c_file_reader import CFileReader +@pytest.mark.needs_root class TestCFileReader(TestCase): """Test the CFileReader class.""" diff --git a/tests/test_notebooks.py b/tests/test_notebooks.py index 7783579a..b74b0dee 100644 --- a/tests/test_notebooks.py +++ b/tests/test_notebooks.py @@ -13,6 +13,7 @@ def common_kwargs(tmpdir): 'cwd' : 'examples' } +@pytest.mark.needs_root def test_correlation(common_kwargs):# pylint: disable=redefined-outer-name """Tests examples/correlation.ipynb""" pm.execute_notebook('examples/correlation.ipynb', **common_kwargs) @@ -21,14 +22,21 @@ def test_getting_started(common_kwargs):# pylint: disable=redefined-outer-name """Tests examples/Getting_started.ipynb""" pm.execute_notebook('examples/Getting_started.ipynb', **common_kwargs) +@pytest.mark.needs_root def test_reading_histograms(common_kwargs):# pylint: disable=redefined-outer-name """Tests examples/reading_histograms.ipynb""" pm.execute_notebook('examples/reading_histograms.ipynb', **common_kwargs) +@pytest.mark.needs_root def test_combine_limits(common_kwargs):# pylint: disable=redefined-outer-name """Tests examples/combine_limits.ipynb""" pm.execute_notebook('examples/combine_limits.ipynb', **common_kwargs) +@pytest.mark.needs_root def test_c_file(common_kwargs):# pylint: disable=redefined-outer-name """Tests examples/read_c_file.ipynb""" pm.execute_notebook('examples/read_c_file.ipynb', **common_kwargs) + +def test_scikithep_histograms(common_kwargs):# pylint: disable=redefined-outer-name + """Tests examples/reading_scikithep_histograms.ipynb""" + pm.execute_notebook('examples/reading_scikithep_histograms.ipynb', **common_kwargs) diff --git a/tests/test_rootfilereader.py b/tests/test_rootfilereader.py index 4cd0c52c..23d1d947 100644 --- a/tests/test_rootfilereader.py +++ b/tests/test_rootfilereader.py @@ -5,12 +5,17 @@ import os import ctypes import numpy as np -import ROOT +import pytest +try: + import ROOT +except ImportError as e: + print(f'Cannot import ROOT: {str(e)}') from hepdata_lib.root_utils import (RootFileReader, get_graph_points, get_hist_1d_points, get_hist_2d_points) from .test_utilities import float_compare, tuple_compare, histogram_compare_1d, make_tmp_root_file +@pytest.mark.needs_root class TestRootFileReader(TestCase): """Test the RootFileReader class.""" diff --git a/tests/test_utilities.py b/tests/test_utilities.py index eee8d9d4..21ebbb00 100644 --- a/tests/test_utilities.py +++ b/tests/test_utilities.py @@ -4,8 +4,10 @@ import os import random import string - -import ROOT +try: + import ROOT +except ImportError as e: + print(f'Cannot import ROOT: {str(e)}') from future.utils import raise_from