recurvedata-lib 0.1.490__py2.py3-none-any.whl → 0.1.492__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.

@@ -1 +1 @@
1
- __version__ = "0.1.490"
1
+ __version__ = "0.1.492"
@@ -1,6 +1,8 @@
1
1
  import json
2
2
  import logging
3
+ import os
3
4
  import time
5
+ from pathlib import Path
4
6
  from typing import Any, TypeVar, overload
5
7
 
6
8
  import httpx
@@ -24,13 +26,90 @@ class Client:
24
26
  config = AgentConfig.load()
25
27
  self.set_config(config)
26
28
 
29
+ @property
30
+ def is_offline_mode(self) -> bool:
31
+ """Check if offline mode is enabled via environment variable"""
32
+ return os.environ.get("RECURVE_OFFLINE_MODE", "").lower() in ("true", "1")
33
+
34
+ @property
35
+ def offline_data_path(self) -> Path:
36
+ """Get the offline data directory path"""
37
+ offline_path = os.environ.get("RECURVE_OFFLINE_DATA_PATH", "offline_data")
38
+ return Path(offline_path)
39
+
27
40
  def set_config(self, config: AgentConfig):
28
41
  self._config = config
29
- self._client = httpx.Client(
30
- base_url=config.server_url,
31
- timeout=config.request_timeout,
32
- headers={"User-Agent": f"RecurveLib/{__version__}"},
33
- )
42
+ # Only create HTTP client if not in offline mode
43
+ if not self.is_offline_mode:
44
+ self._client = httpx.Client(
45
+ base_url=config.server_url,
46
+ timeout=config.request_timeout,
47
+ headers={"User-Agent": f"RecurveLib/{__version__}"},
48
+ )
49
+ else:
50
+ self._client = None
51
+
52
+ def _resolve_offline_file_path(self, path: str, **kwargs) -> Path:
53
+ """Convert API path to local file path with parameterized support"""
54
+ # Remove leading /api/ prefix: /api/executor/connection -> executor/connection
55
+ if path.startswith("/api/"):
56
+ clean_path = path[5:] # Remove "/api/" prefix
57
+ else:
58
+ clean_path = path.lstrip("/")
59
+
60
+ # Extract parameters from kwargs
61
+ params = kwargs.get("params", {})
62
+
63
+ # CORE OPERATOR APIs with parameter-based file structure:
64
+
65
+ # 1. get_connection() API - parameterized by project_id + connection_name
66
+ if clean_path == "executor/connection":
67
+ project_id = params.get("project_id", "0")
68
+ connection_name = params.get("name", "default")
69
+ return self.offline_data_path / "executor/connection" / str(project_id) / f"{connection_name}.json"
70
+
71
+ # 2. get_py_conn_configs() API - parameterized by project_id + project_connection_name
72
+ elif clean_path == "executor/python-conn-configs":
73
+ project_id = params.get("project_id", "0")
74
+ # Python configs use project_connection_name as the key (fallback to other param names for compatibility)
75
+ # Handle empty strings properly - treat them as None/missing
76
+ project_connection_name = (params.get("project_connection_name") or
77
+ params.get("project_conn_name") or
78
+ params.get("pyenv_name") or
79
+ "default")
80
+ return self.offline_data_path / "executor/python-conn-configs" / str(project_id) / f"{project_connection_name}.json"
81
+
82
+ # For any other APIs, raise error do not support offline mode
83
+ raise APIError(f"Offline mode: {path} is not supported")
84
+
85
+ def _read_offline_data(self, method: str, path: str, response_model_class: type[ResponseModelType] | None = None, **kwargs) -> Any:
86
+ """Read API response from local JSON file"""
87
+ file_path = self._resolve_offline_file_path(path, **kwargs)
88
+
89
+ logger.info(f"🔌 Offline mode: Reading from {file_path}")
90
+
91
+ try:
92
+ if not file_path.exists():
93
+ logger.error(f"Offline data file not found: {file_path}")
94
+ raise APIError(f"Offline mode: Required data file not found: {file_path}")
95
+
96
+ with open(file_path, 'r') as f:
97
+ resp_content = json.load(f)
98
+
99
+ # Handle response model validation (same logic as online mode)
100
+ if response_model_class is not None:
101
+ if "code" in resp_content:
102
+ return response_model_class.model_validate(resp_content["data"])
103
+ return response_model_class.model_validate(resp_content)
104
+
105
+ return resp_content.get("data", resp_content)
106
+
107
+ except APIError:
108
+ raise # Re-raise APIError as-is
109
+ except Exception as e:
110
+ logger.error(f"Error reading offline data from {file_path}: {e}")
111
+ raise APIError(f"Offline mode: Failed to read data file {file_path}: {e}")
112
+
34
113
 
