recurvedata-lib 0.1.487__py2.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.

Potentially problematic release.


This version of recurvedata-lib might be problematic. Click here for more details.

Files changed (333) hide show
  1. recurvedata/__init__.py +0 -0
  2. recurvedata/__version__.py +1 -0
  3. recurvedata/client/__init__.py +3 -0
  4. recurvedata/client/client.py +150 -0
  5. recurvedata/client/server_client.py +91 -0
  6. recurvedata/config.py +99 -0
  7. recurvedata/connectors/__init__.py +20 -0
  8. recurvedata/connectors/_register.py +46 -0
  9. recurvedata/connectors/base.py +111 -0
  10. recurvedata/connectors/config_schema.py +1575 -0
  11. recurvedata/connectors/connectors/__init__.py +0 -0
  12. recurvedata/connectors/connectors/aliyun_access_key.py +30 -0
  13. recurvedata/connectors/connectors/auth.py +44 -0
  14. recurvedata/connectors/connectors/azure_blob.py +89 -0
  15. recurvedata/connectors/connectors/azure_synapse.py +79 -0
  16. recurvedata/connectors/connectors/bigquery.py +359 -0
  17. recurvedata/connectors/connectors/clickhouse.py +219 -0
  18. recurvedata/connectors/connectors/dingtalk.py +61 -0
  19. recurvedata/connectors/connectors/doris.py +215 -0
  20. recurvedata/connectors/connectors/es.py +62 -0
  21. recurvedata/connectors/connectors/feishu.py +65 -0
  22. recurvedata/connectors/connectors/ftp.py +50 -0
  23. recurvedata/connectors/connectors/generic.py +49 -0
  24. recurvedata/connectors/connectors/google_cloud_storage.py +115 -0
  25. recurvedata/connectors/connectors/google_service_account.py +225 -0
  26. recurvedata/connectors/connectors/hive.py +207 -0
  27. recurvedata/connectors/connectors/impala.py +210 -0
  28. recurvedata/connectors/connectors/jenkins.py +51 -0
  29. recurvedata/connectors/connectors/mail.py +89 -0
  30. recurvedata/connectors/connectors/microsoft_fabric.py +284 -0
  31. recurvedata/connectors/connectors/mongo.py +79 -0
  32. recurvedata/connectors/connectors/mssql.py +131 -0
  33. recurvedata/connectors/connectors/mysql.py +191 -0
  34. recurvedata/connectors/connectors/n8n.py +141 -0
  35. recurvedata/connectors/connectors/oss.py +74 -0
  36. recurvedata/connectors/connectors/owncloud.py +36 -0
  37. recurvedata/connectors/connectors/phoenix.py +36 -0
  38. recurvedata/connectors/connectors/postgres.py +230 -0
  39. recurvedata/connectors/connectors/python.py +50 -0
  40. recurvedata/connectors/connectors/redshift.py +187 -0
  41. recurvedata/connectors/connectors/s3.py +93 -0
  42. recurvedata/connectors/connectors/sftp.py +87 -0
  43. recurvedata/connectors/connectors/slack.py +35 -0
  44. recurvedata/connectors/connectors/spark.py +99 -0
  45. recurvedata/connectors/connectors/starrocks.py +175 -0
  46. recurvedata/connectors/connectors/tencent_cos.py +40 -0
  47. recurvedata/connectors/connectors/tidb.py +49 -0
  48. recurvedata/connectors/const.py +315 -0
  49. recurvedata/connectors/datasource.py +189 -0
  50. recurvedata/connectors/dbapi.py +469 -0
  51. recurvedata/connectors/fs.py +66 -0
  52. recurvedata/connectors/ftp.py +40 -0
  53. recurvedata/connectors/object_store.py +60 -0
  54. recurvedata/connectors/pigeon.py +172 -0
  55. recurvedata/connectors/proxy.py +104 -0
  56. recurvedata/connectors/service.py +223 -0
  57. recurvedata/connectors/utils.py +47 -0
  58. recurvedata/consts.py +49 -0
  59. recurvedata/core/__init__.py +0 -0
  60. recurvedata/core/config.py +46 -0
  61. recurvedata/core/configurable.py +27 -0
  62. recurvedata/core/consts.py +2 -0
  63. recurvedata/core/templating.py +206 -0
  64. recurvedata/core/tracing.py +223 -0
  65. recurvedata/core/transformer.py +186 -0
  66. recurvedata/core/translation.py +91 -0
  67. recurvedata/dbt/client.py +97 -0
  68. recurvedata/dbt/consts.py +99 -0
  69. recurvedata/dbt/cosmos_utils.py +275 -0
  70. recurvedata/dbt/error_codes.py +18 -0
  71. recurvedata/dbt/schemas.py +98 -0
  72. recurvedata/dbt/service.py +451 -0
  73. recurvedata/dbt/utils.py +246 -0
  74. recurvedata/error_codes.py +71 -0
  75. recurvedata/exceptions.py +72 -0
  76. recurvedata/executors/__init__.py +4 -0
  77. recurvedata/executors/cli/__init__.py +7 -0
  78. recurvedata/executors/cli/connector.py +117 -0
  79. recurvedata/executors/cli/dbt.py +118 -0
  80. recurvedata/executors/cli/main.py +82 -0
  81. recurvedata/executors/cli/parameters.py +18 -0
  82. recurvedata/executors/client.py +190 -0
  83. recurvedata/executors/consts.py +50 -0
  84. recurvedata/executors/debug_executor.py +100 -0
  85. recurvedata/executors/executor.py +300 -0
  86. recurvedata/executors/link_executor.py +189 -0
  87. recurvedata/executors/models.py +34 -0
  88. recurvedata/executors/schemas.py +222 -0
  89. recurvedata/executors/service/__init__.py +0 -0
  90. recurvedata/executors/service/connector.py +380 -0
  91. recurvedata/executors/utils.py +172 -0
  92. recurvedata/filestorage/__init__.py +11 -0
  93. recurvedata/filestorage/_factory.py +33 -0
  94. recurvedata/filestorage/backends/__init__.py +0 -0
  95. recurvedata/filestorage/backends/fsspec.py +45 -0
  96. recurvedata/filestorage/backends/local.py +67 -0
  97. recurvedata/filestorage/backends/oss.py +56 -0
  98. recurvedata/filestorage/interface.py +84 -0
  99. recurvedata/operators/__init__.py +10 -0
  100. recurvedata/operators/base.py +28 -0
  101. recurvedata/operators/config.py +21 -0
  102. recurvedata/operators/context.py +255 -0
  103. recurvedata/operators/dbt_operator/__init__.py +2 -0
  104. recurvedata/operators/dbt_operator/model_pipeline_link_operator.py +55 -0
  105. recurvedata/operators/dbt_operator/operator.py +353 -0
  106. recurvedata/operators/link_operator/__init__.py +1 -0
  107. recurvedata/operators/link_operator/operator.py +120 -0
  108. recurvedata/operators/models.py +55 -0
  109. recurvedata/operators/notify_operator/__init__.py +1 -0
  110. recurvedata/operators/notify_operator/operator.py +180 -0
  111. recurvedata/operators/operator.py +119 -0
  112. recurvedata/operators/python_operator/__init__.py +1 -0
  113. recurvedata/operators/python_operator/operator.py +132 -0
  114. recurvedata/operators/sensor_operator/__init__.py +1 -0
  115. recurvedata/operators/sensor_operator/airflow_utils.py +63 -0
  116. recurvedata/operators/sensor_operator/operator.py +172 -0
  117. recurvedata/operators/spark_operator/__init__.py +1 -0
  118. recurvedata/operators/spark_operator/operator.py +200 -0
  119. recurvedata/operators/spark_operator/spark_sample.py +47 -0
  120. recurvedata/operators/sql_operator/__init__.py +1 -0
  121. recurvedata/operators/sql_operator/operator.py +90 -0
  122. recurvedata/operators/task.py +211 -0
  123. recurvedata/operators/transfer_operator/__init__.py +40 -0
  124. recurvedata/operators/transfer_operator/const.py +10 -0
  125. recurvedata/operators/transfer_operator/dump_aliyun_sls.py +82 -0
  126. recurvedata/operators/transfer_operator/dump_sheet_task_base.py +292 -0
  127. recurvedata/operators/transfer_operator/dump_task_cass.py +155 -0
  128. recurvedata/operators/transfer_operator/dump_task_dbapi.py +209 -0
  129. recurvedata/operators/transfer_operator/dump_task_es.py +113 -0
  130. recurvedata/operators/transfer_operator/dump_task_feishu_sheet.py +114 -0
  131. recurvedata/operators/transfer_operator/dump_task_ftp.py +234 -0
  132. recurvedata/operators/transfer_operator/dump_task_google_sheet.py +66 -0
  133. recurvedata/operators/transfer_operator/dump_task_mongodb.py +168 -0
  134. recurvedata/operators/transfer_operator/dump_task_oss.py +285 -0
  135. recurvedata/operators/transfer_operator/dump_task_python.py +212 -0
  136. recurvedata/operators/transfer_operator/dump_task_s3.py +270 -0
  137. recurvedata/operators/transfer_operator/dump_task_sftp.py +229 -0
  138. recurvedata/operators/transfer_operator/load_task_aliyun_oss.py +107 -0
  139. recurvedata/operators/transfer_operator/load_task_azure_blob.py +115 -0
  140. recurvedata/operators/transfer_operator/load_task_azure_synapse.py +90 -0
  141. recurvedata/operators/transfer_operator/load_task_clickhouse.py +167 -0
  142. recurvedata/operators/transfer_operator/load_task_doris.py +164 -0
  143. recurvedata/operators/transfer_operator/load_task_email.py +188 -0
  144. recurvedata/operators/transfer_operator/load_task_es.py +86 -0
  145. recurvedata/operators/transfer_operator/load_task_filebrowser.py +151 -0
  146. recurvedata/operators/transfer_operator/load_task_ftp.py +19 -0
  147. recurvedata/operators/transfer_operator/load_task_google_bigquery.py +90 -0
  148. recurvedata/operators/transfer_operator/load_task_google_cloud_storage.py +127 -0
  149. recurvedata/operators/transfer_operator/load_task_google_sheet.py +130 -0
  150. recurvedata/operators/transfer_operator/load_task_hive.py +158 -0
  151. recurvedata/operators/transfer_operator/load_task_microsoft_fabric.py +105 -0
  152. recurvedata/operators/transfer_operator/load_task_mssql.py +153 -0
  153. recurvedata/operators/transfer_operator/load_task_mysql.py +157 -0
  154. recurvedata/operators/transfer_operator/load_task_owncloud.py +135 -0
  155. recurvedata/operators/transfer_operator/load_task_postgresql.py +109 -0
  156. recurvedata/operators/transfer_operator/load_task_qcloud_cos.py +119 -0
  157. recurvedata/operators/transfer_operator/load_task_recurve_data_prep.py +75 -0
  158. recurvedata/operators/transfer_operator/load_task_redshift.py +95 -0
  159. recurvedata/operators/transfer_operator/load_task_s3.py +150 -0
  160. recurvedata/operators/transfer_operator/load_task_sftp.py +90 -0
  161. recurvedata/operators/transfer_operator/load_task_starrocks.py +169 -0
  162. recurvedata/operators/transfer_operator/load_task_yicrowds.py +97 -0
  163. recurvedata/operators/transfer_operator/mixin.py +31 -0
  164. recurvedata/operators/transfer_operator/operator.py +231 -0
  165. recurvedata/operators/transfer_operator/task.py +223 -0
  166. recurvedata/operators/transfer_operator/utils.py +134 -0
  167. recurvedata/operators/ui.py +80 -0
  168. recurvedata/operators/utils/__init__.py +51 -0
  169. recurvedata/operators/utils/file_factory.py +150 -0
  170. recurvedata/operators/utils/fs.py +10 -0
  171. recurvedata/operators/utils/lineage.py +265 -0
  172. recurvedata/operators/web_init.py +15 -0
  173. recurvedata/pigeon/connector/__init__.py +294 -0
  174. recurvedata/pigeon/connector/_registry.py +17 -0
  175. recurvedata/pigeon/connector/aliyun_oss.py +80 -0
  176. recurvedata/pigeon/connector/awss3.py +123 -0
  177. recurvedata/pigeon/connector/azure_blob.py +176 -0
  178. recurvedata/pigeon/connector/azure_synapse.py +51 -0
  179. recurvedata/pigeon/connector/cass.py +151 -0
  180. recurvedata/pigeon/connector/clickhouse.py +403 -0
  181. recurvedata/pigeon/connector/clickhouse_native.py +351 -0
  182. recurvedata/pigeon/connector/dbapi.py +571 -0
  183. recurvedata/pigeon/connector/doris.py +166 -0
  184. recurvedata/pigeon/connector/es.py +176 -0
  185. recurvedata/pigeon/connector/feishu.py +1135 -0
  186. recurvedata/pigeon/connector/ftp.py +163 -0
  187. recurvedata/pigeon/connector/google_bigquery.py +283 -0
  188. recurvedata/pigeon/connector/google_cloud_storage.py +130 -0
  189. recurvedata/pigeon/connector/hbase_phoenix.py +108 -0
  190. recurvedata/pigeon/connector/hdfs.py +204 -0
  191. recurvedata/pigeon/connector/hive_impala.py +383 -0
  192. recurvedata/pigeon/connector/microsoft_fabric.py +95 -0
  193. recurvedata/pigeon/connector/mongodb.py +56 -0
  194. recurvedata/pigeon/connector/mssql.py +467 -0
  195. recurvedata/pigeon/connector/mysql.py +175 -0
  196. recurvedata/pigeon/connector/owncloud.py +92 -0
  197. recurvedata/pigeon/connector/postgresql.py +267 -0
  198. recurvedata/pigeon/connector/power_bi.py +179 -0
  199. recurvedata/pigeon/connector/qcloud_cos.py +79 -0
  200. recurvedata/pigeon/connector/redshift.py +123 -0
  201. recurvedata/pigeon/connector/sftp.py +73 -0
  202. recurvedata/pigeon/connector/sqlite.py +42 -0
  203. recurvedata/pigeon/connector/starrocks.py +144 -0
  204. recurvedata/pigeon/connector/tableau.py +162 -0
  205. recurvedata/pigeon/const.py +21 -0
  206. recurvedata/pigeon/csv.py +172 -0
  207. recurvedata/pigeon/docs/datasources-example.json +82 -0
  208. recurvedata/pigeon/docs/images/pigeon_design.png +0 -0
  209. recurvedata/pigeon/docs/lightweight-data-sync-solution.md +111 -0
  210. recurvedata/pigeon/dumper/__init__.py +171 -0
  211. recurvedata/pigeon/dumper/aliyun_sls.py +415 -0
  212. recurvedata/pigeon/dumper/base.py +141 -0
  213. recurvedata/pigeon/dumper/cass.py +213 -0
  214. recurvedata/pigeon/dumper/dbapi.py +346 -0
  215. recurvedata/pigeon/dumper/es.py +112 -0
  216. recurvedata/pigeon/dumper/ftp.py +64 -0
  217. recurvedata/pigeon/dumper/mongodb.py +103 -0
  218. recurvedata/pigeon/handler/__init__.py +4 -0
  219. recurvedata/pigeon/handler/base.py +153 -0
  220. recurvedata/pigeon/handler/csv_handler.py +290 -0
  221. recurvedata/pigeon/loader/__init__.py +87 -0
  222. recurvedata/pigeon/loader/base.py +83 -0
  223. recurvedata/pigeon/loader/csv_to_azure_synapse.py +214 -0
  224. recurvedata/pigeon/loader/csv_to_clickhouse.py +152 -0
  225. recurvedata/pigeon/loader/csv_to_doris.py +215 -0
  226. recurvedata/pigeon/loader/csv_to_es.py +51 -0
  227. recurvedata/pigeon/loader/csv_to_google_bigquery.py +169 -0
  228. recurvedata/pigeon/loader/csv_to_hive.py +468 -0
  229. recurvedata/pigeon/loader/csv_to_microsoft_fabric.py +242 -0
  230. recurvedata/pigeon/loader/csv_to_mssql.py +174 -0
  231. recurvedata/pigeon/loader/csv_to_mysql.py +180 -0
  232. recurvedata/pigeon/loader/csv_to_postgresql.py +248 -0
  233. recurvedata/pigeon/loader/csv_to_redshift.py +240 -0
  234. recurvedata/pigeon/loader/csv_to_starrocks.py +233 -0
  235. recurvedata/pigeon/meta.py +116 -0
  236. recurvedata/pigeon/row_factory.py +42 -0
  237. recurvedata/pigeon/schema/__init__.py +124 -0
  238. recurvedata/pigeon/schema/types.py +13 -0
  239. recurvedata/pigeon/sync.py +283 -0
  240. recurvedata/pigeon/transformer.py +146 -0
  241. recurvedata/pigeon/utils/__init__.py +134 -0
  242. recurvedata/pigeon/utils/bloomfilter.py +181 -0
  243. recurvedata/pigeon/utils/date_time.py +323 -0
  244. recurvedata/pigeon/utils/escape.py +15 -0
  245. recurvedata/pigeon/utils/fs.py +266 -0
  246. recurvedata/pigeon/utils/json.py +44 -0
  247. recurvedata/pigeon/utils/keyed_tuple.py +85 -0
  248. recurvedata/pigeon/utils/mp.py +156 -0
  249. recurvedata/pigeon/utils/sql.py +328 -0
  250. recurvedata/pigeon/utils/timing.py +155 -0
  251. recurvedata/provider_manager.py +0 -0
  252. recurvedata/providers/__init__.py +0 -0
  253. recurvedata/providers/dbapi/__init__.py +0 -0
  254. recurvedata/providers/flywheel/__init__.py +0 -0
  255. recurvedata/providers/mysql/__init__.py +0 -0
  256. recurvedata/schedulers/__init__.py +1 -0
  257. recurvedata/schedulers/airflow.py +974 -0
  258. recurvedata/schedulers/airflow_db_process.py +331 -0
  259. recurvedata/schedulers/airflow_operators.py +61 -0
  260. recurvedata/schedulers/airflow_plugin.py +9 -0
  261. recurvedata/schedulers/airflow_trigger_dag_patch.py +117 -0
  262. recurvedata/schedulers/base.py +99 -0
  263. recurvedata/schedulers/cli.py +228 -0
  264. recurvedata/schedulers/client.py +56 -0
  265. recurvedata/schedulers/consts.py +52 -0
  266. recurvedata/schedulers/debug_celery.py +62 -0
  267. recurvedata/schedulers/model.py +63 -0
  268. recurvedata/schedulers/schemas.py +97 -0
  269. recurvedata/schedulers/service.py +20 -0
  270. recurvedata/schedulers/system_dags.py +59 -0
  271. recurvedata/schedulers/task_status.py +279 -0
  272. recurvedata/schedulers/utils.py +73 -0
  273. recurvedata/schema/__init__.py +0 -0
  274. recurvedata/schema/field.py +88 -0
  275. recurvedata/schema/schema.py +55 -0
  276. recurvedata/schema/types.py +17 -0
  277. recurvedata/schema.py +0 -0
  278. recurvedata/server/__init__.py +0 -0
  279. recurvedata/server/app.py +7 -0
  280. recurvedata/server/connector/__init__.py +0 -0
  281. recurvedata/server/connector/api.py +79 -0
  282. recurvedata/server/connector/schemas.py +28 -0
  283. recurvedata/server/data_service/__init__.py +0 -0
  284. recurvedata/server/data_service/api.py +126 -0
  285. recurvedata/server/data_service/client.py +18 -0
  286. recurvedata/server/data_service/consts.py +1 -0
  287. recurvedata/server/data_service/schemas.py +68 -0
  288. recurvedata/server/data_service/service.py +218 -0
  289. recurvedata/server/dbt/__init__.py +0 -0
  290. recurvedata/server/dbt/api.py +116 -0
  291. recurvedata/server/error_code.py +49 -0
  292. recurvedata/server/exceptions.py +19 -0
  293. recurvedata/server/executor/__init__.py +0 -0
  294. recurvedata/server/executor/api.py +37 -0
  295. recurvedata/server/executor/schemas.py +30 -0
  296. recurvedata/server/executor/service.py +220 -0
  297. recurvedata/server/main.py +32 -0
  298. recurvedata/server/schedulers/__init__.py +0 -0
  299. recurvedata/server/schedulers/api.py +252 -0
  300. recurvedata/server/schedulers/schemas.py +50 -0
  301. recurvedata/server/schemas.py +50 -0
  302. recurvedata/utils/__init__.py +15 -0
  303. recurvedata/utils/_typer.py +61 -0
  304. recurvedata/utils/attrdict.py +19 -0
  305. recurvedata/utils/command_helper.py +20 -0
  306. recurvedata/utils/compat.py +12 -0
  307. recurvedata/utils/compression.py +203 -0
  308. recurvedata/utils/crontab.py +42 -0
  309. recurvedata/utils/crypto_util.py +305 -0
  310. recurvedata/utils/dataclass.py +11 -0
  311. recurvedata/utils/date_time.py +464 -0
  312. recurvedata/utils/dispatch.py +114 -0
  313. recurvedata/utils/email_util.py +104 -0
  314. recurvedata/utils/files.py +386 -0
  315. recurvedata/utils/helpers.py +170 -0
  316. recurvedata/utils/httputil.py +117 -0
  317. recurvedata/utils/imports.py +132 -0
  318. recurvedata/utils/json.py +80 -0
  319. recurvedata/utils/log.py +117 -0
  320. recurvedata/utils/log_capture.py +153 -0
  321. recurvedata/utils/mp.py +178 -0
  322. recurvedata/utils/normalizer.py +102 -0
  323. recurvedata/utils/redis_lock.py +474 -0
  324. recurvedata/utils/registry.py +54 -0
  325. recurvedata/utils/shell.py +15 -0
  326. recurvedata/utils/singleton.py +33 -0
  327. recurvedata/utils/sql.py +6 -0
  328. recurvedata/utils/timeout.py +28 -0
  329. recurvedata/utils/tracing.py +14 -0
  330. recurvedata_lib-0.1.487.dist-info/METADATA +605 -0
  331. recurvedata_lib-0.1.487.dist-info/RECORD +333 -0
  332. recurvedata_lib-0.1.487.dist-info/WHEEL +5 -0
  333. recurvedata_lib-0.1.487.dist-info/entry_points.txt +6 -0
