Skip to content

Commit bf20d70

Browse files
authored
Add Pylint lints (#3577)
1 parent 8162c52 commit bf20d70

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+358
-384
lines changed

benchmarks/benchmarks/tools.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121

2222
def setup():
23-
global adata
23+
global adata # noqa: PLW0603
2424
adata = pbmc68k_reduced()
2525
assert "X_pca" in adata.obsm
2626

docs/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
HERE = Path(__file__).parent
1919
sys.path[:0] = [str(HERE.parent), str(HERE / "extensions")]
20-
import scanpy # noqa
20+
import scanpy
2121

2222
if TYPE_CHECKING:
2323
from sphinx.application import Sphinx

docs/extensions/debug_docstrings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
_pd_orig = sphinx.ext.napoleon._process_docstring
1616

1717

18-
def pd_new(app, what, name, obj, options, lines): # noqa: PLR0917
18+
def pd_new(app, what, name, obj, options, lines) -> None: # noqa: PLR0917
1919
"""Wrap ``sphinx.ext.napoleon._process_docstring``."""
2020
_pd_orig(app, what, name, obj, options, lines)
2121
print(*lines, sep="\n")
2222

2323

24-
def setup(app: Sphinx):
24+
def setup(app: Sphinx) -> None:
2525
"""App setup hook."""
2626
if os.environ.get("DEBUG") is not None:
2727
sphinx.ext.napoleon._process_docstring = pd_new

docs/extensions/function_images.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
def insert_function_images( # noqa: PLR0917
1616
app: Sphinx, what: str, name: str, obj: Any, options: Options, lines: list[str]
17-
):
17+
) -> None:
1818
"""Insert images for plot functions."""
1919
path = app.config.api_dir / f"{name}.png"
2020
if what != "function" or not path.is_file():
@@ -27,7 +27,7 @@ def insert_function_images( # noqa: PLR0917
2727
]
2828

2929

30-
def setup(app: Sphinx):
30+
def setup(app: Sphinx) -> None:
3131
"""App setup hook."""
3232
app.add_config_value("api_dir", Path(), "env")
3333
app.connect("autodoc-process-docstring", insert_function_images)

pyproject.toml

Lines changed: 36 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -226,55 +226,58 @@ docstring-code-format = true
226226