35
114
  @overload
36
115
  def request(self, method: str, path: str, response_model_class: None = None, retries: int = 3, **kwargs) -> Any:
@@ -56,6 +135,11 @@ class Client:
56
135
  retries: int = 1,
57
136
  **kwargs,
58
137
  ) -> Any:
138
+ # Route to offline mode if enabled
139
+ if self.is_offline_mode:
140
+ return self._read_offline_data(method, path, response_model_class, **kwargs)
141
+
142
+ # Original online mode logic
59
143
  self.prepare_header(kwargs)
60
144
  pre_err: httpx.HTTPStatusError | None = None
61
145
  for attempt in range(retries):
@@ -143,8 +227,11 @@ class Client:
143
227
  )
144
228
 
145
229
  def close(self):
146
- self._client.close()
230
+ if self._client:
231
+ self._client.close()
147
232
 
148
233
  @property
149
234
  def base_url(self) -> str:
150
- return str(self._client.base_url)
235
+ if self.is_offline_mode:
236
+ return "offline://localhost"
237
+ return str(self._client.base_url) if self._client else ""
@@ -1073,6 +1073,84 @@ ALL_CONFIG_SCHEMA_DCT = {
1073
1073
  "group": ["destination"],
1074
1074
  "test_required": True,
1075
1075
  },
