XMWAI 0.4.2__py3-none-any.whl → 0.4.4__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 XMWAI might be problematic. Click here for more details.

XMWAI/__init__.py CHANGED
@@ -2,10 +2,12 @@ from .core import story, photo, reply, poem, crack, show # 从子模块导入
2
2
  from .magic_core import birthday # 从子模块导入函数到顶层
3
3
  from .bomb_core import bomb # 从子模块导入函数到顶层
4
4
  from .idiom_core import idiom, searchIdiom, get_json_path # 从子模块导入函数到顶层
5
- from .trial_class import make, get_file_content_as_base64, save_pic, detect_windows_scale, get_file_content_as_base64_2, cartoon # 从子模块导入函数到顶层
5
+ from .trial_class import make, get_file_content_as_base64, save_pic, detect_scale, open_image, get_file_content_as_base64_2, cartoon # 从子模块导入函数到顶层
6
6
  from .web_core import burger, cookbook # 从子模块导入函数到顶层
7
+ from .snake_core import SnakeGame
8
+
7
9
 
8
10
  __all__ = ["story", "photo", "reply", "poem", 'birthday', 'bomb',
9
11
  "idiom", "searchIdiom", "get_json_path", "crack", "show",
10
- "make", "get_file_content_as_base64", "save_pic", "detect_windows_scale",
11
- "get_file_content_as_base64_2", "cartoon", "burger", "cookbook"] # 可选:明确导出的内容
12
+ "make", "get_file_content_as_base64", "save_pic", "detect_scale", "open_image",
13
+ "get_file_content_as_base64_2", "cartoon", "burger", "cookbook", "SnakeGame"] # 可选:明确导出的内容
XMWAI/assets/1.png ADDED
Binary file
File without changes
XMWAI/assets/g.png ADDED
Binary file
XMWAI/assets/h.png ADDED
Binary file
XMWAI/assets/l.png ADDED
Binary file
XMWAI/assets/m.png ADDED
Binary file
XMWAI/assets/s.png ADDED
Binary file
XMWAI/assets/t.png ADDED
Binary file
XMWAI/snake_core.py ADDED
@@ -0,0 +1,426 @@
1
+ import cv2
2
+ import math
3
+ import random
4
+ import cvzone
5
+ import numpy as np
6
+ from cvzone.HandTrackingModule import HandDetector
7
+ from PIL import ImageFont, ImageDraw, Image
8
+ import os
9
+ import time
10
+ import importlib.resources as pkg_resources
11
+ import XMWAI.assets # 引入资源目录
12
+
13
+
14
+ # ---------- 工具函数:从 assets 读取资源 ----------
15
+ def get_asset_path(filename: str) -> str:
16
+ """
17
+ 获取包内资源的绝对路径 (兼容 pip 安装后的情况)
18
+ """
19
+ with pkg_resources.path(XMWAI.assets, filename) as p:
20
+ return str(p)
21
+
22
+
23
+ class SnakeGame:
24
+ def __init__(self, width=720, height=720, snakeInitLength=150, snakeGrowth=50,
25
+ snakeLineWidth=10, snakeHeadSize=15, foodPaths=None, foodNames=None, foodScores=None,
26
+ obstaclePaths=None, fontPath=None):
27
+
28
+ self.resolution = (width, height)
29
+ self.snakeInitLength = snakeInitLength
30
+ self._snakeGrowth = snakeGrowth
31
+ self._snakeHeadSize = snakeHeadSize
32
+ self._foodScores = foodScores if foodScores is not None else [3, 2, 1]
33
+ self.snakeLineWidth = snakeLineWidth
34
+
35
+ # 默认资源路径
36
+ if foodPaths is None:
37
+ foodPaths = [get_asset_path("h.png"),
38
+ get_asset_path("s.png"),
39
+ get_asset_path("t.png")]
40
+ if foodNames is None:
41
+ foodNames = ["汉堡", "薯条", "甜甜圈"]
42
+ if obstaclePaths is None:
43
+ obstaclePaths = [get_asset_path("g.png"),
44
+ get_asset_path("l.png"),
45
+ get_asset_path("m.png")]
46
+ if fontPath is None:
47
+ fontPath = get_asset_path("微软雅黑.ttf")
48
+
49
+ self.foodPaths = foodPaths
50
+ self.foodNames = foodNames
51
+ self.obstaclePaths = obstaclePaths
52
+ self.fontPath = fontPath
53
+
54
+ self.cap = None
55
+ self.detector = None
56
+ self.snake = None
57
+ self.foodManager = None
58
+ self.obstacleManager = None
59
+ self.img = None
60
+ self.overlayTexts = []
61
+
62
+ self.timer = 30
63
+ self.start_time = None
64
+
65
+ self._init_game_objects()
66
+ self.open_window()
67
+
68
+ # ----------- 属性自动同步 -----------
69
+ @property
70
+ def snakeHeadSize(self):
71
+ return self._snakeHeadSize
72
+
73
+ @snakeHeadSize.setter
74
+ def snakeHeadSize(self, value):
75
+ self._snakeHeadSize = value
76
+ if self.snake:
77
+ self.snake.headSize = value
78
+
79
+ @property
80
+ def foodScores(self):
81
+ return self._foodScores
82
+
83
+ @foodScores.setter
84
+ def foodScores(self, scores):
85
+ self._foodScores = scores
86
+ if self.foodManager:
87
+ self.foodManager.foodScores = scores
88
+
89
+ @property
90
+ def snakeGrowth(self):
91
+ return self._snakeGrowth
92
+
93
+ @snakeGrowth.setter
94
+ def snakeGrowth(self, value):
95
+ self._snakeGrowth = value
96
+
97
+ # ---------------- 工具函数 ----------------
98
+ def _putChineseText(self, img, text, pos, fontSize=40, color=(0, 0, 255)):
99
+ img_pil = Image.fromarray(img)
100
+ draw = ImageDraw.Draw(img_pil)
101
+ try:
102
+ font = ImageFont.truetype(self.fontPath, fontSize)
103
+ except OSError:
104
+ font = ImageFont.load_default()
105
+ draw.text(pos, text, font=font, fill=color)
106
+ return np.array(img_pil)
107
+
108
+ def _init_game_objects(self):
109
+ self.snake = self.Snake(color=(0, 0, 255), initLength=self.snakeInitLength,
110
+ lineWidth=self.snakeLineWidth, headSize=self._snakeHeadSize)
111
+ self.foodManager = self.FoodManager(
112
+ self.foodPaths, self.foodNames, self._foodScores)
113
+ self.obstacleManager = self.ObstacleManager(self.obstaclePaths)
114
+ self.obstacleManager.randomObstacles()
115
+
116
+ def _render_frame(self, show_food=True, show_obstacle=True):
117
+ success, self.img = self.cap.read()
118
+ if not success:
119
+ return
120
+ self.img = cv2.flip(self.img, 1)
121
+ hands, self.img = self.detector.findHands(self.img, flipType=False)
122
+ player_head = tuple(hands[0]['lmList'][8][0:2]) if hands else None
123
+
124
+ if not self.snake.gameOver and player_head:
125
+ self.snake.update(self.img, player_head, self.obstacleManager)
126
+
127
+ if not self.snake.gameOver and player_head and show_food:
128
+ cx, cy = player_head
129
+ rx, ry = self.foodManager.foodPoint
130
+ w, h = self.foodManager.wFood, self.foodManager.hFood
131
+ if rx - w//2 <= cx <= rx + w//2 and ry - h//2 <= cy <= ry + h//2:
132
+ self.snake.score += self.foodManager.foodScores[self.foodManager.foodIndex]
133
+ self.snake.allowedLength += self._snakeGrowth
134
+ self.foodManager.randomFoodLocation(self.obstacleManager)
135
+
136
+ if show_obstacle:
137
+ self.img = self.obstacleManager.draw(self.img)
138
+ self.obstacleManager.moveObstacles(
139
+ self.resolution[0], self.resolution[1])
140
+
141
+ if show_food:
142
+ self.img = self.foodManager.draw(self.img)
143
+
144
+ # ---------------- 对外接口 ----------------
145
+ def open_window(self):
146
+ self.cap = cv2.VideoCapture(0)
147
+ self.cap.set(3, self.resolution[0])
148
+ self.cap.set(4, self.resolution[1])
149
+ self.detector = HandDetector(detectionCon=0.7, maxHands=1)
150
+ success, self.img = self.cap.read()
151
+ if not success:
152
+ print("摄像头打开失败")
153
+ return
154
+ self.img = cv2.flip(self.img, 1)
155
+ cv2.imshow("AI Snake", self.img)
156
+ cv2.waitKey(1)
157
+
158
+ def hand(self):
159
+ if self.cap is None:
160
+ print("请先调用 open_window()")
161
+ return
162
+ if self.detector is None:
163
+ self.detector = HandDetector(detectionCon=0.8, maxHands=1)
164
+ while True:
165
+ success, self.img = self.cap.read()
166
+ if not success:
167
+ break
168
+ self.img = cv2.flip(self.img, 1)
169
+ hands, self.img = self.detector.findHands(self.img, flipType=False)
170
+ cv2.imshow("AI Snake", self.img)
171
+ key = cv2.waitKey(1) & 0xFF
172
+ if hands or key == ord('q'):
173
+ break
174
+
175
+ def display(self):
176
+ if self.img is None:
177
+ self._render_frame(show_food=False)
178
+ self.foodManager.randomFoodLocation(self.obstacleManager)
179
+ self._render_frame(show_food=True)
180
+ self.overlayTexts = [
181
+ (f'玩家分数:{self.snake.score}', (50, 50), 30, (255, 0, 255)),
182
+ (f'倒计时:{self.timer} 秒', (50, 120), 30, (255, 0, 255))
183
+ ]
184
+ img_copy = self.img.copy()
185
+ for txt, pos, size, color in self.overlayTexts:
186
+ img_copy = self._putChineseText(img_copy, txt, pos, size, color)
187
+ cv2.imshow("AI Snake", img_copy)
188
+ cv2.waitKey(1)
189
+
190
+ # ---------------- 重置游戏 ----------------
191
+ def reset_game(self):
192
+ self.snake.reset()
193
+ self.snake.headSize = self._snakeHeadSize
194
+ self.obstacleManager.randomObstacles()
195
+ self.foodManager.foodScores = self._foodScores
196
+ self.foodManager.randomFoodLocation(self.obstacleManager)
197
+ self.start_time = time.time()
198
+ self.timer = 30
199
+ if self.cap is None or not self.cap.isOpened():
200
+ self.open_window()
201
+
202
+ # ---------------- 游戏结束 ----------------
203
+ def gameover(self, path=None, size=(100, 100)):
204
+ if path is None:
205
+ path = get_asset_path("1.png")
206
+
207
+ if os.path.exists(path):
208
+ gameover_img = cv2.imread(path)
209
+ gameover_img = cv2.resize(gameover_img, self.resolution)
210
+ else:
211
+ gameover_img = np.zeros(
212
+ (self.resolution[1], self.resolution[0], 3), np.uint8)
213
+ cv2.putText(gameover_img, "Game Over Image Missing!", (50, 100),
214
+ cv2.FONT_HERSHEY_SIMPLEX, 2, (0, 0, 255), 3)
215
+
216
+ gameover_img = self._putChineseText(
217
+ gameover_img, f"最终分数:{self.snake.score}", size, 40, (68, 84, 106))
218
+
219
+ while True:
220
+ cv2.imshow("AI Snake", gameover_img)
221
+ key = cv2.waitKey(0) & 0xFF
222
+ if key == ord('r'): # 重启游戏
223
+ self.reset_game()
224
+ self.start()
225
+ break
226
+ elif key == ord('q'): # 退出游戏
227
+ if self.cap is not None:
228
+ self.cap.release()
229
+ cv2.destroyAllWindows()
230
+ break
231
+
232
+ # ---------------- 游戏主循环 ----------------
233
+ def start(self):
234
+ if self.cap is None or not self.cap.isOpened():
235
+ self.open_window()
236
+ self.start_time = time.time()
237
+ self.timer = 30
238
+
239
+ while True:
240
+ if self.snake.gameOver or self.timer == 0:
241
+ # 游戏结束,进入 gameover 界面
242
+ self.gameover()
243
+ break
244
+
245
+ self._render_frame(show_food=True, show_obstacle=True)
246
+ elapsed = int(time.time() - self.start_time)
247
+ self.timer = max(0, 30 - elapsed)
248
+
249
+ self.overlayTexts = [
250
+ (f'玩家分数:{self.snake.score}', (50, 50), 30, (255, 0, 255)),
251
+ (f'倒计时:{self.timer} 秒', (50, 120), 30, (255, 0, 255))
252
+ ]
253
+
254
+ if self.img is not None:
255
+ img_copy = self.img.copy()
256
+ for txt, pos, size, color in self.overlayTexts:
257
+ img_copy = self._putChineseText(
258
+ img_copy, txt, pos, size, color)
259
+ cv2.imshow("AI Snake", img_copy)
260
+
261
+ key = cv2.waitKey(1)
262
+ if key == ord('r'): # 游戏中途重置
263
+ self.reset_game()
264
+ elif key == ord('q'):
265
+ if self.cap is not None:
266
+ self.cap.release()
267
+ cv2.destroyAllWindows()
268
+ break
269
+
270
+ # ---------------- 内部类:蛇 ----------------
271
+ class Snake:
272
+ def __init__(self, color, initLength=150, lineWidth=10, headSize=15):
273
+ self.points = []
274
+ self.currentLength = 0
275
+ self.allowedLength = initLength
276
+ self.previousHead = None
277
+ self.score = 0
278
+ self.color = color
279
+ self.gameOver = False
280
+ self.lineWidth = lineWidth
281
+ self.headSize = headSize
282
+
283
+ def reset(self):
284
+ self.points = []
285
+ self.currentLength = 0
286
+ self.allowedLength = 150
287
+ self.previousHead = None
288
+ self.score = 0
289
+ self.gameOver = False
290
+
291
+ def update(self, imgMain, currentHead, obstacleManager=None):
292
+ if self.gameOver:
293
+ return
294
+ cx, cy = currentHead
295
+ if cx is None or cy is None:
296
+ return
297
+ if self.previousHead is None:
298
+ self.previousHead = (cx, cy)
299
+ px, py = self.previousHead
300
+
301
+ alpha = 0.7
302
+ cx = int(px * (1 - alpha) + cx * alpha)
303
+ cy = int(py * (1 - alpha) + cy * alpha)
304
+
305
+ maxStep = 50
306
+ dx = cx - px
307
+ dy = cy - py
308
+ distance = math.hypot(dx, dy)
309
+ if distance > maxStep:
310
+ steps = int(distance / maxStep)
311
+ for i in range(1, steps + 1):
312
+ ix = int(px + dx * i / steps)
313
+ iy = int(py + dy * i / steps)
314
+ self.points.append((ix, iy))
315
+ self.currentLength += maxStep
316
+ else:
317
+ self.points.append((cx, cy))
318
+ self.currentLength += distance
319
+
320
+ self.previousHead = (cx, cy)
321
+
322
+ while self.currentLength > self.allowedLength:
323
+ if len(self.points) > 1:
324
+ removed_dx = self.points[1][0] - self.points[0][0]
325
+ removed_dy = self.points[1][1] - self.points[0][1]
326
+ removed_dist = math.hypot(removed_dx, removed_dy)
327
+ self.currentLength -= removed_dist
328
+ self.points.pop(0)
329
+
330
+ for i in range(1, len(self.points)):
331
+ cv2.line(
332
+ imgMain, self.points[i-1], self.points[i], self.color, self.lineWidth)
333
+
334
+ snakeHeadColor = (random.randint(0, 255), random.randint(
335
+ 0, 255), random.randint(0, 255))
336
+ cv2.circle(imgMain, (cx, cy), self.headSize,
337
+ snakeHeadColor, cv2.FILLED)
338
+
339
+ h, w, _ = imgMain.shape
340
+ margin = 5
341
+ if cx <= margin or cx >= w - margin or cy <= margin or cy >= h - margin:
342
+ self.gameOver = True
343
+
344
+ if obstacleManager:
345
+ for ox, oy, ow, oh, *_ in obstacleManager.obstacles:
346
+ if ox <= cx <= ox + ow and oy <= cy <= oy + oh:
347
+ self.gameOver = True
348
+
349
+ # ---------------- 内部类:食物 ----------------
350
+ class FoodManager:
351
+ def __init__(self, foodPaths, foodNames, foodScores):
352
+ self.foodImages = []
353
+ for path in foodPaths:
354
+ if os.path.exists(path):
355
+ self.foodImages.append(
356
+ cv2.imread(path, cv2.IMREAD_UNCHANGED))
357
+ else:
358
+ self.foodImages.append(np.zeros((50, 50, 4), np.uint8))
359
+ self.foodNames = foodNames
360
+ self.foodScores = foodScores
361
+ self.foodIndex = 0
362
+ self.hFood, self.wFood = 0, 0
363
+ self.foodPoint = 0, 0
364
+ self.randomFoodLocation()
365
+
366
+ def randomFoodLocation(self, obstacleManager=None):
367
+ max_attempts = 100
368
+ for _ in range(max_attempts):
369
+ self.foodPoint = random.randint(
370
+ 50, 1230), random.randint(50, 670)
371
+ self.foodIndex = random.randint(0, len(self.foodImages)-1)
372
+ self.hFood, self.wFood, _ = self.foodImages[self.foodIndex].shape
373
+ if obstacleManager:
374
+ overlap = False
375
+ for ox, oy, ow, oh, *_ in obstacleManager.obstacles:
376
+ if ox < self.foodPoint[0] < ox + ow and oy < self.foodPoint[1] < oy + oh:
377
+ overlap = True
378
+ break
379
+ if not overlap:
380
+ return
381
+ return
382
+
383
+ def draw(self, imgMain):
384
+ rx, ry = self.foodPoint
385
+ imgMain = cvzone.overlayPNG(imgMain, self.foodImages[self.foodIndex],
386
+ (rx - self.wFood//2, ry - self.hFood//2))
387
+ return imgMain
388
+
389
+ # ---------------- 内部类:障碍 ----------------
390
+ class ObstacleManager:
391
+ def __init__(self, obstaclePaths):
392
+ self.obstacleImages = []
393
+ for path in obstaclePaths:
394
+ if os.path.exists(path):
395
+ self.obstacleImages.append(
396
+ cv2.imread(path, cv2.IMREAD_UNCHANGED))
397
+ else:
398
+ self.obstacleImages.append(np.zeros((50, 50, 4), np.uint8))
399
+ self.obstacles = []
400
+
401
+ def randomObstacles(self):
402
+ self.obstacles.clear()
403
+ for img in self.obstacleImages:
404
+ h, w, _ = img.shape
405
+ x = random.randint(150, 1230)
406
+ y = random.randint(50, 670)
407
+ dx = random.choice([-5, 5])
408
+ dy = random.choice([-5, 5])
409
+ self.obstacles.append([x, y, w, h, dx, dy, img])
410
+
411
+ def moveObstacles(self, wMax, hMax):
412
+ for obs in self.obstacles:
413
+ x, y, ow, oh, dx, dy, img = obs
414
+ x += dx
415
+ y += dy
416
+ if x <= 0 or x + ow >= wMax:
417
+ dx *= -1
418
+ if y <= 0 or y + oh >= hMax:
419
+ dy *= -1
420
+ obs[0], obs[1], obs[4], obs[5] = x, y, dx, dy
421
+
422
+ def draw(self, imgMain):
423
+ for obs in self.obstacles:
424
+ x, y, w, h, dx, dy, img = obs
425
+ imgMain = cvzone.overlayPNG(imgMain, img, (int(x), int(y)))
426
+ return imgMain
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: XMWAI
3
- Version: 0.4.2
3
+ Version: 0.4.4
4
4
  Summary: Small code King AI related library
5
5
  Home-page: https://github.com/Tonykai88/XMWAI.git
6
6
  Author: pydevelopment
@@ -17,6 +17,7 @@ Requires-Dist: opencv-python>=3.4.18.65
17
17
  Requires-Dist: numpy>=1.26.0
18
18
  Requires-Dist: flask>=3.1.0
19
19
  Requires-Dist: pyecharts>=2.0.8
20
+ Requires-Dist: cvzone>=1.6.1
20
21
  Dynamic: author
21
22
  Dynamic: author-email
22
23
  Dynamic: classifier
@@ -1,10 +1,20 @@
1
- XMWAI/__init__.py,sha256=-0GLZK0X3vEuTbu5n2AX3cWw6SXtJTERtBIAssZq41o,908
1
+ XMWAI/__init__.py,sha256=RFHMM1PF3ELvHSMeKe2G2E0astGPum2ezuf8Hjw4D6g,968
2
2
  XMWAI/bomb_core.py,sha256=h2ZPH3SuoG2L_XOf1dcK8p3lhw5QzhneWl2yMLj1RiU,1819
3
3
  XMWAI/core.py,sha256=rOXj7FnewSdnzBcFLjpnBtrOTCsvMfiycIcdPDagxho,10012
4
4
  XMWAI/idiom_core.py,sha256=yU-VHhqqoutVm6GVikcjL3m9yuB1hUsFBpPYvwY4n5g,1689
5
5
  XMWAI/magic_core.py,sha256=Ms4b12PJ8rjsmceg1VUqWCWx2ebvdhLp4sIF6K_Vaok,3491
6
+ XMWAI/snake_core.py,sha256=oa0pGvcomna_oXknVa5PasbRw0ILEdThfNExJjRbz8w,16418
6
7
  XMWAI/trial_class.py,sha256=fPsl7BZvhzch2FOIG4azr999kjtoly53Acm3LqL8f98,9724
7
8
  XMWAI/web_core.py,sha256=7awPg1kYW3lYrbgylqJvUF3g050bn6H21PgmQ7Kv1wA,10927
9
+ XMWAI/assets/1.png,sha256=eEuKH_M_q3tc_O2bYnuLOsRP-NlJHIbNg0pgrKXEEjw,139720
10
+ XMWAI/assets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ XMWAI/assets/g.png,sha256=hr9hlKJ7y95X-g6-tllrzDNgL1WQkbq5cA5L4jASEAM,11686
12
+ XMWAI/assets/h.png,sha256=qO-kJJOPA8qUth5rqLeOVa_6_n7tU-ABQ14O0EW_YCE,14929
13
+ XMWAI/assets/l.png,sha256=Urm6LxH33HID6ZZbs2oMViUk4GiZ3upLPdsrNU8FlP0,9921
14
+ XMWAI/assets/m.png,sha256=4tl9Rb2JoMD7XLMj3w8jg-92y6D7O-1u0sZCYEoeUtk,10303
15
+ XMWAI/assets/s.png,sha256=v_qmHGmSPhG838vXQdUR2ZX_Z5KLI8T46kaR4P_ktUg,15120
16
+ XMWAI/assets/t.png,sha256=GebzA2UWhXn4u62UeuUjitdpwnJvnxfZ2z_4MFlxvm8,12838
17
+ XMWAI/assets/微软雅黑.ttf,sha256=dpc4EmGE1ojdwHjfIwTdr3olyEDAk3FwyreQSC9AdQ8,15043584
8
18
  XMWAI/file/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
19
  XMWAI/file/idiom.json,sha256=HUtPRUzhxBbWoasjadbmbA_5ngQ5AXLu9weQSZ4hzhk,10319857
10
20
  XMWAI/gif/0.gif,sha256=LGpAReVTyZEb1J-bWYTpxxHbIxmLJ-3wA0lw4cBqdsY,303
@@ -107,8 +117,8 @@ XMWAI/static/images/tomato.png,sha256=FEOEAOdUhW_BDFgTpxOkYc0I5Iu29_gtHb3RIPEej0
107
117
  XMWAI/templates/burger.html,sha256=vDnxpSW8phetyScySsalScZnFKl3LNpy5lJjKxGXgbI,3320
108
118
  XMWAI/templates/nutrition_pie.html,sha256=yJVXI28i-UfvF0xOXGSNLMb8oCJNhh2J3zoRDr5_7DM,5567
109
119
  XMWAI/templates/创意菜谱.html,sha256=RcDgH58QLyUJ9A59wobu3wvchGBY1snVsXcZQZam5M0,4805
110
- xmwai-0.4.2.dist-info/licenses/LICENSE.txt,sha256=bcaIQMrIhdQ3O-PoZlexjmW6h-wLGvHxh5Oksl4ohtc,1066
111
- xmwai-0.4.2.dist-info/METADATA,sha256=6GJQaqzQytoahOWk1ssi_sezcfONReHCN5h2rj9rtJQ,1197
112
- xmwai-0.4.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
113
- xmwai-0.4.2.dist-info/top_level.txt,sha256=yvGcDI-sggK5jqd9wz0saipZvk3oIE3hNGHlqUjxf0c,6
114
- xmwai-0.4.2.dist-info/RECORD,,
120
+ xmwai-0.4.4.dist-info/licenses/LICENSE.txt,sha256=bcaIQMrIhdQ3O-PoZlexjmW6h-wLGvHxh5Oksl4ohtc,1066
121
+ xmwai-0.4.4.dist-info/METADATA,sha256=Qsm0Z6Kkd35aNmUsTNNp4AmIrlIAPYVxnJP8VPiPLs0,1227
122
+ xmwai-0.4.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
123
+ xmwai-0.4.4.dist-info/top_level.txt,sha256=yvGcDI-sggK5jqd9wz0saipZvk3oIE3hNGHlqUjxf0c,6
124
+ xmwai-0.4.4.dist-info/RECORD,,
File without changes