deepfos 1.1.60__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.
Files changed (175) hide show
  1. deepfos/__init__.py +6 -0
  2. deepfos/_version.py +21 -0
  3. deepfos/algo/__init__.py +0 -0
  4. deepfos/algo/graph.py +171 -0
  5. deepfos/algo/segtree.py +31 -0
  6. deepfos/api/V1_1/__init__.py +0 -0
  7. deepfos/api/V1_1/business_model.py +119 -0
  8. deepfos/api/V1_1/dimension.py +599 -0
  9. deepfos/api/V1_1/models/__init__.py +0 -0
  10. deepfos/api/V1_1/models/business_model.py +1033 -0
  11. deepfos/api/V1_1/models/dimension.py +2768 -0
  12. deepfos/api/V1_2/__init__.py +0 -0
  13. deepfos/api/V1_2/dimension.py +285 -0
  14. deepfos/api/V1_2/models/__init__.py +0 -0
  15. deepfos/api/V1_2/models/dimension.py +2923 -0
  16. deepfos/api/__init__.py +0 -0
  17. deepfos/api/account.py +167 -0
  18. deepfos/api/accounting_engines.py +147 -0
  19. deepfos/api/app.py +626 -0
  20. deepfos/api/approval_process.py +198 -0
  21. deepfos/api/base.py +983 -0
  22. deepfos/api/business_model.py +160 -0
  23. deepfos/api/consolidation.py +129 -0
  24. deepfos/api/consolidation_process.py +106 -0
  25. deepfos/api/datatable.py +341 -0
  26. deepfos/api/deep_pipeline.py +61 -0
  27. deepfos/api/deepconnector.py +36 -0
  28. deepfos/api/deepfos_task.py +92 -0
  29. deepfos/api/deepmodel.py +188 -0
  30. deepfos/api/dimension.py +486 -0
  31. deepfos/api/financial_model.py +319 -0
  32. deepfos/api/journal_model.py +119 -0
  33. deepfos/api/journal_template.py +132 -0
  34. deepfos/api/memory_financial_model.py +98 -0
  35. deepfos/api/models/__init__.py +3 -0
  36. deepfos/api/models/account.py +483 -0
  37. deepfos/api/models/accounting_engines.py +756 -0
  38. deepfos/api/models/app.py +1338 -0
  39. deepfos/api/models/approval_process.py +1043 -0
  40. deepfos/api/models/base.py +234 -0
  41. deepfos/api/models/business_model.py +805 -0
  42. deepfos/api/models/consolidation.py +711 -0
  43. deepfos/api/models/consolidation_process.py +248 -0
  44. deepfos/api/models/datatable_mysql.py +427 -0
  45. deepfos/api/models/deep_pipeline.py +55 -0
  46. deepfos/api/models/deepconnector.py +28 -0
  47. deepfos/api/models/deepfos_task.py +386 -0
  48. deepfos/api/models/deepmodel.py +308 -0
  49. deepfos/api/models/dimension.py +1576 -0
  50. deepfos/api/models/financial_model.py +1796 -0
  51. deepfos/api/models/journal_model.py +341 -0
  52. deepfos/api/models/journal_template.py +854 -0
  53. deepfos/api/models/memory_financial_model.py +478 -0
  54. deepfos/api/models/platform.py +178 -0
  55. deepfos/api/models/python.py +221 -0
  56. deepfos/api/models/reconciliation_engine.py +411 -0
  57. deepfos/api/models/reconciliation_report.py +161 -0
  58. deepfos/api/models/role_strategy.py +884 -0
  59. deepfos/api/models/smartlist.py +237 -0
  60. deepfos/api/models/space.py +1137 -0
  61. deepfos/api/models/system.py +1065 -0
  62. deepfos/api/models/variable.py +463 -0
  63. deepfos/api/models/workflow.py +946 -0
  64. deepfos/api/platform.py +199 -0
  65. deepfos/api/python.py +90 -0
  66. deepfos/api/reconciliation_engine.py +181 -0
  67. deepfos/api/reconciliation_report.py +64 -0
  68. deepfos/api/role_strategy.py +234 -0
  69. deepfos/api/smartlist.py +69 -0
  70. deepfos/api/space.py +582 -0
  71. deepfos/api/system.py +372 -0
  72. deepfos/api/variable.py +154 -0
  73. deepfos/api/workflow.py +264 -0
  74. deepfos/boost/__init__.py +6 -0
  75. deepfos/boost/py_jstream.py +89 -0
  76. deepfos/boost/py_pandas.py +20 -0
  77. deepfos/cache.py +121 -0
  78. deepfos/config.py +6 -0
  79. deepfos/core/__init__.py +27 -0
  80. deepfos/core/cube/__init__.py +10 -0
  81. deepfos/core/cube/_base.py +462 -0
  82. deepfos/core/cube/constants.py +21 -0
  83. deepfos/core/cube/cube.py +408 -0
  84. deepfos/core/cube/formula.py +707 -0
  85. deepfos/core/cube/syscube.py +532 -0
  86. deepfos/core/cube/typing.py +7 -0
  87. deepfos/core/cube/utils.py +238 -0
  88. deepfos/core/dimension/__init__.py +11 -0
  89. deepfos/core/dimension/_base.py +506 -0
  90. deepfos/core/dimension/dimcreator.py +184 -0
  91. deepfos/core/dimension/dimension.py +472 -0
  92. deepfos/core/dimension/dimexpr.py +271 -0
  93. deepfos/core/dimension/dimmember.py +155 -0
  94. deepfos/core/dimension/eledimension.py +22 -0
  95. deepfos/core/dimension/filters.py +99 -0
  96. deepfos/core/dimension/sysdimension.py +168 -0
  97. deepfos/core/logictable/__init__.py +5 -0
  98. deepfos/core/logictable/_cache.py +141 -0
  99. deepfos/core/logictable/_operator.py +663 -0
  100. deepfos/core/logictable/nodemixin.py +673 -0
  101. deepfos/core/logictable/sqlcondition.py +609 -0
  102. deepfos/core/logictable/tablemodel.py +497 -0
  103. deepfos/db/__init__.py +36 -0
  104. deepfos/db/cipher.py +660 -0
  105. deepfos/db/clickhouse.py +191 -0
  106. deepfos/db/connector.py +195 -0
  107. deepfos/db/daclickhouse.py +171 -0
  108. deepfos/db/dameng.py +101 -0
  109. deepfos/db/damysql.py +189 -0
  110. deepfos/db/dbkits.py +358 -0
  111. deepfos/db/deepengine.py +99 -0
  112. deepfos/db/deepmodel.py +82 -0
  113. deepfos/db/deepmodel_kingbase.py +83 -0
  114. deepfos/db/edb.py +214 -0
  115. deepfos/db/gauss.py +83 -0
  116. deepfos/db/kingbase.py +83 -0
  117. deepfos/db/mysql.py +184 -0
  118. deepfos/db/oracle.py +131 -0
  119. deepfos/db/postgresql.py +192 -0
  120. deepfos/db/sqlserver.py +99 -0
  121. deepfos/db/utils.py +135 -0
  122. deepfos/element/__init__.py +89 -0
  123. deepfos/element/accounting.py +348 -0
  124. deepfos/element/apvlprocess.py +215 -0
  125. deepfos/element/base.py +398 -0
  126. deepfos/element/bizmodel.py +1269 -0
  127. deepfos/element/datatable.py +2467 -0
  128. deepfos/element/deep_pipeline.py +186 -0
  129. deepfos/element/deepconnector.py +59 -0
  130. deepfos/element/deepmodel.py +1806 -0
  131. deepfos/element/dimension.py +1254 -0
  132. deepfos/element/fact_table.py +427 -0
  133. deepfos/element/finmodel.py +1485 -0
  134. deepfos/element/journal.py +840 -0
  135. deepfos/element/journal_template.py +943 -0
  136. deepfos/element/pyscript.py +412 -0
  137. deepfos/element/reconciliation.py +553 -0
  138. deepfos/element/rolestrategy.py +243 -0
  139. deepfos/element/smartlist.py +457 -0
  140. deepfos/element/variable.py +756 -0
  141. deepfos/element/workflow.py +560 -0
  142. deepfos/exceptions/__init__.py +239 -0
  143. deepfos/exceptions/hook.py +86 -0
  144. deepfos/lazy.py +104 -0
  145. deepfos/lazy_import.py +84 -0
  146. deepfos/lib/__init__.py +0 -0
  147. deepfos/lib/_javaobj.py +366 -0
  148. deepfos/lib/asynchronous.py +879 -0
  149. deepfos/lib/concurrency.py +107 -0
  150. deepfos/lib/constant.py +39 -0
  151. deepfos/lib/decorator.py +310 -0
  152. deepfos/lib/deepchart.py +778 -0
  153. deepfos/lib/deepux.py +477 -0
  154. deepfos/lib/discovery.py +273 -0
  155. deepfos/lib/edb_lexer.py +789 -0
  156. deepfos/lib/eureka.py +156 -0
  157. deepfos/lib/filterparser.py +751 -0
  158. deepfos/lib/httpcli.py +106 -0
  159. deepfos/lib/jsonstreamer.py +80 -0
  160. deepfos/lib/msg.py +394 -0
  161. deepfos/lib/nacos.py +225 -0
  162. deepfos/lib/patch.py +92 -0
  163. deepfos/lib/redis.py +241 -0
  164. deepfos/lib/serutils.py +181 -0
  165. deepfos/lib/stopwatch.py +99 -0
  166. deepfos/lib/subtask.py +572 -0
  167. deepfos/lib/sysutils.py +703 -0
  168. deepfos/lib/utils.py +1003 -0
  169. deepfos/local.py +160 -0
  170. deepfos/options.py +670 -0
  171. deepfos/translation.py +237 -0
  172. deepfos-1.1.60.dist-info/METADATA +33 -0
  173. deepfos-1.1.60.dist-info/RECORD +175 -0
  174. deepfos-1.1.60.dist-info/WHEEL +5 -0
  175. deepfos-1.1.60.dist-info/top_level.txt +1 -0
