scmcp-shared 0.2.0__py3-none-any.whl → 0.2.5__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.
@@ -3,7 +3,9 @@ import inspect
3
3
  from pathlib import Path
4
4
  import scanpy as sc
5
5
  from fastmcp import FastMCP , Context
6
+ from fastmcp.exceptions import ToolError
6
7
  from ..schema.util import *
8
+ from ..schema import AdataModel
7
9
  from ..util import filter_args, forward_request, get_ads, generate_msg,add_op_log
8
10
 
9
11
 
@@ -11,9 +13,12 @@ ul_mcp = FastMCP("SCMCP-Util-Server")
11
13
 
12
14
 
13
15
  @ul_mcp.tool()
14
- async def query_op_log(request: QueryOpLogModel = QueryOpLogModel()):
16
+ async def query_op_log(
17
+ request: QueryOpLogModel = QueryOpLogModel(),
18
+ adinfo: AdataModel = AdataModel()
19
+ ):
15
20
  """Query the adata operation log"""
16
- adata = get_ads().get_adata(request=request)
21
+ adata = get_ads().get_adata(adinfo=adinfo)
17
22
  op_dic = adata.uns["operation"]["op"]
18
23
  opids = adata.uns["operation"]["opid"][-n:]
19
24
  op_list = []
@@ -24,7 +29,8 @@ async def query_op_log(request: QueryOpLogModel = QueryOpLogModel()):
24
29
 
25
30
  @ul_mcp.tool()
26
31
  async def mark_var(
27
- request: MarkVarModel = MarkVarModel()
32
+ request: MarkVarModel = MarkVarModel(),
33
+ adinfo: AdataModel = AdataModel()
28
34
  ):
29
35
  """
30
36
  Determine if each gene meets specific conditions and store results in adata.var as boolean values.
@@ -32,10 +38,10 @@ async def mark_var(
32
38
  The tool should be called first when calculate quality control metrics for mitochondrion, ribosomal, harhemoglobin genes, or other qc_vars.
33
39
  """
34
40
  try:
35
- result = await forward_request("ul_mark_var", request)
41
+ result = await forward_request("ul_mark_var", request, adinfo)
36
42
  if result is not None:
37
43
  return result
38
- adata = get_ads().get_adata(request=request)
44
+ adata = get_ads().get_adata(adinfo=adinfo)
39
45
  var_name = request.var_name
40
46
  gene_class = request.gene_class
41
47
  pattern_type = request.pattern_type
