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

Enable pylint and other linting rules in ruff #417

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 2 additions & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ repos:
rev: v0.4.5
hooks:
- id: ruff
args: ["--preview"]
- id: ruff-format
- repo: https://github.com/codespell-project/codespell
rev: v2.3.0
Expand All @@ -56,4 +57,4 @@ repos:
args: [--nbqa-dont-skip-bad-cells]
- id: nbqa-ruff
additional_dependencies: [ruff==v0.4.5]
args: ["--ignore=E722,F821,S110"]
args: ["--ignore=E722,F821,S110", "--preview"]
20 changes: 9 additions & 11 deletions becquerel/core/calibration.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,9 @@ def _eval_expression(
if y_low and y_high:
msg = msg + msg_low + " and " + msg_high
elif y_low:
msg = msg + msg_low
msg += msg_low
else:
msg = msg + msg_high
msg += msg_high
warnings.warn(msg, CalibrationWarning)
return y

Expand Down Expand Up @@ -240,9 +240,8 @@ def _validate_expression(
raise CalibrationError(f"Independent variable {ind_var} must be 'x' or 'y'")
ind_var_appears = False
for node in ast.walk(ast.parse(expression)):
if type(node) is ast.Name:
if node.id == ind_var:
ind_var_appears = True
if type(node) is ast.Name and node.id == ind_var:
ind_var_appears = True
if not ind_var_appears:
raise CalibrationError(
f'Independent variable "{ind_var}" must appear in the expression:\n'
Expand All @@ -267,12 +266,11 @@ def _validate_expression(
"Parameter indices in expression are not contiguous:\n"
f"{expression}\n{param_indices}"
)
if params is not None:
if len(param_indices) != len(params):
raise CalibrationError(
"Not enough parameter indices in expression:\n"
f"{expression}\n{param_indices}"
)
if params is not None and len(param_indices) != len(params):
raise CalibrationError(
"Not enough parameter indices in expression:\n"
f"{expression}\n{param_indices}"
)

# make sure the expression can be evaluated
if params is not None:
Expand Down
23 changes: 9 additions & 14 deletions becquerel/core/fitting.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,9 +203,7 @@ def _is_count_like(y):
"""
if np.any(y < 0):
return False
if not np.allclose(np.rint(y).astype(int), y):
return False
return True
return np.allclose(np.rint(y).astype(int), y)


class ConstantModel(Model):
Expand Down Expand Up @@ -693,7 +691,7 @@ def _translate_model(self, m):
raise FittingError(f"Unknown model type: {m}")

def _make_model(self, model):
if isinstance(model, str) or isinstance(model, Model):
if isinstance(model, (str, Model)):
model = [model]
# Convert the model(s) to a list of Model classes / Model instancess
self._model_cls_cnt = {}
Expand Down Expand Up @@ -750,10 +748,9 @@ def _guess_param_defaults(self, **kwargs):

def guess_param_defaults(self, update=False, **kwargs):
defaults = self._guess_param_defaults(**kwargs)
if update:
if defaults is not None:
for dp in defaults:
self.set_param(*dp)
if update and defaults is not None:
for dp in defaults:
self.set_param(*dp)
return defaults

def fit(self, backend="lmfit", guess=None, limits=None):
Expand Down Expand Up @@ -807,7 +804,7 @@ def fit(self, backend="lmfit", guess=None, limits=None):
self.params, # self.result.params,
x=self.x_roi,
weights=self.dx_roi,
fit_kws={"reduce_fcn": lambda r: np.sum(r)},
fit_kws={"reduce_fcn": np.sum},
method="Nelder-Mead",
calc_covar=False,
) # no, bounds, default would be L-BFGS-B'
Expand Down Expand Up @@ -871,9 +868,7 @@ def model_loss(*args):
min_vals[lim[0]] = lim[2]
elif lim[1] == "max":
max_vals[lim[0]] = lim[2]
limits_i = {
p: (min_vals.get(p, None), max_vals.get(p, None)) for p in free_vars
}
limits_i = {p: (min_vals.get(p), max_vals.get(p)) for p in free_vars}
except NotImplementedError:
# If the model/component does not have a guess() method
limits_i = {}
Expand Down Expand Up @@ -1440,8 +1435,8 @@ def custom_plot(
# Add info about the ROI and units
if self.roi:
s += "ROI: [{:.3f}, {:.3f}]\n".format(*self.roi)
s += "X units: {:s}\n".format(self.xmode if self.xmode else "None")
s += "Y units: {:s}\n".format(self.ymode if self.ymode else "None")
s += "X units: {:s}\n".format(self.xmode or "None")
s += "Y units: {:s}\n".format(self.ymode or "None")
Comment on lines +1438 to +1439
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dislike this change---it relies on a subtlety unique to python that not even many intermediate python programmers know, just for the sake of a little brevity. Could we disable this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your alternate idea: convert both to f"{str(self.xymode)}" for maximum conciseness.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is almost certainly FURB110

# Add to empty axis
txt_ax.text(
x=0.01,
Expand Down
21 changes: 10 additions & 11 deletions becquerel/core/plotting.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,18 +94,17 @@ def xmode(self, mode):
self._xmode = "energy"
else:
self._xmode = "channel"
elif mode.lower() in ("kev", "energy"):
if not self.spec.is_calibrated:
raise PlottingError(
"Spectrum is not calibrated, however "
"x axis was requested as energy"
)
self._xmode = "energy"
elif mode.lower() in ("channel", "channels", "chn", "chns"):
self._xmode = "channel"
else:
if mode.lower() in ("kev", "energy"):
if not self.spec.is_calibrated:
raise PlottingError(
"Spectrum is not calibrated, however "
"x axis was requested as energy"
)
self._xmode = "energy"
elif mode.lower() in ("channel", "channels", "chn", "chns"):
self._xmode = "channel"
else:
raise PlottingError(f"Unknown x data mode: {mode}")
raise PlottingError(f"Unknown x data mode: {mode}")

# Then, set the _xedges and _xlabel based on the _xmode
xedges, xlabel = self.spec.parse_xmode(self._xmode)
Expand Down
87 changes: 44 additions & 43 deletions becquerel/core/spectrum.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,12 +176,11 @@ def __init__(

if realtime is not None:
self.realtime = float(realtime)
if self.livetime is not None:
if self.livetime > self.realtime:
raise ValueError(
f"Livetime ({self.livetime}) cannot exceed realtime "
f"({self.realtime})"
)
if self.livetime is not None and self.livetime > self.realtime:
raise ValueError(
f"Livetime ({self.livetime}) cannot exceed realtime "
f"({self.realtime})"
)

self.start_time = handle_datetime(start_time, "start_time", allow_none=True)
self.stop_time = handle_datetime(stop_time, "stop_time", allow_none=True)
Expand Down Expand Up @@ -903,20 +902,26 @@ def _add_sub_error_checking(self, other):
"calibrated spectrum. If both have the same calibration, "
'please use the "calibrate_like" method'
)
if self.is_calibrated and other.is_calibrated:
if not np.all(self.bin_edges_kev == other.bin_edges_kev):
raise NotImplementedError(
"Addition/subtraction for arbitrary calibrated spectra "
"not implemented"
)
# TODO: if both spectra are calibrated but with different
# calibrations, should one be rebinned to match?
if not self.is_calibrated and not other.is_calibrated:
if not np.all(self.bin_edges_raw == other.bin_edges_raw):
raise NotImplementedError(
"Addition/subtraction for arbitrary uncalibrated "
"spectra not implemented"
)
if (
self.is_calibrated
and other.is_calibrated
and not np.all(self.bin_edges_kev == other.bin_edges_kev)
):
raise NotImplementedError(
"Addition/subtraction for arbitrary calibrated spectra "
"not implemented"
)
# TODO: if both spectra are calibrated but with different
# calibrations, should one be rebinned to match?
if (
not self.is_calibrated
and not other.is_calibrated
and not np.all(self.bin_edges_raw == other.bin_edges_raw)
):
raise NotImplementedError(
"Addition/subtraction for arbitrary uncalibrated "
"spectra not implemented"
)

def __mul__(self, other):
"""Return a new Spectrum object with counts (or CPS) scaled up.
Expand All @@ -937,7 +942,7 @@ def __mul__(self, other):
# This line adds the right multiplication
__rmul__ = __mul__

def __div__(self, other):
def __truediv__(self, other):
"""Return a new Spectrum object with counts (or CPS) scaled down.

Args:
Expand All @@ -953,9 +958,6 @@ def __div__(self, other):

return self._mul_div(other, div=True)

# This line adds true division
__truediv__ = __div__

def _mul_div(self, scaling_factor: float, div=False):
"""Multiply or divide a spectrum by a scalar. Handle errors.

Expand All @@ -980,13 +982,12 @@ def _mul_div(self, scaling_factor: float, div=False):
or np.isnan(scaling_factor)
):
raise ValueError("Scaling factor must be nonzero and finite")
else:
if (
scaling_factor.nominal_value == 0
or np.isinf(scaling_factor.nominal_value)
or np.isnan(scaling_factor.nominal_value)
):
raise ValueError("Scaling factor must be nonzero and finite")
elif (
scaling_factor.nominal_value == 0
or np.isinf(scaling_factor.nominal_value)
or np.isnan(scaling_factor.nominal_value)
):
raise ValueError("Scaling factor must be nonzero and finite")
if div:
multiplier = 1 / scaling_factor
else:
Expand Down Expand Up @@ -1146,10 +1147,7 @@ def has_uniform_bins(self, use_kev=None, rtol=None) -> bool:
# first non-uniform bin.
iterator = iter(bin_widths)
x0 = next(iterator, None)
for x in iterator:
if abs(x / x0 - 1.0) > rtol:
return False
return True
return all(abs(x / x0 - 1.0) <= rtol for x in iterator)

def find_bin_index(self, x: float, use_kev=None) -> int:
"""Find the Spectrum bin index or indices containing x-axis value(s) x.
Expand Down Expand Up @@ -1348,13 +1346,16 @@ def rebin(
"Cannot rebin spectrum without energy calibration"
) # TODO: why not?
in_spec = self.counts_vals
if method.lower() == "listmode":
if (self._counts is None) and (self.livetime is not None):
warnings.warn(
"Rebinning by listmode method without explicit counts "
"provided in Spectrum object",
SpectrumWarning,
)
if (
method.lower() == "listmode"
and (self._counts is None)
and (self.livetime is not None)
):
warnings.warn(
"Rebinning by listmode method without explicit counts "
"provided in Spectrum object",
SpectrumWarning,
)
out_spec = rebin(
in_spec,
self.bin_edges_kev,
Expand Down Expand Up @@ -1476,7 +1477,7 @@ def plot(self, *fmt, **kwargs):
color = ax.get_lines()[-1].get_color()
if emode == "band":
plotter.errorband(color=color, alpha=alpha * 0.5, label="_nolegend_")
elif emode == "bars" or emode == "bar":
elif emode in ("bars", "bar"):
plotter.errorbar(color=color, label="_nolegend_")
elif emode != "none":
raise SpectrumError(f"Unknown error mode '{emode}', use 'bars' or 'band'")
Expand Down
4 changes: 2 additions & 2 deletions becquerel/io/h5.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def write_h5(name: str | h5py.File | h5py.Group, dsets: dict, attrs: dict) -> No
"""
with open_h5(name, "w") as file:
# write the datasets
for key in dsets.keys():
for key in dsets:
try:
file.create_dataset(
key,
Expand Down Expand Up @@ -137,7 +137,7 @@ def read_h5(name: str | h5py.File | h5py.Group) -> tuple[dict, dict, list]:
skipped = []
with open_h5(name, "r") as file:
# read the datasets
for key in file.keys():
for key in file:
# skip any non-datasets
if not isinstance(file[key], h5py.Dataset):
skipped.append(str(key))
Expand Down
2 changes: 1 addition & 1 deletion becquerel/parsers/cnf.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ def read(filename, verbose=False, cal_kwargs=None):
data["counts"] = counts

# clean up null characters in any strings
for key in data.keys():
for key in data:
if isinstance(data[key], str):
data[key] = data[key].replace("\x00", " ")
data[key] = data[key].replace("\x01", " ")
Expand Down
2 changes: 1 addition & 1 deletion becquerel/parsers/iec1455.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def read(filename, verbose=False, cal_kwargs=None):
energy_channel_pairs = []
energy_res_pairs = [] # currently unused
energy_eff_pairs = [] # currently unused
with Path(filename).open() as f:
with Path(filename).open(encoding="utf-8") as f:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really necessary?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is PLW1514.

record = 1
# loop over lines
for line in f:
Expand Down
2 changes: 1 addition & 1 deletion becquerel/parsers/spc.py
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,7 @@ def read(filename, verbose=False, cal_kwargs=None):
raise BecquerelParserError("Calibration parameters not found") from exc

# clean up null characters in any strings
for key in data.keys():
for key in data:
if isinstance(data[key], str):
data[key] = data[key].replace("\x00", " ")
data[key] = data[key].replace("\x01", " ")
Expand Down
7 changes: 3 additions & 4 deletions becquerel/parsers/spe.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ def read(filename, verbose=False, cal_kwargs=None):
counts = []
channels = []
cal_coeff = []
with Path(filename).open() as f:
with Path(filename).open(encoding="utf-8") as f:
# read & remove newlines from end of each line
lines = [line.strip() for line in f]
i = 0
Expand Down Expand Up @@ -97,9 +97,8 @@ def read(filename, verbose=False, cal_kwargs=None):
while i < len(lines) and not lines[i].startswith("$"):
values.append(lines[i])
i += 1
if i < len(lines):
if lines[i].startswith("$"):
i -= 1
if i < len(lines) and lines[i].startswith("$"):
i -= 1
if len(values) == 1:
values = values[0]
data[key] = values
Expand Down
Loading
Loading