227227
[tool.ruff.lint]
228228
select = [
229-
"B", # Likely bugs and design issues
230-
"D", # Documentation style
231-
"E", # Error detected by Pycodestyle
232-
"EM", # Traceback-friendly error messages
233-
"F", # Errors detected by Pyflakes
234-
"FBT", # No positional boolean parameters
235-
"I", # Import sorting
236-
"ICN", # Follow import conventions
237-
"PIE", # Syntax simplifications
238-
"PLR0917", # Ban APIs with too many positional parameters
239-
"PT", # Pytest style
240-
"PTH", # Pathlib instead of os.path
241-
"PYI", # Typing
242-
"SIM", # Simplify control flow
243-
"TC", # Manage type checking blocks
244-
"UP", # Update legacy syntax
245-
"TID251", # Banned imports
246-
"W", # Warning detected by Pycodestyle
229+
"B", # Likely bugs and design issues
230+
"D", # Documentation style
231+
"E", # Error detected by Pycodestyle
232+
"EM", # Traceback-friendly error messages
233+
"F", # Errors detected by Pyflakes
234+
"FBT", # No positional boolean parameters
235+
"I", # Import sorting
236+
"ICN", # Follow import conventions
237+
"PIE", # Syntax simplifications
238+
"PL", # Pylint
239+
"PT", # Pytest style
240+
"PTH", # Pathlib instead of os.path
241+
"PYI", # Typing
242+
"RUF100", # Unused noqa
243+
"SIM", # Simplify control flow
244+
"TC", # Manage type checking blocks
245+
"UP", # Update legacy syntax
246+
"TID251", # Banned imports
247+
"W", # Warning detected by Pycodestyle
247248
]
249+
external = [ "PLR0917" ] # preview lint that we use
248250
ignore = [
249-
# line too long -> we accept long comment lines; black gets rid of long code lines
250-
"E501",
251-
# module level import not at top of file -> required to circumvent circular imports for Scanpys API
252-
"E402",
253251
# E266 too many leading '#' for block comment -> Scanpy allows them for comments into sections
254252
"E262",
253+
# module level import not at top of file -> required to circumvent circular imports for Scanpys API
254+
"E402",
255+
# line too long -> we accept long comment lines; black gets rid of long code lines
256+
"E501",
255257
# allow I, O, l as variable names -> I is the identity matrix, i, j, k, l is reasonable indexing notation
256258
"E741",
257-
# `Literal["..."] | str` is useful for autocompletion
258-
"PYI051",
259259
# We ban blank lines before docstrings instead of the opposite
260260
"D203",
261261
# We want multiline summaries to start on the first line, not the second
262262
"D213",
263263
# TODO: replace our current param docs reuse with this and remove it here:
264264
"D417",
265+
# Numbers like “2” aren’t that “magic”.
266+
"PLR2004",
267+
# `Literal["..."] | str` is useful for autocompletion
268+
"PYI051",
265269
]
266270
[tool.ruff.lint.per-file-ignores]
267271
# Do not assign a lambda expression, use a def
268272
"src/scanpy/tools/_rank_genes_groups.py" = [ "E731" ]
269273
# No need for docstrings for all benchmarks
270274
"benchmarks/**/*.py" = [ "D102", "D103" ]
271-
# No need for docstrings for all test modules and test functions
272-
"tests/**/*.py" = [ "D100", "D101", "D103" ]
275+
# D*: No need for docstrings for all test modules and test functions
276+
# PLR0913: Test may use many fixtures
277+
"tests/**/*.py" = [ "D100", "D101", "D103", "PLR0913" ]
273278
[tool.ruff.lint.isort]
274279
known-first-party = [ "scanpy", "testing.scanpy" ]
275280
required-imports = [ "from __future__ import annotations" ]
276-
[tool.ruff.lint.pydocstyle]
277-
convention = "numpy"
278281
[tool.ruff.lint.flake8-tidy-imports.banned-api]
279282
"pytest.importorskip".msg = "Use the “@needs” decorator/mark instead"
280283
"pandas.api.types.is_categorical_dtype".msg = "Use isinstance(s.dtype, CategoricalDtype) instead"
@@ -290,6 +293,11 @@ convention = "numpy"
290293
[tool.ruff.lint.flake8-type-checking]
291294
exempt-modules = [ ]
292295
strict = true
296+
[tool.ruff.lint.pydocstyle]
297+
convention = "numpy"
298+
[tool.ruff.lint.pylint]
299+
max-args = 10
300+
max-positional-args = 5
293301

294302
[tool.towncrier]
295303
name = "scanpy"

src/scanpy/_settings.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class ScanpyConfig:
9393
N_PCS: int
9494
"""Default number of principal components to use."""
9595

