From 0f88ee294f7191ac7495cc7904ee5ff9b1b0f130 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 30 Oct 2024 16:14:20 +0000 Subject: [PATCH 1/8] Update cruft with batchpr --- .cruft.json | 5 ++- .github/workflows/sub_package_update.yml | 48 +++++++++++++++--------- .pre-commit-config.yaml | 2 +- .ruff.toml | 31 ++++++++++++--- pyproject.toml | 1 + sunkit_spex/_dev/scm_version.py | 4 +- 6 files changed, 64 insertions(+), 27 deletions(-) diff --git a/.cruft.json b/.cruft.json index f7635986..f1047645 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/sunpy/package-template", - "commit": "aec53b81aed2e7e534045e59303d82712fe82fb1", + "commit": "51fb616094a4d7577c8898445aa50effb89afa31", "checkout": null, "context": { "cookiecutter": { @@ -16,7 +16,8 @@ "enable_dynamic_dev_versions": "y", "include_example_code": "n", "include_cruft_update_github_workflow": "y", - "_sphinx_theme": "alabaster", + "use_extended_ruff_linting": "n", + "_sphinx_theme": "sunpy", "_parent_project": "", "_install_requires": "", "_copy_without_render": [ diff --git a/.github/workflows/sub_package_update.yml b/.github/workflows/sub_package_update.yml index 74558476..0b657f24 100644 --- a/.github/workflows/sub_package_update.yml +++ b/.github/workflows/sub_package_update.yml @@ -21,14 +21,6 @@ jobs: runs-on: ubuntu-latest strategy: fail-fast: true - matrix: - include: - - add-paths: . - body: apply the changes to this repo. - branch: cruft/update - commit-message: "Automatic package template update" - title: Updates from the package template - steps: - uses: actions/checkout@v4 @@ -55,25 +47,47 @@ jobs: echo "has_changes=$CHANGES" >> "$GITHUB_OUTPUT" - name: Run update if available + id: cruft_update if: steps.check.outputs.has_changes == '1' run: | git config --global user.email "${{ github.actor }}@users.noreply.github.com" git config --global user.name "${{ github.actor }}" - cruft update --skip-apply-ask --refresh-private-variables + cruft_output=$(cruft update --skip-apply-ask --refresh-private-variables) + echo $cruft_output git restore --staged . - - name: Create pull request + if [[ "$cruft_output" == *"Failed to cleanly apply the update, there may be merge conflicts."* ]]; then + echo merge_conflicts=1 >> $GITHUB_OUTPUT + else + echo merge_conflicts=0 >> $GITHUB_OUTPUT + fi + + - name: Check if only .cruft.json is modified + id: cruft_json if: steps.check.outputs.has_changes == '1' + run: | + git status --porcelain=1 + if [[ "$(git status --porcelain=1)" == " M .cruft.json" ]]; then + echo "Only .cruft.json is modified. Exiting workflow early." + echo "has_changes=0" >> "$GITHUB_OUTPUT" + else + echo "has_changes=1" >> "$GITHUB_OUTPUT" + fi + + - name: Create pull request + if: steps.cruft_json.outputs.has_changes == '1' uses: peter-evans/create-pull-request@v7 with: token: ${{ secrets.GITHUB_TOKEN }} - add-paths: ${{ matrix.add-paths }} - commit-message: ${{ matrix.commit-message }} - branch: ${{ matrix.branch }} + add-paths: "." + commit-message: "Automatic package template update" + branch: "cruft/update" delete-branch: true - branch-suffix: timestamp - title: ${{ matrix.title }} + draft: ${{ steps.cruft_update.outputs.merge_conflicts == '1' }} + title: "Updates from the package template" body: | - This is an autogenerated PR, which will ${{ matrix.body }}. - [Cruft](https://cruft.github.io/cruft/) has detected updates from the Package Template + This is an autogenerated PR, which will applies the latest changes from the [SunPy Package Template](https://github.com/sunpy/package-template). + If this pull request has been opened as a draft there are conflicts which need fixing. + + **To run the CI on this pull request you will need to close it and reopen it.** diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 384cd331..c8adbd8f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ repos: # This should be before any formatting hooks like isort - repo: https://github.com/astral-sh/ruff-pre-commit - rev: "v0.6.9" + rev: "v0.7.1" hooks: - id: ruff args: ["--fix"] diff --git a/.ruff.toml b/.ruff.toml index 346fe4b1..1c2d1a56 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -10,16 +10,34 @@ exclude = [ ] [lint] -select = ["E", "F", "W", "UP", "PT"] +select = [ + "E", + "F", + "W", + "UP", + "PT", +] extend-ignore = [ - # pycodestyle (E, W) - "E501", # LineTooLong # TODO! fix + # pycodestyle + "E501", # ignore line length will use a formatter instead + "E712", # Avoid equality comparisons to True; use if {cond}: for truth checks + "E721", # type comparison Use is and is not for type comparisons, or isinstance() for isinstance checks + # upgrades + "UP038", # Use | in isinstance - not compatible with models and is slower # pytest (PT) "PT001", # Always use pytest.fixture() "PT004", # Fixtures which don't return anything should have leading _ - "PT007", # Parametrize should be lists of tuples # TODO! fix - "PT011", # Too broad exception assert # TODO! fix + "PT011", # except(ValueRaises) is too broad "PT023", # Always use () on pytest decorators + # flake8-pie + "PIE808", # Disallow passing 0 as the first argument to range + # flake8-use-pathlib + "PTH123", # open() should be replaced by Path.open() + # Ruff + "RUF003", # Ignore ambiguous quote marks, doesn't allow ' in comments + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "RUF013", # PEP 484 prohibits implicit `Optional` + "RUF015", # Prefer `next(iter(...))` over single element slice ] [lint.per-file-ignores] @@ -33,6 +51,9 @@ extend-ignore = [ "docs/*.py" = [ "INP001", # Implicit-namespace-package. The examples are not a package. ] +"examples/**.py" = [ + "T201", # allow use of print in examples +] "__init__.py" = ["E402", "F401", "F403"] "test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] diff --git a/pyproject.toml b/pyproject.toml index bfd79d33..b7a78722 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -45,6 +45,7 @@ tests = [ docs = [ "sphinx", "sphinx-automodapi", + "sunpy-sphinx-theme", "packaging", "sphinx-changelog", "sphinx-gallery", diff --git a/sunkit_spex/_dev/scm_version.py b/sunkit_spex/_dev/scm_version.py index 1bcf0dd9..988debf5 100644 --- a/sunkit_spex/_dev/scm_version.py +++ b/sunkit_spex/_dev/scm_version.py @@ -1,11 +1,11 @@ # Try to use setuptools_scm to get the current version; this is only used # in development installations from the git repository. -import os.path +from pathlib import Path try: from setuptools_scm import get_version - version = get_version(root=os.path.join('..', '..'), relative_to=__file__) + version = get_version(root=Path('../..'), relative_to=__file__) except ImportError: raise except Exception as e: From 8332d5fca7cbe7f1b04b5edb163c201ec048f86b Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Wed, 30 Oct 2024 16:45:05 +0000 Subject: [PATCH 2/8] Update pyproject.toml --- pyproject.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index b7a78722..40197e36 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,7 +49,6 @@ docs = [ "packaging", "sphinx-changelog", "sphinx-gallery", - "sunpy-sphinx-theme", ] [project.urls] From 83fb3372544d906e1fa5f4cadc8084a8040c13d1 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 09:38:09 +0000 Subject: [PATCH 3/8] cleanup --- .cruft.json | 2 +- .ruff.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.cruft.json b/.cruft.json index f1047645..6efcb8a5 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/sunpy/package-template", - "commit": "51fb616094a4d7577c8898445aa50effb89afa31", + "commit": "6c92c2f29b9e4e2f054823922a2f62a16f74802c", "checkout": null, "context": { "cookiecutter": { diff --git a/.ruff.toml b/.ruff.toml index 1c2d1a56..6763fe75 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -54,7 +54,7 @@ extend-ignore = [ "examples/**.py" = [ "T201", # allow use of print in examples ] -"__init__.py" = ["E402", "F401", "F403"] +"__init__.py" = ["E402", "F401", "F403", "F405"] "test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] [lint.pydocstyle] From 9d98f737ad09017b78982c92d6e241bf32dcc31e Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 09:38:50 +0000 Subject: [PATCH 4/8] add extended ruff linting --- .cruft.json | 2 +- .ruff.toml | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/.cruft.json b/.cruft.json index 6efcb8a5..8da81584 100644 --- a/.cruft.json +++ b/.cruft.json @@ -16,7 +16,7 @@ "enable_dynamic_dev_versions": "y", "include_example_code": "n", "include_cruft_update_github_workflow": "y", - "use_extended_ruff_linting": "n", + "use_extended_ruff_linting": "y", "_sphinx_theme": "sunpy", "_parent_project": "", "_install_requires": "", diff --git a/.ruff.toml b/.ruff.toml index 6763fe75..eee3bcd0 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -16,6 +16,23 @@ select = [ "W", "UP", "PT", + "N", + "BLE", + "A", + "C4", + "INP", + "PIE", + "T20", + "RET", + "TID", + "PTH", + "PD", + "PLC", + "PLE", + "FLY", + "NPY", + "PERF", + "RUF", ] extend-ignore = [ # pycodestyle From 9d227cb965b865c0e513e097fdd628e289d8a246 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 09:41:32 +0000 Subject: [PATCH 5/8] ignore mixed case --- .ruff.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.ruff.toml b/.ruff.toml index eee3bcd0..471558a2 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -55,6 +55,8 @@ extend-ignore = [ "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` "RUF013", # PEP 484 prohibits implicit `Optional` "RUF015", # Prefer `next(iter(...))` over single element slice + # PEP8 Naming + "N802", # Mixed case variables ] [lint.per-file-ignores] From 15e436fe684a3e43a3e02b8cabd16f90dc1d9523 Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 10:15:19 +0000 Subject: [PATCH 6/8] ruff --- .ruff.toml | 5 ++- docs/conf.py | 4 +- examples/fitting_RHESSI_spectra.py | 3 +- examples/fitting_custom_spectra.py | 5 ++- examples/skip_fitting_NuSTAR_spectra.py | 3 +- sunkit_spex/data/__init__.py | 0 sunkit_spex/legacy/emission.py | 41 +++++++++---------- sunkit_spex/legacy/integrate.py | 3 +- sunkit_spex/legacy/io.py | 13 +++--- .../legacy/tests/test_photon_power_law.py | 24 +++++------ sunkit_spex/legacy/tests/test_thermal_.py | 7 ++-- sunkit_spex/legacy/thermal.py | 3 +- sunkit_spex/models/physical/io.py | 13 +++--- sunkit_spex/models/physical/tests/__init__.py | 0 .../models/physical/tests/test_thermal.py | 7 ++-- sunkit_spex/models/physical/thermal.py | 3 +- sunkit_spex/spectrum/spectrum.py | 10 ++--- sunkit_spex/spectrum/uncertainty.py | 3 +- 18 files changed, 68 insertions(+), 79 deletions(-) create mode 100644 sunkit_spex/data/__init__.py create mode 100644 sunkit_spex/models/physical/tests/__init__.py diff --git a/.ruff.toml b/.ruff.toml index 471558a2..4ed03c19 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -56,7 +56,9 @@ extend-ignore = [ "RUF013", # PEP 484 prohibits implicit `Optional` "RUF015", # Prefer `next(iter(...))` over single element slice # PEP8 Naming - "N802", # Mixed case variables + "N802", # Function name should be lowercase + "N803", # Argument name should be lowercase + "N806", # Variable in function should be lowercase ] [lint.per-file-ignores] @@ -72,6 +74,7 @@ extend-ignore = [ ] "examples/**.py" = [ "T201", # allow use of print in examples + "INP001", ] "__init__.py" = ["E402", "F401", "F403", "F405"] "test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] diff --git a/docs/conf.py b/docs/conf.py index 6510b0a6..07261173 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -51,7 +51,7 @@ ] # Add any paths that contain templates here, relative to this directory. -# templates_path = ["_templates"] # NOQA: ERA001 +# templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -101,7 +101,7 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -# html_static_path = ["_static"] # NOQA: ERA001 +# html_static_path = ["_static"] # By default, when rendering docstrings for classes, sphinx.ext.autodoc will # make docs with the class-level docstring and the class-method docstrings, diff --git a/examples/fitting_RHESSI_spectra.py b/examples/fitting_RHESSI_spectra.py index 8e57cdb5..45172586 100644 --- a/examples/fitting_RHESSI_spectra.py +++ b/examples/fitting_RHESSI_spectra.py @@ -19,6 +19,7 @@ import matplotlib.pyplot as plt import numpy as np +from numpy.exceptions import VisibleDeprecationWarning from parfive import Downloader from astropy.time import Time @@ -28,7 +29,7 @@ warnings.filterwarnings("ignore", category=RuntimeWarning) try: - warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning) + warnings.filterwarnings("ignore", category=VisibleDeprecationWarning) except AttributeError: warnings.filterwarnings("ignore", category=np.exceptions.VisibleDeprecationWarning) diff --git a/examples/fitting_custom_spectra.py b/examples/fitting_custom_spectra.py index 75af7a85..d502e68b 100644 --- a/examples/fitting_custom_spectra.py +++ b/examples/fitting_custom_spectra.py @@ -18,12 +18,13 @@ import matplotlib.pyplot as plt import numpy as np +from numpy.exceptions import VisibleDeprecationWarning from sunkit_spex.legacy.fitting.fitter import Fitter warnings.filterwarnings("ignore", category=RuntimeWarning) try: - warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning) + warnings.filterwarnings("ignore", category=VisibleDeprecationWarning) except AttributeError: warnings.filterwarnings("ignore", category=np.exceptions.VisibleDeprecationWarning) @@ -74,7 +75,7 @@ def gauss(a, b, c, energies=None): chan_bins = np.stack((np.arange(0, maxi, step), np.arange(step, maxi + step, step)), axis=-1) gauss_mod1 = gauss(1.3e4, 350, 60, energies=chan_bins) gauss_mod2 = gauss(3e3, 600, 60, energies=chan_bins) -noise = np.random.randint(low=10, high=100, size=(len(chan_bins))) / 100 * 5 +noise = np.random.Generator().integers(low=10, high=100, size=(len(chan_bins))) / 100 * 5 fake_data = gauss_mod1 + gauss_mod2 + noise diff --git a/examples/skip_fitting_NuSTAR_spectra.py b/examples/skip_fitting_NuSTAR_spectra.py index 684b57f6..3fe914a5 100644 --- a/examples/skip_fitting_NuSTAR_spectra.py +++ b/examples/skip_fitting_NuSTAR_spectra.py @@ -10,13 +10,14 @@ import matplotlib.pyplot as plt import numpy as np +from numpy.exceptions import VisibleDeprecationWarning from parfive import Downloader from sunkit_spex.legacy.fitting.fitter import Fitter, load warnings.filterwarnings("ignore", category=RuntimeWarning) try: - warnings.filterwarnings("ignore", category=np.VisibleDeprecationWarning) + warnings.filterwarnings("ignore", category=VisibleDeprecationWarning) except AttributeError: warnings.filterwarnings("ignore", category=np.exceptions.VisibleDeprecationWarning) diff --git a/sunkit_spex/data/__init__.py b/sunkit_spex/data/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sunkit_spex/legacy/emission.py b/sunkit_spex/legacy/emission.py index e210cd68..e22465f2 100755 --- a/sunkit_spex/legacy/emission.py +++ b/sunkit_spex/legacy/emission.py @@ -102,7 +102,7 @@ def __init__(self, *, p, q, eelow, eebrk, eehigh, norm=True): def __eq__(self, other): return all( - [getattr(self, name) == getattr(other, name) for name in ["p", "q", "eelow", "eebrk", "eehigh"]] + getattr(self, name) == getattr(other, name) for name in ["p", "q", "eelow", "eebrk", "eehigh"] ) and isinstance(other, self.__class__) def flux(self, electron_energy): @@ -211,9 +211,8 @@ def collisional_loss(electron_energy): beta = np.sqrt(1.0 - (1.0 / gamma**2)) # TODO figure out what number is? - energy_loss_rate = np.log(6.9447e9 * electron_energy) / beta + return np.log(6.9447e9 * electron_energy) / beta - return energy_loss_rate def bremsstrahlung_cross_section(electron_energy, photon_energy, z=1.2): @@ -301,9 +300,8 @@ def bremsstrahlung_cross_section(electron_energy, photon_energy, z=1.2): fe = (a2 / a1) * (1.0 - np.exp(-2.0 * np.pi * a1)) / (1.0 - np.exp(-2.0 * np.pi * a2)) # Compute the differential cross section (units cm^2). - cross_section = twoar02 * fe * crtmp + return twoar02 * fe * crtmp - return cross_section def _get_integrand(x_log, *, model, electron_dist, photon_energy, z, efd=True): @@ -354,18 +352,18 @@ def _get_integrand(x_log, *, model, electron_dist, photon_energy, z, efd=True): return ( electron_energy * np.log(10) * density * brem_cross * pc / collision_loss / ((electron_energy / mc2) + 1.0) ) - elif model == "thin-target": + if model == "thin-target": if efd: return electron_energy * np.log(10) * electron_dist.flux(electron_energy) * brem_cross * (mc2 / clight) - else: - return ( - electron_energy - * np.log(10) - * electron_dist.flux(electron_energy) - * brem_cross - * pc - / ((electron_energy / mc2) + 1.0) - ) + return ( + electron_energy + * np.log(10) + * electron_dist.flux(electron_energy) + * brem_cross + * pc + / ((electron_energy / mc2) + 1.0) + ) + return None def _integrate_part( @@ -469,6 +467,7 @@ def _integrate_part( # If all point have reached criterion return value and flags if i.size == 0: return intsum, ier + return None def _split_and_integrate(*, model, photon_energies, maxfcn, rerr, eelow, eebrk, eehigh, p, q, z, efd, integrator=None): @@ -641,10 +640,11 @@ def _split_and_integrate(*, model, photon_energies, maxfcn, rerr, eelow, eebrk, DmlinO = (intsum1 + intsum2 + intsum3) * (mc2 / clight) ier = ier1 + ier2 + ier3 return DmlinO, ier - elif model == "thin-target": + if model == "thin-target": Dmlin = intsum2 + intsum3 ier = ier2 + ier3 return Dmlin, ier + return None def bremsstrahlung_thin_target(photon_energies, p, eebrk, q, eelow, eehigh, efd=True, integrator=None): @@ -742,8 +742,7 @@ def bremsstrahlung_thin_target(photon_energies, p, eebrk, q, eelow, eehigh, efd= flux *= fcoeff return flux - else: - raise Warning("The photon energies are higher than the highest electron energy or not " "greater than zero") + raise Warning("The photon energies are higher than the highest electron energy or not " "greater than zero") def bremsstrahlung_thick_target(photon_energies, p, eebrk, q, eelow, eehigh, integrator=None): @@ -836,8 +835,6 @@ def bremsstrahlung_thick_target(photon_energies, p, eebrk, q, eelow, eehigh, int integrator=integrator, ) - flux = (fcoeff / decoeff) * flux + return (fcoeff / decoeff) * flux - return flux - else: - raise Warning("The photon energies are higher than the highest electron energy or not " "greater than zero") + raise Warning("The photon energies are higher than the highest electron energy or not " "greater than zero") diff --git a/sunkit_spex/legacy/integrate.py b/sunkit_spex/legacy/integrate.py index ac7931da..e7510d4a 100644 --- a/sunkit_spex/legacy/integrate.py +++ b/sunkit_spex/legacy/integrate.py @@ -91,9 +91,8 @@ def gauss_legendre(func, a, b, n=5, args=(), func_kwargs={}): xi, wi, ) = _legendre_roots(a, b, n) - integral = np.sum(wi * func(xi, *args, **func_kwargs), axis=1) + return np.sum(wi * func(xi, *args, **func_kwargs), axis=1) - return integral def fixed_quad(func, a, b, n=5, args=(), func_kwargs={}): diff --git a/sunkit_spex/legacy/io.py b/sunkit_spex/legacy/io.py index 32e8b3dc..f81c0c00 100644 --- a/sunkit_spex/legacy/io.py +++ b/sunkit_spex/legacy/io.py @@ -111,7 +111,7 @@ def load_chianti_lines_lite(): line_intensities *= 4 * np.pi * u.sr # Put data into intuitive structure and return it. - line_intensities_per_EM_at_source = xarray.DataArray( + return xarray.DataArray( line_intensities.value, dims=["lines", "temperature"], coords={ @@ -127,7 +127,6 @@ def load_chianti_lines_lite(): }, ) - return line_intensities_per_EM_at_source @manager.require( @@ -174,7 +173,7 @@ def load_chianti_continuum(): intensities *= 4 * np.pi intensity_unit *= u.sr # Put file data into intuitive structure and return data. - continuum_intensities = xarray.DataArray( + return xarray.DataArray( intensities, dims=["element_index", "temperature", "wavelength"], coords={ @@ -189,7 +188,6 @@ def load_chianti_continuum(): "chianti_doc": _clean_chianti_doc(contents["chianti_doc"]), }, ) - return continuum_intensities @manager.require( @@ -242,11 +240,10 @@ def load_xray_abundances(abundance_type=None): except KeyError: pass n_elements = len(contents[list(contents.keys())[0]]) - columns = [np.arange(1, n_elements + 1)] + list(contents.values()) - names = ["atomic number"] + list(contents.keys()) - abundances = Table(columns, names=names) + columns = [np.arange(1, n_elements + 1), *list(contents.values())] + names = ["atomic number", *list(contents.keys())] + return Table(columns, names=names) - return abundances def read_abundance_genx(filename): diff --git a/sunkit_spex/legacy/tests/test_photon_power_law.py b/sunkit_spex/legacy/tests/test_photon_power_law.py index b08fb4b9..8be7f4b4 100644 --- a/sunkit_spex/legacy/tests/test_photon_power_law.py +++ b/sunkit_spex/legacy/tests/test_photon_power_law.py @@ -11,13 +11,13 @@ def test_different_bins(): energy_bins1 = np.linspace(10, 100, num=30) << u.keV energy_bins2 = np.linspace(10, 100, num=10) << u.keV - shared_kw = dict( - norm_energy=3 << u.keV, - norm_flux=1 << ppl.PHOTON_RATE_UNIT, - break_energy=20 << u.keV, - lower_index=1, - upper_index=3, - ) + shared_kw = { + "norm_energy": 3 << u.keV, + "norm_flux": 1 << ppl.PHOTON_RATE_UNIT, + "break_energy": 20 << u.keV, + "lower_index": 1, + "upper_index": 3, + } dist1 = ppl.compute_broken_power_law(energy_edges=energy_bins1, **shared_kw) dist2 = ppl.compute_broken_power_law(energy_edges=energy_bins2, **shared_kw) @@ -119,11 +119,11 @@ def test_single_power_law(): energy_bins1 = np.linspace(10, 100, num=30) << u.keV energy_bins2 = np.linspace(10, 100, num=10) << u.keV - shared_kw = dict( - norm_energy=3 << u.keV, - norm_flux=1 << ppl.PHOTON_RATE_UNIT, - index=2, - ) + shared_kw = { + "norm_energy": 3 << u.keV, + "norm_flux": 1 << ppl.PHOTON_RATE_UNIT, + "index": 2, + } dist1 = ppl.compute_power_law(energy_edges=energy_bins1, **shared_kw) dist2 = ppl.compute_power_law(energy_edges=energy_bins2, **shared_kw) diff --git a/sunkit_spex/legacy/tests/test_thermal_.py b/sunkit_spex/legacy/tests/test_thermal_.py index f3dfc8d1..4ca69ac4 100644 --- a/sunkit_spex/legacy/tests/test_thermal_.py +++ b/sunkit_spex/legacy/tests/test_thermal_.py @@ -566,7 +566,7 @@ def chianti_kev_lines_Fe2(): return inputs, ssw_output -@pytest.mark.parametrize("ssw", (fvth_simple,)) +@pytest.mark.parametrize("ssw", [fvth_simple]) def test_thermal_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.thermal_emission(*input_args) @@ -574,7 +574,7 @@ def test_thermal_emission_against_ssw(ssw): np.testing.assert_allclose(output.value, expected_value, rtol=0.03) -@pytest.mark.parametrize("ssw", (chianti_kev_cont_simple, chianti_kev_cont_Fe2)) +@pytest.mark.parametrize("ssw", [chianti_kev_cont_simple, chianti_kev_cont_Fe2]) def test_continuum_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.continuum_emission(*input_args) @@ -582,7 +582,7 @@ def test_continuum_emission_against_ssw(ssw): np.testing.assert_allclose(output.value, expected_value, rtol=0.03) -@pytest.mark.parametrize("ssw", (chianti_kev_lines_simple, chianti_kev_lines_Fe2)) +@pytest.mark.parametrize("ssw", [chianti_kev_lines_simple, chianti_kev_lines_Fe2]) def test_line_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.line_emission(*input_args) @@ -626,5 +626,4 @@ def test_energy_out_of_range_warning(): warnings.simplefilter("always") # Trigger a warning. output = thermal.line_emission(np.arange(3, 28, 0.5) * u.keV, 6 * u.MK, 1e44 / u.cm**3) # noqa - print(w[0].category) assert issubclass(w[0].category, UserWarning) diff --git a/sunkit_spex/legacy/thermal.py b/sunkit_spex/legacy/thermal.py index c2f60165..b0acf031 100644 --- a/sunkit_spex/legacy/thermal.py +++ b/sunkit_spex/legacy/thermal.py @@ -462,9 +462,8 @@ def _line_emission(energy_edges_keV, temperature_K, abundances): # Scale flux by observer distance, emission measure and spectral bin width # and put into correct units. energy_bin_widths = (energy_edges_keV[1:] - energy_edges_keV[:-1]) * u.keV - flux = flux * LINE_GRID["intensity unit"] / energy_bin_widths + return flux * LINE_GRID["intensity unit"] / energy_bin_widths - return flux def _interpolate_continuum_intensities(data_grid, log10T_grid, energy_grid_keV, energy_keV, log10T): diff --git a/sunkit_spex/models/physical/io.py b/sunkit_spex/models/physical/io.py index d8905fa7..c6672f9a 100644 --- a/sunkit_spex/models/physical/io.py +++ b/sunkit_spex/models/physical/io.py @@ -111,7 +111,7 @@ def load_chianti_lines_lite(): line_intensities *= 4 * np.pi * u.sr # Put data into intuitive structure and return it. - line_intensities_per_EM_at_source = xarray.DataArray( + return xarray.DataArray( line_intensities.value, dims=["lines", "temperature"], coords={ @@ -127,7 +127,6 @@ def load_chianti_lines_lite(): }, ) - return line_intensities_per_EM_at_source @manager.require( @@ -174,7 +173,7 @@ def load_chianti_continuum(): intensities *= 4 * np.pi intensity_unit *= u.sr # Put file data into intuitive structure and return data. - continuum_intensities = xarray.DataArray( + return xarray.DataArray( intensities, dims=["element_index", "temperature", "wavelength"], coords={ @@ -189,7 +188,6 @@ def load_chianti_continuum(): "chianti_doc": _clean_chianti_doc(contents["chianti_doc"]), }, ) - return continuum_intensities @manager.require( @@ -242,11 +240,10 @@ def load_xray_abundances(abundance_type=None): except KeyError: pass n_elements = len(contents[list(contents.keys())[0]]) - columns = [np.arange(1, n_elements + 1)] + list(contents.values()) - names = ["atomic number"] + list(contents.keys()) - abundances = Table(columns, names=names) + columns = [np.arange(1, n_elements + 1), *list(contents.values())] + names = ["atomic number", *list(contents.keys())] + return Table(columns, names=names) - return abundances def read_abundance_genx(filename): diff --git a/sunkit_spex/models/physical/tests/__init__.py b/sunkit_spex/models/physical/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/sunkit_spex/models/physical/tests/test_thermal.py b/sunkit_spex/models/physical/tests/test_thermal.py index 3de91857..fea5f841 100644 --- a/sunkit_spex/models/physical/tests/test_thermal.py +++ b/sunkit_spex/models/physical/tests/test_thermal.py @@ -566,7 +566,7 @@ def chianti_kev_lines_Fe2(): return inputs, ssw_output -@pytest.mark.parametrize("ssw", (fvth_simple,)) +@pytest.mark.parametrize("ssw", [fvth_simple]) def test_thermal_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.thermal_emission(*input_args) @@ -574,7 +574,7 @@ def test_thermal_emission_against_ssw(ssw): np.testing.assert_allclose(output.value, expected_value, rtol=0.03) -@pytest.mark.parametrize("ssw", (chianti_kev_cont_simple, chianti_kev_cont_Fe2)) +@pytest.mark.parametrize("ssw", [chianti_kev_cont_simple, chianti_kev_cont_Fe2]) def test_continuum_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.continuum_emission(*input_args) @@ -582,7 +582,7 @@ def test_continuum_emission_against_ssw(ssw): np.testing.assert_allclose(output.value, expected_value, rtol=0.03) -@pytest.mark.parametrize("ssw", (chianti_kev_lines_simple, chianti_kev_lines_Fe2)) +@pytest.mark.parametrize("ssw", [chianti_kev_lines_simple, chianti_kev_lines_Fe2]) def test_line_emission_against_ssw(ssw): input_args, expected = ssw() output = thermal.line_emission(*input_args) @@ -626,5 +626,4 @@ def test_energy_out_of_range_warning(): warnings.simplefilter("always") # Trigger a warning. output = thermal.line_emission(np.arange(3, 28, 0.5) * u.keV, 6 * u.MK, 1e44 / u.cm**3) # noqa - print(w[0].category) assert issubclass(w[0].category, UserWarning) diff --git a/sunkit_spex/models/physical/thermal.py b/sunkit_spex/models/physical/thermal.py index d2db52db..307c844a 100644 --- a/sunkit_spex/models/physical/thermal.py +++ b/sunkit_spex/models/physical/thermal.py @@ -466,9 +466,8 @@ def _line_emission(energy_edges_keV, temperature_K, abundances): # Scale flux by observer distance, emission measure and spectral bin width # and put into correct units. energy_bin_widths = (energy_edges_keV[1:] - energy_edges_keV[:-1]) * u.keV - flux = flux * LINE_GRID["intensity unit"] / energy_bin_widths + return flux * LINE_GRID["intensity unit"] / energy_bin_widths - return flux def _interpolate_continuum_intensities(data_grid, log10T_grid, energy_grid_keV, energy_keV, log10T): diff --git a/sunkit_spex/spectrum/spectrum.py b/sunkit_spex/spectrum/spectrum.py index 305782d5..e186669e 100644 --- a/sunkit_spex/spectrum/spectrum.py +++ b/sunkit_spex/spectrum/spectrum.py @@ -40,12 +40,11 @@ def pixel_to_world(self, *args, **kwargs): return u.Quantity(super().pixel_to_world_values(*args, **kwargs)) return super().pixel_to_world(*args, **kwargs).to(orig_array.unit, equivalencies=u.spectral()) - tabular_gwcs = SpectralGWCS(forward_transform=forward_transform, input_frame=coord_frame, output_frame=spec_frame) + return SpectralGWCS(forward_transform=forward_transform, input_frame=coord_frame, output_frame=spec_frame) # Store the intended unit from the origin input array # tabular_gwcs._input_unit = orig_array.unit - return tabular_gwcs class SpectralAxis(SpectralCoord): @@ -61,7 +60,7 @@ class SpectralAxis(SpectralCoord): are interpreted as bin edges or bin centers. Defaults to "centers". """ - _equivalent_unit = SpectralCoord._equivalent_unit + (u.pixel,) + _equivalent_unit = (*SpectralCoord._equivalent_unit, u.pixel) def __new__(cls, value, *args, bin_specification="centers", **kwargs): # Enforce pixel axes are ascending @@ -113,8 +112,7 @@ def bin_edges(self): """ if hasattr(self, "_bin_edges"): return self._bin_edges - else: - return self._edges_from_centers(self.value, self.unit) + return self._edges_from_centers(self.value, self.unit) class Spectrum(NDCube): @@ -180,7 +178,7 @@ def __init__( if data is not None: if not isinstance(data, u.Quantity): raise ValueError("Flux must be a `Quantity` object.") - elif data.isscalar: + if data.isscalar: data = u.Quantity([data]) # Ensure that the unit information codified in the quantity object is diff --git a/sunkit_spex/spectrum/uncertainty.py b/sunkit_spex/spectrum/uncertainty.py index 24d56529..ff6499d7 100644 --- a/sunkit_spex/spectrum/uncertainty.py +++ b/sunkit_spex/spectrum/uncertainty.py @@ -72,8 +72,7 @@ def uncertainty_type(self): def _convert_uncertainty(self, other_uncert): if isinstance(other_uncert, PoissonUncertainty): return other_uncert - else: - raise IncompatibleUncertaintiesException + raise IncompatibleUncertaintiesException def _propagate_add(self, other_uncert, result_data, correlation): return super()._propagate_add_sub( From 325bfdfa4dbef8a5c7a1e5c8ffe552a35e4fbb8d Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 10:19:59 +0000 Subject: [PATCH 7/8] More ruff --- .cruft.json | 2 +- .ruff.toml | 46 +++++++++++++++----------- examples/fitting_custom_spectra.py | 2 +- sunkit_spex/legacy/emission.py | 2 -- sunkit_spex/legacy/integrate.py | 1 - sunkit_spex/legacy/io.py | 2 -- sunkit_spex/legacy/thermal.py | 1 - sunkit_spex/models/physical/io.py | 2 -- sunkit_spex/models/physical/thermal.py | 1 - sunkit_spex/spectrum/spectrum.py | 1 - 10 files changed, 28 insertions(+), 32 deletions(-) diff --git a/.cruft.json b/.cruft.json index 8da81584..772f3675 100644 --- a/.cruft.json +++ b/.cruft.json @@ -1,6 +1,6 @@ { "template": "https://github.com/sunpy/package-template", - "commit": "6c92c2f29b9e4e2f054823922a2f62a16f74802c", + "commit": "4ca8e60aac805d5f736de80c45ae0aba96b4cb85", "checkout": null, "context": { "cookiecutter": { diff --git a/.ruff.toml b/.ruff.toml index 4ed03c19..26ad66c0 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -35,26 +35,23 @@ select = [ "RUF", ] extend-ignore = [ - # pycodestyle + # pycodestyle (E, W) "E501", # ignore line length will use a formatter instead - "E712", # Avoid equality comparisons to True; use if {cond}: for truth checks - "E721", # type comparison Use is and is not for type comparisons, or isinstance() for isinstance checks - # upgrades + # pyupgrade (UP) "UP038", # Use | in isinstance - not compatible with models and is slower # pytest (PT) "PT001", # Always use pytest.fixture() "PT004", # Fixtures which don't return anything should have leading _ - "PT011", # except(ValueRaises) is too broad "PT023", # Always use () on pytest decorators - # flake8-pie + # flake8-pie (PIE) "PIE808", # Disallow passing 0 as the first argument to range - # flake8-use-pathlib + # flake8-use-pathlib (PTH) "PTH123", # open() should be replaced by Path.open() - # Ruff + # Ruff (RUF) "RUF003", # Ignore ambiguous quote marks, doesn't allow ' in comments - "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` - "RUF013", # PEP 484 prohibits implicit `Optional` - "RUF015", # Prefer `next(iter(...))` over single element slice + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` + "RUF013", # PEP 484 prohibits implicit `Optional` + "RUF015", # Prefer `next(iter(...))` over single element slice # PEP8 Naming "N802", # Function name should be lowercase "N803", # Argument name should be lowercase @@ -62,22 +59,31 @@ extend-ignore = [ ] [lint.per-file-ignores] -# Part of configuration, not a package. -"setup.py" = ["INP001"] -"conftest.py" = ["INP001"] +"setup.py" = [ + "INP001", # File is part of an implicit namespace package. +] +"conftest.py" = [ + "INP001", # File is part of an implicit namespace package. +] "docs/conf.py" = [ - "E402", # Module imports not at top of file - "F403" + "E402" # Module imports not at top of file ] "docs/*.py" = [ - "INP001", # Implicit-namespace-package. The examples are not a package. + "INP001", # File is part of an implicit namespace package. ] "examples/**.py" = [ "T201", # allow use of print in examples - "INP001", + "INP001", # File is part of an implicit namespace package. +] +"__init__.py" = [ + "E402", # Module level import not at top of cell + "F401", # Unused import + "F403", # from {name} import * used; unable to detect undefined names + "F405", # {name} may be undefined, or defined from star imports +] +"test_*.py" = [ + "E402", # Module level import not at top of cell ] -"__init__.py" = ["E402", "F401", "F403", "F405"] -"test_*.py" = ["B011", "D", "E402", "PGH001", "S101"] [lint.pydocstyle] convention = "numpy" diff --git a/examples/fitting_custom_spectra.py b/examples/fitting_custom_spectra.py index d502e68b..78234405 100644 --- a/examples/fitting_custom_spectra.py +++ b/examples/fitting_custom_spectra.py @@ -75,7 +75,7 @@ def gauss(a, b, c, energies=None): chan_bins = np.stack((np.arange(0, maxi, step), np.arange(step, maxi + step, step)), axis=-1) gauss_mod1 = gauss(1.3e4, 350, 60, energies=chan_bins) gauss_mod2 = gauss(3e3, 600, 60, energies=chan_bins) -noise = np.random.Generator().integers(low=10, high=100, size=(len(chan_bins))) / 100 * 5 +noise = np.random.default_rng().integers(low=10, high=100, size=(len(chan_bins))) / 100 * 5 fake_data = gauss_mod1 + gauss_mod2 + noise diff --git a/sunkit_spex/legacy/emission.py b/sunkit_spex/legacy/emission.py index e22465f2..09affcf3 100755 --- a/sunkit_spex/legacy/emission.py +++ b/sunkit_spex/legacy/emission.py @@ -214,7 +214,6 @@ def collisional_loss(electron_energy): return np.log(6.9447e9 * electron_energy) / beta - def bremsstrahlung_cross_section(electron_energy, photon_energy, z=1.2): """ Compute the relativistic electron-ion bremsstrahlung cross section @@ -303,7 +302,6 @@ def bremsstrahlung_cross_section(electron_energy, photon_energy, z=1.2): return twoar02 * fe * crtmp - def _get_integrand(x_log, *, model, electron_dist, photon_energy, z, efd=True): """ Return the value of the integrand for the thick- or thin-target bremsstrahlung models. diff --git a/sunkit_spex/legacy/integrate.py b/sunkit_spex/legacy/integrate.py index e7510d4a..c5d1021b 100644 --- a/sunkit_spex/legacy/integrate.py +++ b/sunkit_spex/legacy/integrate.py @@ -94,7 +94,6 @@ def gauss_legendre(func, a, b, n=5, args=(), func_kwargs={}): return np.sum(wi * func(xi, *args, **func_kwargs), axis=1) - def fixed_quad(func, a, b, n=5, args=(), func_kwargs={}): """ Compute a definite integral using fixed-order Gaussian quadrature. diff --git a/sunkit_spex/legacy/io.py b/sunkit_spex/legacy/io.py index f81c0c00..a7004d5b 100644 --- a/sunkit_spex/legacy/io.py +++ b/sunkit_spex/legacy/io.py @@ -128,7 +128,6 @@ def load_chianti_lines_lite(): ) - @manager.require( "chianti_continuum", [ @@ -245,7 +244,6 @@ def load_xray_abundances(abundance_type=None): return Table(columns, names=names) - def read_abundance_genx(filename): # Read file. contents = read_genx(filename) diff --git a/sunkit_spex/legacy/thermal.py b/sunkit_spex/legacy/thermal.py index b0acf031..8d22e4e5 100644 --- a/sunkit_spex/legacy/thermal.py +++ b/sunkit_spex/legacy/thermal.py @@ -465,7 +465,6 @@ def _line_emission(energy_edges_keV, temperature_K, abundances): return flux * LINE_GRID["intensity unit"] / energy_bin_widths - def _interpolate_continuum_intensities(data_grid, log10T_grid, energy_grid_keV, energy_keV, log10T): # Determine valid range based on limits of intensity grid's spectral extent # and the normalized temperature component of intensity. diff --git a/sunkit_spex/models/physical/io.py b/sunkit_spex/models/physical/io.py index c6672f9a..dc27ad8c 100644 --- a/sunkit_spex/models/physical/io.py +++ b/sunkit_spex/models/physical/io.py @@ -128,7 +128,6 @@ def load_chianti_lines_lite(): ) - @manager.require( "chianti_continuum", [ @@ -245,7 +244,6 @@ def load_xray_abundances(abundance_type=None): return Table(columns, names=names) - def read_abundance_genx(filename): # Read file. contents = read_genx(filename) diff --git a/sunkit_spex/models/physical/thermal.py b/sunkit_spex/models/physical/thermal.py index 307c844a..731b70d6 100644 --- a/sunkit_spex/models/physical/thermal.py +++ b/sunkit_spex/models/physical/thermal.py @@ -469,7 +469,6 @@ def _line_emission(energy_edges_keV, temperature_K, abundances): return flux * LINE_GRID["intensity unit"] / energy_bin_widths - def _interpolate_continuum_intensities(data_grid, log10T_grid, energy_grid_keV, energy_keV, log10T): # Determine valid range based on limits of intensity grid's spectral extent # and the normalized temperature component of intensity. diff --git a/sunkit_spex/spectrum/spectrum.py b/sunkit_spex/spectrum/spectrum.py index e186669e..6bab8128 100644 --- a/sunkit_spex/spectrum/spectrum.py +++ b/sunkit_spex/spectrum/spectrum.py @@ -46,7 +46,6 @@ def pixel_to_world(self, *args, **kwargs): # tabular_gwcs._input_unit = orig_array.unit - class SpectralAxis(SpectralCoord): """ Coordinate object representing spectral values corresponding to a specific From 15455ea806abb435d4727abddb5104a2b2f1867d Mon Sep 17 00:00:00 2001 From: Stuart Mumford Date: Thu, 31 Oct 2024 10:31:20 +0000 Subject: [PATCH 8/8] PT011 --- .ruff.toml | 4 ++++ sunkit_spex/models/physical/tests/test_thermal.py | 14 ++++++++------ 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/.ruff.toml b/.ruff.toml index 26ad66c0..f16a41b8 100644 --- a/.ruff.toml +++ b/.ruff.toml @@ -84,6 +84,10 @@ extend-ignore = [ "test_*.py" = [ "E402", # Module level import not at top of cell ] +# Be a bit more lax on legacy +"sunkit_spex/legacy/tests/*.py" = [ + "PT011", # `pytest.raises(ValueError)` is too broad +] [lint.pydocstyle] convention = "numpy" diff --git a/sunkit_spex/models/physical/tests/test_thermal.py b/sunkit_spex/models/physical/tests/test_thermal.py index fea5f841..69045793 100644 --- a/sunkit_spex/models/physical/tests/test_thermal.py +++ b/sunkit_spex/models/physical/tests/test_thermal.py @@ -591,32 +591,34 @@ def test_line_emission_against_ssw(ssw): def test_scalar_energy_input(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="energy_edges must be a 1-D astropy Quantity with length greater than 1"): thermal.thermal_emission(10 * u.keV, 6 * u.MK, 1e44 / u.cm**3) def test_len1_energy_input(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="energy_edges must be a 1-D astropy Quantity with length greater than 1"): thermal.thermal_emission([10] * u.keV, 6 * u.MK, 1e44 / u.cm**3) def test_energy_out_of_range_error(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="All input energy values must be within the range"): thermal.thermal_emission([0.01, 10] * u.keV, 6 * u.MK, 1e44 / u.cm**3) def test_temperature_out_of_range_error(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="All input temperature values must be within the range"): thermal.thermal_emission([5, 10] * u.keV, 0.1 * u.MK, 1e44 / u.cm**3) def test_relative_abundance_negative_input(): - with pytest.raises(ValueError): + with pytest.raises(ValueError, match="Relative abundances cannot be negative."): thermal.thermal_emission([5, 10] * u.keV, 10 * u.MK, 1e44 / u.cm**3, relative_abundances=((26, -1))) def test_relative_abundance_invalid_atomic_number_input(): - with pytest.raises(ValueError): + with pytest.raises( + ValueError, match="Relative abundances can only be set for elements with atomic numbers in range" + ): thermal.thermal_emission([5, 10] * u.keV, 10 * u.MK, 1e44 / u.cm**3, relative_abundances=((100, 1)))