scmcp-shared 0.2.5__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
scmcp_shared/server/pp.py CHANGED
@@ -1,379 +1,325 @@
1
-
2
1
  import os
3
2
  import inspect
4
3
  import scanpy as sc
5
- from fastmcp import FastMCP , Context
4
+ from fastmcp import FastMCP, Context
6
5
  from fastmcp.exceptions import ToolError
7
6
  from ..schema.pp import *
8
- from ..schema import AdataModel
7
+ from ..schema import AdataInfo
9
8
  from ..util import filter_args, add_op_log, forward_request, get_ads, generate_msg
10
- from ..logging_config import setup_logger
11
- logger = setup_logger()
12
-
13
-
14
- pp_mcp = FastMCP("ScanpyMCP-PP-Server")
15
-
16
-
17
- @pp_mcp.tool()
18
- async def subset_cells(
19
- request: SubsetCellModel = SubsetCellModel(),
20
- adinfo: AdataModel = AdataModel()
21
- ):
22
- """filter or subset cells based on total genes expressed counts and numbers. or values in adata.obs[obs_key]"""
23
-
24
- try:
25
- result = await forward_request("subset_cells", request, adinfo)
26
- if result is not None:
27
- return result
28
-
29
- ads = get_ads()
30
- adata = ads.get_adata(adinfo=adinfo).copy()
31
- func_kwargs = filter_args(request, sc.pp.filter_cells)
32
- if func_kwargs:
33
- sc.pp.filter_cells(adata, **func_kwargs)
34
- add_op_log(adata, sc.pp.filter_cells, func_kwargs, adinfo)
35
- # Subset based on obs (cells) criteria
36
- if request.obs_key is not None:
37
- if request.obs_key not in adata.obs.columns:
38
- raise ValueError(f"Key '{request.obs_key}' not found in adata.obs")
39
- mask = True # Start with all cells selected
40
- if request.obs_value is not None:
41
- mask = mask & (adata.obs[request.obs_key] == request.obs_value)
42
- if request.obs_min is not None:
43
- mask = mask & (adata.obs[request.obs_key] >= request.obs_min)
44
- if request.obs_max is not None:
45
- mask = mask & (adata.obs[request.obs_key] <= request.obs_max)
46
- adata = adata[mask, :]
47
- add_op_log(adata, "subset_cells",
48
- {
49
- "obs_key": request.obs_key, "obs_value": request.obs_value,
50
- "obs_min": request.obs_min, "obs_max": request.obs_max
51
- }, adinfo
52
- )
53
- ads.set_adata(adata, adinfo=adinfo)
54
- return [
55
- generate_msg(adinfo, adata, ads)
56
- ]
57
- except ToolError as e:
58
- raise ToolError(e)
59
- except Exception as e:
60
- if hasattr(e, '__context__') and e.__context__:
61
- raise ToolError(e.__context__)
62
- else:
63
- raise ToolError(e)
64
-
65
-
66
- @pp_mcp.tool()
67
- async def subset_genes(
68
- request: SubsetGeneModel = SubsetGeneModel(),
69
- adinfo: AdataModel = AdataModel()
70
- ):
71
- """filter or subset genes based on number of cells or counts, or values in adata.var[var_key] or subset highly variable genes"""
72
- try:
73
- result = await forward_request("pp_subset_genes", request, adinfo)
74
- if result is not None:
75
- return result
76
- func_kwargs = filter_args(request, sc.pp.filter_genes)
77
- ads = get_ads()
78
- adata = ads.get_adata(adinfo=adinfo).copy()
79
- if func_kwargs:
80
- sc.pp.filter_genes(adata, **func_kwargs)
81
- add_op_log(adata, sc.pp.filter_genes, func_kwargs, adinfo)
82
- if request.var_key is not None:
83
- if request.var_key not in adata.var.columns:
84
- raise ValueError(f"Key '{request.var_key}' not found in adata.var")
85
- mask = True # Start with all genes selected
86
- if request.var_min is not None:
87
- mask = mask & (adata.var[request.var_key] >= request.var_min)
88
- if request.var_max is not None:
89
- mask = mask & (adata.var[request.var_key] <= request.var_max)
90
- adata = adata[:, mask]
91
- if request.highly_variable is not None:
92
- adata = adata[:, adata.var.highly_variable]
93
- add_op_log(adata, "subset_genes",
94
- {
95
- "var_key": request.var_key, "var_value": request.var_value,
96
- "var_min": request.var_min, "var_max": request.var_max, "hpv": request.highly_variable
97
- }, adinfo
98
- )
99
- ads.set_adata(adata, adinfo=adinfo)
100
- return [
101
- generate_msg(adinfo, adata, ads)
102
- ]
103
- except ToolError as e:
104
- raise ToolError(e)
105
- except Exception as e:
106
- if hasattr(e, '__context__') and e.__context__:
107
- raise ToolError(e.__context__)
108
- else:
109
- raise ToolError(e)
110
-
111
- @pp_mcp.tool()
112
- async def calculate_qc_metrics(
113
- request: CalculateQCMetrics = CalculateQCMetrics(),
114
- adinfo: AdataModel = AdataModel()
115
- ):
116
- """Calculate quality control metrics(common metrics: total counts, gene number, percentage of counts in ribosomal and mitochondrial) for AnnData."""
117
-
118
- try:
119
- result = await forward_request("pp_calculate_qc_metrics", request, adinfo)
120
- if result is not None:
121
- return result
122
- logger.info(f"calculate_qc_metrics {request.model_dump()}")
123
- func_kwargs = filter_args(request, sc.pp.calculate_qc_metrics)
124
- ads = get_ads()
125
- adata = ads.get_adata(adinfo=adinfo)
126
- func_kwargs["inplace"] = True
127
- try:
128
- sc.pp.calculate_qc_metrics(adata, **func_kwargs)
129
- add_op_log(adata, sc.pp.calculate_qc_metrics, func_kwargs, adinfo)
130
- except KeyError as e:
131
- raise KeyError(f"Cound find {e} in adata.var")
132
- return [
133
- generate_msg(adinfo, adata, ads)
134
- ]
135
- except ToolError as e:
136
- raise ToolError(e)
137
- except Exception as e:
138
- if hasattr(e, '__context__') and e.__context__:
139
- raise ToolError(e.__context__)
140
- else:
141
- raise ToolError(e)
142
-
143
-
144
- @pp_mcp.tool()
145
- async def log1p(
146
- request: Log1PModel = Log1PModel(),
147
- adinfo: AdataModel = AdataModel()
148
- ):
149
- """Logarithmize the data matrix"""
150
-
151
- try:
152
- result = await forward_request("pp_log1p", request, adinfo)
153
- if result is not None:
154
- return result
155
- func_kwargs = filter_args(request, sc.pp.log1p)
156
- ads = get_ads()
157
- adata = ads.get_adata(adinfo=adinfo).copy()
158
- try:
159
- sc.pp.log1p(adata, **func_kwargs)
160
- adata.raw = adata.copy()
161
- add_op_log(adata, sc.pp.log1p, func_kwargs, adinfo)
162
- except Exception as e:
163
- raise e
164
- ads.set_adata(adata, adinfo=adinfo)
165
- return [
166
- generate_msg(adinfo, adata, ads)
167
- ]
168
- except ToolError as e:
169
- raise ToolError(e)
170
- except Exception as e:
171
- if hasattr(e, '__context__') and e.__context__:
172
- raise ToolError(e.__context__)
173
- else:
174
- raise ToolError(e)
175
-
176
-
177
- @pp_mcp.tool()
178
- async def normalize_total(
179
- request: NormalizeTotalModel = NormalizeTotalModel(),
180
- adinfo: AdataModel = AdataModel()
181
- ):
182
- """Normalize counts per cell to the same total count"""
183
-
184
- try:
185
- result = await forward_request("pp_normalize_total", request, adinfo)
186
- if result is not None:
187
- return result
188
- func_kwargs = filter_args(request, sc.pp.normalize_total)
189
- ads = get_ads()
190
- adata = ads.get_adata(adinfo=adinfo).copy()
191
- sc.pp.normalize_total(adata, **func_kwargs)
192
- add_op_log(adata, sc.pp.normalize_total, func_kwargs, adinfo)
193
- ads.set_adata(adata, adinfo=adinfo)
194
- return [
195
- generate_msg(adinfo, adata, ads)
196
- ]
197
- except ToolError as e:
198
- raise ToolError(e)
199
- except Exception as e:
200
- if hasattr(e, '__context__') and e.__context__:
201
- raise ToolError(e.__context__)
202
- else:
203
- raise ToolError(e)
204
-
205
-
206
-
207
- @pp_mcp.tool()
208
- async def highly_variable_genes(
209
- request: HighlyVariableGenesModel = HighlyVariableGenesModel(),
210
- adinfo: AdataModel = AdataModel()
211
- ):
212
- """Annotate highly variable genes"""
213
-
214
- try:
215
- result = await forward_request("pp_highly_variable_genes", request, adinfo)
216
- if result is not None:
217
- return result
218
- try:
219
- func_kwargs = filter_args(request, sc.pp.highly_variable_genes)
220
- ads = get_ads()
221
- adata = ads.get_adata(adinfo=adinfo)
222
- sc.pp.highly_variable_genes(adata, **func_kwargs)
223
- add_op_log(adata, sc.pp.highly_variable_genes, func_kwargs, adinfo)
224
- except Exception as e:
225
- logger.error(f"Error in pp_highly_variable_genes: {str(e)}")
226
- raise e
227
- return [
228
- generate_msg(adinfo, adata, ads)
229
- ]
230
- except ToolError as e:
231
- raise ToolError(e)
232
- except Exception as e:
233
- if hasattr(e, '__context__') and e.__context__:
234
- raise ToolError(e.__context__)
235
- else:
236
- raise ToolError(e)
237
-
238
-
239
- @pp_mcp.tool()
240
- async def regress_out(
241
- request: RegressOutModel,
242
- adinfo: AdataModel = AdataModel()
243
- ):
244
- """Regress out (mostly) unwanted sources of variation."""
245
-
246
- try:
247
- result = await forward_request("pp_regress_out", request, adinfo)
248
- if result is not None:
249
- return result
250
- func_kwargs = filter_args(request, sc.pp.regress_out)
251
- ads = get_ads()
252
- adata = ads.get_adata(adinfo=adinfo).copy()
253
- sc.pp.regress_out(adata, **func_kwargs)
254
- add_op_log(adata, sc.pp.regress_out, func_kwargs, adinfo)
255
- ads.set_adata(adata, adinfo=adinfo)
256
- return [
257
- generate_msg(adinfo, adata, ads)
258
- ]
259
- except ToolError as e:
260
- raise ToolError(e)
261
- except Exception as e:
262
- if hasattr(e, '__context__') and e.__context__:
263
- raise ToolError(e.__context__)
264
- else:
265
- raise ToolError(e)
266
-
267
- @pp_mcp.tool()
268
- async def scale(
269
- request: ScaleModel = ScaleModel(),
270
- adinfo: AdataModel = AdataModel()
271
- ):
272
- """Scale data to unit variance and zero mean"""
273
-
274
- try:
275
- result = await forward_request("pp_scale", request, adinfo)
276
- if result is not None:
277
- return result
278
- func_kwargs = filter_args(request, sc.pp.scale)
279
- ads = get_ads()
280
- adata = ads.get_adata(adinfo=adinfo).copy()
281
-
282
- sc.pp.scale(adata, **func_kwargs)
283
- add_op_log(adata, sc.pp.scale, func_kwargs, adinfo)
284
-
285
- ads.set_adata(adata, adinfo=adinfo)
286
- return [
287
- generate_msg(adinfo, adata, ads)
288
- ]
289
- except ToolError as e:
290
- raise ToolError(e)
291
- except Exception as e:
292
- if hasattr(e, '__context__') and e.__context__:
293
- raise ToolError(e.__context__)
294
- else:
295
- raise ToolError(e)
296
-
297
- @pp_mcp.tool()
298
- async def combat(
299
- request: CombatModel = CombatModel(),
300
- adinfo: AdataModel = AdataModel()
301
- ):
302
- """ComBat function for batch effect correction"""
303
-
304
- try:
305
- result = await forward_request("pp_combat", request, adinfo)
306
- if result is not None:
307
- return result
308
- func_kwargs = filter_args(request, sc.pp.combat)
309
- ads = get_ads()
310
- adata = ads.get_adata(adinfo=adinfo).copy()
311
-
312
- sc.pp.combat(adata, **func_kwargs)
313
- add_op_log(adata, sc.pp.combat, func_kwargs, adinfo)
314
-
315
- ads.set_adata(adata, adinfo=adinfo)
316
- return [
317
- generate_msg(adinfo, adata, ads)
318
- ]
319
- except ToolError as e:
320
- raise ToolError(e)
321
- except Exception as e:
322
- if hasattr(e, '__context__') and e.__context__:
323
- raise ToolError(e.__context__)
324
- else:
325
- raise ToolError(e)
326
-
327
- @pp_mcp.tool()
328
- async def scrublet(
329
- request: ScrubletModel = ScrubletModel(),
330
- adinfo: AdataModel = AdataModel()
331
- ):
332
- """Predict doublets using Scrublet"""
333
-
334
- try:
335
- result = await forward_request("pp_scrublet", request, adinfo)
336
- if result is not None:
337
- return result
338
- func_kwargs = filter_args(request, sc.pp.scrublet)
339
- ads = get_ads()
340
- adata = ads.get_adata(adinfo=adinfo)
341
- sc.pp.scrublet(adata, **func_kwargs)
342
- add_op_log(adata, sc.pp.scrublet, func_kwargs, adinfo)
343
- return [
344
- generate_msg(adinfo, adata, ads)
345
- ]
346
- except ToolError as e:
347
- raise ToolError(e)
348
- except Exception as e:
349
- if hasattr(e, '__context__') and e.__context__:
350
- raise ToolError(e.__context__)
351
- else:
352
- raise ToolError(e)
353
-
354
- @pp_mcp.tool()
355
- async def neighbors(
356
- request: NeighborsModel = NeighborsModel(),
357
- adinfo: AdataModel = AdataModel()
358
- ):
359
- """Compute nearest neighbors distance matrix and neighborhood graph"""
360
-
361
- try:
362
- result = await forward_request("pp_neighbors", request, adinfo)
363
- if result is not None:
364
- return result
365
- func_kwargs = filter_args(request, sc.pp.neighbors)
366
- ads = get_ads()
367
- adata = ads.get_adata(adinfo=adinfo)
368
- sc.pp.neighbors(adata, **func_kwargs)
369
- add_op_log(adata, sc.pp.neighbors, func_kwargs, adinfo)
370
- return [
371
- generate_msg(adinfo, adata, ads)
372
- ]
373
- except ToolError as e:
374
- raise ToolError(e)
375
- except Exception as e:
376
- if hasattr(e, '__context__') and e.__context__:
377
- raise ToolError(e.__context__)
378
- else:
379
- raise ToolError(e)
9
+ from .base import BaseMCP
10
+
11
+
12
+ class ScanpyPreprocessingMCP(BaseMCP):
13
+ def __init__(self, include_tools: list = None, exclude_tools: list = None, AdataInfo: AdataInfo = AdataInfo):
14
+ """
15
+ Initialize ScanpyPreprocessingMCP with optional tool filtering.
16
+
17
+ Args:
18
+ include_tools (list, optional): List of tool names to include. If None, all tools are included.
19
+ exclude_tools (list, optional): List of tool names to exclude. If None, no tools are excluded.
20
+ AdataInfo: The AdataInfo class to use for type annotations.
21
+ """
22
+ super().__init__("ScanpyMCP-PP-Server", include_tools, exclude_tools, AdataInfo)
23
+
24
+ def _tool_subset_cells(self):
25
+ def _subset_cells(request: SubsetCellModel, adinfo: self.AdataInfo=self.AdataInfo()):
26
+ """filter or subset cells based on total genes expressed counts and numbers. or values in adata.obs[obs_key]"""
27
+ try:
28
+ result = forward_request("subset_cells", request, adinfo)
29
+ if result is not None:
30
+ return result
31
+
32
+ ads = get_ads()
33
+ adata = ads.get_adata(adinfo=adinfo).copy()
34
+ func_kwargs = filter_args(request, sc.pp.filter_cells)
35
+ if func_kwargs:
36
+ sc.pp.filter_cells(adata, **func_kwargs)
37
+ add_op_log(adata, sc.pp.filter_cells, func_kwargs, adinfo)
38
+ # Subset based on obs (cells) criteria
39
+ if request.obs_key is not None:
40
+ if request.obs_key not in adata.obs.columns:
41
+ raise ValueError(f"Key '{request.obs_key}' not found in adata.obs")
42
+ mask = True # Start with all cells selected
43
+ if request.obs_value is not None:
44
+ mask = mask & (adata.obs[request.obs_key] == request.obs_value)
45
+ if request.obs_min is not None:
46
+ mask = mask & (adata.obs[request.obs_key] >= request.obs_min)
47
+ if request.obs_max is not None:
48
+ mask = mask & (adata.obs[request.obs_key] <= request.obs_max)
49
+ adata = adata[mask, :]
50
+ add_op_log(adata, "subset_cells",
51
+ {
52
+ "obs_key": request.obs_key, "obs_value": request.obs_value,
53
+ "obs_min": request.obs_min, "obs_max": request.obs_max
54
+ }, adinfo
55
+ )
56
+ ads.set_adata(adata, adinfo=adinfo)
57
+ return [generate_msg(adinfo, adata, ads)]
58
+ except ToolError as e:
59
+ raise ToolError(e)
60
+ except Exception as e:
61
+ if hasattr(e, '__context__') and e.__context__:
62
+ raise ToolError(e.__context__)
63
+ else:
64
+ raise ToolError(e)
65
+ return _subset_cells
66
+
67
+ def _tool_subset_genes(self):
68
+ def _subset_genes(request: SubsetGeneModel, adinfo: self.AdataInfo=self.AdataInfo()):
69
+ """filter or subset genes based on number of cells or counts, or values in adata.var[var_key] or subset highly variable genes"""
70
+ try:
71
+ result = forward_request("pp_subset_genes", request, adinfo)
72
+ if result is not None:
73
+ return result
74
+ func_kwargs = filter_args(request, sc.pp.filter_genes)
75
+ ads = get_ads()
76
+ adata = ads.get_adata(adinfo=adinfo).copy()
77
+ if func_kwargs:
78
+ sc.pp.filter_genes(adata, **func_kwargs)
79
+ add_op_log(adata, sc.pp.filter_genes, func_kwargs, adinfo)
80
+ if request.var_key is not None:
81
+ if request.var_key not in adata.var.columns:
82
+ raise ValueError(f"Key '{request.var_key}' not found in adata.var")
83
+ mask = True # Start with all genes selected
84
+ if request.var_min is not None:
85
+ mask = mask & (adata.var[request.var_key] >= request.var_min)
86
+ if request.var_max is not None:
87
+ mask = mask & (adata.var[request.var_key] <= request.var_max)
88
+ adata = adata[:, mask]
89
+ if request.highly_variable is not None:
90
+ adata = adata[:, adata.var.highly_variable]
91
+ add_op_log(adata, "subset_genes",
92
+ {
93
+ "var_key": request.var_key, "var_value": request.var_value,
94
+ "var_min": request.var_min, "var_max": request.var_max, "hpv": request.highly_variable
95
+ }, adinfo
96
+ )
97
+ ads.set_adata(adata, adinfo=adinfo)
98
+ return [generate_msg(adinfo, adata, ads)]
99
+ except ToolError as e:
100
+ raise ToolError(e)
101
+ except Exception as e:
102
+ if hasattr(e, '__context__') and e.__context__:
103
+ raise ToolError(e.__context__)
104
+ else:
105
+ raise ToolError(e)
106
+ return _subset_genes
107
+
108
+ def _tool_calculate_qc_metrics(self):
109
+ def _calculate_qc_metrics(request: CalculateQCMetrics, adinfo: self.AdataInfo=self.AdataInfo()):
110
+ """Calculate quality control metrics(common metrics: total counts, gene number, percentage of counts in ribosomal and mitochondrial) for AnnData."""
111
+ try:
112
+ result = forward_request("pp_calculate_qc_metrics", request, adinfo)
113
+ if result is not None:
114
+ return result
115
+
116
+ func_kwargs = filter_args(request, sc.pp.calculate_qc_metrics)
117
+ ads = get_ads()
118
+ adata = ads.get_adata(adinfo=adinfo)
119
+ func_kwargs["inplace"] = True
120
+ try:
121
+ sc.pp.calculate_qc_metrics(adata, **func_kwargs)
122
+ add_op_log(adata, sc.pp.calculate_qc_metrics, func_kwargs, adinfo)
123
+ except KeyError as e:
124
+ raise KeyError(f"Cound find {e} in adata.var")
125
+ return [generate_msg(adinfo, adata, ads)]
126
+ except ToolError as e:
127
+ raise ToolError(e)
128
+ except Exception as e:
129
+ if hasattr(e, '__context__') and e.__context__:
130
+ raise ToolError(e.__context__)
131
+ else:
132
+ raise ToolError(e)
133
+ return _calculate_qc_metrics
134
+
135
+ def _tool_log1p(self):
136
+ def _log1p(request: Log1PModel, adinfo: self.AdataInfo=self.AdataInfo()):
137
+ """Logarithmize the data matrix"""
138
+ try:
139
+ result = forward_request("pp_log1p", request, adinfo)
140
+ if result is not None:
141
+ return result
142
+ func_kwargs = filter_args(request, sc.pp.log1p)
143
+ ads = get_ads()
144
+ adata = ads.get_adata(adinfo=adinfo).copy()
145
+ try:
146
+ sc.pp.log1p(adata, **func_kwargs)
147
+ adata.raw = adata.copy()
148
+ add_op_log(adata, sc.pp.log1p, func_kwargs, adinfo)
149
+ except Exception as e:
150
+ raise e
151
+ ads.set_adata(adata, adinfo=adinfo)
152
+ return [generate_msg(adinfo, adata, ads)]
153
+ except ToolError as e:
154
+ raise ToolError(e)
155
+ except Exception as e:
156
+ if hasattr(e, '__context__') and e.__context__:
157
+ raise ToolError(e.__context__)
158
+ else:
159
+ raise ToolError(e)
160
+ return _log1p
161
+
162
+ def _tool_normalize_total(self):
163
+ def _normalize_total(request: NormalizeTotalModel, adinfo: self.AdataInfo=self.AdataInfo()):
164
+ """Normalize counts per cell to the same total count"""
165
+ try:
166
+ result = forward_request("pp_normalize_total", request, adinfo)
167
+ if result is not None:
168
+ return result
169
+ func_kwargs = filter_args(request, sc.pp.normalize_total)
170
+ ads = get_ads()
171
+ adata = ads.get_adata(adinfo=adinfo).copy()
172
+ sc.pp.normalize_total(adata, **func_kwargs)
173
+ add_op_log(adata, sc.pp.normalize_total, func_kwargs, adinfo)
174
+ ads.set_adata(adata, adinfo=adinfo)
175
+ return [generate_msg(adinfo, adata, ads)]
176
+ except ToolError as e:
177
+ raise ToolError(e)
178
+ except Exception as e:
179
+ if hasattr(e, '__context__') and e.__context__:
180
+ raise ToolError(e.__context__)
181
+ else:
182
+ raise ToolError(e)
183
+ return _normalize_total
184
+
185
+ def _tool_highly_variable_genes(self):
186
+ def _highly_variable_genes(request: HighlyVariableGenesModel, adinfo: self.AdataInfo=self.AdataInfo()):
187
+ """Annotate highly variable genes"""
188
+ try:
189
+ result = forward_request("pp_highly_variable_genes", request, adinfo)
190
+ if result is not None:
191
+ return result
192
+ try:
193
+ func_kwargs = filter_args(request, sc.pp.highly_variable_genes)
194
+ ads = get_ads()
195
+ adata = ads.get_adata(adinfo=adinfo)
196
+ sc.pp.highly_variable_genes(adata, **func_kwargs)
197
+ add_op_log(adata, sc.pp.highly_variable_genes, func_kwargs, adinfo)
198
+ except Exception as e:
199
+ raise e
200
+ return [generate_msg(adinfo, adata, ads)]
201
+ except ToolError as e:
202
+ raise ToolError(e)
203
+ except Exception as e:
204
+ if hasattr(e, '__context__') and e.__context__:
205
+ raise ToolError(e.__context__)
206
+ else:
207
+ raise ToolError(e)
208
+ return _highly_variable_genes
209
+
210
+ def _tool_regress_out(self):
211
+ def _regress_out(request: RegressOutModel, adinfo: self.AdataInfo=self.AdataInfo()):
212
+ """Regress out (mostly) unwanted sources of variation."""
213
+ try:
214
+ result = forward_request("pp_regress_out", request, adinfo)
215
+ if result is not None:
216
+ return result
217
+ func_kwargs = filter_args(request, sc.pp.regress_out)
218
+ ads = get_ads()
219
+ adata = ads.get_adata(adinfo=adinfo).copy()
220
+ sc.pp.regress_out(adata, **func_kwargs)
221
+ add_op_log(adata, sc.pp.regress_out, func_kwargs, adinfo)
222
+ ads.set_adata(adata, adinfo=adinfo)
223
+ return [generate_msg(adinfo, adata, ads)]
224
+ except ToolError as e:
225
+ raise ToolError(e)
226
+ except Exception as e:
227
+ if hasattr(e, '__context__') and e.__context__:
228
+ raise ToolError(e.__context__)
229
+ else:
230
+ raise ToolError(e)
231
+ return _regress_out
232
+
233
+ def _tool_scale(self):
234
+ def _scale(request: ScaleModel, adinfo: self.AdataInfo=self.AdataInfo()):
235
+ """Scale data to unit variance and zero mean"""
236
+ try:
237
+ result = forward_request("pp_scale", request, adinfo)
238
+ if result is not None:
239
+ return result
240
+ func_kwargs = filter_args(request, sc.pp.scale)
241
+ ads = get_ads()
242
+ adata = ads.get_adata(adinfo=adinfo).copy()
243
+
244
+ sc.pp.scale(adata, **func_kwargs)
245
+ add_op_log(adata, sc.pp.scale, func_kwargs, adinfo)
246
+
247
+ ads.set_adata(adata, adinfo=adinfo)
248
+ return [generate_msg(adinfo, adata, ads)]
249
+ except ToolError as e:
250
+ raise ToolError(e)
251
+ except Exception as e:
252
+ if hasattr(e, '__context__') and e.__context__:
253
+ raise ToolError(e.__context__)
254
+ else:
255
+ raise ToolError(e)
256
+ return _scale
257
+
258
+ def _tool_combat(self):
259
+ def _combat(request: CombatModel, adinfo: self.AdataInfo=self.AdataInfo()):
260
+ """ComBat function for batch effect correction"""
261
+ try:
262
+ result = forward_request("pp_combat", request, adinfo)
263
+ if result is not None:
264
+ return result
265
+ func_kwargs = filter_args(request, sc.pp.combat)
266
+ ads = get_ads()
267
+ adata = ads.get_adata(adinfo=adinfo).copy()
268
+
269
+ sc.pp.combat(adata, **func_kwargs)
270
+ add_op_log(adata, sc.pp.combat, func_kwargs, adinfo)
271
+
272
+ ads.set_adata(adata, adinfo=adinfo)
273
+ return [generate_msg(adinfo, adata, ads)]
274
+ except ToolError as e:
275
+ raise ToolError(e)
276
+ except Exception as e:
277
+ if hasattr(e, '__context__') and e.__context__:
278
+ raise ToolError(e.__context__)
279
+ else:
280
+ raise ToolError(e)
281
+ return _combat
282
+
283
+ def _tool_scrublet(self):
284
+ def _scrublet(request: ScrubletModel, adinfo: self.AdataInfo=self.AdataInfo()):
285
+ """Predict doublets using Scrublet"""
286
+ try:
287
+ result = forward_request("pp_scrublet", request, adinfo)
288
+ if result is not None:
289
+ return result
290
+ func_kwargs = filter_args(request, sc.pp.scrublet)
291
+ ads = get_ads()
292
+ adata = ads.get_adata(adinfo=adinfo)
293
+ sc.pp.scrublet(adata, **func_kwargs)
294
+ add_op_log(adata, sc.pp.scrublet, func_kwargs, adinfo)
295
+ return [generate_msg(adinfo, adata, ads)]
296
+ except ToolError as e:
297
+ raise ToolError(e)
298
+ except Exception as e:
299
+ if hasattr(e, '__context__') and e.__context__:
300
+ raise ToolError(e.__context__)
301
+ else:
302
+ raise ToolError(e)
303
+ return _scrublet
304
+
305
+ def _tool_neighbors(self):
306
+ def _neighbors(request: NeighborsModel, adinfo: self.AdataInfo=self.AdataInfo()):
307
+ """Compute nearest neighbors distance matrix and neighborhood graph"""
308
+ try:
309
+ result = forward_request("pp_neighbors", request, adinfo)
310
+ if result is not None:
311
+ return result
312
+ func_kwargs = filter_args(request, sc.pp.neighbors)
313
+ ads = get_ads()
314
+ adata = ads.get_adata(adinfo=adinfo)
315
+ sc.pp.neighbors(adata, **func_kwargs)
316
+ add_op_log(adata, sc.pp.neighbors, func_kwargs, adinfo)
317
+ return [generate_msg(adinfo, adata, ads)]
318
+ except ToolError as e:
319
+ raise ToolError(e)
320
+ except Exception as e:
321
+ if hasattr(e, '__context__') and e.__context__:
322
+ raise ToolError(e.__context__)
323
+ else:
324
+ raise ToolError(e)
325
+ return _neighbors