recurvedata-lib 0.1.491__py2.py3-none-any.whl → 0.1.496__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.491"
1
+ __version__ = "0.1.496"
@@ -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",
@@ -1492,6 +1570,26 @@ ALL_CONFIG_SCHEMA_DCT = {
1492
1570
  "group": ["destination"],
1493
1571
  "test_required": True,
1494
1572
  },
1573
+ "recurvedata.connectors.connectors.wecom": {
1574
+ "type": "wecom",
1575
+ "ui_type": "WeCom",
1576
+ "category": ["others"],
1577
+ "config_schema": {
1578
+ "type": "object",
1579
+ "properties": {
1580
+ "webhook_url": {"type": "string", "title": _l("WeCom Webhook URL")},
1581
+ },
1582
+ "order": [
1583
+ "webhook_url",
1584
+ ],
1585
+ "required": [
1586
+ "webhook_url",
1587
+ ],
1588
+ },
1589
+ "enabled": True,
1590
+ "group": ["integration"],
1591
+ "test_required": True,
1592
+ },
1495
1593
  }
1496
1594
 
1497
1595
  # auto generated finish
@@ -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()
@@ -0,0 +1,66 @@
1
+ from urllib.parse import urlparse
2
+
3
+ import requests
4
+
5
+ from recurvedata.connectors._register import register_connector_class
6
+ from recurvedata.connectors.base import RecurveConnectorBase
7
+ from recurvedata.consts import ConnectorGroup
8
+ from recurvedata.core.translation import _l
9
+
10
+ CONNECTION_TYPE = "wecom"
11
+ UI_CONNECTION_TYPE = "WeCom"
12
+
13
+
14
+ @register_connector_class([CONNECTION_TYPE, UI_CONNECTION_TYPE])
15
+ class WeCom(RecurveConnectorBase):
16
+ connection_type = CONNECTION_TYPE
17
+ ui_connection_type = UI_CONNECTION_TYPE
18
+ group = [ConnectorGroup.INTEGRATION]
19
+
20
+ config_schema = {
21
+ "type": "object",
22
+ "properties": {
23
+ "webhook_url": {"type": "string", "title": _l("WeCom Webhook URL")},
24
+ },
25
+ "order": [
26
+ "webhook_url",
27
+ ],
28
+ "required": [
29
+ "webhook_url",
30
+ ],
31
+ }
32
+
33
+ @property
34
+ def webhook_url(self):
35
+ return self.conf["webhook_url"]
36
+
37
+ @property
38
+ def wecom_conf(self):
39
+ if hasattr(self, "_wecom_conf"):
40
+ return self._wecom_conf
41
+ self.init_config()
42
+ return self._wecom_conf
43
+
44
+ def test_connection(self):
45
+ """
46
+ Test WeCom webhook connection.
47
+ Validate URL format and network connectivity without sending actual messages.
48
+ """
49
+ # Validate URL format
50
+ parsed_url = urlparse(self.webhook_url)
51
+ if not parsed_url.scheme or not parsed_url.netloc:
52
+ raise ValueError("Invalid webhook URL format")
53
+
54
+ if parsed_url.scheme != "https":
55
+ raise ValueError("WeCom webhook URL must use HTTPS")
56
+
57
+ # Verify WeCom domain
58
+ if "qyapi.weixin.qq.com" not in parsed_url.netloc:
59
+ raise ValueError("Invalid WeCom webhook URL domain")
60
+
61
+ # Test network connectivity (use HEAD request without sending message body)
62
+ try:
63
+ requests.head(self.webhook_url, timeout=10, allow_redirects=True)
64
+ # Connection test only, status code not checked (HEAD may return 405)
65
+ except requests.exceptions.RequestException as e:
66
+ raise ValueError(f"Failed to connect to WeCom webhook: {str(e)}")
@@ -397,19 +397,27 @@ class DBAPIBase(RecurveConnectorBase):
397
397
  set_env_dbt_password(self.password or "")
398
398
 
399
399
  @classmethod
400
- def order_sql(cls, sql: str, orders: list[dict[str, str]] = None, return_sql: bool = True):
401
- """
402
- order the sql by the orders
403
- """
400
+ def get_dialect(cls):
404
401
  # dialect impala -> hive, cuz there is no dialect 'impala' in sqlglot
