py-wisharetec 0.1.0__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.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: UTF-8 -*-
@@ -0,0 +1,374 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: UTF-8 -*-
3
+ import hashlib
4
+ from typing import Optional, Union
5
+
6
+ import diskcache
7
+ import httpx
8
+ import redis
9
+ from pydantic import HttpUrl
10
+
11
+ from py_wisharetec.saas.utils import json_is_valid
12
+
13
+
14
+ class Saas:
15
+ def __init__(
16
+ self,
17
+ base_url: Optional[HttpUrl] = "https://saas.wisharetec.com/",
18
+ account: Optional[str] = None,
19
+ password: Optional[str] = None,
20
+ cache_config: Optional[dict] = None,
21
+ client_kwargs: Optional[dict] = None
22
+ ):
23
+ self.base_url = base_url or "https://saas.wisharetec.com/"
24
+ self.base_url = self.base_url[:-1] if self.base_url.endswith("/") else self.base_url
25
+
26
+ # 处理账号密码
27
+ self.account = account or ""
28
+ self.password = password or ""
29
+ self.cache_config = cache_config or {}
30
+ self.cache_config = {
31
+ **{
32
+ "instance": None,
33
+ "key": f"py_wisharetec_saas_{self.account}",
34
+ "expire": 7100,
35
+ },
36
+ **self.cache_config,
37
+ }
38
+ self.client_kwargs = client_kwargs or {}
39
+ self.client_kwargs = {
40
+ **{
41
+ "base_url": self.base_url,
42
+ "timeout": 60,
43
+ "verify": False,
44
+ "headers": {
45
+ "client": "co-pc"
46
+ }
47
+ },
48
+ **self.client_kwargs,
49
+ }
50
+ self.token = ""
51
+
52
+ def client(self) -> httpx.Client:
53
+ """
54
+ 创建并返回同步HTTP客户端
55
+
56
+ Returns:
57
+ httpx.Client: 配置好的同步HTTP客户端实例
58
+ """
59
+ return httpx.Client(**self.client_kwargs)
60
+
61
+ def async_client(self) -> httpx.AsyncClient:
62
+ """
63
+ 创建并返回异步HTTP客户端
64
+
65
+ Returns:
66
+ httpx.AsyncClient: 配置好的异步HTTP客户端实例
67
+ """
68
+ return httpx.AsyncClient(**self.client_kwargs)
69
+
70
+ def query_space_manage_tree(self, client: Optional[httpx.Client] = None, **kwargs) -> httpx.Response:
71
+ """
72
+ 查询空间管理树
73
+
74
+ :param client: 可选的同步客户端实例
75
+ :param **kwargs: 传递给request的额外参数
76
+ :return: httpx.Response - HTTP响应对象
77
+ """
78
+
79
+ kwargs = kwargs or {}
80
+ kwargs = {
81
+ **{
82
+ "method": "GET",
83
+ "url": "/api/space/space/manageTree",
84
+ "headers": {
85
+ "client": "co-pc",
86
+ "authorization": self.token,
87
+ },
88
+ "json": {
89
+ "account": self.account,
90
+ "password": hashlib.md5(self.password.encode("utf-8")).hexdigest(),
91
+ },
92
+ },
93
+ **kwargs,
94
+ }
95
+
96
+ # 发送请求
97
+ if isinstance(client, httpx.Client):
98
+ response = client.request(**kwargs)
99
+ else:
100
+ with self.client() as _client:
101
+ response = _client.request(**kwargs)
102
+ return response
103
+
104
+ def login(self, client: Optional[httpx.Client] = None, **kwargs) -> httpx.Response:
105
+ """
106
+ 登录 SaaS 系统
107
+
108
+ :param client: 可选的同步客户端实例
109
+ :param kwargs: 传递给request的额外参数
110
+ :return: httpx.Response - HTTP响应对象
111
+ """
112
+ kwargs = kwargs or {}
113
+ kwargs = {
114
+ **{
115
+ "method": "POST",
116
+ "url": "/api/user/loginInteractive",
117
+ "headers": {
118
+ "client": "co-pc"
119
+ },
120
+ "json": {
121
+ "account": self.account,
122
+ "password": hashlib.md5(self.password.encode("utf-8")).hexdigest(),
123
+ },
124
+ },
125
+ **kwargs,
126
+ }
127
+
128
+ # 发送请求
129
+ if isinstance(client, httpx.Client):
130
+ response = client.request(**kwargs)
131
+ else:
132
+ with self.client() as _client:
133
+ response = _client.request(**kwargs)
134
+
135
+ if response.is_success:
136
+ if json_is_valid(
137
+ schema={
138
+ "type": "object",
139
+ "properties": {
140
+ # "tenantSimpleInfoVList": {"type": "array", "minItems": 1},
141
+ "userLoginV": {
142
+ "type": "object",
143
+ "properties": {
144
+ "userInfoV": {
145
+ "type": "object",
146
+ "properties": {
147
+ "id": {"type": "string", "minLength": 1}
148
+ },
149
+ "required": ["id"]
150
+ },
151
+ },
152
+ "required": ["userInfoV"]
153
+ }
154
+ },
155
+ "required": ["userLoginV"],
156
+ },
157
+ data=response.json(),
158
+ ):
159
+ self.token = response.headers.get("authorization", "")
160
+ else:
161
+ self.token = ""
162
+
163
+ return response
164
+
165
+ def refresh_token(
166
+ self,
167
+ login_func_kwargs: Optional[dict] = None,
168
+ query_space_manage_tree_func_kwargs: Optional[dict] = None,
169
+ ) -> "Saas":
170
+ login_func_kwargs = login_func_kwargs or {}
171
+ query_space_manage_tree_func_kwargs = query_space_manage_tree_func_kwargs or {}
172
+ cache_key = self.cache_config.get("key", f"py_wisharetec_saas_{self.account}")
173
+ cache_expire = self.cache_config.get("expire", 7100)
174
+ cache_instance = self.cache_config.get("instance", None)
175
+ if not isinstance(cache_instance, Union[diskcache.Cache, redis.Redis]):
176
+ self.login(**login_func_kwargs)
177
+ else:
178
+ self.token = cache_instance.get(cache_key)
179
+ if not isinstance(self.token, str) or not len(self.token):
180
+ self.login(**login_func_kwargs)
181
+ response_json = self.query_space_manage_tree(**query_space_manage_tree_func_kwargs).json()
182
+ if not json_is_valid(
183
+ schema={
184
+ "type": "array",
185
+ "minItems": 1,
186
+ },
187
+ data=response_json,
188
+ ):
189
+ self.login(**login_func_kwargs)
190
+ if json_is_valid(
191
+ schema={
192
+ "type": "array",
193
+ "minItems": 1,
194
+ },
195
+ data=response_json,
196
+ ):
197
+ if isinstance(cache_instance, diskcache.Cache):
198
+ cache_instance.set(cache_key, self.token, expire=cache_expire)
199
+ elif isinstance(cache_instance, redis.Redis):
200
+ cache_instance.set(cache_key, self.token, ex=cache_expire)
201
+ return self
202
+
203
+ def request(self, client: Optional[httpx.Client] = None, **kwargs) -> httpx.Response:
204
+ kwargs = kwargs or {}
205
+ kwargs = {
206
+ "headers": {
207
+ "client": "co-pc",
208
+ "authorization": self.token,
209
+ },
210
+ **kwargs,
211
+ }
212
+ if isinstance(client, httpx.Client):
213
+ response = client.request(**kwargs)
214
+ else:
215
+ with self.client() as _client:
216
+ response = _client.request(**kwargs)
217
+ return response
218
+
219
+ async def async_query_space_manage_tree(self, client: Optional[httpx.AsyncClient] = None,
220
+ **kwargs) -> httpx.Response:
221
+ """
222
+ 查询空间管理树
223
+
224
+ :param client: 可选的同步客户端实例
225
+ :param **kwargs: 传递给request的额外参数
226
+ :return: httpx.Response - HTTP响应对象
227
+ """
228
+
229
+ kwargs = kwargs or {}
230
+ kwargs = {
231
+ **{
232
+ "method": "GET",
233
+ "url": "/api/space/space/manageTree",
234
+ "headers": {
235
+ "client": "co-pc",
236
+ "authorization": self.token,
237
+ },
238
+ "json": {
239
+ "account": self.account,
240
+ "password": hashlib.md5(self.password.encode("utf-8")).hexdigest(),
241
+ },
242
+ },
243
+ **kwargs,
244
+ }
245
+
246
+ # 发送请求
247
+ if isinstance(client, httpx.AsyncClient):
248
+ response = await client.request(**kwargs)
249
+ else:
250
+ async with self.async_client() as _client:
251
+ response = await _client.request(**kwargs)
252
+ return response
253
+
254
+ async def async_login(self, client: Optional[httpx.AsyncClient] = None, **kwargs) -> httpx.Response:
255
+ """
256
+ 登录 SaaS 系统
257
+
258
+ :param client: 可选的异步客户端实例
259
+ :param kwargs: 传递给request的额外参数
260
+ :return: httpx.Response - HTTP响应对象
261
+ """
262
+ kwargs = kwargs or {}
263
+ kwargs = {
264
+ **{
265
+ "method": "POST",
266
+ "url": "/api/user/loginInteractive",
267
+ "headers": {
268
+ "client": "co-pc"
269
+ },
270
+ "json": {
271
+ "account": self.account,
272
+ "password": hashlib.md5(self.password.encode("utf-8")).hexdigest(),
273
+ },
274
+ },
275
+ **kwargs,
276
+ }
277
+
278
+ # 发送请求
279
+ if isinstance(client, httpx.AsyncClient):
280
+ response = await client.request(**kwargs)
281
+ else:
282
+ async with self.async_client() as _client:
283
+ response = await _client.request(**kwargs)
284
+
285
+ if response.is_success:
286
+ if json_is_valid(
287
+ schema={
288
+ "type": "object",
289
+ "properties": {
290
+ # "tenantSimpleInfoVList": {"type": "array", "minItems": 1},
291
+ "userLoginV": {
292
+ "type": "object",
293
+ "properties": {
294
+ "userInfoV": {
295
+ "type": "object",
296
+ "properties": {
297
+ "id": {"type": "string", "minLength": 1}
298
+ },
299
+ "required": ["id"]
300
+ },
301
+ },
302
+ "required": ["userInfoV"]
303
+ }
304
+ },
305
+ "required": ["userLoginV"],
306
+ },
307
+ data=response.json(),
308
+ ):
309
+ self.token = response.headers.get("authorization", "")
310
+ else:
311
+ self.token = ""
312
+
313
+ return response
314
+
315
+ async def async_refresh_token(
316
+ self,
317
+ login_func_kwargs: Optional[dict] = None,
318
+ query_space_manage_tree_func_kwargs: Optional[dict] = None,
319
+ ) -> "Saas":
320
+ login_func_kwargs = login_func_kwargs or {}
321
+ query_space_manage_tree_func_kwargs = query_space_manage_tree_func_kwargs or {}
322
+ cache_key = self.cache_config.get("key", f"py_wisharetec_saas_{self.account}")
323
+ cache_expire = self.cache_config.get("expire", 7100)
324
+ cache_instance = self.cache_config.get("instance", None)
325
+ if not isinstance(cache_instance, Union[diskcache.Cache, redis.Redis]):
326
+ await self.async_login(**login_func_kwargs)
327
+ else:
328
+ self.token = cache_instance.get(cache_key)
329
+ if not isinstance(self.token, str) or not len(self.token):
330
+ await self.async_login(**login_func_kwargs)
331
+ response = await self.async_query_space_manage_tree(**query_space_manage_tree_func_kwargs)
332
+ if not json_is_valid(
333
+ schema={
334
+ "type": "object",
335
+ "properties": {
336
+ "children": {"type": "array", "minItems": 1},
337
+ },
338
+ "required": ["children"],
339
+ },
340
+ data=response.json(),
341
+ ):
342
+ await self.async_login(**login_func_kwargs)
343
+ response = await self.async_query_space_manage_tree(**query_space_manage_tree_func_kwargs)
344
+ if json_is_valid(
345
+ schema={
346
+ "type": "object",
347
+ "properties": {
348
+ "children": {"type": "array", "minItems": 1},
349
+ },
350
+ "required": ["children"],
351
+ },
352
+ data=response.json(),
353
+ ):
354
+ if isinstance(cache_instance, diskcache.Cache):
355
+ cache_instance.set(cache_key, self.token, expire=cache_expire)
356
+ elif isinstance(cache_instance, redis.Redis):
357
+ cache_instance.set(cache_key, self.token, ex=cache_expire)
358
+ return self
359
+
360
+ async def async_request(self, client: Optional[httpx.AsyncClient] = None, **kwargs) -> httpx.Response:
361
+ kwargs = kwargs or {}
362
+ kwargs = {
363
+ "headers": {
364
+ "client": "co-pc",
365
+ "authorization": self.token,
366
+ },
367
+ **kwargs,
368
+ }
369
+ if isinstance(client, httpx.AsyncClient):
370
+ response = await client.request(**kwargs)
371
+ else:
372
+ async with self.async_client() as _client:
373
+ response = await _client.request(**kwargs)
374
+ return response
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: UTF-8 -*-
3
+ from datetime import datetime
4
+ from typing import Any, Optional, Union
5
+
6
+ import httpx
7
+ from jsonpath_ng import parse
8
+ from jsonschema.validators import Draft202012Validator
9
+
10
+
11
+ def json_find_first(expression: str, data: Any) -> Any:
12
+ """
13
+ 使用 JSONPath 表达式从数据中查找第一个匹配项
14
+
15
+ Args:
16
+ expression: JSONPath 表达式,用于指定要查找的数据路径
17
+ data: 待查询的 JSON 数据
18
+
19
+ Returns:
20
+ Any: 第一个匹配的值,如果没有匹配项则返回 None
21
+ """
22
+ results = [i.value for i in parse(expression).find(data)]
23
+ if isinstance(results, list) and len(results) > 0:
24
+ return results[0]
25
+ return None
26
+
27
+
28
+ def json_is_valid(schema: Optional[dict], data: Any) -> bool:
29
+ """
30
+ 校验 JSON 数据是否符合指定的 JSON Schema
31
+
32
+ Args:
33
+ schema: JSON Schema 字典
34
+ data: 待校验的 JSON 数据
35
+
36
+ Returns:
37
+ bool: 校验是否成功
38
+ """
39
+ return Draft202012Validator(schema).is_valid(data)
@@ -0,0 +1,227 @@
1
+ Metadata-Version: 2.4
2
+ Name: py-wisharetec
3
+ Version: 0.1.0
4
+ Summary: Wisharetec SaaS 系统的 Python 客户端库,提供同步和异步接口调用能力。
5
+ Author-email: Guolei <174000902@qq.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 郭磊
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://gitee.com/guolei19850528/py_wisharetec
29
+ Project-URL: Repository, https://gitee.com/guolei19850528/py_wisharetec.git
30
+ Keywords: wisharetec,python,client,api,wisharetec saas,saas
31
+ Classifier: License :: OSI Approved :: MIT License
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Developers
34
+ Classifier: Programming Language :: Python :: 3
35
+ Classifier: Programming Language :: Python :: 3.10
36
+ Classifier: Programming Language :: Python :: 3.11
37
+ Classifier: Programming Language :: Python :: 3.12
38
+ Classifier: Programming Language :: Python :: 3.13
39
+ Classifier: Operating System :: OS Independent
40
+ Requires-Python: >=3.10
41
+ Description-Content-Type: text/markdown
42
+ License-File: LICENSE
43
+ Requires-Dist: httpx>=0.27.0
44
+ Requires-Dist: pydantic>=2.0
45
+ Requires-Dist: jsonpath-ng>=1.5.3
46
+ Requires-Dist: jsonschema>=4.21.0
47
+ Requires-Dist: diskcache>=5.6.3
48
+ Requires-Dist: redis>=4.6.0
49
+ Provides-Extra: dev
50
+ Requires-Dist: pytest>=7.0; extra == "dev"
51
+ Requires-Dist: pytest-cov>=4.0; extra == "dev"
52
+ Requires-Dist: setuptools>=61.0; extra == "dev"
53
+ Requires-Dist: twine>=4.0; extra == "dev"
54
+ Dynamic: license-file
55
+
56
+ # py_wisharetec
57
+
58
+ Wisharetec SaaS 系统的 Python 客户端库,提供同步和异步接口调用能力。
59
+
60
+ ## 功能特性
61
+
62
+ - 同步/异步 HTTP 客户端支持
63
+ - 登录认证与令牌管理
64
+ - 令牌缓存机制(支持 diskcache 和 Redis)
65
+ - 请求封装与响应处理
66
+ - JSON 数据校验与查询工具
67
+
68
+ ## 安装
69
+
70
+ ```bash
71
+ pip install py_wisharetec
72
+ ```
73
+
74
+ ## 快速开始
75
+
76
+ ### 同步模式
77
+
78
+ ```python
79
+ from py_wisharetec.saas import Saas
80
+
81
+ # 初始化客户端
82
+ saas = Saas(
83
+ base_url="https://saas.wisharetec.com/",
84
+ account="your_account",
85
+ password="your_password"
86
+ )
87
+
88
+ # 登录并刷新令牌
89
+ saas.refresh_token()
90
+
91
+ # 发起请求
92
+ response = saas.request(
93
+ method="GET",
94
+ url="/api/your-endpoint"
95
+ )
96
+
97
+ print(response.json())
98
+ ```
99
+
100
+ ### 异步模式
101
+
102
+ ```python
103
+ import asyncio
104
+ from py_wisharetec.saas import Saas
105
+
106
+ async def main():
107
+ # 初始化客户端
108
+ saas = Saas(
109
+ base_url="https://saas.wisharetec.com/",
110
+ account="your_account",
111
+ password="your_password"
112
+ )
113
+
114
+ # 异步登录并刷新令牌
115
+ await saas.async_refresh_token()
116
+
117
+ # 发起异步请求
118
+ response = await saas.async_request(
119
+ method="GET",
120
+ url="/api/your-endpoint"
121
+ )
122
+
123
+ print(response.json())
124
+
125
+ asyncio.run(main())
126
+ ```
127
+
128
+ ## 配置选项
129
+
130
+ ### 初始化参数
131
+
132
+ | 参数 | 类型 | 默认值 | 说明 |
133
+ |------|------|--------|------|
134
+ | base_url | str | https://saas.wisharetec.com/ | SaaS 服务基础 URL |
135
+ | account | str | None | 账号 |
136
+ | password | str | None | 密码 |
137
+ | cache_config | dict | {} | 缓存配置 |
138
+ | client_kwargs | dict | {} | HTTP 客户端额外参数 |
139
+
140
+ ### 缓存配置
141
+
142
+ ```python
143
+ cache_config = {
144
+ "instance": None, # diskcache.Cache 或 redis.Redis 实例
145
+ "key": "py_wisharetec_saas_{account}", # 缓存键名
146
+ "expire": 7100 # 过期时间(秒)
147
+ }
148
+ ```
149
+
150
+ ### 客户端配置
151
+
152
+ ```python
153
+ client_kwargs = {
154
+ "timeout": 60, # 请求超时时间
155
+ "verify": False, # 是否验证 SSL 证书
156
+ "headers": {
157
+ "client": "co-pc"
158
+ }
159
+ }
160
+ ```
161
+
162
+ ## API 参考
163
+
164
+ ### Saas 类
165
+
166
+ #### 同步方法
167
+
168
+ - `client()` - 创建同步 HTTP 客户端
169
+ - `login(**kwargs)` - 登录系统
170
+ - `refresh_token(**kwargs)` - 刷新令牌
171
+ - `request(**kwargs)` - 发起请求
172
+ - `query_space_manage_tree(**kwargs)` - 查询空间管理树
173
+
174
+ #### 异步方法
175
+
176
+ - `async_client()` - 创建异步 HTTP 客户端
177
+ - `async_login(**kwargs)` - 异步登录系统
178
+ - `async_refresh_token(**kwargs)` - 异步刷新令牌
179
+ - `async_request(**kwargs)` - 发起异步请求
180
+ - `async_query_space_manage_tree(**kwargs)` - 异步查询空间管理树
181
+
182
+ ### 工具函数
183
+
184
+ - `json_find_first(expression, data)` - 使用 JSONPath 查找第一个匹配项
185
+ - `json_is_valid(schema, data)` - 校验 JSON 数据是否符合 Schema
186
+
187
+ ## 示例
188
+
189
+ ### 使用缓存
190
+
191
+ ```python
192
+ import diskcache
193
+ from py_wisharetec.saas import Saas
194
+
195
+ # 创建缓存实例
196
+ cache = diskcache.Cache("./cache")
197
+
198
+ # 初始化客户端(带缓存)
199
+ saas = Saas(
200
+ account="your_account",
201
+ password="your_password",
202
+ cache_config={
203
+ "instance": cache,
204
+ "expire": 7200
205
+ }
206
+ )
207
+
208
+ # 登录(自动使用缓存)
209
+ saas.refresh_token()
210
+ ```
211
+
212
+ ## 依赖
213
+
214
+ - httpx >= 0.27.0
215
+ - pydantic >= 2.0.0
216
+ - diskcache >= 5.0.0 (可选)
217
+ - redis >= 5.0.0 (可选)
218
+ - jsonpath-ng >= 1.6.0
219
+ - jsonschema >= 4.0.0
220
+
221
+ ## 主页
222
+
223
+ [https://gitee.com/guolei19850528/py_wisharetec](https://gitee.com/guolei19850528/py_wisharetec)
224
+
225
+ ## 许可证
226
+
227
+ MIT License
@@ -0,0 +1,8 @@
1
+ py_wisharetec/__init__.py,sha256=_ZTg5hKBudrPm7yfzWY8oBRoNo5CbL9XHuXmgaCyXUU,47
2
+ py_wisharetec/saas/__init__.py,sha256=96sd7Ddfu_wqWN7ObjW5cq8pUdgc6xsNZ_HYWCPN8yw,13733
3
+ py_wisharetec/saas/utils.py,sha256=javvTs8Qs79dBaHHE9BSuUL88DE-jmdeaXQYsp-eAME,1053
4
+ py_wisharetec-0.1.0.dist-info/licenses/LICENSE,sha256=HazFJNaQP3rbY1dvg3hAmO-FZ6v7DwM0YHwXvWsGG3A,1063
5
+ py_wisharetec-0.1.0.dist-info/METADATA,sha256=zI8uCeRr4FpTXw8DICYrlFtCUqqtGA_az_IjTaK5QHo,6075
6
+ py_wisharetec-0.1.0.dist-info/WHEEL,sha256=K260EYznzXsJYBQGqmI8VTxEdiZYNvDZwW9cBh9-_MA,91
7
+ py_wisharetec-0.1.0.dist-info/top_level.txt,sha256=xmDUuBaqaxh1JS1OLd9gdYW69_clZzeFu3pEU46x2xw,14
8
+ py_wisharetec-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (83.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 郭磊
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ py_wisharetec