1076
+ "recurvedata.connectors.connectors.n8n": {
1077
+ "type": "n8n",
1078
+ "ui_type": "n8n",
1079
+ "category": ["others"],
1080
+ "config_schema": {
1081
+ "type": "object",
1082
+ "properties": {
1083
+ "url": {
1084
+ "type": "string",
1085
+ "title": _l("API Address"),
1086
+ "description": _l("The URL of the n8n API, e.g. https://localhost:5678/api/v1"),
1087
+ },
1088
+ "api_key": {"type": "string", "title": _l("API KEY")},
1089
+ "timeout": {
1090
+ "type": "number",
1091
+ "title": _l("Timeout"),
1092
+ "description": _l("The timeout of the n8n API, e.g. 60"),
1093
+ "default": 60,
1094
+ },
1095
+ "webhook_credential": {
1096
+ "type": "object",
1097
+ "title": _l("Webhook Trigger Node Credential"),
1098
+ "description": _l("The credential of the n8n webhook trigger node"),
1099
+ "properties": {
1100
+ "credential_type": {
1101
+ "type": "string",
1102
+ "title": _l("Credential Type"),
1103
+ "enum": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
1104
+ "enumNames": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
1105
+ "default": "None",
1106
+ },
1107
+ "basic_auth": {
1108
+ "ui:hidden": '{{parentFormData.credential_type !== "Basic Auth"}}',
1109
+ "type": "object",
1110
+ "title": _l("Basic Auth"),
1111
+ "description": _l("The basic auth of the n8n webhook trigger node"),
1112
+ "properties": {
1113
+ "username": {"type": "string", "title": _l("Username")},
1114
+ "password": {"type": "string", "title": _l("Password")},
1115
+ },
1116
+ },
1117
+ "header_auth": {
1118
+ "ui:hidden": '{{parentFormData.credential_type !== "Header Auth"}}',
1119
+ "type": "object",
1120
+ "title": _l("Header Auth"),
1121
+ "description": _l("The header auth of the n8n webhook trigger node"),
1122
+ "properties": {
1123
+ "header_name": {"type": "string", "title": _l("Header Name")},
1124
+ "header_value": {"type": "string", "title": _l("Header Value")},
1125
+ },
1126
+ },
1127
+ "jwt_auth": {
1128
+ "ui:hidden": '{{parentFormData.credential_type !== "JWT Auth"}}',
1129
+ "type": "object",
1130
+ "title": _l("JWT Auth"),
1131
+ "description": _l("The jwt auth of the n8n webhook trigger node"),
1132
+ "properties": {
1133
+ "jwt_token": {"type": "string", "title": _l("JWT Token")},
1134
+ },
1135
+ },
1136
+ },
1137
+ "order": ["credential_type", "basic_auth", "header_auth", "jwt_auth"],
1138
+ },
1139
+ "proxies": HTTP_PROXY_CONFIG_SCHEMA["proxies"],
1140
+ },
1141
+ "order": ["url", "api_key", "timeout", "webhook_credential", "proxies"],
1142
+ "required": ["url", "api_key"],
1143
+ "secret": [
1144
+ "api_key",
1145
+ "webhook_credential.basic_auth.password",
1146
+ "webhook_credential.header_auth.header_value",
1147
+ "webhook_credential.jwt_auth.jwt_token",
1148
+ ],
1149
+ },
1150
+ "enabled": True,
1151
+ "group": ["destination"],
1152
+ "test_required": True,
1153
+ },
1076
1154
  "recurvedata.connectors.connectors.oss": {
1077
1155
  "type": "oss",
1078
1156
  "ui_type": "Aliyun OSS",
@@ -0,0 +1,141 @@
1
+ import base64
2
+ from contextlib import contextmanager
3
+ from urllib.parse import urlparse
4
+
5
+ import httpx
6
+
7
+ from recurvedata.connectors._register import register_connector_class
8
+ from recurvedata.connectors.base import RecurveConnectorBase
9
+ from recurvedata.connectors.proxy import HTTP_PROXY_CONFIG_SCHEMA, HttpProxyMixin
10
+ from recurvedata.consts import ConnectorGroup
11
+ from recurvedata.core.translation import _l
12
+
13
+ CONNECTION_TYPE = "n8n"
14
+ UI_CONNECTION_TYPE = "n8n"
15
+
16
+
17
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
18
+ class N8N(HttpProxyMixin, RecurveConnectorBase):
19
+ connection_type = CONNECTION_TYPE
20
+ ui_connection_type = UI_CONNECTION_TYPE
21
+ group = [ConnectorGroup.DESTINATION]
22
+
23
+ config_schema = {
24
+ "type": "object",
25
+ "properties": {
26
+ "url": {
27
+ "type": "string",
28
+ "title": _l("API Address"),
29
+ "description": _l("The URL of the n8n API, e.g. https://localhost:5678/api/v1"),
30
+ },
31
+ "api_key": {"type": "string", "title": _l("API KEY")},
32
+ "timeout": {
33
+ "type": "number",
34
+ "title": _l("Timeout"),
35
+ "description": _l("The timeout of the n8n API, e.g. 60"),
36
+ "default": 60,
37
+ },
38
+ "webhook_credential": {
39
+ "type": "object",
40
+ "title": _l("Webhook Trigger Node Credential"),
41
+ "description": _l("The credential of the n8n webhook trigger node"),
42
+ "properties": {
43
+ "credential_type": {
44
+ "type": "string",
45
+ "title": _l("Credential Type"),
46
+ "enum": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
47
+ "enumNames": ["Basic Auth", "Header Auth", "JWT Auth", "None"],
48
+ "default": "None",
49
+ },
50
+ "basic_auth": {
51
+ "ui:hidden": '{{parentFormData.credential_type !== "Basic Auth"}}',
52
+ "type": "object",
53
+ "title": _l("Basic Auth"),
54
+ "description": _l("The basic auth of the n8n webhook trigger node"),
55
+ "properties": {
56
+ "username": {"type": "string", "title": _l("Username")},
57
+ "password": {"type": "string", "title": _l("Password")},
58
+ },
59
+ },
60
+ "header_auth": {
61
+ "ui:hidden": '{{parentFormData.credential_type !== "Header Auth"}}',
62
+ "type": "object",
63
+ "title": _l("Header Auth"),
64
+ "description": _l("The header auth of the n8n webhook trigger node"),
65
+ "properties": {
66
+ "header_name": {"type": "string", "title": _l("Header Name")},
67
+ "header_value": {"type": "string", "title": _l("Header Value")},
68
+ },
69
+ },
70
+ "jwt_auth": {
71
+ "ui:hidden": '{{parentFormData.credential_type !== "JWT Auth"}}',
72
+ "type": "object",
73
+ "title": _l("JWT Auth"),
74
+ "description": _l("The jwt auth of the n8n webhook trigger node"),
75
+ "properties": {
76
+ "jwt_token": {"type": "string", "title": _l("JWT Token")},
77
+ },
78
+ },
79
+ },
80
+ "order": ["credential_type", "basic_auth", "header_auth", "jwt_auth"],
81
+ },
82
+ "proxies": HTTP_PROXY_CONFIG_SCHEMA["proxies"],
83
+ },
84
+ "order": ["url", "api_key", "timeout", "webhook_credential", "proxies"],
85
+ "required": ["url", "api_key"],
86
+ "secret": [
87
+ "api_key",
88
+ "webhook_credential.basic_auth.password",
89
+ "webhook_credential.header_auth.header_value",
90
+ "webhook_credential.jwt_auth.jwt_token",
91
+ ],
92
+ }
93
+
94
+ def test_connection(self):
95
+ pass
96
+
97
+ @contextmanager
98
+ def _n8n_client(self) -> httpx.Client:
99
+ with self._init_proxy_manager():
100
+ yield httpx.Client(
101
+ base_url=f"{self.url}", headers={"X-N8N-API-KEY": f"{self.api_key}"}, timeout=self.timeout
102
+ )
103
+
104
+ def get_workflows(self) -> list[dict]:
105
+ path = "/workflows"
106
+ workflows = []
107
+ cursor = None
108
+ with self._n8n_client() as client:
109
+ response = client.get(path)
110
+ workflows.extend(response.json()["data"])
111
+ if response.json()["nextCursor"] and response.json()["nextCursor"] != cursor:
112
+ cursor = response.json()["nextCursor"]
113
+ while cursor:
114
+ response = client.get(path, params={"cursor": cursor})
115
+ workflows.extend(response.json()["data"])
116
+ cursor = response.json()["nextCursor"]
117
+ return workflows
118
+
119
+ def _trigger_workflow_via_webhook(self, webhook_id: str, payload: dict) -> dict:
120
+ main_url = f"{urlparse(self.url).scheme}://{urlparse(self.url).netloc}"
121
+ webhook_url = f"{main_url}/webhook/{webhook_id}"
122
+ headers = {}
123
+ credential_type = self.webhook_credential.get("credential_type")
124
+ basic_auth = self.webhook_credential.get("basic_auth", {})
125
+ header_auth = self.webhook_credential.get("header_auth", {})
126
+ jwt_auth = self.webhook_credential.get("jwt_auth", {})
127
+ with self._init_proxy_manager():
128
+ if credential_type == "Basic Auth":
129
+ username = basic_auth.get("username", "")
130
+ password = basic_auth.get("password", "")
131
+ headers["Authorization"] = f'Basic {base64.b64encode(f"{username}:{password}".encode()).decode()}'
132
+ elif credential_type == "Header Auth":
133
+ header_name = header_auth.get("header_name", "")
134
+ header_value = header_auth.get("header_value", "")
135
+ headers[header_name] = header_value
136
+ elif credential_type == "JWT Auth":
137
+ jwt_token = jwt_auth.get("jwt_token", "")
138
+ headers["Authorization"] = f"Bearer {jwt_token}"
139
+ response = httpx.post(url=webhook_url, headers=headers, timeout=self.timeout, json=payload)
140
+
141
+ return response.json()
@@ -182,19 +182,14 @@ class AirflowDbService:
182
182
  include_downstream: bool,
183
183
  failed_only: bool,
184
184
  ):