@@ -0,0 +1,71 @@
1
+ """
2
+ copied from recurve-server
3
+ """
4
+
5
+ from enum import Enum, EnumMeta
6
+
7
+
8
+ class ErrorCodeMeta(EnumMeta):
9
+ _error_codes = set()
10
+
11
+ def __new__(metacls, clsname, bases, classdict):
12
+ enum_members = {k: v for k, v in classdict.items() if not k.startswith("_")}
13
+ for name, code in enum_members.items():
14
+ if type(code) is not tuple:
15
+ continue
16
+ for error_code in metacls._error_codes:
17
+ if code[0] == error_code[1][0]:
18
+ raise ValueError(f"Error code {code[0]} in {clsname} already exists globally")
19
+ metacls._error_codes.add((name, code))
20
+ return super().__new__(metacls, clsname, bases, classdict)
21
+
22
+ @classmethod
23
+ def error_codes(cls):
24
+ return sorted(list(cls._error_codes), key=lambda x: x[1][0])
25
+
26
+
27
+ class BaseErrorCode(Enum, metaclass=ErrorCodeMeta):
28
+ @property
29
+ def code(self):
30
+ return self.value[0]
31
+
32
+ @property
33
+ def message(self):
34
+ return self.value[1]
35
+
36
+ def to_dict(self):
37
+ return {"code": self.code, "msg": self.message}
38
+
39
+ def exception(self, data: dict | None = None):
40
+ from recurvedata.exceptions import RecurveException
41
+
42
+ return RecurveException(data=data, code=self)
43
+
44
+
45
+ class ErrorCode(BaseErrorCode):
46
+ # 00: General
47
+ UNAUTHORIZED = ("A0001", "Unauthorized")
48
+ PERMISSION_DENIED = ("A0003", "Permission Denied")
49
+ NOT_FOUND = ("A0004", "Not Found")
50
+ ALREADY_EXISTS = ("A0008", "Already Exists")
51
+ INVALID_ARGUMENT = ("A0010", "Invalid Argument")
52
+ MAX_FILE_SIZE_EXCEEDED = ("A0011", "Max File Size Exceeded")
53
+ INVALID_FILE_FORMAT = ("A0012", "Invalid File Format")
54
+ INVALID_READONLY_QUERY = ("A0013", "Query Should be Read Only")
55
+ INTERNAL_SERVER_ERROR = ("B0001", "Internal Server Error")
56
+ NOT_IMPLEMENTED = ("B0002", "Not Implemented")
57
+ FAILED_TO_PARSE_QUERY = ("B0003", "Failed to Parse Query")
58
+
59
+ UNKNOWN_ERROR = ("D0001", "Unknown Error")
60
+
61
+ # agent task
62
+ API_REQUEST_FAILED = ("A1202", "API request failed")
63
+ MAX_RETRIES_EXCEEDED = ("A1203", "max retries exceeded")
64
+ TIMEOUT = ("A1204", "time out")
65
+
66
+ # data service
67
+ DP_FETCH_CONNECTION_FAILED = ("A1401", "DP fetch connection failed")
68
+ PREVIEW_DATA_FAILED = ("A1402", "Preview data failed")
69
+
70
+
71
+ ERR = ErrorCode # shortcut
@@ -0,0 +1,72 @@
1
+ from functools import wraps
2
+
3
+ from recurvedata.error_codes import ERR, BaseErrorCode
4
+
5
+
6
+ class RecurveException(Exception):
7
+ _default_code: ERR = ERR.UNKNOWN_ERROR
8
+
9
+ def __init__(self, data: dict | str = None, code: BaseErrorCode = None):
10
+ self.code = code or self._default_code
11
+ self.data = data
12
+
13
+ def to_dict(self) -> dict:
14
+ return self.code.to_dict() | {"data": self.data}
15
+
16
+
17
+ class InvalidArgument(RecurveException):
18
+ _default_code: ERR = ERR.INVALID_ARGUMENT
19
+
20
+
21
+ class APIError(RecurveException):
22
+ """Raised when an API request fails."""
23
+
24
+ _default_code: ERR = ERR.API_REQUEST_FAILED
25
+
26
+
27
+ class UnauthorizedError(RecurveException):
28
+ """Raised when an unauthorized request is made."""
29
+
30
+ _default_code = ERR.UNAUTHORIZED
31
+
32
+
33
+ class MaxRetriesExceededException(RecurveException):
34
+ """Raised when the maximum number of retries is exceeded."""
35
+
36
+ _default_code: ERR = ERR.MAX_RETRIES_EXCEEDED
37
+
38
+
39
+ class TimeoutException(RecurveException):
40
+ """Raised when a timeout occurs."""
41
+
42
+ _default_code: ERR = ERR.TIMEOUT
43
+
44
+
45
+ class WrapRecurveException(RecurveException):
46
+ """
47
+ raised in wrap_error function
48
+ """
49
+
50
+ def __init__(self, code: ERR, exception: Exception, data: dict | str = None):
51
+ super().__init__(data=data, code=code)
52
+ self.exception: Exception = exception
53
+
54
+ def to_dict(self) -> dict:
55
+ return self.code.to_dict() | {"data": self.data, "exception": str(self.exception)}
56
+
57
+
58
+ def wrap_error(err_code: ERR):
59
+ def decorator(func):
60
+ @wraps(func)
61
+ def wrapper(*args, **kwargs):
62
+ try:
63
+ return func(*args, **kwargs)
64
+ except RecurveException:
65
+ raise
66
+ except Exception as e:
67
+ wrapped_error = WrapRecurveException(err_code, e)
68
+ raise wrapped_error
69
+
70
+ return wrapper
71
+
72
+ return decorator
@@ -0,0 +1,4 @@
1
+ from recurvedata.executors.cli import cli
2
+ from recurvedata.executors.debug_executor import DebugExecutor
3
+ from recurvedata.executors.executor import Executor
4
+ from recurvedata.executors.link_executor import LinkExecutor
@@ -0,0 +1,7 @@
1
+ from recurvedata.executors.utils import patch_pandas_mysql_connector_cext_missing
2
+
3
+ patch_pandas_mysql_connector_cext_missing()
4
+
5
+ from recurvedata.executors.cli.main import cli # noqa: E402
6
+
7
+ __all__ = ["cli"]
@@ -0,0 +1,117 @@
1
+ import json
2
+
3
+ import typer
4
+
5
+ from recurvedata.executors.service.connector import ConnectionService
6
+ from recurvedata.executors.utils import run_with_result_handling
7
+ from recurvedata.utils import init_logging
8
+ from recurvedata.utils._typer import RecurveTyper
9
+
10
+ cli = RecurveTyper()
11
+
12
+
13
+ @cli.callback()
14
+ def init():
15
+ init_logging()
16
+
17
+
18
+ @cli.command()
19
+ def test_connection(
20
+ connection_type: str = typer.Option(
21
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
22
+ ),
23
+ config: str = typer.Option(..., "--config", help="JSON string of connection configuration"),
24
+ result_filename: str = typer.Option(None, "--result-filename", help="Filename to save the json result"),
25
+ timeout: int = typer.Option(30, "--timeout", help="Timeout for the connection test in seconds"),
26
+ ):
27
+ """
28
+ Test if a connection is valid
29
+ """
30
+ connection_config = json.loads(config)
31
+ run_with_result_handling(
32
+ ConnectionService.test_connection, timeout, result_filename, connection_type, connection_config
33
+ )
34
+
35
+
36
+ @cli.command()
37
+ async def list_databases(
38
+ connection_type: str = typer.Option(
39
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
40
+ ),
41
+ config: str = typer.Option(..., "--config", help="JSON string of connection configuration"),
42
+ result_filename: str = typer.Option(None, "--result-filename", help="Filename to save the json result"),
43
+ ):
44
+ """
45
+ List databases for a given connection
46
+ """
47
+ connection_config = json.loads(config)
48
+ run_with_result_handling(
49
+ ConnectionService.list_databases,
50
+ result_filename=result_filename,
51
+ connection_type=connection_type,
52
+ connection_config=connection_config,
53
+ )
54
+
55
+
56
+ @cli.command()
57
+ async def list_tables(
58
+ connection_type: str = typer.Option(
59
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
60
+ ),
61
+ config: str = typer.Option(..., "--config", help="JSON string of connection configuration"),
62
+ database: str = typer.Option(..., "--database", help="Database name"),
63
+ result_filename: str = typer.Option(None, "--result-filename", help="Filename to save the json result"),
64
+ ):
65
+ """List tables for a given connection and database"""
66
+ connection_config = json.loads(config)
67
+ run_with_result_handling(
68
+ ConnectionService.list_tables,
69
+ result_filename=result_filename,
70
+ connection_type=connection_type,
71
+ connection_config=connection_config,
72
+ database=database,
73
+ )
74
+
75
+
76
+ @cli.command()
77
+ async def list_columns(
78
+ connection_type: str = typer.Option(
79
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
80
+ ),
81
+ config: str = typer.Option(..., "--config", help="JSON string of connection configuration"),
82
+ database: str = typer.Option(..., "--database", help="Database name"),
83
+ table: str = typer.Option(..., "--table", help="Table name"),
84
+ result_filename: str = typer.Option(None, "--result-filename", help="Filename to save the json result"),
85
+ ):
86
+ """List columns for a given connection, database and table"""
87
+ connection_config = json.loads(config)
88
+ run_with_result_handling(
89
+ ConnectionService.list_columns,
90
+ result_filename=result_filename,
91
+ connection_type=connection_type,
92
+ connection_config=connection_config,
93
+ database_name=database,
94
+ table_name=table,
95
+ )
96
+
97
+
98
+ @cli.command()
99
+ async def list_full_databases(
100
+ connection_type: str = typer.Option(
101
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
102
+ ),
103
+ config: str = typer.Option(..., "--config", help="JSON string of connection configuration"),
104
+ result_filename: str = typer.Option(None, "--result-filename", help="Filename to save the json result"),
105
+ ):
106
+ """List full databases for a given connection"""
107
+ connection_config = json.loads(config)
108
+ run_with_result_handling(
109
+ ConnectionService.list_full_databases,
110
+ result_filename=result_filename,
111
+ connection_type=connection_type,
112
+ connection_config=connection_config,
113
+ )
114
+
115
+
116
+ if __name__ == "__main__":
117
+ cli()
@@ -0,0 +1,118 @@
1
+ import logging
2
+
3
+ from recurvedata.client.server_client import ServerDbtClient
4
+ from recurvedata.core.tracing import Tracing
5
+ from recurvedata.executors.cli import parameters as param
6
+ from recurvedata.executors.utils import run_with_result_handling
7
+ from recurvedata.utils import init_logging
8
+ from recurvedata.utils._typer import RecurveTyper
9
+ from recurvedata.utils.sql import trim_replace_special_character
10
+
11
+ dbt_tracer = Tracing()
12
+ cli = RecurveTyper()
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ @cli.callback()
17
+ def init():
18
+ from recurvedata.core.tracing import Tracing
19
+
20
+ if not Tracing.is_instantiated():
21
+ from recurvedata.utils.tracing import create_dp_tracer
22
+
23
+ create_dp_tracer("recurve-lib-dbt")
24
+ init_logging()
25
+
26
+
27
+ @cli.command()
28
+ @dbt_tracer.create_span(context_payload_name="tracing_context")
29
+ async def compile(
30
+ project_id: int = param.project_id,
31
+ alias: str = param.alias,
32
+ sql: str = param.sql,
33
+ tracing_context: str = param.tracing_context,
34
+ force_regenerate_dir: bool = param.force_regenerate_dir,
35
+ result_filename: str = param.result_filename,
36
+ ):
37
+ try:
38
+ _compile_using_server(project_id, alias, sql, force_regenerate_dir, result_filename)
39
+ except Exception as e:
40
+ logger.exception(f"_compile_using_server failed: {e}, try _compile_using_service")
41
+ _compile_using_service(project_id, alias, sql, force_regenerate_dir, result_filename)
42
+
43
+
44
+ @dbt_tracer.create_span()
45
+ def _compile_using_server(project_id: int, alias: str, sql: str, force_regenerate_dir: bool, result_filename: str):
46
+ client = ServerDbtClient()
47
+ res = client.compile(project_id, sql, alias, force_regenerate_dir)
48
+ if result_filename:
49
+ res.model_dump_json_file(result_filename)
50
+
51
+
52
+ @dbt_tracer.create_span()
53
+ def _compile_using_service(
54
+ project_id: int, alias: str, sql: str, force_regenerate_dir: bool, result_filename: str = param.result_filename
55
+ ):
56
+ from recurvedata.dbt.service import DbtService
57
+
58
+ sql = trim_replace_special_character(sql)
59
+ service = DbtService(
60
+ project_id=project_id,
61
+ project_connection_name=alias,
62
+ force_regenerate_dir=force_regenerate_dir,
63
+ need_fetch_variable=True,
64
+ )
65
+ run_with_result_handling(service.compile, inline_sql=sql, result_filename=result_filename)
66
+
67
+
68
+ @cli.command()
69
+ async def preview(
70
+ project_id: int = param.project_id,
71
+ alias: str = param.alias,
72
+ sql: str = param.sql,
73
+ limit: int = param.limit,
74
+ no_data: bool = param.no_data,
75
+ force_regenerate_dir: bool = param.force_regenerate_dir,
76
+ result_filename: str = param.result_filename,
77
+ ):
78
+ try:
79
+ _preview_using_server(project_id, alias, sql, limit, force_regenerate_dir, result_filename) # todo: add no_data
80
+ except Exception as e:
81
+ logger.exception(f"_preview_using_server failed: {e}, try _preview_using_service")
82
+ _preview_using_service(project_id, alias, sql, limit, force_regenerate_dir, no_data, result_filename)
83
+
84
+
85
+ def _preview_using_server(
86
+ project_id: int, alias: str, sql: str, limit: int, force_regenerate_dir: bool, result_filename: str
87
+ ):
88
+ client = ServerDbtClient()
89
+ res = client.preview(project_id, sql, alias, limit, force_regenerate_dir)
90
+ if result_filename:
91
+ res.model_dump_json_file(result_filename)
92
+
93
+
94
+ def _preview_using_service(
95
+ project_id: int,
96
+ alias: str,
97
+ sql: str,
98
+ limit: int,
99
+ force_regenerate_dir: bool,
100
+ no_data: bool,
101
+ result_filename: str = param.result_filename,
102
+ ):
103
+ from recurvedata.dbt.service import DbtService
104
+
105
+ sql = trim_replace_special_character(sql, strip_sufix=True)
106
+ service = DbtService(
107
+ project_id=project_id,
108
+ project_connection_name=alias,
109
+ force_regenerate_dir=force_regenerate_dir,
110
+ need_fetch_variable=True,
111
+ )
112
+ run_with_result_handling(
113
+ service.preview, inline_sql=sql, limit=limit, result_filename=result_filename, no_data=no_data
114
+ )
115
+
116
+
117
+ if __name__ == "__main__":
118
+ cli()
@@ -0,0 +1,82 @@
1
+ import typer
2
+
3
+ from recurvedata.consts import ScheduleType
4
+ from recurvedata.executors.cli.connector import cli as connector_cli
5
+ from recurvedata.executors.cli.dbt import cli as dbt_cli
6
+ from recurvedata.executors.debug_executor import DebugExecutor
7
+ from recurvedata.executors.executor import Executor
8
+ from recurvedata.utils import init_logging
9
+ from recurvedata.utils._typer import RecurveTyper
10
+ from recurvedata.utils.date_time import astimezone
11
+
12
+ cli = RecurveTyper()
13
+ cli.add_typer(connector_cli, name="connector")
14
+ cli.add_typer(dbt_cli, name="dbt")
15
+
16
+
17
+ @cli.callback()
18
+ def init():
19
+ from recurvedata.core.tracing import Tracing
20
+
21
+ if not Tracing.is_instantiated():
22
+ from recurvedata.utils.tracing import create_dp_tracer
23
+
24
+ create_dp_tracer("recurve-lib")
25
+ init_logging()
26
+
27
+
28
+ @cli.command()
29
+ def execute(
30
+ dag_slug: str = typer.Option(..., "--dag_slug", help="Unique identifier for the DAG"),
31
+ node_slug: str = typer.Option(..., "--node_slug", help="Unique identifier for the node"),
32
+ execution_date: str = typer.Option(..., "--execution_date", help="Date/time of execution"),
33
+ # RecurveLink settings
34
+ link_workflow_id: int = typer.Option(None, "--link_workflow_id", help="ID of linked workflow"),
35
+ link_node_id: int = typer.Option(None, "--link_node_id", help="ID of linked node"),
36
+ is_link_workflow: bool = typer.Option(False, "--is_link_workflow", help="Whether this is a linked workflow"),
37
+ ):
38
+ """
39
+ Execute a specific node in a DAG at the given execution date.
40
+
41
+ Optionally configure workflow linking settings.
42
+ """
43
+ # Initialize and run the executor
44
+ executor = Executor(dag_slug=dag_slug, node_slug=node_slug, execution_date=execution_date)
45
+ executor.set_link_settings(
46
+ link_workflow_id=link_workflow_id, link_node_id=link_node_id, is_link_workflow=is_link_workflow
47
+ )
48
+ executor.run()
49
+
50
+
51
+ @cli.command()
52
+ def debug(
53
+ workflow_id: int = typer.Option(..., "--workflow_id", help="ID of the workflow to debug"),
54
+ node_key: str = typer.Option(..., "--node_key", help="Key identifier of the node to debug"),
55
+ schedule_type: ScheduleType = typer.Option(..., "--schedule_type", help="Type of schedule"),
56
+ schedule_interval: str = typer.Option(..., "--schedule_interval", help="Schedule interval specification"),
57
+ execution_date: str = typer.Option(..., "--execution_date", help="Execution timestamp"),
58
+ timezone: str = typer.Option(..., "--timezone", help="Timezone for execution"),
59
+ celery_task_id: str = typer.Option(..., "--celery_task_id", help="Celery task ID for tracking"),
60
+ ):
61
+ """
62
+ Debug a workflow node by executing it in isolation.
63
+
64
+ This command allows debugging a specific node from a workflow by running it independently.
65
+ The execution context (schedule, timing, etc) can be controlled via the parameters.
66
+ """
67
+ # execution_date = ensure_datetime(execution_date).replace(tzinfo=ensure_tz(timezone))
68
+ execution_date = astimezone(execution_date, timezone)
69
+ executor = DebugExecutor(
70
+ workflow_id=workflow_id,
71
+ node_key=node_key,
72
+ schedule_type=schedule_type,
73
+ schedule_interval=schedule_interval,
74
+ execution_date=execution_date,
75
+ timezone=timezone,
76
+ celery_task_id=celery_task_id,
77
+ )
78
+ executor.run()
79
+
80
+
81
+ if __name__ == "__main__":
82
+ cli()
@@ -0,0 +1,18 @@
1
+ from typer import Option
2
+
3
+ connection_type = Option(
4
+ ..., "--connection-type", help="Type of the connection (e.g., 'mysql', 'postgresql', 'snowflake')"
5
+ )
6
+ connection_config = Option(..., "--config", help="JSON string of connection configuration")
7
+ result_filename = Option(None, "--result-filename", help="Filename to save the json result")
8
+ timeout = Option(30, "--timeout", help="Timeout for the connection test in seconds")
9
+ database = Option(..., "--database", help="Database name")
10
+ table = Option(..., "--table", help="Table name")
11
+ project_id = Option(..., "--project-id", help="Project ID")
12
+ connection_name = Option(..., "--connection-name", help="Connection Name")
13
+ alias = Option(..., "--alias", help="Alias") # project connection name
14
+ limit = Option(100, "--limit")
15
+ sql = Option(..., "--sql")
16
+ tracing_context = Option(None, "--tracing-context", help="Tracing context")
17
+ force_regenerate_dir = Option(False, "--force-regenerate-dir", help="Whether to force regenerate dbt project")
18
+ no_data = Option(False, "--no-data", help="Whether to include data in preview result")
@@ -0,0 +1,190 @@
1
+ import datetime
2
+ from typing import Any, Optional
3
+
4
+ from recurvedata.client import Client
5
+ from recurvedata.config import RECURVE_EXECUTOR_PYENV_NAME
6
+ from recurvedata.consts import ETLExecutionStatus, Operator
7
+ from recurvedata.executors.schemas import (
8
+ ConnectionItem,
9
+ DebugEnd,
10
+ DebugLogRecord,
11
+ DebugStart,
12
+ JobNodeItem,
13
+ TaskInstanceEnd,
14
+ TaskInstanceStart,
15
+ TaskInstanceStartResponse,
16
+ TaskLogRecord,
17
+ WorkflowNodeItem,
18
+ )
19
+ from recurvedata.utils import get_env_id
20
+
21
+
22
+ class ExecutorClient(Client):
23
+ def get_node(self, job_id: int, node_id: int) -> JobNodeItem:
24
+ params = {
25
+ "env_id": get_env_id(),
26
+ "job_id": job_id,
27
+ }
28
+
29
+ return self.request(
30
+ "GET", path=f"/api/executor/node/{node_id}", response_model_class=JobNodeItem, params=params
31
+ )
32
+
33
+ def get_connection(self, project_id: int, connection_name: str) -> ConnectionItem:
34
+ params = {
35
+ "env_id": get_env_id(),
36
+ "name": connection_name,
37
+ "project_id": project_id,
38
+ }
39
+ return self.request("GET", path="/api/executor/connection", response_model_class=ConnectionItem, params=params)
40
+
41
+ def task_instance_start(
42
+ self,
43
+ job_id: int,
44
+ node_id: int,
45
+ operator: Operator,
46
+ task: str,
47
+ execution_date: datetime.datetime,
48
+ rendered_config: dict,
49
+ start_time: datetime.datetime,
50
+ hostname: Optional[str],
51
+ pid: Optional[int],
52
+ ):
53
+ payload = TaskInstanceStart(
54
+ job_id=job_id,
55
+ node_id=node_id,
56
+ task=task,
57
+ operator=operator,
58
+ execution_date=execution_date.isoformat(),
59
+ rendered_config=rendered_config,
60
+ start_time=start_time.isoformat(),
61
+ hostname=hostname,
62
+ pid=pid,
63
+ ).model_dump()
64
+ params = {
65
+ "env_id": get_env_id(),
66
+ }
67
+ return self.request(
68
+ "POST",
69
+ path="/api/executor/start",
70
+ response_model_class=TaskInstanceStartResponse,
71
+ params=params,
72
+ json=payload,
73
+ )
74
+
75
+ def task_instance_end(
76
+ self,
77
+ meta: Any,
78
+ traceback: Any,
79
+ status: ETLExecutionStatus,
80
+ end_time: datetime.datetime,
81
+ execution_date: datetime.datetime,
82
+ job_id: int,
83
+ node_id: int,
84
+ run_id: str,
85
+ current_retry_number: Optional[int],
86
+ max_retry_number: Optional[int],
87
+ link_workflow_id: Optional[int],
88
+ link_node_id: Optional[int],
89
+ data_interval_end: Optional[str],
90
+ **kwargs,
91
+ ):
92
+ payload = TaskInstanceEnd(
93
+ job_id=job_id,
94
+ node_id=node_id,
95
+ run_id=run_id,
96
+ end_time=end_time.isoformat(),
97
+ execution_date=execution_date.isoformat(),
98
+ meta=meta,
99
+ traceback=traceback,
100
+ status=status,
101
+ current_retry_number=current_retry_number,
102
+ max_retry_number=max_retry_number,
103
+ link_workflow_id=link_workflow_id,
104
+ link_node_id=link_node_id,
105
+ data_interval_end=data_interval_end,
106
+ ).model_dump()
107
+ params = {
108
+ "env_id": get_env_id(),
109
+ }
110
+ return self.request(
111
+ "POST",
112
+ path="/api/executor/end",
113
+ params=params,
114
+ json=payload,
115
+ timeout=10, # todo: backend process time is slow, dispatch still wait
116
+ )
117
+
118
+ def get_workflow_node(self, workflow_id: int, node_id: int) -> WorkflowNodeItem:
119
+ params = {
120
+ "env_id": get_env_id(),
121
+ "workflow_id": workflow_id,
122
+ }
123
+ return self.request(
124
+ "GET", path=f"/api/executor/workflow_node/{node_id}", response_model_class=WorkflowNodeItem, params=params
125
+ )
126
+
127
+ def send_back_logs(self, record: TaskLogRecord):
128
+ params = {
129
+ "env_id": get_env_id(),
130
+ }
131
+ return self.request("POST", path="/api/executor/logs", params=params, json={"records": [record.model_dump()]})
132
+
133
+ def send_back_debug_logs(self, record: DebugLogRecord):
134
+ params = {
135
+ "env_id": get_env_id(),
136
+ }
137
+ return self.request(
138
+ "POST", path="/api/executor/debug_logs", params=params, json={"records": [record.model_dump()]}
139
+ )
140
+
141
+ def get_debug_node(self, workflow_id: int, node_key: str) -> WorkflowNodeItem:
142
+ params = {
143
+ "env_id": get_env_id(),
144
+ "node_key": node_key,
145
+ }
146
+ return self.request(
147
+ "GET", path=f"/api/executor/debug_node/{workflow_id}", response_model_class=WorkflowNodeItem, params=params
148
+ )
149
+
150
+ def debug_start(self, workflow_id: int, node_key: str, celery_task_id: str):
151
+ payload = DebugStart(workflow_id=workflow_id, node_key=node_key, celery_task_id=celery_task_id).model_dump()
152
+ params = {
153
+ "env_id": get_env_id(),
154
+ }
155
+ return self.request(
156
+ "POST",
157
+ path="/api/executor/debug_start",
158
+ params=params,
159
+ json=payload,
160
+ )
161
+
162
+ def debug_end(self, workflow_id: int, node_key: str, celery_task_id: str, is_success: bool):
163
+ payload = DebugEnd(
164
+ workflow_id=workflow_id, node_key=node_key, celery_task_id=celery_task_id, is_success=is_success
165
+ ).model_dump()
166
+ params = {
167
+ "env_id": get_env_id(),
168
+ }
169
+ return self.request(
170
+ "POST",
171
+ path="/api/executor/debug_end",
172
+ params=params,
173
+ json=payload,
174
+ timeout=10,
175
+ )
176
+
177
+ def get_py_conn_configs(
178
+ self,
179
+ conn_type: str = "python",
180
+ pyenv_name: str = RECURVE_EXECUTOR_PYENV_NAME,
181
+ project_conn_name: str = "",
182
+ project_id: int = 0,
183
+ ) -> dict:
184
+ params = {
185
+ "conn_type": conn_type,
186
+ "pyenv_name": pyenv_name,
187
+ "project_conn_name": project_conn_name,
188
+ "project_id": project_id,
189
+ }
190
+ return self.request("GET", path="/api/executor/python-conn-configs", params=params)