Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch code formatting and linting to ruff #398

Merged
merged 39 commits into from
May 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
a7db278
Start migration to ruff
markbandstra Feb 23, 2024
2301297
Add previous linting and checks and fix a few issues
markbandstra Feb 23, 2024
38bf109
Enable flake8-bugbear
markbandstra Feb 23, 2024
931d6c1
Fix exception chaining
markbandstra Feb 23, 2024
56c60af
Add several checks that do not result in any issues
markbandstra Feb 23, 2024
1df5b14
Enable ruff preview mode and fix small issues that come up
markbandstra Feb 23, 2024
3213e3c
Enable flake8-builtins
markbandstra Feb 23, 2024
181616e
Enable flake8-comprehensions
markbandstra Feb 23, 2024
baff018
Enable flake8-future-annotations
markbandstra Feb 23, 2024
974636c
Enable flake8-implicit-str-concat
markbandstra Feb 23, 2024
059f430
Enable flake8-import-conventions
markbandstra Feb 23, 2024
e685273
Enable flake8-pie
markbandstra Feb 23, 2024
d5fff6c
Enable flake8-raise
markbandstra Feb 23, 2024
fa3cae4
Enable tryceratops
markbandstra Feb 23, 2024
c8a03c0
Enable numpy-specific rules
markbandstra Feb 23, 2024
694aa6d
Enable perflint
markbandstra Feb 23, 2024
ff6b020
Enable refurb
markbandstra Feb 23, 2024
8ccad99
Fix metadata issue
markbandstra Feb 23, 2024
b2a75db
Replace black with ruff
markbandstra Feb 24, 2024
cccab11
Remove flake8 and black settings
markbandstra Feb 24, 2024
409615b
Add ruff badge to README
markbandstra Feb 24, 2024
2dc9961
Reorder badges
markbandstra Feb 24, 2024
3cd95b3
Add license badge
markbandstra Feb 24, 2024
5a95a57
Merge branch 'main' into switch-linting-to-ruff
markbandstra Apr 10, 2024
50d8d4f
Remove pytest-ruff
markbandstra May 23, 2024
5fb023a
Turn off ruff preview mode
markbandstra May 23, 2024
1027054
Merge branch 'main' into switch-linting-to-ruff
markbandstra May 23, 2024
5ddd184
Fix ruff issues due to merge
markbandstra May 23, 2024
6c61012
Add ruff linting and formatting for notebooks
markbandstra May 23, 2024
0bf792f
Update ruff version
markbandstra May 23, 2024
d1cb99f
Remove black from requirements.txt
markbandstra May 23, 2024
c1fd7ae
Add note that one can run pre-commit on a per-file basis
markbandstra May 23, 2024
2492e66
Revert "Remove black from requirements.txt"
markbandstra May 23, 2024
5409b63
Fix typo
markbandstra May 25, 2024
5e4c0b2
Enable flake8-blind-except
markbandstra May 25, 2024
880eaff
Enable flake8-use-pathlib in ruff
markbandstra May 26, 2024
8785f27
Make sure Path.open not used as a class method
markbandstra May 27, 2024
0d04095
Add pandas-vet
markbandstra May 28, 2024
472dced
Fix double quotes
markbandstra May 29, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 8 additions & 47 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,41 +27,13 @@ repos:
- id: requirements-txt-fixer
- id: sort-simple-yaml
- id: trailing-whitespace
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.2
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.5
hooks:
- id: pyupgrade
args: [--py38-plus]
- repo: https://github.com/asottile/yesqa
rev: v1.5.0
hooks:
- id: yesqa
- repo: https://github.com/pycqa/isort
rev: 5.13.2
hooks:
- id: isort
args: [--profile, black, --float-to-top, --color]
- repo: https://github.com/psf/black
rev: 24.4.2
hooks:
- id: black
- repo: https://github.com/PyCQA/autoflake
rev: v2.3.1
hooks:
- id: autoflake
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-bugbear]
args: ["--ignore=W503,B015,B028"]
- repo: https://github.com/PyCQA/bandit
rev: 1.7.8
hooks:
- id: bandit
args: [--skip, "B101"]
- id: ruff
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
rev: v2.3.0
hooks:
- id: codespell
args: [
Expand All @@ -82,17 +54,6 @@ repos:
- id: nbqa-check-ast
additional_dependencies: [pre-commit-hooks]
args: [--nbqa-dont-skip-bad-cells]
- id: nbqa-pyupgrade
additional_dependencies: [pyupgrade==v3.15.0]
args: [--py37-plus]
- id: nbqa-isort
additional_dependencies: [isort==5.13.2]
args: [--profile=black, --float-to-top]
- id: nbqa-black
additional_dependencies: [black==24.1.1]
- id: nbqa-pydocstyle
additional_dependencies: [pydocstyle==6.3.0]
args: ["--ignore=D100,D103"]
- id: nbqa-flake8
additional_dependencies: [flake8==7.0.0, flake8-bugbear]
args: [--max-line-length=88, "--ignore=E203,E722,F821,W503,B001,B015"]
- id: nbqa-ruff
additional_dependencies: [ruff==v0.4.5]
args: ["--ignore=E722,F821,S110"]
7 changes: 6 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ Please follow these guidelines when contributing to this project.
## Developer Instructions

```bash
pip install -r requirements.txt
pip install -r requirements-dev.txt
python setup.py develop

Expand All @@ -30,6 +29,12 @@ new code, and it can also be run at any time using the following command:
pre-commit run --all
```

or run on any files not yet committed to the repository using

```bash
pre-commit run --files <filename1> <filename2> ...
```

### Running the tests

(Requires `requirements-dev.txt` to be installed)
Expand Down
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# becquerel

[![tests](https://github.com/lbl-anp/becquerel/actions/workflows/tests.yaml/badge.svg?branch=)](https://github.com/lbl-anp/becquerel/actions/workflows/tests.yaml)
[![Coverage Status](https://coveralls.io/repos/github/lbl-anp/becquerel/badge.svg?branch=main)](https://coveralls.io/github/lbl-anp/becquerel?branch=main)
[![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
[![PyPI version](https://img.shields.io/pypi/v/becquerel.svg)](https://pypi.org/project/becquerel)
[![PyPI pyversions](https://img.shields.io/pypi/pyversions/becquerel.svg)](https://pypi.org/project/becquerel)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![PyPI license](https://img.shields.io/pypi/l/becquerel.svg)](https://pypi.python.org/project/becquerel)
[![tests](https://github.com/lbl-anp/becquerel/actions/workflows/tests.yaml/badge.svg?branch=)](https://github.com/lbl-anp/becquerel/actions/workflows/tests.yaml)
[![Coverage Status](https://coveralls.io/repos/github/lbl-anp/becquerel/badge.svg?branch=main)](https://coveralls.io/github/lbl-anp/becquerel?branch=main)

Becquerel is a Python package for analyzing nuclear spectroscopic
measurements. The core functionalities are reading and writing different
Expand Down Expand Up @@ -50,12 +51,13 @@ The dependencies `beautifulsoup4`, `lxml` and `html5lib` are necessary for
[`pandas`][1].

Developers require additional requirements which are listed in
`requirements-dev.txt`. We use [`pytest`][2] for unit testing, [`black`][3] for
code formatting and are converting to [`numpydoc`][4].
`requirements-dev.txt`. We use [`pytest`][2] for unit testing, [`ruff`][3] for
code formatting and linting, and are planning to eventually support
[`numpydoc`][4] docstrings.

[1]: https://pandas.pydata.org/pandas-docs/stable/install.html#dependencies
[2]: https://docs.pytest.org/en/latest/
[3]: https://black.readthedocs.io/en/stable/
[3]: https://docs.astral.sh/ruff/
[4]: https://numpydoc.readthedocs.io/en/latest/format.html

## Copyright Notice
Expand Down
38 changes: 19 additions & 19 deletions becquerel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,44 @@
warnings.simplefilter("default", DeprecationWarning)

__all__ = [
"__description__",
"__url__",
"__version__",
"__license__",
"__copyright__",
"core",
"utils",
"fitting",
"AutoCalibrator",
"AutoCalibratorError",
"LinearEnergyCal",
"EnergyCalError",
"BadInput",
"Calibration",
"CalibrationError",
"CalibrationWarning",
"Element",
"EnergyCalError",
"Fitter",
"GaussianPeakFilter",
"Isotope",
"IsotopeQuantity",
"LinearEnergyCal",
"PeakFilter",
"PeakFilterError",
"GaussianPeakFilter",
"PeakFinder",
"PeakFinderError",
"SpectrumPlotter",
"PlottingError",
"rebin",
"RebinError",
"RebinWarning",
"Spectrum",
"SpectrumError",
"UncalibratedError",
"SpectrumPlotter",
"SpectrumWarning",
"UncalibratedError",
"UncertaintiesError",
"__copyright__",
"__description__",
"__license__",
"__url__",
"__version__",
"core",
"fitting",
"materials",
"nndc",
"parsers",
"rebin",
"tools",
"nndc",
"utils",
"xcom",
"materials",
"Element",
"Isotope",
"IsotopeQuantity",
]
1 change: 0 additions & 1 deletion becquerel/__metadata__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
"""becquerel package metadata."""

__name__ = "becquerel"
__author__ = "The Becquerel Development Team"
__maintainer__ = __author__
__email__ = "[email protected]"
Expand Down
16 changes: 8 additions & 8 deletions becquerel/core/autocal.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ def find_best_gain(
snrs = np.array(snrs)
n_req = len(required_energies)
# make sure the required and optional sets do not overlap
optional = sorted(list(set(optional) - set(required_energies)))
optional = sorted(set(optional) - set(required_energies))
n_opt = len(optional)
n_set = n_req + n_opt
best_fom = None
Expand Down Expand Up @@ -187,9 +187,9 @@ def find_best_gain(
"Valid calibration found:\n"
f"FOM: {fom:15.9f}"
f" gain: {gain:6.3f}"
f" ergs: {str(comb_erg):50s}"
f" de: {str(de):50s}"
f" chans: {str(comb_chan):40s}"
f" ergs: {comb_erg!s:50s}"
f" de: {de!s:50s}"
f" chans: {comb_chan!s:40s}"
)
if best_fom is None:
best_fom = fom + 1.0
Expand All @@ -204,15 +204,15 @@ def find_best_gain(
"Best calibration so far:\n"
f"FOM: {best_fom:15.9f}"
f" gain: {best_gain:6.3f}"
f" ergs: {str(best_ergs):50s}"
f" de: {str(de):50s}"
f" chans: {str(best_chans):40s}"
f" ergs: {best_ergs!s:50s}"
f" de: {de!s:50s}"
f" chans: {best_chans!s:40s}"
)
n_set -= 1
if best_gain is None:
return None
else:
print("found best gain: %f keV/channel" % best_gain)
print(f"found best gain: {best_gain:f} keV/channel")
return {
"gain": best_gain,
"channels": best_chans,
Expand Down
30 changes: 15 additions & 15 deletions becquerel/core/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,15 +53,15 @@ def _validate_domain_range(domain, rng):
# must be length-2 iterables
try:
len(domain)
except TypeError:
raise CalibrationError(f"Domain must be length-2 iterable: {domain}")
except TypeError as exc:
raise CalibrationError(f"Domain must be length-2 iterable: {domain}") from exc
domain = np.asarray(domain)
if not (len(domain) == 2 and domain.ndim == 1):
raise CalibrationError(f"Domain must be length-2 iterable: {domain}")
try:
len(rng)
except TypeError:
raise CalibrationError(f"Range must be length-2 iterable: {rng}")
except TypeError as exc:
raise CalibrationError(f"Range must be length-2 iterable: {rng}") from exc
rng = np.asarray(rng)
if not (len(rng) == 2 and rng.ndim == 1):
raise CalibrationError(f"Range must contain two values: {rng}")
Expand Down Expand Up @@ -230,10 +230,10 @@ def _validate_expression(
# apply black formatting for consistency and error checking
try:
expression = black.format_str(expression, mode=black.FileMode())
except (black.InvalidInput, blib2to3.pgen2.tokenize.TokenError):
except (black.InvalidInput, blib2to3.pgen2.tokenize.TokenError) as exc:
raise CalibrationError(
f"Error while running black on expression:\n{expression}"
)
) from exc

# make sure `ind_var` appears in the formula
if ind_var not in ["x", "y"]:
Expand All @@ -252,10 +252,10 @@ def _validate_expression(
# make sure each parameter appears at least once
try:
param_indices = _param_indices(expression)
except ValueError:
except ValueError as exc:
raise CalibrationError(
f"Unable to extract indices to parameters:\n{expression}"
)
) from exc
if len(param_indices) > 0:
if param_indices.min() != 0:
raise CalibrationError(
Expand Down Expand Up @@ -288,11 +288,11 @@ def _validate_expression(
domain=domain,
rng=rng,
)
except CalibrationError:
except CalibrationError as exc:
raise CalibrationError(
f"Cannot evaluate expression for float {ind_var} = {x_val}:\n"
f"{expression}\n{safe_eval.symtable['x']}"
)
) from exc
try:
_eval_expression(
expression,
Expand All @@ -303,11 +303,11 @@ def _validate_expression(
domain=domain,
rng=rng,
)
except CalibrationError:
except CalibrationError as exc:
raise CalibrationError(
f"Cannot evaluate expression for array {ind_var} = {x_arr}:\n"
f"{expression}\n{safe_eval.symtable['x']}"
)
) from exc

return expression.strip()

Expand Down Expand Up @@ -628,7 +628,7 @@ def __repr__(self):
if len(self.attrs) > 0:
for key in self.attrs:
result += ", "
result += f"{key}={repr(self.attrs[key])}"
result += f"{key}={self.attrs[key]!r}"
result += ")"
return result

Expand Down Expand Up @@ -907,7 +907,7 @@ def read(cls, name):
-------
calibration : becquerel.Calibration
"""
dsets, attrs, skipped = io.h5.read_h5(name)
dsets, attrs, _ = io.h5.read_h5(name)
if "params" not in dsets:
raise CalibrationError('Expected dataset "params"')
if "expression" not in dsets:
Expand Down Expand Up @@ -1237,7 +1237,7 @@ def plot(self, ax=None):
has_points = self.points_x.size > 0

if ax is None:
fig, ax = plt.subplots(1 + has_points, 1, sharex=True)
_, ax = plt.subplots(1 + has_points, 1, sharex=True)

if has_points:
assert ax.shape == (2,)
Expand Down
24 changes: 14 additions & 10 deletions becquerel/core/energycal.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
""""Energy calibration classes"""
"""Energy calibration classes"""

import warnings
from abc import ABCMeta, abstractmethod, abstractproperty
Expand Down Expand Up @@ -73,8 +73,8 @@ def from_points(cls, chlist, kevlist, include_origin=False):

try:
cond = len(chlist) != len(kevlist)
except TypeError:
raise BadInput("Inputs must be one dimensional iterables")
except TypeError as exc:
raise BadInput("Inputs must be one dimensional iterables") from exc
if cond:
raise BadInput("Channels and energies must be same length")

Expand All @@ -86,8 +86,8 @@ def from_points(cls, chlist, kevlist, include_origin=False):
for ch, kev in zip(chlist, kevlist):
try:
cal.new_calpoint(ch, kev)
except (ValueError, TypeError):
raise BadInput("Inputs must be one dimensional iterables")
except (ValueError, TypeError) as exc:
raise BadInput("Inputs must be one dimensional iterables") from exc
cal.update_fit()
return cal

Expand Down Expand Up @@ -306,7 +306,7 @@ def plot(self, ax=None):
has_points = self.channels.size > 0

if ax is None:
fig, ax = plt.subplots(1 + has_points, 1, sharex=True)
_, ax = plt.subplots(1 + has_points, 1, sharex=True)

if has_points:
assert ax.shape == (2,)
Expand Down Expand Up @@ -389,17 +389,21 @@ def slope(self):

try:
return self._coeffs["b"]
except KeyError:
raise EnergyCalError("Slope coefficient not yet supplied or calculated.")
except KeyError as exc:
raise EnergyCalError(
"Slope coefficient not yet supplied or calculated."
) from exc

@property
def offset(self):
"""Return the offset coefficient value."""

try:
return self._coeffs["c"]
except KeyError:
raise EnergyCalError("Offset coefficient not yet supplied or calculated.")
except KeyError as exc:
raise EnergyCalError(
"Offset coefficient not yet supplied or calculated."
) from exc

def _ch2kev(self, ch):
"""Convert scalar OR np.array of channel(s) to energies.
Expand Down
Loading
Loading