96-
def __init__(
96+
def __init__( # noqa: PLR0913
9797
self,
9898
*,
9999
verbosity: Verbosity | int | str = Verbosity.warning,
@@ -406,7 +406,7 @@ def categories_to_ignore(self, categories_to_ignore: Iterable[str]):
406406
"transparent",
407407
"ipython_format",
408408
)
409-
def set_figure_params(
409+
def set_figure_params( # noqa: PLR0913
410410
self,
411411
*,
412412
scanpy: bool = True,

src/scanpy/_utils/__init__.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -188,10 +188,10 @@ def wrapper(*args, **kwargs):
188188
return decorator
189189

190190

191-
def _import_name(name: str) -> Any:
191+
def _import_name(full_name: str) -> Any:
192192
from importlib import import_module
193193

194-
parts = name.split(".")
194+
parts = full_name.split(".")
195195
obj = import_module(parts[0])
196196
for _i, name in enumerate(parts[1:]):
197197
i = _i
@@ -387,7 +387,7 @@ def compute_association_matrix_of_groups(
387387
asso_matrix: list[list[float]] = []
388388
for ipred_group, pred_group in enumerate(adata.obs[prediction].cat.categories):
389389
if "?" in pred_group:
390-
pred_group = str(ipred_group)
390+
pred_group = str(ipred_group) # noqa: PLW2901
391391
# starting from numpy version 1.13, subtractions of boolean arrays are deprecated
392392
mask_pred = adata.obs[prediction].values == pred_group
393393
mask_pred_int = mask_pred.astype(np.int8)
@@ -927,7 +927,9 @@ def select_groups(
927927
return groups_order_subset, groups_masks_obs
928928

929929

930-
def warn_with_traceback(message, category, filename, lineno, file=None, line=None): # noqa: PLR0917
930+
def warn_with_traceback( # noqa: PLR0917
931+
message, category, filename, lineno, file=None, line=None
932+
) -> None:
931933
"""Get full tracebacks when warning is raised by setting.
932934
933935
warnings.showwarning = warn_with_traceback

src/scanpy/experimental/pp/_highly_variable_genes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ def clac_clipped_res_dense(gene: int, cell: int) -> np.float64:
128128
return residuals
129129

130130

131-
def _highly_variable_pearson_residuals(
131+
def _highly_variable_pearson_residuals( # noqa: PLR0912, PLR0915
132132
adata: AnnData,
133133
*,
134134
theta: float = 100,
@@ -308,7 +308,7 @@ def _highly_variable_pearson_residuals(
308308
layer=doc_layer,
309309
inplace=doc_inplace,
310310
)
311-
def highly_variable_genes(
311+
def highly_variable_genes( # noqa: PLR0913
312312
adata: AnnData,
313313
*,
314314
theta: float = 100,

src/scanpy/experimental/pp/_recipes.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
check_values=doc_check_values,
3434
inplace=doc_inplace,
3535
)
36-
def recipe_pearson_residuals(
36+
def recipe_pearson_residuals( # noqa: PLR0913
3737
adata: AnnData,
3838
*,
3939
theta: float = 100,

src/scanpy/external/exporting.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"neighbors_key",
3434
"overwrite",
3535
)
36-
def spring_project(
36+
def spring_project( # noqa: PLR0912, PLR0915
3737
adata: AnnData,
3838
project_dir: Path | str,
3939
embedding_method: str,
@@ -90,17 +90,16 @@ def spring_project(
9090
if embedding_method not in adata.obsm_keys():
9191
if "X_" + embedding_method in adata.obsm_keys():
9292
embedding_method = "X_" + embedding_method
93+
elif embedding_method in adata.uns:
94+
embedding_method = (
95+
"X_"
96+
+ embedding_method
97+
+ "_"
98+
+ adata.uns[embedding_method]["params"]["layout"]
99+
)
93100
else:
94-
if embedding_method in adata.uns:
95-
embedding_method = (
96-
"X_"
97-
+ embedding_method
98-
+ "_"
99-
+ adata.uns[embedding_method]["params"]["layout"]
100-
)
101-
else:
102-
msg = f"Run the specified embedding method `{embedding_method}` first."
103-
raise ValueError(msg)
101+
msg = f"Run the specified embedding method `{embedding_method}` first."
102+
raise ValueError(msg)
104103

105104
coords = adata.obsm[embedding_method]
106105

@@ -477,8 +476,6 @@ def _export_PAGA_to_SPRING(adata, paga_coords, outpath):
477476

478477
Path(outpath).write_text(json.dumps(PAGA_data, indent=4))
479478

480-
return None
481-
482479

483480
@old_positionals(
484481
"embedding_keys",
@@ -490,7 +487,7 @@ def _export_PAGA_to_SPRING(adata, paga_coords, outpath):
490487
"port",
491488
"do_debug",
492489
)
493-
def cellbrowser(
490+
def cellbrowser( # noqa: PLR0913
494491
adata: AnnData,
495492
data_dir: Path | str,
496493
data_name: str,

0 commit comments

Comments
 (0)