185
- kwargs = {
186
- "dag_id": dag.dag_id,
187
- "run_id": run_id,
188
- "execution_start_date": min_execution_date,
189
- "execution_end_date": max_execution_date,
190
- }
191
- if min_execution_date or max_execution_date:
192
- kwargs.pop("run_id")
193
- drs: list[DagRun] = DagRun.find(**kwargs)
185
+ drs: list[DagRun] = DagRun.find(
186
+ dag_id=dag.dag_id,
187
+ run_id=run_id,
188
+ execution_start_date=min_execution_date,
189
+ execution_end_date=max_execution_date,
190
+ )
194
191
  if not drs:
195
- logger.info(
196
- f"skip rerun, no dag_run found for {dag.dag_id} at {run_id}, or between {min_execution_date} and {max_execution_date}"
197
- )
192
+ logger.info(f"skip rerun, no dag_run found for {dag.dag_id} at {run_id}")
198
193
  return
199
194
  clear_start_date = min([dr.execution_date for dr in drs])
200
195
  clear_end_date = max([dr.execution_date for dr in drs])
@@ -210,16 +205,14 @@ class AirflowDbService:
210
205
  for task_id in clear_task_ids:
211
206
  if task_id in dag.task_dict:
212
207
  task = dag.task_dict[task_id]
