Skip to content

Commit 089ed92

Browse files
authored
(chore): add bugbear and PIE lints (#1967)
1 parent 9fccaf5 commit 089ed92

34 files changed

Lines changed: 146 additions & 130 deletions

pyproject.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,7 @@ docstring-code-format = true
181181

182182
[tool.ruff.lint]
183183
select = [
184+
"B", # Likely bugs and design issues
184185
"BLE", # Blind except
185186
"C4", # Comprehensions
186187
"E", # Error detected by Pycodestyle
@@ -191,6 +192,7 @@ select = [
191192
"ICN", # Follow import conventions
192193
"ISC", # Implicit string concatenation
193194
"PERF", # Performance
195+
"PIE", # Syntax simplifications
194196
"PTH", # Pathlib instead of os.path
195197
"PT", # Pytest conventions
196198
"PL", # Pylint
@@ -221,6 +223,8 @@ allowed-confusables = [ "×", "’", "–", "α" ]
221223
[tool.ruff.lint.isort]
222224
known-first-party = [ "anndata" ]
223225
required-imports = [ "from __future__ import annotations" ]
226+
[tool.ruff.lint.flake8-bugbear]
227+
extend-immutable-calls = [ "slice" ]
224228
[tool.ruff.lint.flake8-tidy-imports.banned-api]
225229
"subprocess.call".msg = "Use `subprocess.run([…])` instead"
226230
"subprocess.check_call".msg = "Use `subprocess.run([…], check=True)` instead"

src/anndata/_core/aligned_df.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,8 @@ def _gen_dataframe_df(
8080
raise _mk_df_error(source, attr, length, len(anno))
8181
anno = anno.copy(deep=False)
8282
if not is_string_dtype(anno.index):
83-
warnings.warn("Transforming to str index.", ImplicitModificationWarning)
83+
msg = "Transforming to str index."
84+
warnings.warn(msg, ImplicitModificationWarning, stacklevel=2)
8485
anno.index = anno.index.astype(str)
8586
if not len(anno.columns):
8687
anno.columns = anno.columns.astype(str)

src/anndata/_core/anndata.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -391,10 +391,10 @@ def _init_as_actual( # noqa: PLR0912, PLR0913, PLR0915
391391
_check_2d_shape(X)
392392
# if type doesn’t match, a copy is made, otherwise, use a view
393393
if dtype is not None:
394-
warnings.warn(
395-
"The dtype argument is deprecated and will be removed in late 2024.",
396-
FutureWarning,
394+
msg = (
395+
"The dtype argument is deprecated and will be removed in late 2024."
397396
)
397+
warnings.warn(msg, FutureWarning, stacklevel=3)
398398
if issparse(X) or isinstance(X, ma.MaskedArray):
399399
# TODO: maybe use view on data attribute of sparse matrix
400400
# as in readwrite.read_10x_h5
@@ -1300,11 +1300,11 @@ def obs_vector(self, k: str, *, layer: str | None = None) -> np.ndarray:
13001300
if "X" in self.layers:
13011301
pass
13021302
else:
1303-
warnings.warn(
1303+
msg = (
13041304
"In a future version of AnnData, access to `.X` by passing"
1305-
" `layer='X'` will be removed. Instead pass `layer=None`.",
1306-
FutureWarning,
1305+
" `layer='X'` will be removed. Instead pass `layer=None`."
13071306
)
1307+
warnings.warn(msg, FutureWarning, stacklevel=2)
13081308
layer = None
13091309
return get_vector(self, k, "obs", "var", layer=layer)
13101310

@@ -1332,11 +1332,11 @@ def var_vector(self, k, *, layer: str | None = None) -> np.ndarray:
13321332
if "X" in self.layers:
13331333
pass
13341334
else:
1335-
warnings.warn(
1335+
msg = (
13361336
"In a future version of AnnData, access to `.X` by passing "
1337-
"`layer='X'` will be removed. Instead pass `layer=None`.",
1338-
FutureWarning,
1337+
"`layer='X'` will be removed. Instead pass `layer=None`."
13391338
)
1339+
warnings.warn(msg, FutureWarning, stacklevel=2)
13401340
layer = None
13411341
return get_vector(self, k, "var", "obs", layer=layer)
13421342

@@ -2100,7 +2100,7 @@ def _infer_shape_for_axis(
21002100
for elem in [xxx, xxxm, xxxp]:
21012101
if elem is not None and hasattr(elem, "shape"):
21022102
return elem.shape[0]
2103-
for elem, id in zip([layers, xxxm, xxxp], ["layers", "xxxm", "xxxp"]):
2103+
for elem, id in zip([layers, xxxm, xxxp], ["layers", "xxxm", "xxxp"], strict=True):
21042104
if elem is not None:
21052105
elem = cast("Mapping", elem)
21062106
for sub_elem in elem.values():

src/anndata/_core/merge.py

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
801801
raise NotImplementedError(msg)
802802
# TODO: behaviour here should be chosen through a merge strategy
803803
df = pd.concat(
804-
unify_dtypes(f(x) for f, x in zip(reindexers, arrays)),
804+
unify_dtypes(f(x) for f, x in zip(reindexers, arrays, strict=True)),
805805
axis=axis,
806806
ignore_index=True,
807807
)
@@ -816,7 +816,9 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
816816
msg = "Cannot concatenate an AwkwardArray with other array types."
817817
raise NotImplementedError(msg)
818818

819-
return ak.concatenate([f(a) for f, a in zip(reindexers, arrays)], axis=axis)
819+
return ak.concatenate(
820+
[f(a) for f, a in zip(reindexers, arrays, strict=True)], axis=axis
821+
)
820822
elif any(isinstance(a, CupySparseMatrix) for a in arrays):
821823
import cupyx.scipy.sparse as cpsparse
822824

@@ -829,7 +831,7 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
829831
return sparse_stack(
830832
[
831833
f(as_cp_sparse(a), axis=1 - axis, fill_value=fill_value)
832-
for f, a in zip(reindexers, arrays)
834+
for f, a in zip(reindexers, arrays, strict=True)
833835
],
834836
format="csr",
835837
)
@@ -842,7 +844,7 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
842844
return cp.concatenate(
843845
[
844846
f(cp.asarray(x), fill_value=fill_value, axis=1 - axis)
845-
for f, x in zip(reindexers, arrays)
847+
for f, x in zip(reindexers, arrays, strict=True)
846848
],
847849
axis=axis,
848850
)
@@ -856,7 +858,7 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
856858
axis=1 - axis,
857859
fill_value=fill_value,
858860
)
859-
for f, a in zip(reindexers, arrays)
861+
for f, a in zip(reindexers, arrays, strict=True)
860862
],
861863
format="csr",
862864
)
@@ -871,7 +873,7 @@ def concat_arrays(arrays, reindexers, axis=0, index=None, fill_value=None): # n
871873
return np.concatenate(
872874
[
873875
f(x, fill_value=fill_value, axis=1 - axis)
874-
for f, x in zip(reindexers, arrays)
876+
for f, x in zip(reindexers, arrays, strict=True)
875877
],
876878
axis=axis,
877879
)
@@ -932,7 +934,7 @@ def gen_outer_reindexers(els, shapes, new_index: pd.Index, *, axis=0):
932934
(lambda x: x)
933935
if not_missing(el)
934936
else (lambda _, shape=shape: pd.DataFrame(index=range(shape)))
935-
for el, shape in zip(els, shapes)
937+
for el, shape in zip(els, shapes, strict=True)
936938
]
937939
elif any(isinstance(el, AwkArray) for el in els if not_missing(el)):
938940
import awkward as ak
@@ -1025,7 +1027,7 @@ def outer_concat_aligned_mapping(
10251027
fill_value=fill_value,
10261028
off_axis_size=off_axis_size,
10271029
)
1028-
for el, n in zip(els, ns)
1030+
for el, n in zip(els, ns, strict=True)
10291031
],
10301032
cur_reindexers,
10311033
axis=concat_axis,
@@ -1046,7 +1048,8 @@ def concat_pairwise_mapping(
10461048

10471049
for k in join_keys(mappings):
10481050
els = [
1049-
m.get(k, sparse_class((s, s), dtype=bool)) for m, s in zip(mappings, shapes)
1051+
m.get(k, sparse_class((s, s), dtype=bool))
1052+
for m, s in zip(mappings, shapes, strict=True)
10501053
]
10511054
if all(isinstance(el, CupySparseMatrix | CupyArray) for el in els):
10521055
result[k] = _cp_block_diag(els, format="csr")
@@ -1075,7 +1078,7 @@ def merge_outer(mappings, batch_keys, *, join_index="-", merge=merge_unique):
10751078
all_keys = union_keys(mappings)
10761079
out = merge(mappings)
10771080
for key in all_keys.difference(out.keys()):
1078-
for b, m in zip(batch_keys, mappings):
1081+
for b, m in zip(batch_keys, mappings, strict=True):
10791082
val = m.get(key, None)
10801083
if val is not None:
10811084
out[f"{key}{join_index}{b}"] = val
@@ -1633,7 +1636,7 @@ def concat( # noqa: PLR0912, PLR0913, PLR0915
16331636
alt_mapping = merge(
16341637
[
16351638
{k: r(v, axis=0) for k, v in getattr(a, f"{alt_axis_name}m").items()}
1636-
for r, a in zip(reindexers, adatas)
1639+
for r, a in zip(reindexers, adatas, strict=True)
16371640
],
16381641
)
16391642
alt_pairwise = merge(
@@ -1642,7 +1645,7 @@ def concat( # noqa: PLR0912, PLR0913, PLR0915
16421645
k: r(r(v, axis=0), axis=1)
16431646
for k, v in getattr(a, f"{alt_axis_name}p").items()
16441647
}
1645-
for r, a in zip(reindexers, adatas)
1648+
for r, a in zip(reindexers, adatas, strict=True)
16461649
]
16471650
)
16481651
uns = uns_merge([a.uns for a in adatas])
@@ -1668,11 +1671,11 @@ def concat( # noqa: PLR0912, PLR0913, PLR0915
16681671
axis=axis,
16691672
)
16701673
elif any(has_raw):
1671-
warn(
1674+
msg = (
16721675
"Only some AnnData objects have `.raw` attribute, "
1673-
"not concatenating `.raw` attributes.",
1674-
UserWarning,
1676+
"not concatenating `.raw` attributes."
16751677
)
1678+
warn(msg, UserWarning, stacklevel=2)
16761679
return AnnData(
16771680
**{
16781681
"X": X,

src/anndata/_core/sparse_dataset.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ def get_compressed_vectors_for_slices(
306306
if len(slices) < 2: # there is only one slice so no need to concatenate
307307
return data, indices, start_indptr
308308
end_indptr = np.concatenate(
309-
[s[1:] - o for s, o in zip(indptr_indices[1:], offsets)]
309+
[s[1:] - o for s, o in zip(indptr_indices[1:], offsets, strict=True)]
310310
)
311311
indptr = np.concatenate([start_indptr, end_indptr])
312312
return data, indices, indptr
@@ -499,10 +499,10 @@ def _normalize_index(
499499
return row, col
500500

501501
def __setitem__(self, index: Index | tuple[()], value) -> None:
502-
warnings.warn(
503-
"__setitem__ for backed sparse will be removed in the next anndata release.",
504-
FutureWarning,
502+
msg = (
503+
"__setitem__ for backed sparse will be removed in the next anndata release."
505504
)
505+
warnings.warn(msg, FutureWarning, stacklevel=2)
506506
row, col = self._normalize_index(index)
507507
mock_matrix = self._to_backed()
508508
mock_matrix[row, col] = value

src/anndata/_core/storage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def __repr__():
4747
if isinstance(value, (*array_data_structure_types, Dataset2D)):
4848
if isinstance(value, np.matrix):
4949
msg = f"{name} should not be a np.matrix, use np.ndarray instead."
50-
warnings.warn(msg, ImplicitModificationWarning)
50+
warnings.warn(msg, ImplicitModificationWarning, stacklevel=3)
5151
value = value.A
5252
return value
5353
is_non_csc_r_array_or_matrix = (

src/anndata/_core/views.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ def convert_all(arrs: Iterable[np.ndarray]) -> Iterable[np.ndarray]:
153153
results = (results,)
154154
results = tuple(
155155
(np.asarray(result) if output is None else output)
156-
for result, output in zip(results, outputs)
156+
for result, output in zip(results, outputs, strict=True)
157157
)
158158
return results[0] if len(results) == 1 else results
159159

src/anndata/_io/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ def __getattr__(key: str):
99
from .. import io
1010

1111
attr = getattr(io, key)
12-
warnings.warn(
12+
msg = (
1313
f"Importing {key} from `anndata._io` is deprecated. "
14-
"Please use anndata.io instead.",
15-
FutureWarning,
14+
"Please use anndata.io instead."
1615
)
16+
warnings.warn(msg, FutureWarning, stacklevel=2)
1717
return attr

src/anndata/_io/h5ad.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -296,11 +296,11 @@ def _read_raw(
296296
@report_read_key_on_error
297297
def read_dataframe_legacy(dataset: h5py.Dataset) -> pd.DataFrame:
298298
"""Read pre-anndata 0.7 dataframes."""
299-
warn(
300-
f"'{dataset.name}' was written with a very old version of AnnData. "
301-
"Consider rewriting it.",
302-
OldFormatWarning,
299+
msg = (
300+
f"{dataset.name!r} was written with a very old version of AnnData. "
301+
"Consider rewriting it."
303302
)
303+
warn(msg, OldFormatWarning, stacklevel=2)
304304
if H5PY_V3:
305305
df = pd.DataFrame(
306306
_decode_structured_array(

src/anndata/_io/read.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,11 +227,11 @@ def read_loom( # noqa: PLR0912, PLR0913
227227
"""
228228
# Deprecations
229229
if obsm_names is not None:
230-
warn(
230+
msg = (
231231
"Argument obsm_names has been deprecated in favour of `obsm_mapping`. "
232-
"In 0.9 this will be an error.",
233-
FutureWarning,
232+
"In 0.9 this will be an error."
234233
)
234+
warn(msg, FutureWarning, stacklevel=2)
235235
if obsm_mapping != {}:
236236
msg = (
237237
"Received values for both `obsm_names` and `obsm_mapping`. This is "
@@ -240,11 +240,11 @@ def read_loom( # noqa: PLR0912, PLR0913
240240
raise ValueError(msg)
241241
obsm_mapping = obsm_names
242242
if varm_names is not None:
243-
warn(
243+
msg = (
244244
"Argument varm_names has been deprecated in favour of `varm_mapping`. "
245-
"In 0.9 this will be an error.",
246-
FutureWarning,
245+
"In 0.9 this will be an error."
247246
)
247+
warn(msg, FutureWarning, stacklevel=2)
248248
if varm_mapping != {}:
249249
msg = (
250250
"Received values for both `varm_names` and `varm_mapping`. This is "

0 commit comments

Comments
 (0)