Skip to content

Commit 0be5e2c

Browse files
committed
lint
1 parent e2ee6fc commit 0be5e2c

File tree

7 files changed

+121
-128
lines changed

7 files changed

+121
-128
lines changed

pyproject.toml

+83-91
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,19 @@ name = "flowsom"
77
version = "0.2.1"
88
description = "The complete FlowSOM package known from R, now available in Python!"
99
readme = "README.md"
10+
license = { file = "LICENSE" }
11+
maintainers = [
12+
{ name = "Artuur Couckuyt", email = "[email protected]" },
13+
{ name = "Benjamin Rombaut", email = "[email protected]" },
14+
{ name = "Yvan Saeys", email = "[email protected]" },
15+
{ name = "Sofie Van Gassen", email = "[email protected]" },
16+
]
17+
authors = [
18+
{ name = "Artuur Couckuyt", email = "[email protected]" },
19+
{ name = "Benjamin Rombaut", email = "[email protected]" },
20+
{ name = "Yvan Saeys", email = "[email protected]" },
21+
{ name = "Sofie Van Gassen", email = "[email protected]" },
22+
]
1023
requires-python = ">=3.10"
1124
classifiers = [
1225
"Programming Language :: Python :: 3 :: Only",
@@ -15,65 +28,51 @@ classifiers = [
1528
"Programming Language :: Python :: 3.12",
1629
"Programming Language :: Python :: 3.13",
1730
]
18-
license = { file = "LICENSE" }
19-
authors = [
20-
{ name = "Artuur Couckuyt", email = "[email protected]" },
21-
{ name = "Benjamin Rombaut", email = "[email protected]" },
22-
{ name = "Yvan Saeys", email = "[email protected]" },
23-
{ name = "Sofie Van Gassen", email = "[email protected]" },
24-
]
25-
maintainers = [
26-
{ name = "Artuur Couckuyt", email = "[email protected]" },
27-
{ name = "Benjamin Rombaut", email = "[email protected]" },
28-
{ name = "Yvan Saeys", email = "[email protected]" },
29-
{ name = "Sofie Van Gassen", email = "[email protected]" },
30-
]
31-
urls.Documentation = "https://flowsom.readthedocs.io/en/latest/"
32-
urls.Source = "https://github.com/saeyslab/FlowSOM_Python"
33-
urls.Home-page = "https://github.com/saeyslab/FlowSOM_Python"
3431
dependencies = [
35-
"mudata",
36-
"numpy",
37-
"matplotlib",
38-
"pandas",
39-
"scipy",
40-
"readfcs",
41-
"scikit-learn",
42-
"igraph",
43-
# for debug logging (referenced from the issue template)
44-
"session-info2",
45-
"numba",
46-
"scanpy",
47-
"seaborn",
48-
"anndata",
49-
"loguru",
32+
"anndata",
33+
"igraph",
34+
"loguru",
35+
"matplotlib",
36+
"mudata",
37+
"numba",
38+
"numpy",
39+
"pandas",
40+
"readfcs",
41+
"scanpy",
42+
"scikit-learn",
43+
"scipy",
44+
"seaborn",
45+
# for debug logging (referenced from the issue template)
46+
"session-info2",
5047
]
5148

52-
[project.optional-dependencies]
53-
dev = ["pre-commit", "twine>=4.0.2"]
54-
doc = [
55-
"docutils>=0.8,!=0.18.*,!=0.19.*",
56-
"sphinx>=4",
57-
"sphinx-book-theme>=1",
58-
"myst-nb>=1.1",
59-
"sphinxcontrib-bibtex>=1",
60-
"sphinx-autodoc-typehints",
61-
"sphinxext-opengraph",
62-
"sphinx-copybutton",
63-
"sphinx-tabs",
64-
"pandas",
65-
# Until pybtex >0.24.0 releases: https://bitbucket.org/pybtex-devs/pybtex/issues/169/
66-
"setuptools",
67-
# For notebooks
68-
"ipykernel",
69-
"ipython",
70-
# For example notebooks
71-
"pytometry>=0.1.5",
49+
optional-dependencies.dev = [ "pre-commit", "twine>=4.0.2" ]
50+
optional-dependencies.doc = [
51+
"docutils>=0.8,!=0.18.*,!=0.19.*",
52+
# For notebooks
53+
"ipykernel",
54+
"ipython",
55+
"myst-nb>=1.1",
56+
"pandas",
57+
# For example notebooks
58+
"pytometry>=0.1.5",
59+
# Until pybtex >0.24.0 releases: https://bitbucket.org/pybtex-devs/pybtex/issues/169/
60+
"setuptools",
61+
"sphinx>=4",
62+
"sphinx-autodoc-typehints",
63+
"sphinx-book-theme>=1",
64+
"sphinx-copybutton",
65+
"sphinx-tabs",
66+
"sphinxcontrib-bibtex>=1",
67+
"sphinxext-opengraph",
7268
]
73-
test = ["pytest", "coverage"]
69+
optional-dependencies.test = [ "coverage", "pytest" ]
70+
urls.Documentation = "https://flowsom.readthedocs.io/en/latest/"
71+
urls.Home-page = "https://github.com/saeyslab/FlowSOM_Python"
72+
urls.Source = "https://github.com/saeyslab/FlowSOM_Python"
7473

7574
[tool.hatch.build.targets.wheel]
76-
packages = ['src/flowsom']
75+
packages = [ 'src/flowsom' ]
7776

7877
[tool.hatch.envs.default]
7978
installer = "uv"
@@ -88,32 +87,13 @@ scripts.clean = "git clean -fdX -- {args:docs}"
8887
[tool.hatch.envs.hatch-test]
8988
features = [ "test" ]
9089

91-
[tool.coverage.run]
92-
source = ["flowsom"]
93-
omit = ["**/test_*.py"]
94-
95-
[tool.pytest.ini_options]
96-
testpaths = [ "tests" ]
97-
xfail_strict = true
98-
addopts = [
99-
"--import-mode=importlib", # allow using test files with same name
100-
]
101-
102-
[tool.fawltydeps]
103-
code = ["src"] # Only search for imports under ./src
104-
deps = ["pyproject.toml"] # Only look for declared dependencies here
105-
ignore_undeclared = ["flowsom"]
106-
10790
[tool.ruff]
10891
line-length = 120
109-
src = ["src"]
110-
extend-include = ["*.ipynb"]
111-
112-
[tool.ruff.format]
113-
docstring-code-format = true
92+
src = [ "src" ]
93+
extend-include = [ "*.ipynb" ]
11494

115-
[tool.ruff.lint]
116-
select = [
95+
format.docstring-code-format = true
96+
lint.select = [
11797
"B", # flake8-bugbear
11898
"BLE", # flake8-blind-except
11999
"C4", # flake8-comprehensions
@@ -126,7 +106,7 @@ select = [
126106
"UP", # pyupgrade
127107
"W", # Warning detected by Pycodestyle
128108
]
129-
ignore = [
109+
lint.ignore = [
130110
"B008", # Errors from function calls in argument defaults. These are fine when the result is immutable.
131111
"D100", # Missing docstring in public module
132112
"D104", # Missing docstring in public package
@@ -141,23 +121,35 @@ ignore = [
141121
"E731", # Do not assign a lambda expression, use a def -> lambda expression assignments are convenient
142122
"E741", # allow I, O, l as variable names -> I is the identity matrix
143123
]
124+
lint.per-file-ignores."*/__init__.py" = [ "F401" ]
125+
lint.per-file-ignores."docs/*" = [ "I" ]
126+
lint.per-file-ignores."tests/*" = [ "D" ]
127+
lint.pydocstyle.convention = "numpy"
144128

145-
[tool.ruff.lint.pydocstyle]
146-
convention = "numpy"
129+
[tool.pytest.ini_options]
130+
testpaths = [ "tests" ]
131+
xfail_strict = true
132+
addopts = [
133+
"--import-mode=importlib", # allow using test files with same name
134+
]
135+
136+
[tool.coverage.run]
137+
source = [ "flowsom" ]
138+
omit = [ "**/test_*.py" ]
147139

148-
[tool.ruff.lint.per-file-ignores]
149-
"docs/*" = ["I"]
150-
"tests/*" = ["D"]
151-
"*/__init__.py" = ["F401"]
140+
[tool.fawltydeps]
141+
code = [ "src" ] # Only search for imports under ./src
142+
deps = [ "pyproject.toml" ] # Only look for declared dependencies here
143+
ignore_undeclared = [ "flowsom" ]
152144

153145
[tool.cruft]
154146
skip = [
155-
"tests",
156-
"src/**/__init__.py",
157-
"src/**/basic.py",
158-
"docs/api.md",
159-
"docs/changelog.md",
160-
"docs/references.bib",
161-
"docs/references.md",
162-
"docs/notebooks/example.ipynb",
147+
"tests",
148+
"src/**/__init__.py",
149+
"src/**/basic.py",
150+
"docs/api.md",
151+
"docs/changelog.md",
152+
"docs/references.bib",
153+
"docs/references.md",
154+
"docs/notebooks/example.ipynb",
163155
]

src/flowsom/models/batch/_som.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Code adapted from student assignment Computational Biology 2024, Ghent University."""
22

3-
from typing import Callable
3+
from collections.abc import Callable
44

55
import numpy as np
66
from numba import jit, prange

src/flowsom/models/batch/som_estimator.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,9 @@ def fit(
7070
alpha = self.alpha
7171

7272
if codes is not None:
73-
assert (
74-
(codes.shape[1] == X.shape[1]) and (codes.shape[0] == xdim * ydim)
75-
), "If codes is not NULL, it should have the same number of columns as the data and the number of rows should correspond with xdim*ydim"
73+
assert (codes.shape[1] == X.shape[1]) and (codes.shape[0] == xdim * ydim), (
74+
"If codes is not NULL, it should have the same number of columns as the data and the number of rows should correspond with xdim*ydim"
75+
)
7676

7777
if importance is not None:
7878
X = np.stack([X[:, i] * importance[i] for i in range(len(importance))], axis=1)

src/flowsom/models/som_estimator.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ def fit(
6464
alpha = self.alpha
6565

6666
if codes is not None:
67-
assert (
68-
(codes.shape[1] == X.shape[1]) and (codes.shape[0] == xdim * ydim)
69-
), "If codes is not NULL, it should have the same number of columns as the data and the number of rows should correspond with xdim*ydim"
67+
assert (codes.shape[1] == X.shape[1]) and (codes.shape[0] == xdim * ydim), (
68+
"If codes is not NULL, it should have the same number of columns as the data and the number of rows should correspond with xdim*ydim"
69+
)
7070

7171
if importance is not None:
7272
X = np.stack([X[:, i] * importance[i] for i in range(len(importance))], axis=1)

src/flowsom/pl/_plot_helper_functions.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -104,14 +104,14 @@ def plot_FlowSOM(
104104

105105
# Warnings
106106
if node_sizes is not None:
107-
assert nNodes == len(
108-
node_sizes
109-
), 'Length of "node_sizes" should be equal to number of clusters in FlowSOM object'
107+
assert nNodes == len(node_sizes), (
108+
'Length of "node_sizes" should be equal to number of clusters in FlowSOM object'
109+
)
110110

111111
if background_values is not None:
112-
assert (
113-
background_values.shape[0] == fsom.mudata["cell_data"].uns["n_nodes"]
114-
), "Length of background_values should be equal to number of clusters in FlowSOM object"
112+
assert background_values.shape[0] == fsom.mudata["cell_data"].uns["n_nodes"], (
113+
"Length of background_values should be equal to number of clusters in FlowSOM object"
114+
)
115115

116116
# Node sizes
117117
node_sizes = parse_node_sizes(

src/flowsom/pl/plot_functions.py

+16-15
Original file line numberDiff line numberDiff line change
@@ -82,9 +82,9 @@ def plot_2D_scatters(
8282
if xy_labels is None:
8383
xy_labels = ["marker"]
8484
assert metaclusters is not None or clusters is not None, "Please add clusters or metaclusters to plot."
85-
assert (
86-
"marker" in xy_labels or "channel" in xy_labels
87-
), 'xy_labels should be a list containing "marker" and/or "channel".'
85+
assert "marker" in xy_labels or "channel" in xy_labels, (
86+
'xy_labels should be a list containing "marker" and/or "channel".'
87+
)
8888
if clusters is not None:
8989
assert isinstance(clusters[0], list), "clusters should be a list of lists."
9090
if metaclusters is not None:
@@ -124,7 +124,7 @@ def plot_2D_scatters(
124124
col = np.asarray([gg_color_hue()(i) for i in range(n_metaclusters)])[
125125
np.array([int(i) for i in metacluster])[n]
126126
]
127-
col_dict = dict(zip(np.unique(df_ss[:, 2]), col))
127+
col_dict = dict(zip(np.unique(df_ss[:, 2]), col, strict=False))
128128
cols = [col_dict[i] for i in df_ss[:, 2]]
129129

130130
df_c = np.c_[fsom.get_cluster_data()[n, indices_markers].X, n]
@@ -134,7 +134,7 @@ def plot_2D_scatters(
134134
df_ss = df_ss[:, indices_markers]
135135
df_ss = np.c_[df_ss, cell_metacluster[metaclusters_OI]]
136136
col = np.asarray([gg_color_hue()(i) for i in range(n_metaclusters)])[n]
137-
col_dict = dict(zip(np.unique(df_ss[:, 2]), col))
137+
col_dict = dict(zip(np.unique(df_ss[:, 2]), col, strict=False))
138138
cols = [col_dict[i] for i in df_ss[:, 2]]
139139
cl_in_mcl = np.where(np.isin(metacluster.astype(int), n))[0]
140140
df_c = np.c_[fsom.get_cluster_data()[cl_in_mcl, indices_markers].X, cl_in_mcl]
@@ -193,9 +193,9 @@ def plot_labels(fsom, labels, max_node_size=0, text_size=20, text_color="black",
193193
"""
194194
if not isinstance(labels, np.ndarray):
195195
labels = np.asarray(labels)
196-
assert (
197-
labels.shape[0] == fsom.get_cell_data().uns["n_nodes"]
198-
), "Length of labels should be the same as the number of nodes in your FlowSOM object"
196+
assert labels.shape[0] == fsom.get_cell_data().uns["n_nodes"], (
197+
"Length of labels should be the same as the number of nodes in your FlowSOM object"
198+
)
199199
fig, ax, layout, _ = plot_FlowSOM(fsom=fsom, max_node_size=max_node_size, **kwargs)
200200
ax = add_text(ax, layout, labels, text_size, text_color)
201201
ax.axis("equal")
@@ -250,9 +250,9 @@ def plot_variable(
250250
"""
251251
if not isinstance(variable, np.ndarray):
252252
variable = np.asarray(variable)
253-
assert (
254-
variable.shape[0] == fsom.get_cell_data().uns["n_nodes"]
255-
), "Length of variable should be the same as the number of nodes in your FlowSOM object"
253+
assert variable.shape[0] == fsom.get_cell_data().uns["n_nodes"], (
254+
"Length of variable should be the same as the number of nodes in your FlowSOM object"
255+
)
256256
if variable.dtype == "object":
257257
string_to_number = {string: index for index, string in enumerate(np.unique(variable))}
258258
variable = np.asarray([string_to_number[string] for string in variable])
@@ -380,12 +380,12 @@ def plot_pies(
380380
"""
381381
if not isinstance(cell_types, np.ndarray):
382382
cell_types = np.asarray(cell_types)
383-
assert (
384-
cell_types.shape[0] == fsom.get_cell_data().shape[0]
385-
), "Length of cell_types should be the same as the number of cells in your FlowSOM object"
383+
assert cell_types.shape[0] == fsom.get_cell_data().shape[0], (
384+
"Length of cell_types should be the same as the number of cells in your FlowSOM object"
385+
)
386386
fig, ax, layout, scaled_node_size = plot_FlowSOM(fsom, **kwargs)
387387
unique_cell_types = np.unique(cell_types)
388-
color_dict = dict(zip(unique_cell_types, cmap(np.linspace(0, 1, len(unique_cell_types)))))
388+
color_dict = dict(zip(unique_cell_types, cmap(np.linspace(0, 1, len(unique_cell_types))), strict=False))
389389

390390
for cl in range(fsom.get_cell_data().uns["n_nodes"]):
391391
node_cell_types = cell_types[fsom.get_cell_data().obs["clustering"] == cl]
@@ -512,6 +512,7 @@ def FlowSOMmary(fsom, plot_file="./FlowSOMmary.pdf"):
512512
zip(
513513
np.unique(subset_fsom.obs["metaclustering"]),
514514
gg_color_hue()(np.linspace(0, 1, n_metaclusters)),
515+
strict=False,
515516
)
516517
)
517518
fig, ax = plt.subplots()

src/flowsom/tl/getter_functions.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ def get_channels(obj, markers: np.ndarray, exact=True):
1616
:param exact: If True, a strict search is performed. If False, regexps can be used.
1717
:type exact: boolean
1818
"""
19-
assert obj.__class__.__name__ == "FlowSOM" or isinstance(
20-
obj, ad.AnnData
21-
), "Please provide an FCS file or a FlowSOM object"
19+
assert obj.__class__.__name__ == "FlowSOM" or isinstance(obj, ad.AnnData), (
20+
"Please provide an FCS file or a FlowSOM object"
21+
)
2222
if obj.__class__.__name__ == "FlowSOM":
2323
object_markers = np.asarray(
2424
[re.sub(" <.*", "", pretty_colname) for pretty_colname in obj.mudata["cell_data"].var["pretty_colnames"]]
@@ -64,9 +64,9 @@ def get_markers(obj, channels, exact=True):
6464
:param exact: If True, a strict search is performed. If False, regexps can be used.
6565
:type exact: boolean
6666
"""
67-
assert obj.__class__.__name__ == "FlowSOM" or isinstance(
68-
obj, ad.AnnData
69-
), "Please provide an FCS file or a FlowSOM object"
67+
assert obj.__class__.__name__ == "FlowSOM" or isinstance(obj, ad.AnnData), (
68+
"Please provide an FCS file or a FlowSOM object"
69+
)
7070
if obj.__class__.__name__ == "FlowSOM":
7171
object_markers = np.asarray(
7272
[re.sub(" <.*", "", pretty_colname) for pretty_colname in obj.mudata["cell_data"].var["pretty_colnames"]]
@@ -255,9 +255,9 @@ def get_features(
255255
if filenames is not None:
256256
assert len(filenames) != nfiles, "The number of file names should be equal to the number of files"
257257
assert all(i in ["metaclusters", "clusters"] for i in level), "Level should be 'metaclusters' or 'clusters'"
258-
assert all(
259-
i in ["counts", "percentages", "MFIs", "percentages_positive"] for i in type
260-
), "Type should be 'counts', 'percentages','MFI' or 'percentages_positive'"
258+
assert all(i in ["counts", "percentages", "MFIs", "percentages_positive"] for i in type), (
259+
"Type should be 'counts', 'percentages','MFI' or 'percentages_positive'"
260+
)
261261
if "MFIs" in type:
262262
assert MFI is not None, "If type is 'MFIs', MFI should be provided"
263263
MFI = list(get_channels(fsom, MFI).keys())

0 commit comments

Comments
 (0)