213
- # Use Airflow's built-in method to get all upstream tasks
214
- upstream_task_ids = task.get_flat_relative_ids(upstream=True)
208
+ upstream_task_ids = [t.task_id for t in task.upstream_list]
215
209
  expanded_task_ids.update(upstream_task_ids)
216
210
 
217
211
  if include_downstream:
218
212
  for task_id in clear_task_ids:
219
213
  if task_id in dag.task_dict:
220
214
  task = dag.task_dict[task_id]
221
- # Use Airflow's built-in method to get all downstream tasks
222
- downstream_task_ids = task.get_flat_relative_ids(upstream=False)
215
+ downstream_task_ids = [t.task_id for t in task.downstream_list]
223
216
  expanded_task_ids.update(downstream_task_ids)
224
217
 
225
218
  clear_task_ids = list(expanded_task_ids)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: recurvedata-lib
3
- Version: 0.1.490
3
+ Version: 0.1.492
4
4
  Summary: Common Library for ReOrc Data Platform
5
5
  Author-email: Reorc Team <contact@recurvedata.com>
6
6
  Requires-Dist: croniter
@@ -1,5 +1,5 @@
1
1
  recurvedata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- recurvedata/__version__.py,sha256=Vdccql4HgdaQGJdyGOQGfd8Y-7mydx7au0zyvvDKbw0,24
2
+ recurvedata/__version__.py,sha256=nLCZ9XH8tY-NsC6qhMGzxaKDlG3iuUYkf6rh-HS8yLo,24
3
3
  recurvedata/config.py,sha256=rbpccM6qr8ekdEC5p7XtsivayxmL64-Nb9ogrcWMgX8,3848
4
4
  recurvedata/consts.py,sha256=y5BuAHBrz1jAcS5NgZxnrkfomQv3_5hvgafYwpLKpV8,1224
5
5
  recurvedata/error_codes.py,sha256=y4OLrs0_2iLWdvQJEV10m-414uPkUdm4v0D7bE8iWOM,2303
@@ -7,12 +7,12 @@ recurvedata/exceptions.py,sha256=-Jtm1MXk06ViNOP176MRELFOujjYkZI_IkZY4hzwhRo,187
7
7
  recurvedata/provider_manager.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
8
  recurvedata/schema.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  recurvedata/client/__init__.py,sha256=FnX9HH-2dXADluNfucg98JPMfruMoBpN9ER9lZkVQvQ,49
10
- recurvedata/client/client.py,sha256=Z3JccGzQbRoWr0qnfvUam2_7zxOnyFSn1wV5A5F6jUI,5613
10
+ recurvedata/client/client.py,sha256=UPbHZxb8q6l_pnwEZaD8nEFj_HMejBlhiLlWZAEqE3U,9806
11
11
  recurvedata/client/server_client.py,sha256=bZ55S_tk_fI3JDLU3txha2HKbS4hKUG6jLehj3HnQc0,3033
12
12
  recurvedata/connectors/__init__.py,sha256=1VpGyGu9FA7lAvKZv0Z8j9ZzSi4i-L3_PyLcxdbrfs4,577
13
13
  recurvedata/connectors/_register.py,sha256=7NYVIJk9PufhTJWyj7JkPt9p66Cc1ieCBPpFi24cMwo,1639
14
14
  recurvedata/connectors/base.py,sha256=tuCUq2hij8GknWUKs1sP8HC_JZQie2csxoy3s-7Bb_4,3714
15
- recurvedata/connectors/config_schema.py,sha256=tIEUPLyimVEK3luWOEF1DNnZh6pPZC3p6zYyBiJ0P3Y,58395
15
+ recurvedata/connectors/config_schema.py,sha256=iGiZ-uKwMHM4LGlrlHAVdIpHoJ148wDa1L53HHKSH6k,62286
16
16
  recurvedata/connectors/const.py,sha256=3Zl4wZ0AP6t9x63QoRM6GMG6ZDgTQW-JacOv7lJBcwI,10153
17
17
  recurvedata/connectors/datasource.py,sha256=w09SskSeNJjNc7qUEp0N3UV-YnMX2flzTutg1BaLqO0,5573
18
18
  recurvedata/connectors/dbapi.py,sha256=tmmk7JdecI6NMu_HiLdxWeRpZjX2B4t1RY10SytwBNU,14173
@@ -46,6 +46,7 @@ recurvedata/connectors/connectors/microsoft_fabric.py,sha256=NgEDZp1hRV0r_bg67HM
46
46
  recurvedata/connectors/connectors/mongo.py,sha256=5-ql_2o0BP7SpRg2dN5mQbG40Eb0nMqXH0getXuaw6c,2468