@@ -64,120 +70,124 @@ async def mark_var(
64
70
 
65
71
  res = {var_name: adata.var[var_name].value_counts().to_dict(), "msg": f"add '{var_name}' column in adata.var"}
66
72
  func_kwargs = {"var_name": var_name, "gene_class": gene_class, "pattern_type": pattern_type, "patterns": patterns}
67
- add_op_log(adata, "mark_var", func_kwargs)
73
+ add_op_log(adata, "mark_var", func_kwargs, adinfo)
68
74
  return res
69
- except KeyError as e:
70
- raise e
75
+ except ToolError as e:
76
+ raise ToolError(e)
71
77
  except Exception as e:
72
78
  if hasattr(e, '__context__') and e.__context__:
73
- raise Exception(f"{str(e.__context__)}")
79
+ raise ToolError(e.__context__)
74
80
  else:
75
- raise e
81
+ raise ToolError(e)
76
82
 
77
83
 
78
84
  @ul_mcp.tool()
79
85
  async def list_var(
80
- request: ListVarModel = ListVarModel()
86
+ request: ListVarModel = ListVarModel(),
87
+ adinfo: AdataModel = AdataModel()
81
88
  ):
82
89
  """List key columns in adata.var. It should be called for checking when other tools need var key column names as input."""
83
90
  try:
84
- result = await forward_request("ul_list_var", request)
91
+ result = await forward_request("ul_list_var", request, adinfo)
85
92
  if result is not None:
86
93
  return result
87
- adata = get_ads().get_adata(request=request)
94
+ adata = get_ads().get_adata(adinfo=adinfo)
88
95
  columns = list(adata.var.columns)
89
- add_op_log(adata, list_var, {})
96
+ add_op_log(adata, list_var, {}, adinfo)
90
97
  return columns
91
- except KeyError as e:
92
- raise e
98
+ except ToolError as e:
99
+ raise ToolError(e)
93
100
  except Exception as e:
94
101
  if hasattr(e, '__context__') and e.__context__:
95
- raise Exception(f"{str(e.__context__)}")
102
+ raise ToolError(e.__context__)
96
103
  else:
97
- raise e
104
+ raise ToolError(e)
98
105
 
99
106
  @ul_mcp.tool()
100
107
  async def list_obs(
101
- request: ListObsModel = ListObsModel()
108
+ request: ListObsModel = ListObsModel(),
109
+ adinfo: AdataModel = AdataModel()
102
110
  ):
103
111
  """List key columns in adata.obs. It should be called before other tools need obs key column names input."""
104
112
  try:
105
- result = await forward_request("ul_list_obs", request)
113
+ result = await forward_request("ul_list_obs", request, adinfo)
106
114
  if result is not None:
107
115
  return result
108
- adata = get_ads().get_adata(request=request)
116
+ adata = get_ads().get_adata(adinfo=adinfo)
109
117
  columns = list(adata.obs.columns)
110
- add_op_log(adata, list_obs, {})
118
+ add_op_log(adata, list_obs, {}, adinfo)
111
119
  return columns
112
- except KeyError as e:
113
- raise e
120
+ except ToolError as e:
121
+ raise ToolError(e)
114
122
  except Exception as e:
115
123
  if hasattr(e, '__context__') and e.__context__:
116
- raise Exception(f"{str(e.__context__)}")
124
+ raise ToolError(e.__context__)
117
125
  else:
118
- raise e
126
+ raise ToolError(e)
119
127
 
120
128
  @ul_mcp.tool()
121
129
  async def check_var(
122
- request: VarNamesModel = VarNamesModel()
130
+ request: VarNamesModel = VarNamesModel(),
131
+ adinfo: AdataModel = AdataModel()
123
132
  ):
124
133
  """Check if genes/variables exist in adata.var_names. This tool should be called before gene expression visualizations or color by genes."""
125
134
  try:
126
- result = await forward_request("ul_check_var", request)
135
+ result = await forward_request("ul_check_var", request, adinfo)
127
136
  if result is not None:
128
137
  return result
129
- adata = get_ads().get_adata(request=request)
138
+ adata = get_ads().get_adata(adinfo=adinfo)
130
139
  var_names = request.var_names
131
140
  result = {v: v in adata.var_names for v in var_names}
132
- add_op_log(adata, check_var, {"var_names": var_names})
141
+ add_op_log(adata, check_var, {"var_names": var_names}, adinfo)
133
142
  return result
134
- except KeyError as e:
135
- raise e
143
+ except ToolError as e:
144
+ raise ToolError(e)
136
145
  except Exception as e:
137
146
  if hasattr(e, '__context__') and e.__context__:
138
- raise Exception(f"{str(e.__context__)}")
147
+ raise ToolError(e.__context__)
139
148
  else:
140
- raise e
149
+ raise ToolError(e)
141
150
 
142
151
  @ul_mcp.tool()
143
152
  async def merge_adata(
144
- request: ConcatAdataModel = ConcatAdataModel()
153
+ request: ConcatBaseModel = ConcatBaseModel(),
154
+ adinfo: AdataModel = AdataModel()
145
155
  ):
146
156
  """Merge multiple adata objects."""
147
157
 
148
158
  try:
149
- result = await forward_request("ul_merge_adata", request)
159
+ result = await forward_request("ul_merge_adata", request, adinfo)
150
160
  if result is not None:
151
161
  return result
152
162
  ads = get_ads()
153
- adata = ads.get_adata(request=request)
163
+ adata = ads.get_adata(adinfo=adinfo)
154
164
  kwargs = {k: v for k, v in request.model_dump().items() if v is not None}
155
165
  merged_adata = adata.concat(list(ads.adata_dic[dtype].values()), **kwargs)
156
166
  ads.adata_dic[dtype] = {}
157
167
  ads.active_id = "merged_adata"
158
- add_op_log(merged_adata, ad.concat, kwargs)
168
+ add_op_log(merged_adata, ad.concat, kwargs, adinfo)
159
169
  ads.adata_dic[ads.active_id] = merged_adata
160
170
  return {"status": "success", "message": "Successfully merged all AnnData objects"}
161
- except KeyError as e:
162
- raise e
171
+ except ToolError as e:
172
+ raise ToolError(e)
163
173
  except Exception as e:
164
174
  if hasattr(e, '__context__') and e.__context__:
165
- raise Exception(f"{str(e.__context__)}")
175
+ raise ToolError(e.__context__)
166
176
  else:
167
- raise e
177
+ raise ToolError(e)
168
178
 
169
179
 
170
180
  @ul_mcp.tool()
171
181
  async def set_dpt_iroot(
172
182
  request: DPTIROOTModel,
173
-
183
+ adinfo: AdataModel = AdataModel()
174
184
  ):
175
185
  """Set the iroot cell"""
176
186
  try:
177
- result = await forward_request("ul_set_dpt_iroot", request)
187
+ result = await forward_request("ul_set_dpt_iroot", request, adinfo)
178
188
  if result is not None:
179
189
  return result
180
- adata = get_ads().get_adata(request=request)
190
+ adata = get_ads().get_adata(adinfo=adinfo)
181
191
  diffmap_key = request.diffmap_key
182
192
  dimension = request.dimension
183
193
  direction = request.direction
@@ -189,28 +199,29 @@ async def set_dpt_iroot(
189
199
  adata.uns["iroot"] = adata.obsm[diffmap_key][:, dimension].argmax()
190
200
 
191
201
  func_kwargs = {"diffmap_key": diffmap_key, "dimension": dimension, "direction": direction}
192
- add_op_log(adata, "set_dpt_iroot", func_kwargs)
202
+ add_op_log(adata, "set_dpt_iroot", func_kwargs, adinfo)
193
203
 
194
204
  return {"status": "success", "message": f"Successfully set root cell for DPT using {direction} of dimension {dimension}"}
195
- except KeyError as e:
196
- raise e
205
+ except ToolError as e:
206
+ raise ToolError(e)
197
207
  except Exception as e:
198
208
  if hasattr(e, '__context__') and e.__context__:
199
- raise Exception(f"{str(e.__context__)}")
209
+ raise ToolError(e.__context__)
200
210
  else:
201
- raise e
211
+ raise ToolError(e)
202
212
 
203
213
  @ul_mcp.tool()
204
214
  async def add_layer(
205
215
  request: AddLayerModel,
216
+ adinfo: AdataModel = AdataModel()
206
217
  ):
207
218
  """Add a layer to the AnnData object.
208
219
  """
209
220
  try:
210
- result = await forward_request("ul_add_layer", request)
221
+ result = await forward_request("ul_add_layer", request, adinfo)
211
222
  if result is not None:
212
223
  return result
213
- adata = get_ads().get_adata(request=request)
224
+ adata = get_ads().get_adata(adinfo=adinfo)
214
225
  layer_name = request.layer_name
215
226
 
216
227
  # Check if layer already exists
@@ -220,19 +231,19 @@ async def add_layer(
220
231
  adata.layers[layer_name] = adata.X.copy()
221
232
 
222
233
  func_kwargs = {"layer_name": layer_name}
223
- add_op_log(adata, "add_layer", func_kwargs)
234
+ add_op_log(adata, "add_layer", func_kwargs, adinfo)
224
235
 
225
236
  return {
226
237
  "status": "success",
227
238
  "message": f"Successfully added layer '{layer_name}' to adata.layers"
228
239
  }
229
- except KeyError as e:
230
- raise e
240
+ except ToolError as e:
241
+ raise ToolError(e)
231
242
  except Exception as e:
232
243
  if hasattr(e, '__context__') and e.__context__:
233
- raise Exception(f"{str(e.__context__)}")
244
+ raise ToolError(e.__context__)
234
245
  else:
235
- raise e
246
+ raise ToolError(e)
236
247
 
237
248
  @ul_mcp.tool()
238
249
  async def check_samples():
@@ -241,10 +252,10 @@ async def check_samples():
241
252
  try:
242
253
  ads = get_ads()
243
254
  return {"sampleid": [list(ads.adata_dic[dk].keys()) for dk in ads.adata_dic.keys()]}
244
- except KeyError as e:
245
- raise e
255
+ except ToolError as e:
256
+ raise ToolError(e)
246
257
  except Exception as e:
247
258
  if hasattr(e, '__context__') and e.__context__:
248
- raise Exception(f"{str(e.__context__)}")
259
+ raise ToolError(e.__context__)
249
260
  else:
250
- raise e
261
+ raise ToolError(e)
scmcp_shared/util.py CHANGED
@@ -2,7 +2,7 @@ import inspect
2
2
  import os
3
3
  from pathlib import Path
4
4
  from fastmcp.server.dependencies import get_context
5
-
5
+ from fastmcp.exceptions import ToolError
6
6
 
7
7
 
8
8
  def get_env(key):
@@ -19,7 +19,7 @@ def filter_args(request, func, **extra_kwargs):
19
19
  return func_kwargs
20
20
 
21
21
 
22
- def add_op_log(adata, func, kwargs):
22
+ def add_op_log(adata, func, kwargs, adinfo):
23
23
  import hashlib
24
24
  import json
25
25
 
@@ -37,7 +37,7 @@ def add_op_log(adata, func, kwargs):
37
37
  func_name = func.__class__.__name__
38
38
  else:
39
39
  func_name = str(func)
40
- new_kwargs = {}
40
+ new_kwargs = {**adinfo.model_dump()}
41
41
  for k,v in kwargs.items():
42
42
  if isinstance(v, tuple):
43
43
  new_kwargs[k] = list(v)
@@ -50,6 +50,7 @@ def add_op_log(adata, func, kwargs):
50
50
  hash_input = f"{func_name}:{kwargs_str}"
51
51
  hash_key = hashlib.md5(hash_input.encode()).hexdigest()
52
52
  adata.uns["operation"]["op"][hash_key] = {func_name: new_kwargs}
53
+ adata.uns["operation"]["opid"] = list(adata.uns["operation"]["opid"])
53
54
  adata.uns["operation"]["opid"].append(hash_key)
54
55
  from .logging_config import setup_logger
55
56
  logger = setup_logger(log_file=get_env("LOG_FILE"))
@@ -103,7 +104,7 @@ def set_fig_path(axes, func=None, **kwargs):
103
104
  args.append(f"{k}-{'-'.join([str(i) for i in v])}")
104
105
  else:
105
106
  args.append(f"{k}-{v}")
106
- args_str = "_".join(args)
107
+ args_str = "_".join(args).replace(" ", "")
107
108
  fig_path = fig_dir / f"{func_name}_{args_str}.png"
108
109
  try:
109
110
  savefig(axes, fig_path)
@@ -134,13 +135,22 @@ async def get_figure(request):
134
135
  return FileResponse(figure_path)
135
136
 
136
137
 
137
- async def forward_request(func, request, **kwargs):
138
+ def add_figure_route(server):
139
+ from starlette.routing import Route
140
+ server._additional_http_routes = [Route("/figures/{figure_name}", endpoint=get_figure)]
141
+
142
+
143
+ async def forward_request(func, request, adinfo, **kwargs):
138
144
  from fastmcp import Client
139
145
  forward_url = get_env("FORWARD")
140
146
  request_kwargs = request.model_dump()
141
147
  request_args = request.model_fields_set
142
- func_kwargs = {"request": {k: request_kwargs.get(k) for k in request_args}}
143
- func_kwargs.update({k:v for k,v in kwargs.items() if v is not None})
148
+ func_kwargs = {
149
+ "request": {k: request_kwargs.get(k) for k in request_args},
150
+ "adinfo": adinfo.model_dump()
151
+ }
152
+ print(func_kwargs)
153
+ # func_kwargs.update({k:v for k,v in kwargs.items() if v is not None})
144
154
  if not forward_url:
145
155
  return None
146
156
 
@@ -151,8 +161,14 @@ async def forward_request(func, request, **kwargs):
151
161
  try:
152
162
  result = await client.call_tool(func, func_kwargs)
153
163
  return result
164
+ except ToolError as e:
165
+ raise ToolError(e)
154
166
  except Exception as e:
155
- raise e
167
+ if hasattr(e, '__context__') and e.__context__:
168
+ raise Exception(f"{str(e.__context__)}")
169
+ else:
170
+ raise e
171
+
156
172
 
157
173
  def obsm2adata(adata, obsm_key):
158
174
  from anndata import AnnData
@@ -163,39 +179,25 @@ def obsm2adata(adata, obsm_key):
163
179
  return AnnData(adata.obsm[obsm_key], obs=adata.obs, obsm=adata.obsm)
164
180
 
165
181
 
166
- async def get_figure(request):
167
- figure_name = request.path_params["figure_name"]
168
- figure_path = f"./figures/{figure_name}"
169
-
170
- if not os.path.isfile(figure_path):
171
- return Response(content={"error": "figure not found"}, media_type="application/json")
172
-
173
- return FileResponse(figure_path)
174
-
175
-
176
- def add_figure_route(server):
177
- from starlette.routing import Route
178
- server._additional_http_routes = [Route("/figures/{figure_name}", endpoint=get_figure)]
179
-
180
-
181
182
  def get_ads():
182
183
  ctx = get_context()
183
184
  ads = ctx.request_context.lifespan_context
184
185
  return ads
185
186
 
186
187
 
187
- def generate_msg(request, adata, ads):
188
- kwargs = request.model_dump()
189
- sampleid = kwargs.get("sampleid")
190
- dtype = kwargs.get("dtype", "exp")
191
- return {"sampleid": sampleid or ads.active_id, "dtype": dtype, "adata": adata}
188
+ def generate_msg(adinfo, adata, ads):
189
+ return {"sampleid": adinfo.sampleid or ads.active_id, "dtype": adinfo.adtype, "adata": adata}
190
+
192
191
 
192
+ def sc_like_plot(plot_func, adata, request, adinfo, **kwargs):
193
+ from matplotlib import pyplot as plt
193
194
 
194
- def sc_like_plot(plot_func, adata, request, **kwargs):
195
195
  func_kwargs = filter_args(request, plot_func, show=False, save=False)
196
196
  axes = plot_func(adata, **func_kwargs)
197
+ if axes is None:
198
+ axes = plt.gca()
197
199
  fig_path = set_fig_path(axes, plot_func, **func_kwargs)
198
- add_op_log(adata, plot_func, func_kwargs)
200
+ add_op_log(adata, plot_func, func_kwargs, adinfo)
199
201
  return fig_path
200
202
 
201
203
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: scmcp_shared
3
- Version: 0.2.0
3
+ Version: 0.2.5
4
4
  Summary: A shared function libray for scmcphub
5
5
  Author-email: shuang <hsh-me@outlook.com>
6
6
  License: BSD 3-Clause License
@@ -0,0 +1,19 @@
1
+ scmcp_shared/__init__.py,sha256=9WXFueskl8xksdIA9MJUyauJD-k4jXiK79wuJfC3MuI,24
2
+ scmcp_shared/logging_config.py,sha256=eCuLuyxMmbj8A1E0VqYWoKA5JPTSbo6cmjS4LOyd0RQ,872
3
+ scmcp_shared/util.py,sha256=pQFqFVf8leGqKK38RgXzgtEvXWi8dZBjTuJB0rKTOnw,6957
4
+ scmcp_shared/schema/__init__.py,sha256=iwFLgyjBFYDS2OMCXhv54Jb3LUQr2EH5POqaqN5gBX0,412
5
+ scmcp_shared/schema/io.py,sha256=jpWcw4nUKsCRkG9ybOHLb2kS4wyN16Rj1AuzR-8Sm6U,4893
6
+ scmcp_shared/schema/pl.py,sha256=rzE09wHMY3JR56HZc-QfIUUM0fGXRKd-7Dh3CrQrFB0,29547
7
+ scmcp_shared/schema/pp.py,sha256=48F6oKf-I8IZuNQDfq_Lpp3fLLKA4PruqRje_ZrtTyw,21664
8
+ scmcp_shared/schema/tl.py,sha256=9mYTwdk3XSJ2A_TdqS_974VSS7Nl4NKyewVN1bwnJBg,34450
9
+ scmcp_shared/schema/util.py,sha256=x_2GPsmliHabi9V5C6YEv_M8ZHJsinDZJ6ePWrLPmcI,4815
10
+ scmcp_shared/server/__init__.py,sha256=zLV89TwMICoaAIqx65_Vbh1RP72Z0EgSvqBMFZhYBpc,1790
11
+ scmcp_shared/server/io.py,sha256=0XzQoxipwK4ml2gmPfrZqMYyy37kqyUyr8hSCnjzyVE,2631
12
+ scmcp_shared/server/pl.py,sha256=9DGF7PM-rB1CDMdv_vHiSZmI-ggMWcRKEZwB1GVenxI,12426
13
+ scmcp_shared/server/pp.py,sha256=6p6jGTunU858uuGoXHodiWTdHRufKs9-_WlWtS82esQ,12976
14
+ scmcp_shared/server/tl.py,sha256=OAF-dFQ5Lhw8RrAUir860IWgbPcaeAyQbFdKcP_tlQ4,14912
15
+ scmcp_shared/server/util.py,sha256=MXDpJA1Zn2eFE_LpW5t4eVUdGr6dUOXIM8KoyfweJUA,9607
16
+ scmcp_shared-0.2.5.dist-info/METADATA,sha256=dTs0LfEmYxRWQpv9rkvfnkeqzCRTGrHfzhZZCYBLdeE,2099
17
+ scmcp_shared-0.2.5.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ scmcp_shared-0.2.5.dist-info/licenses/LICENSE,sha256=YNr1hpea195yq-wGtB8j-2dGtt7A5G00WENmxa7JGco,1495
19
+ scmcp_shared-0.2.5.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- from pydantic import Field, BaseModel,ConfigDict
2
-
3
-
4
- class AdataModel(BaseModel):
5
- """Input schema for the adata tool."""
6
- sampleid: str = Field(default=None, description="adata sampleid")
7
- adtype: str = Field(default="exp", description="adata.X data type")
8
-
9
- model_config = ConfigDict(
10
- extra="ignore"
11
- )
@@ -1,20 +0,0 @@
1
- scmcp_shared/__init__.py,sha256=ZJMoFGVU2-tXepjhh5Fhy8ZyNHtOIGZWbJygHmqwmCA,24
2
- scmcp_shared/logging_config.py,sha256=eCuLuyxMmbj8A1E0VqYWoKA5JPTSbo6cmjS4LOyd0RQ,872
3
- scmcp_shared/util.py,sha256=Ul88pY0aHFN42tiWwexO5LfQJgNslyOD4Jb9HTMy3uI,6814
4
- scmcp_shared/schema/__init__.py,sha256=fwMJT4mQ3rvRJpSz9ruwwZU1GbXsUYPVQRpP56Az_JM,28
5
- scmcp_shared/schema/base.py,sha256=hWh0534xon4gyIxwXUBVR067uFiW73nZccSs6DkcwYc,326
6
- scmcp_shared/schema/io.py,sha256=yxa0BGQXuHCUvv6ZGQApPqMOlyJuC-eQNk7jlRGsMJ8,4913
7
- scmcp_shared/schema/pl.py,sha256=I9SCJgjmfB0VTNio4uNnP26ajWCnsHPsDoO-yMuzYVo,29578
8
- scmcp_shared/schema/pp.py,sha256=Uhe28zD71UDZbziesH6bgQXIRXQz_HHrr8-Hb4RQsKI,21696
9
- scmcp_shared/schema/tl.py,sha256=QV0dP-5ZtEKUDmSV9m5ryx7uQub4rw4in7PYsxId5nU,34485
10
- scmcp_shared/schema/util.py,sha256=R0MThHKKGYGGJu-hMc-i7fomW-6ugaoQqPi_hbKrB5A,4844
11
- scmcp_shared/server/__init__.py,sha256=vGYGUpLt8XHRbJI84Ox6WnK4ntzsbTal99Uu14nav60,1796
12
- scmcp_shared/server/io.py,sha256=agioBQeTREqOX8qJsP2aOTkaU3dT04uvObWhUSfOPTg,2422
13
- scmcp_shared/server/pl.py,sha256=0KIB9n-h5L3F4TOO_ezPAEX7TVCVBOsiZd6bqD5WbGQ,11126
14
- scmcp_shared/server/pp.py,sha256=iS-GrU2-Fyw0phLI4A44g2Y3LpFfYF7AWU5ueyzLFxY,12116
15
- scmcp_shared/server/tl.py,sha256=k8GCTmrFh63jI5REO4-BmfCgyvcGgNpvtsxrn_0K67k,13228
16
- scmcp_shared/server/util.py,sha256=eBkwWUSrndTb6RHujgXGZYU1aitJEocEuV6U5IOLsws,9030
17
- scmcp_shared-0.2.0.dist-info/METADATA,sha256=Ayh--1Js_wfAHx1Wep-tw0fxihV9ikUYRWttSwAZL88,2099
18
- scmcp_shared-0.2.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
19
- scmcp_shared-0.2.0.dist-info/licenses/LICENSE,sha256=YNr1hpea195yq-wGtB8j-2dGtt7A5G00WENmxa7JGco,1495
20
- scmcp_shared-0.2.0.dist-info/RECORD,,