405
- dialect = "hive" if cls.connection_type == "impala" else (cls.connection_type or None)
402
+ return "hive" if cls.connection_type == "impala" else (cls.connection_type or None)
403
+
404
+ @classmethod
405
+ def clean_sql(cls, sql):
406
+ dialect = cls.get_dialect()
406
407
  # Parse the SQL query
407
408
  parsed = sqlglot.parse_one(sql, read=dialect)
408
409
  # since some sql dialects have special identifier, we need to use the dialect to generate the clean sql
409
- clean_sql = parsed.sql(dialect=dialect, comments=False)
410
+ return parsed.sql(dialect=dialect, comments=False)
411
+
412
+ @classmethod
413
+ def order_sql(cls, sql: str, orders: list[dict[str, str]] = None, return_sql: bool = True):
414
+ """
415
+ order the sql by the orders
416
+ """
417
+ dialect = cls.get_dialect()
410
418
  # Wrap the entire query with a subquery
411
419
  alias = "_recurve_limit_subquery"
412
- subquery = exp.Subquery(this=clean_sql, alias=alias)
420
+ subquery = exp.Subquery(this=cls.clean_sql(sql), alias=alias)
413
421
 
414
422
  # Create a new SELECT statement with the subquery and the LIMIT clause
415
423
  outer_select = exp.select("*").from_(subquery)
@@ -436,7 +444,7 @@ class DBAPIBase(RecurveConnectorBase):
436
444
  no validation on sql.
437
445
  If the sql is DML, then execute it will raise an error.
438
446
  """
439
- dialect = "hive" if cls.connection_type == "impala" else (cls.connection_type or None)
447
+ dialect = cls.get_dialect()
440
448
 
441
449
  outer_select = cls.order_sql(sql, orders, return_sql=False)
442
450
 
@@ -456,7 +464,7 @@ class DBAPIBase(RecurveConnectorBase):
456
464
  no validation on sql.
457
465
  If the sql is DML, then execute it will raise an error.
458
466
  """
459
- return f"SELECT COUNT(1) FROM ({sql}) AS cnt_subquery"
467
+ return f"SELECT COUNT(1) FROM ({cls.clean_sql(sql)}) AS cnt_subquery"
460
468
 
461
469
 
462
470
  @dataclass
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: recurvedata-lib
3
- Version: 0.1.491
3
+ Version: 0.1.496
4
4
  Summary: Common Library for ReOrc Data Platform
5
5
  Author-email: Reorc Team <contact@recurvedata.com>
6
6
  Requires-Dist: croniter
@@ -77,6 +77,7 @@ Requires-Dist: dbt-duckdb==1.8.4; extra == 'dbt'
77
77
  Requires-Dist: dbt-extractor==0.5.1; extra == 'dbt'
78
78
  Requires-Dist: dbt-fabric==1.9.4; extra == 'dbt'
79
79
  Requires-Dist: dbt-impala==1.8.0; extra == 'dbt'
80
+ Requires-Dist: dbt-mysql==1.8.0+recurve.1; extra == 'dbt'
80
81
  Requires-Dist: dbt-postgres==1.8.2; extra == 'dbt'
81
82
  Requires-Dist: dbt-redshift==1.8.1; extra == 'dbt'
82
83
  Requires-Dist: dbt-semantic-interfaces==0.5.1; extra == 'dbt'
@@ -1,5 +1,5 @@
1
1
  recurvedata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- recurvedata/__version__.py,sha256=zK7PI8pL8sGJb_fo1G1GjHdPpzyl1z9_ro5Q-ccc1S4,24
2
+ recurvedata/__version__.py,sha256=Y8Rd0ubG5uL_xudTnGRSLi5Dxc73nGvHR1VMlDgyh0Y,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
@@ -12,10 +12,10 @@ recurvedata/client/server_client.py,sha256=bZ55S_tk_fI3JDLU3txha2HKbS4hKUG6jLehj
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=fcTSyeVRA3tKvhD_kAvjk9lKo9TZlbDYRrIw9DKLkfk,62854
16
16
  recurvedata/connectors/const.py,sha256=3Zl4wZ0AP6t9x63QoRM6GMG6ZDgTQW-JacOv7lJBcwI,10153