47
47
  recurvedata/connectors/connectors/mssql.py,sha256=jScErON-mZ04atRqcoOE9AChvdAELRrzI2NHf8Gg4JI,4353
48
48
  recurvedata/connectors/connectors/mysql.py,sha256=NYVkTKqDEQA4_Vtp9L3RUFWiM1B3ON7KSq0zoquXHCQ,6438
49
+ recurvedata/connectors/connectors/n8n.py,sha256=dJ_J0HSvEChNtfLYZenwQyIb6v2WxKVbh2NggSptGUU,6419
49
50
  recurvedata/connectors/connectors/oss.py,sha256=CTb8_zkXhc_Y6uJc5HkP87Ea5lA7pKUe_iIeDyz0WSM,2779
50
51
  recurvedata/connectors/connectors/owncloud.py,sha256=hvi0sr164SpXaQ4GrIVjBKZeZPJ9mhO82dzohiYd1SI,1261
51
52
  recurvedata/connectors/connectors/phoenix.py,sha256=v4lAdyDaot5nIYg-2V-uqzHnn6AAp5EINTNpzW_USVM,1069
@@ -255,7 +256,7 @@ recurvedata/providers/flywheel/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
255
256
  recurvedata/providers/mysql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
256
257
  recurvedata/schedulers/__init__.py,sha256=euiId3-BC5JpPmXb2gSzXBycfLMCJyvjn3c5D2ZqK8U,21
257
258
  recurvedata/schedulers/airflow.py,sha256=lX3nc-m2vrwuNuSABL560oaMR6t9G5mwhrKCExmrQDw,36198
258
- recurvedata/schedulers/airflow_db_process.py,sha256=XGGHMrJQbyp0ZpRX2P13S1mD6RhjBdOGHF3ZnN-lI0Q,13842
259
+ recurvedata/schedulers/airflow_db_process.py,sha256=_WxtWg5uFQrGV3JeXWWl2jxAZQ7bnXmvMCyXSCNqycw,13459
259
260
  recurvedata/schedulers/airflow_operators.py,sha256=-AfLwT9Oi8_JDCclc2XhspZ13cbTcu6ftj6yBw2-AWw,2011
260
261
  recurvedata/schedulers/airflow_plugin.py,sha256=aUE1YxXJuMR4hL2hJ1XailBw3wA-V_ljYBVQz39MvYE,242
261
262
  recurvedata/schedulers/airflow_trigger_dag_patch.py,sha256=bUVYRYDozgYeLcpQEmLZsNKoFbVS2PvuSwnpbIyTS0U,4300
@@ -326,7 +327,7 @@ recurvedata/utils/singleton.py,sha256=15PaK2nP9H5PyO26IZzQPpfzlW5h_Bp1NHA6QPb4H0
326
327
  recurvedata/utils/sql.py,sha256=u3XRPv8_vsrMFMm-O1xyV63ZXChAFVHmJj2_xbRwcNg,264
327
328
  recurvedata/utils/timeout.py,sha256=U5ssSgoyVRqop9P8vmyI3BJI-OnMH2k22PdzTh-JN4c,780
328
329
  recurvedata/utils/tracing.py,sha256=gpK8q00ZjZmI81YpgQtDBPLzBvVSYpPA0sIq4wqnvBc,472
329
- recurvedata_lib-0.1.490.dist-info/METADATA,sha256=zCK3ZgqwJFaOdNHNT3sizdIy8ZiH2S_GAL5BgEItylc,27743
330
- recurvedata_lib-0.1.490.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
331
- recurvedata_lib-0.1.490.dist-info/entry_points.txt,sha256=4KBBIfooz3wqXBoLlidRRP4_r36JUCnIF4BFn4igtms,209
332
- recurvedata_lib-0.1.490.dist-info/RECORD,,
330
+ recurvedata_lib-0.1.492.dist-info/METADATA,sha256=4t9YLZ1Kg0mYym7vIsUURwNgtxqTH5FXx-V2HlIMqLU,27743
331
+ recurvedata_lib-0.1.492.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
332
+ recurvedata_lib-0.1.492.dist-info/entry_points.txt,sha256=4KBBIfooz3wqXBoLlidRRP4_r36JUCnIF4BFn4igtms,209
333
+ recurvedata_lib-0.1.492.dist-info/RECORD,,