deepfos/lib/deepux.py ADDED
@@ -0,0 +1,477 @@
1
+ """DeepUX组件数据源"""
2
+ import asyncio
3
+ import functools
4
+ from typing import Dict, Type, List, Union, Tuple
5
+
6
+ import numpy as np
7
+ import pandas as pd
8
+ from pydantic import Field
9
+
10
+ from deepfos import OPTION
11
+ from deepfos.api.models import BaseModel
12
+ from deepfos.api.models.dimension import DimensionMemberBean
13
+
14
+ __all__ = [
15
+ 'BaseField',
16
+ 'String',
17
+ 'Integer',
18
+ 'Boolean',
19
+ 'Json',
20
+ 'Float',
21
+ 'DateTime',
22
+ 'UUID',
23
+ 'as_datasource',
24
+ 'Struct',
25
+ 'NodeStruct',
26
+ 'EdgeStruct',
27
+ 'to_desc'
28
+ ]
29
+
30
+ FLAG_FOR_META = "describe"
31
+ FIELDS = "fields"
32
+ STRUCT_FIELD = "objectInfos"
33
+ ELE_INFO_FIELD = "elementInfo"
34
+ DATA_FIELD = "json"
35
+ DESC_FIELD = "description"
36
+
37
+ NODES = "nodes"
38
+ EDGES = "edges"
39
+ OBJECT_TYPE = "object[]"
40
+
41
+
42
+ class BaseField(BaseModel):
43
+ name: str = None
44
+ type: str = None
45
+
46
+ def __init__(self, name: str = None, **data):
47
+ super().__init__(name=name, **data)
48
+
49
+ def to_dict(self) -> Dict:
50
+ return self.dict()
51
+
52
+
53
+ class ScalarField(BaseField):
54
+ def fit(self, df: pd.DataFrame, field_name: str):
55
+ pass
56
+
57
+
58
+ class String(ScalarField):
59
+ """文本"""
60
+ type = 'str'
61
+
62
+ def fit(self, df: pd.DataFrame, field_name: str):
63
+ df[field_name] = np.where(
64
+ df[field_name].isna(),
65
+ df[field_name],
66
+ df[field_name].astype(str, errors='ignore')
67
+ )
68
+
69
+
70
+ class Integer(ScalarField):
71
+ """整数"""
72
+ type = 'int'
73
+
74
+ def fit(self, df: pd.DataFrame, field_name: str):
75
+ df[field_name] = np.where(
76
+ df[field_name].isna(),
77
+ df[field_name],
78
+ pd.to_numeric(df[field_name], errors='ignore', downcast='signed')
79
+ )
80
+
81
+
82
+ class Boolean(ScalarField):
83
+ """布尔"""
84
+ type = 'bool'
85
+
86
+ def fit(self, df: pd.DataFrame, field_name: str):
87
+ df[field_name] = np.where(
88
+ df[field_name].isna(),
89
+ df[field_name],
90
+ df[field_name].astype(bool, errors='ignore')
91
+ )
92
+
93
+
94
+ class Json(ScalarField):
95
+ """多语言文本(json)"""
96
+ type = 'json'
97
+
98
+
99
+ class Float(ScalarField):
100
+ """小数"""
101
+ type = 'float'
102
+
103
+ def fit(self, df: pd.DataFrame, field_name: str):
104
+ df[field_name] = np.where(
105
+ df[field_name].isna(),
106
+ df[field_name],
107
+ pd.to_numeric(df[field_name], errors='ignore')
108
+ )
109
+
110
+
111
+ class DateTime(ScalarField):
112
+ """日期时间"""
113
+ type = 'datetime'
114
+
115
+
116
+ class UUID(ScalarField):
117
+ """uuid"""
118
+ type = 'uuid'
119
+
120
+
121
+ class ObjectField(BaseField):
122
+ """表示对象类型的结构信息, 图格式数据源专用"""
123
+ type = OBJECT_TYPE
124
+ fields: List[ScalarField] = Field(default_factory=list)
125
+
126
+
127
+ class StructMeta(type):
128
+ def __new__(mcs, cls_name, bases, namespace: dict):
129
+ namespace['fields'] = mcs.collect_fields(bases, namespace)
130
+ return type.__new__(mcs, cls_name, bases, namespace)
131
+
132
+ @staticmethod
133
+ def collect_fields(bases, ns):
134
+ fields = {}
135
+ if len(bases) > 0:
136
+ for parent in bases:
137
+ if (
138
+ issubclass(parent, Struct)
139
+ and hasattr(parent, 'fields')
140
+ ):
141
+ fields.update(parent.fields)
142
+
143
+ for field_name, anno in ns.get('__annotations__', {}).items():
144
+ if (
145
+ isinstance(anno, type)
146
+ and issubclass(anno, ScalarField)
147
+ and field_name not in ns
148
+ ):
149
+ fields.update({field_name: anno(name=field_name)})
150
+
151
+ for k, v in dict(ns).items():
152
+ if isinstance(v, ScalarField):
153
+ ns.pop(k)
154
+ if v.name is None:
155
+ v.name = k
156
+ fields.update({v.name: v})
157
+
158
+ return fields
159
+
160
+
161
+ class Struct(metaclass=StructMeta):
162
+ """数据源字段信息结构
163
+
164
+ .. admonition:: 示例
165
+
166
+ .. code-block:: python
167
+
168
+ from deepfos.lib.deepux import Struct
169
+
170
+ class Data(Struct):
171
+ # 声明字段 text 为str类型
172
+ text = String()
173
+ # 声明字段 int1 为int类型
174
+ integer = Integer('int1')
175
+ # 声明字段 float_ 为float类型
176
+ float_: Float
177
+ # 声明字段 dt 为datetime类型
178
+ datetime = DateTime(name='dt')
179
+
180
+ See Also:
181
+ :meth:`as_datasource`
182
+ :class:`NodeStruct`
183
+ :class:`EdgeStruct`
184
+
185
+ """
186
+ @classmethod
187
+ def structure(cls):
188
+ return [field.to_dict() for field in cls.fields.values()] # noqa
189
+
190
+ @classmethod
191
+ def to_dict(cls):
192
+ return {
193
+ ELE_INFO_FIELD: {
194
+ DESC_FIELD: OPTION.general.task_info.get('element_desc', {})
195
+ },
196
+ STRUCT_FIELD: [
197
+ {FIELDS: cls.structure()}
198
+ ]
199
+ }
200
+
201
+
202
+ class NodeStruct(Struct):
203
+ """图格式的数据源Node字段信息结构
204
+
205
+ 默认的_id(节点主键)和label(节点描述)字段已设置,
206
+ 继承后即可声明其他节点键信息
207
+
208
+ .. admonition:: 示例
209
+
210
+ .. code-block:: python
211
+
212
+ from deepfos.lib.deepux import NodeStruct
213
+
214
+ class NodeData(NodeStruct):
215
+ # 声明节点键 text 为str类型
216
+ text = String()
217
+ # 声明节点键 int1 为int类型
218
+ integer = Integer('int1')
219
+ # 声明节点键 float_ 为float类型
220
+ float_: Float
221
+ # 声明节点键 dt 为datetime类型
222
+ datetime = DateTime(name='dt')
223
+
224
+ See Also:
225
+ :meth:`as_datasource`
226
+ :class:`Struct`
227
+ :class:`EdgeStruct`
228
+
229
+ """
230
+ _id: String
231
+ label: String
232
+
233
+
234
+ class EdgeStruct(Struct):
235
+ """图格式的数据源Edge字段信息结构
236
+
237
+ 默认的source(关系的来源)和target(关系的目标)字段已设置,
238
+ 继承后即可声明其他节点关系信息
239
+
240
+ .. admonition:: 示例
241
+
242
+ .. code-block:: python
243
+
244
+ from deepfos.lib.deepux import EdgeStruct
245
+
246
+ class EdgeData(EdgeStruct):
247
+ # 声明节点关系 color 字段为str类型
248
+ color = String()
249
+ # 声明节点关系 size 字段为int类型
250
+ size = Integer('size')
251
+
252
+ See Also:
253
+ :meth:`as_datasource`
254
+ :class:`Struct`
255
+ :class:`NodeStruct`
256
+
257
+ """
258
+ source: String
259
+ target: String
260
+
261
+
262
+ class GraphStruct(Struct):
263
+ node: NodeStruct
264
+ edge: EdgeStruct
265
+
266
+ @classmethod
267
+ def structure(cls):
268
+ return [
269
+ ObjectField(
270
+ name=NODES,
271
+ fields=list(cls.node.fields.values()) # noqa
272
+ ).to_dict(),
273
+ ObjectField(
274
+ name=EDGES,
275
+ fields=list(cls.edge.fields.values()) # noqa
276
+ ).to_dict(),
277
+ ]
278
+
279
+
280
+ def to_desc(data: List[DimensionMemberBean]):
281
+ desc = {}
282
+ for dim in data:
283
+ desc[dim.name] = dim.multilingual.get(
284
+ OPTION.api.header.get('language', 'en'),
285
+ dim.name
286
+ )
287
+ return desc
288
+
289
+
290
+ def _resolve_param(args: tuple):
291
+ if len(args) == 2:
292
+ return args[1]
293
+ if len(args) == 1:
294
+ return args[0]
295
+ raise ValueError("main函数入参数非法")
296
+
297
+
298
+ def _check_str_dict(data: Dict[str, Union[Dict, str]], value_type):
299
+ if any(not isinstance(k, str) for k in data):
300
+ raise ValueError("字段描述格式非法")
301
+
302
+ if any(not isinstance(v, value_type) for v in data.values()):
303
+ raise ValueError("字段描述格式非法")
304
+
305
+ if value_type == str:
306
+ return
307
+
308
+ for v in data.values():
309
+ _check_str_dict(v, str)
310
+
311
+
312
+ def valid_df(df, struct: Dict[str, ScalarField]):
313
+ if lacked := (set(struct).difference(df.columns)):
314
+ raise ValueError(f'字段: {lacked} 缺失')
315
+
316
+ df = df[list(struct.keys())]
317
+ df = df.replace({None: np.nan})
318
+ df = df.replace({np.nan: None})
319
+ for field_name, field_type in struct.items():
320
+ field_type.fit(df, field_name)
321
+ return df
322
+
323
+
324
+ def _resolve_return_value(
325
+ return_value,
326
+ struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]],
327
+ graph_struct: bool,
328
+ node_edge: bool
329
+ ) -> Tuple[Dict, Dict]:
330
+ is_two_tuples = isinstance(return_value, tuple) and len(return_value) == 2
331
+ desc = {}
332
+ if graph_struct:
333
+ if (
334
+ not is_two_tuples
335
+ and any([not isinstance(each, pd.DataFrame) for each in return_value])
336
+ ):
337
+ raise ValueError(
338
+ "图结构的数据源预期main函数返回值为"
339
+ "nodes和edges内容组成的pandas DataFrame"
340
+ )
341
+ node_struct, edge_struct = struct
342
+ if node_edge:
343
+ nodes, edges = return_value
344
+ else:
345
+ edges, nodes = return_value
346
+ nodes = valid_df(nodes, node_struct.fields)
347
+ edges = valid_df(edges, edge_struct.fields)
348
+ data = [{NODES: nodes.to_dict('records'), EDGES: edges.to_dict('records')}]
349
+ elif is_two_tuples:
350
+ df, desc = return_value
351
+ if not isinstance(df, pd.DataFrame):
352
+ raise TypeError('通用结构的数据源预期main函数返回的第一个值为pandas DataFrame')
353
+
354
+ if not isinstance(desc, dict):
355
+ raise TypeError("通用结构的数据源预期main函数返回的第二个值为字段描述,类型为字典")
356
+
357
+ _check_str_dict(desc, dict)
358
+ df = valid_df(df, struct.fields) # noqa
359
+ data = df.to_dict('records')
360
+ elif isinstance(return_value, pd.DataFrame):
361
+ df = valid_df(return_value, struct.fields) # noqa
362
+ data = df.to_dict('records')
363
+ else:
364
+ raise ValueError(
365
+ "通用结构的数据源预期main函数返回值为pandas DataFrame"
366
+ "或pandas DataFrame与字段描述字典组成的元组"
367
+ )
368
+
369
+ return data, desc
370
+
371
+
372
+ def _resolve_struct_desc(
373
+ struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]],
374
+ graph_struct: bool
375
+ ) -> Dict:
376
+ if graph_struct:
377
+ # order preprared outside
378
+ node_struct, edge_struct = struct
379
+ helper_struct = GraphStruct
380
+ helper_struct.node, helper_struct.edge = node_struct, edge_struct
381
+ return helper_struct.to_dict()
382
+ else:
383
+ return struct.to_dict()
384
+
385
+
386
+ def as_datasource(
387
+ func=None,
388
+ struct: Union[Type[Struct], Tuple[Type[Struct], Type[Struct]]] = None,
389
+ ):
390
+ """用作DeepUX数据源的main函数装饰器
391
+
392
+ Args:
393
+ func: 返回pandas DataFrame和字段描述(可选)的main方法
394
+ struct: 定义字段及其字段类型的类名称,必填
395
+
396
+ .. admonition:: 用法示例
397
+
398
+ .. code-block:: python
399
+
400
+ from deepfos.lib.deepux import as_datasource, Struct
401
+
402
+ # 声明结构信息
403
+ class Data(Struct):
404
+ ...
405
+
406
+ @as_datasource(struct=Data)
407
+ def main(p2):
408
+ ...
409
+
410
+ See Also:
411
+ :class:`Struct`
412
+ :class:`NodeStruct`
413
+ :class:`EdgeStruct`
414
+
415
+ """
416
+ if func is None:
417
+ return functools.partial(as_datasource, struct=struct)
418
+
419
+ if struct is None:
420
+ raise ValueError("需定义DeepUX数据源的字段信息")
421
+
422
+ struct_is_tuple = isinstance(struct, tuple)
423
+ graph_struct = (
424
+ struct_is_tuple and len(struct) == 2
425
+ and (
426
+ (issubclass(struct[0], NodeStruct) and issubclass(struct[1], EdgeStruct))
427
+ or
428
+ (issubclass(struct[0], EdgeStruct) and issubclass(struct[1], NodeStruct))
429
+ )
430
+ )
431
+
432
+ if not struct_is_tuple and not issubclass(struct, Struct):
433
+ raise ValueError(
434
+ "DeepUX数据源的字段信息需为Struct的子类"
435
+ "或EdgeStruct和NodeStruct的子类组成的元组"
436
+ )
437
+
438
+ if struct_is_tuple and not graph_struct:
439
+ raise ValueError(
440
+ "DeepUX图结构数据源的字段信息需为EdgeStruct和NodeStruct的子类组成的元组"
441
+ )
442
+
443
+ node_edge = True
444
+ if graph_struct and issubclass(struct[1], NodeStruct): # noqa
445
+ node_edge = False
446
+ struct = struct[1], struct[0] # noqa
447
+
448
+ if asyncio.iscoroutinefunction(func):
449
+ async def wrapper(*args):
450
+ param = _resolve_param(args)
451
+
452
+ if param == FLAG_FOR_META:
453
+ return _resolve_struct_desc(struct, graph_struct)
454
+
455
+ maybe_df_desc = await func(*args)
456
+
457
+ data, desc = _resolve_return_value(
458
+ maybe_df_desc, struct, graph_struct, node_edge
459
+ )
460
+
461
+ return {DATA_FIELD: data, DESC_FIELD: desc}
462
+ else:
463
+ def wrapper(*args):
464
+ param = _resolve_param(args)
465
+
466
+ if param == FLAG_FOR_META:
467
+ return _resolve_struct_desc(struct, graph_struct)
468
+
469
+ maybe_df_desc = func(*args)
470
+
471
+ data, desc = _resolve_return_value(
472
+ maybe_df_desc, struct, graph_struct, node_edge
473
+ )
474
+
475
+ return {DATA_FIELD: data, DESC_FIELD: desc}
476
+
477
+ return functools.wraps(func)(wrapper)