From 06fece1380024af6346f3ff3c2f5a8880ca47cd0 Mon Sep 17 00:00:00 2001 From: Emmanuel Bourret Date: Sun, 16 Aug 2026 21:46:11 -0400 Subject: [PATCH 1/3] MAINT: pin ruff rule set and sort imports CI installed ruff via the unpinned `ruff>=0.4` extra and ran it with no configuration, so the rule set was whatever ruff defaulted to that day. ruff 0.16.1 lints under different defaults than 0.15.20 did and reported 30 errors on unchanged sources, breaking CI on an unrelated Dependabot PR. Select E, F, W, I explicitly and set line-length to 80, matching the project convention, so the rule set no longer moves with the tool. The longest line in src/ was already exactly 80, so the limit costs nothing. Sorts imports across src/, tests/ and tools/, wraps 19 over-long test lines, and drops two unused test imports. No assertion or test logic is changed. --- pyproject.toml | 6 +++ src/spectroplot/_patterns.py | 1 + src/spectroplot/data_reader.py | 12 +++-- src/spectroplot/functions.py | 9 ++-- src/spectroplot/spectroplot.py | 92 +++++++++++++++++++++++---------- tests/test_data_reader.py | 13 +++-- tests/test_functions.py | 94 +++++++++++++++++++++++++--------- tests/test_integration.py | 5 +- tools/sum_spectra.py | 7 +-- 9 files changed, 172 insertions(+), 67 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index f2788a6..0800542 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,3 +39,9 @@ spectroplot = "spectroplot:main" [tool.setuptools.packages.find] where = ["src"] + +[tool.ruff] +line-length = 80 + +[tool.ruff.lint] +select = ["E", "F", "W", "I"] diff --git a/src/spectroplot/_patterns.py b/src/spectroplot/_patterns.py index 56229e8..bb27b61 100644 --- a/src/spectroplot/_patterns.py +++ b/src/spectroplot/_patterns.py @@ -5,6 +5,7 @@ """ import re + from spectroplot.global_constants import RE_SPECTRUM_ROOT_PATTERN RE_SPECTRUM_ROOT = re.compile(RE_SPECTRUM_ROOT_PATTERN) diff --git a/src/spectroplot/data_reader.py b/src/spectroplot/data_reader.py index d77f3bf..acf885d 100644 --- a/src/spectroplot/data_reader.py +++ b/src/spectroplot/data_reader.py @@ -5,16 +5,18 @@ """ import logging -import re #regex - -from pathlib import Path #path processing (replace os) +import re #regex +from pathlib import Path #path processing (replace os) from typing import Iterator, Optional, Tuple +from spectroplot._patterns import RE_SPECTRUM_ROOT from spectroplot.global_constants import ( - SPECSTRING_START, SPECSTRING_END, IR_STRING, VPT2_STRING, + IR_STRING, RAMAN_STRING, + SPECSTRING_END, + SPECSTRING_START, + VPT2_STRING, ) -from spectroplot._patterns import RE_SPECTRUM_ROOT logger = logging.getLogger(__name__) logger.addHandler(logging.NullHandler()) diff --git a/src/spectroplot/functions.py b/src/spectroplot/functions.py index d190c01..123a732 100644 --- a/src/spectroplot/functions.py +++ b/src/spectroplot/functions.py @@ -5,10 +5,13 @@ """ from typing import Optional -import numpy as np #element-wise tensor processing -import pandas as pd #dataframe processing -from spectroplot.global_constants import npt_nm, npt_wn, npt_ev, CONV_WNTOEV + +import numpy as np #element-wise tensor processing +import pandas as pd #dataframe processing + from spectroplot._patterns import RE_SPECTRUM_ROOT +from spectroplot.global_constants import CONV_WNTOEV, npt_ev, npt_nm, npt_wn + def show_plots(ext: str, s: list[bool]) -> bool: """Check if the file type matches any requested plot type. diff --git a/src/spectroplot/spectroplot.py b/src/spectroplot/spectroplot.py index 1600d57..f3f0887 100644 --- a/src/spectroplot/spectroplot.py +++ b/src/spectroplot/spectroplot.py @@ -4,37 +4,77 @@ @author: Emmanuel Bourret """ -import sys #sys files processing +import argparse #argument parser +import sys #sys files processing +from pathlib import Path #path processing from typing import Optional -from pathlib import Path #path processing -import argparse #argument parser -import numpy as np #element-wise tensor processing -import pandas as pd #dataframes processing -import matplotlib.pyplot as plt #plots -import seaborn as sns #color palettes -from scipy.signal import find_peaks #peak detection -from spectroplot.global_constants import ( - th_fac, esd_fac, ex_fac, color_palette, - label_tddft, label_sticks, label_expt, label_roots, - label_ir, label_raman, label_vpt2, label_vpt2_overt, - label_sticks_vib, - show_single_lineshape, show_single_lineshape_area, - show_conv_spectrum, show_sticks, show_exp_spectrum, - show_esd_spectrum, show_single_root_area, - show_label_peaks, show_label_roots, - show_minor_ticks, show_grid, show_legend, linear_locator, - y_label, y_label_PL, x_label_wn, x_label_ev, x_label_nm, - label_rotation_angle, figure_dpi, acs_w, acs_h, output_name, - CONV_WNTOEV, w_nm, w_wn, w_ev, w_ir, w_raman, -) +import matplotlib.pyplot as plt #plots +import numpy as np #element-wise tensor processing +import pandas as pd #dataframes processing +import seaborn as sns #color palettes +from scipy.signal import find_peaks #peak detection + from spectroplot._patterns import RE_SPECTRUM_ROOT +from spectroplot.data_reader import SpectrumData #spectrum data parser from spectroplot.functions import ( - atLeastTwo, plotType, show_plots, rootSum, - xdataPrep, xdatamin, xdatamax, plotxrange, - lineshape, normalization, rounddown, roundup, + atLeastTwo, + lineshape, + normalization, + plotType, + plotxrange, + rootSum, + rounddown, + roundup, + show_plots, + xdatamax, + xdatamin, + xdataPrep, +) +from spectroplot.global_constants import ( + CONV_WNTOEV, + acs_h, + acs_w, + color_palette, + esd_fac, + ex_fac, + figure_dpi, + label_expt, + label_ir, + label_raman, + label_roots, + label_rotation_angle, + label_sticks, + label_sticks_vib, + label_tddft, + label_vpt2, + label_vpt2_overt, + linear_locator, + output_name, + show_conv_spectrum, + show_esd_spectrum, + show_exp_spectrum, + show_grid, + show_label_peaks, + show_label_roots, + show_legend, + show_minor_ticks, + show_single_lineshape, + show_single_lineshape_area, + show_single_root_area, + show_sticks, + th_fac, + w_ev, + w_ir, + w_nm, + w_raman, + w_wn, + x_label_ev, + x_label_nm, + x_label_wn, + y_label, + y_label_PL, ) -from spectroplot.data_reader import SpectrumData #spectrum data parser def _plot_tddft(ax, row, i, plt_range_x, w, ls_gauss, palette, lw, diff --git a/tests/test_data_reader.py b/tests/test_data_reader.py index ca30c1e..9bb0b28 100644 --- a/tests/test_data_reader.py +++ b/tests/test_data_reader.py @@ -5,11 +5,11 @@ """ import sys + sys.path.insert(0, "src") from spectroplot.data_reader import SpectrumData -from spectroplot.global_constants import SPECSTRING_START, SPECSTRING_END - +from spectroplot.global_constants import SPECSTRING_END, SPECSTRING_START DATA_DIR = "data" @@ -296,7 +296,9 @@ def test_read_ir_lines(self): assert abs(x[2] - 300.0) < 1e-6 def test_read_ir_early_exit(self): - x, y = self.sd.read_ir(lines=IR_LINES + ["extra\n", " 4: 400.00 0.200000\n"]) + x, y = self.sd.read_ir( + lines=IR_LINES + ["extra\n", " 4: 400.00 0.200000\n"] + ) assert len(x) == 3, "should stop at blank line, not read extra" @@ -311,7 +313,10 @@ def test_read_raman_lines(self): assert abs(y[0] - 0.5) < 1e-6 def test_read_raman_early_exit(self): - x, y = self.sd.read_raman(lines=RAMAN_LINES + ["extra\n", " 4: 400.00 0.200000 0.500000\n"]) + x, y = self.sd.read_raman( + lines=RAMAN_LINES + + ["extra\n", " 4: 400.00 0.200000 0.500000\n"] + ) assert len(x) == 3, "should stop at blank line, not read extra" diff --git a/tests/test_functions.py b/tests/test_functions.py index d81109b..940d7a1 100644 --- a/tests/test_functions.py +++ b/tests/test_functions.py @@ -4,27 +4,43 @@ @author: Emmanuel Bourret """ +import sys + import numpy as np import pandas as pd -import sys + sys.path.insert(0, "src") -from hypothesis import given, strategies as st, settings +from hypothesis import given, settings +from hypothesis import strategies as st from spectroplot.functions import ( - wntonm, wntoev, nmtown, nmtoev, - lineshape, normalization, atLeastTwo, - plotType, roundup, rounddown, unitConverter, - show_plots, is_unique, rootSum, - xdataPrep, xdatamin, xdatamax, plotxrange, + atLeastTwo, + is_unique, + lineshape, + nmtoev, + nmtown, + normalization, + plotType, + plotxrange, + rootSum, + rounddown, + roundup, + show_plots, + unitConverter, + wntoev, + wntonm, + xdatamax, + xdatamin, + xdataPrep, ) -from spectroplot.global_constants import CONV_WNTOEV # Strategies for property-based converter tests positive_floats = st.floats(min_value=1, max_value=1e6, allow_infinity=False, allow_nan=False) positive_nparrays = st.lists( - st.floats(min_value=1, max_value=1e6, allow_infinity=False, allow_nan=False), + st.floats(min_value=1, max_value=1e6, allow_infinity=False, + allow_nan=False), min_size=1, max_size=10, ).map(np.array) @@ -221,32 +237,56 @@ def test_rounddown_nm(self): class TestShowPlots: def test_out_any_true(self): - assert show_plots(".out", [True, False, False, False, False, False, False]) is True - assert show_plots(".out", [False, True, False, False, False, False, False]) is True - assert show_plots(".out", [False, False, True, False, False, False, False]) is True - assert show_plots(".out", [False, False, False, True, False, False, False]) is True + assert show_plots( + ".out", [True, False, False, False, False, False, False] + ) is True + assert show_plots( + ".out", [False, True, False, False, False, False, False] + ) is True + assert show_plots( + ".out", [False, False, True, False, False, False, False] + ) is True + assert show_plots( + ".out", [False, False, False, True, False, False, False] + ) is True def test_out_all_false(self): - assert show_plots(".out", [False, False, False, False, False, False, False]) is False + assert show_plots( + ".out", [False, False, False, False, False, False, False] + ) is False def test_asc_true(self): - assert show_plots(".asc", [False, False, False, False, True, False, False]) is True + assert show_plots( + ".asc", [False, False, False, False, True, False, False] + ) is True def test_asc_false(self): - assert show_plots(".asc", [False, False, False, False, False, False, False]) is False + assert show_plots( + ".asc", [False, False, False, False, False, False, False] + ) is False def test_spectrum_true(self): - assert show_plots(".spectrum", [False, False, False, False, False, True, False]) is True - assert show_plots(".spectrum", [False, False, False, False, False, False, True]) is True + assert show_plots( + ".spectrum", [False, False, False, False, False, True, False] + ) is True + assert show_plots( + ".spectrum", [False, False, False, False, False, False, True] + ) is True def test_spectrum_false(self): - assert show_plots(".spectrum", [False, False, False, False, False, False, False]) is False + assert show_plots( + ".spectrum", [False, False, False, False, False, False, False] + ) is False def test_root_ext_true(self): - assert show_plots(".spectrum.root1", [False, False, False, False, False, True, False]) is True + assert show_plots( + ".spectrum.root1", [False, False, False, False, False, True, False] + ) is True def test_unknown_ext(self): - assert show_plots(".xyz", [True, True, True, True, True, True, True]) is False + assert show_plots( + ".xyz", [True, True, True, True, True, True, True] + ) is False class TestIsUnique: @@ -275,7 +315,9 @@ def test_basic_sum(self): result = rootSum(df) assert len(result) == 1 np.testing.assert_array_equal(result.iloc[0]["xdata"], xdata) - np.testing.assert_array_equal(result.iloc[0]["ydata"], np.array([5.0, 7.0, 9.0])) + np.testing.assert_array_equal( + result.iloc[0]["ydata"], np.array([5.0, 7.0, 9.0]) + ) def test_different_names_raises(self): df = pd.DataFrame([ @@ -351,7 +393,9 @@ def test_asc(self): assert xdatamin(row, 10.0) == 400.0 - 30.0 def test_out(self): - row = pd.Series({"ext": ".out", "xdata_plot": np.array([1000.0, 2000.0])}) + row = pd.Series( + {"ext": ".out", "xdata_plot": np.array([1000.0, 2000.0])} + ) assert xdatamin(row, 10.0) == 1000.0 @@ -361,5 +405,7 @@ def test_asc(self): assert xdatamax(row, 10.0) == 500.0 + 30.0 def test_out(self): - row = pd.Series({"ext": ".out", "xdata_plot": np.array([1000.0, 2000.0])}) + row = pd.Series( + {"ext": ".out", "xdata_plot": np.array([1000.0, 2000.0])} + ) assert xdatamax(row, 10.0) == 2000.0 diff --git a/tests/test_integration.py b/tests/test_integration.py index d6c58dc..b249126 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -5,7 +5,6 @@ """ import sys -from pathlib import Path from unittest.mock import MagicMock, patch import pytest @@ -95,7 +94,9 @@ def test_nonexistent_file_skipped(self): assert exc_info.value.code == 1 def test_mixed_types(self): - mock_ax = self._run_main([TEST_FILES["tddft"], TEST_FILES["experimental"]]) + mock_ax = self._run_main( + [TEST_FILES["tddft"], TEST_FILES["experimental"]] + ) assert mock_ax.plot.called def test_multiple_esd_roots(self): diff --git a/tools/sum_spectra.py b/tools/sum_spectra.py index 84496d6..82c9c35 100644 --- a/tools/sum_spectra.py +++ b/tools/sum_spectra.py @@ -1,8 +1,9 @@ #!/usr/bin/python3 -import sys +import argparse import os import re -import argparse +import sys + import numpy as np #global variables @@ -42,7 +43,7 @@ intenslist.append(float(line.strip().split()[1])) fclist.append(float(line.strip().split()[2])) htlist.append(float(line.strip().split()[3])) - + print("{0:s}\t{1:e}".format(filename_root,max(intenslist))) energylist_tot = energylist if index == 0: From 2fab027d7517c9235fc92d92b3ffadd77987af37 Mon Sep 17 00:00:00 2001 From: Emmanuel Bourret Date: Sun, 16 Aug 2026 21:46:30 -0400 Subject: [PATCH 2/3] FIX: undefined name in sum_spectra error path The `except IOError` handler referenced `filename`, which is not defined: the loop variable is `path` and `args.filename` is the argparse list. A missing input file therefore raised NameError from the handler instead of reporting the file, and left sys.exit(1) unreachable. --- tools/sum_spectra.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/sum_spectra.py b/tools/sum_spectra.py index 82c9c35..2c5b2e0 100644 --- a/tools/sum_spectra.py +++ b/tools/sum_spectra.py @@ -58,7 +58,7 @@ #file not found -> exit here except IOError: - print(f"'{filename}'" + " not found") + print(f"'{path}'" + " not found") sys.exit(1) data = np.column_stack([energylist_tot, intenslist_tot, fclist_tot, htlist_tot]) From 521e6401691dda8317ab9f376c3c97cc049eb533 Mon Sep 17 00:00:00 2001 From: Emmanuel Bourret Date: Sun, 16 Aug 2026 21:46:30 -0400 Subject: [PATCH 3/3] MAINT: lint the whole repository in CI Extends `ruff check src/` to `ruff check .`, so tests/ and tools/ are linted too. src/ being the only linted path is why an undefined name went unnoticed in tools/. --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ed0018d..bcff952 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,6 +18,6 @@ jobs: - run: pip install -e ".[dev]" - - run: ruff check src/ + - run: ruff check . - run: pytest -v tests/