17
17
  recurvedata/connectors/datasource.py,sha256=w09SskSeNJjNc7qUEp0N3UV-YnMX2flzTutg1BaLqO0,5573
18
- recurvedata/connectors/dbapi.py,sha256=tmmk7JdecI6NMu_HiLdxWeRpZjX2B4t1RY10SytwBNU,14173
18
+ recurvedata/connectors/dbapi.py,sha256=I9KlaOdWwDrOtZeDAZdBU1MxmkARnLCkZDnY_Vm9llI,14293
19
19
  recurvedata/connectors/fs.py,sha256=ZZlDq4Bd7aA09ZarZwyWrzFEwUgG5GHm8JPctHw7Ako,1460
20
20
  recurvedata/connectors/ftp.py,sha256=9H_xON87ESpatU5e8kCwwbYwLRnIH1ClqazIbDe-YfU,1091
21
21
  recurvedata/connectors/object_store.py,sha256=Fd3jGnUQ79sNuC8L-0nSDxwHh32emU0RuIqTAOZx4IM,1714
@@ -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
@@ -59,6 +60,7 @@ recurvedata/connectors/connectors/spark.py,sha256=zaTrsA0fgIHLLR3LZ_D9pd3KIccZBe
59
60
  recurvedata/connectors/connectors/starrocks.py,sha256=IdrlcKz0vUASokdR4QVjZqCXoVZ_dFBEdJUnDlMaScQ,6028
60
61
  recurvedata/connectors/connectors/tencent_cos.py,sha256=1f_31aNW8lznuygE_N7tQbK9PToGIRUkFnmhHSRmq54,1392
61
62
  recurvedata/connectors/connectors/tidb.py,sha256=2VkZ8x6fhmbcGRc7ekTwOADGgWiRFVHr4tnzuQVfqDU,1924
63
+ recurvedata/connectors/connectors/wecom.py,sha256=1n6DZeaSEEWDiLwuMWIxx1QYpOPJPKQUACUtGcgEvUQ,2134
62
64
  recurvedata/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
63
65
  recurvedata/core/config.py,sha256=_wmsWpPNgZE-mUSn1u3C7m4Zf5j2XEhZLhtUY5llbYo,1436
64
66
  recurvedata/core/configurable.py,sha256=RekN9mY46Sb_IPLW5aciNg0xF8E47CtMbHPOsar3tfU,719
@@ -326,7 +328,7 @@ recurvedata/utils/singleton.py,sha256=15PaK2nP9H5PyO26IZzQPpfzlW5h_Bp1NHA6QPb4H0
326
328
  recurvedata/utils/sql.py,sha256=u3XRPv8_vsrMFMm-O1xyV63ZXChAFVHmJj2_xbRwcNg,264
327
329
  recurvedata/utils/timeout.py,sha256=U5ssSgoyVRqop9P8vmyI3BJI-OnMH2k22PdzTh-JN4c,780
328
330
  recurvedata/utils/tracing.py,sha256=gpK8q00ZjZmI81YpgQtDBPLzBvVSYpPA0sIq4wqnvBc,472
329
- recurvedata_lib-0.1.491.dist-info/METADATA,sha256=kzD3uWpsIulJ8PoZ70Qyz2ESKK0vZEr8k_1yD4sUg0w,27743
330
- recurvedata_lib-0.1.491.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
331
- recurvedata_lib-0.1.491.dist-info/entry_points.txt,sha256=4KBBIfooz3wqXBoLlidRRP4_r36JUCnIF4BFn4igtms,209
332
- recurvedata_lib-0.1.491.dist-info/RECORD,,
331
+ recurvedata_lib-0.1.496.dist-info/METADATA,sha256=BiSQLgBoteUjIHtU3IBWdDhKF_rr8CkQ7cB5rqu7avM,27801
332
+ recurvedata_lib-0.1.496.dist-info/WHEEL,sha256=tkmg4JIqwd9H8mL30xA7crRmoStyCtGp0VWshokd1Jc,105
333
+ recurvedata_lib-0.1.496.dist-info/entry_points.txt,sha256=4KBBIfooz3wqXBoLlidRRP4_r36JUCnIF4BFn4igtms,209
334
+ recurvedata_lib-0.1.496.dist-info/RECORD,,