mdbq 3.10.7__py3-none-any.whl → 3.10.9__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.
mdbq/mysql/uploader.py CHANGED
@@ -1,3 +1,17 @@
1
+ """
2
+ MySQL数据上传工具类
3
+
4
+ 这个模块提供了一个用于将数据上传到MySQL数据库的工具类。它支持以下主要功能:
5
+ 1. 自动创建数据库和表
6
+ 2. 支持数据分表存储
7
+ 3. 支持数据重复检查和更新
8
+ 4. 支持批量数据插入
9
+ 5. 支持多种事务模式
10
+ 6. 自动类型转换和验证
11
+ 7. 连接池管理
12
+ 8. 错误重试机制
13
+ """
14
+
1
15
  # -*- coding:utf-8 -*-
2
16
  import datetime
3
17
  import re
@@ -6,8 +20,6 @@ from functools import wraps
6
20
  import warnings
7
21
  import pymysql
8
22
  import pandas as pd
9
- import os
10
- import logging
11
23
  from mdbq.log import mylogger
12
24
  from typing import Union, List, Dict, Optional, Any, Tuple, Set
13
25
  from dbutils.pooled_db import PooledDB
@@ -60,23 +72,40 @@ def count_decimal_places(num_str):
60
72
 
61
73
 
62
74
  class StatementCache(OrderedDict):
63
- """基于OrderedDict实现的LRU缓存策略,用于缓存SQL语句"""
75
+ """
76
+ 基于OrderedDict实现的LRU缓存策略,用于缓存SQL语句
77
+
78
+ 这个类继承自OrderedDict,实现了最近最少使用(LRU)的缓存策略。
79
+ 当缓存达到最大容量时,会自动删除最早添加的项。
80
+ """
64
81
  def __init__(self, maxsize=100):
65
82
  """
66
83
  初始化缓存
67
84
 
68
- :param maxsize: 最大缓存大小,默认为100
85
+ :param maxsize: 最大缓存大小,默认为100条SQL语句
69
86
  """
70
87
  super().__init__()
71
88
  self.maxsize = maxsize
72
89
 
73
90
  def __setitem__(self, key, value):
91
+ """
92
+ 重写设置项方法,实现LRU策略
93
+
94
+ :param key: 缓存键
95
+ :param value: 缓存值
96
+ """
74
97
  super().__setitem__(key, value)
75
98
  if len(self) > self.maxsize:
76
99
  self.popitem(last=False)
77
100
 
78
101
 
79
102
  class MySQLUploader:
103
+ """
104
+ MySQL数据上传工具类
105
+
106
+ 提供了一系列方法用于将数据上传到MySQL数据库,支持自动建表、分表、数据验证等功能。
107
+ 使用连接池管理数据库连接,提供错误重试机制。
108
+ """
80
109
  def __init__(
81
110
  self,
82
111
  username: str,
@@ -94,6 +123,8 @@ class MySQLUploader:
94
123
  ssl: Optional[Dict] = None
95
124
  ):
96
125
  """
126
+ 初始化MySQL上传器
127
+
97
128
  :param username: 数据库用户名
98
129
  :param password: 数据库密码
99
130
  :param host: 数据库主机地址,默认为localhost
@@ -106,8 +137,6 @@ class MySQLUploader:
106
137
  :param connect_timeout: 连接超时(秒),默认为10
107
138
  :param read_timeout: 读取超时(秒),默认为30
108
139
  :param write_timeout: 写入超时(秒),默认为30
109
- :param base_excute_col: # 排重插入数据时始终排除该列
110
- :param case_sensitive: # 是否保持大小写敏感,默认为False(转为小写)
111
140
  :param ssl: SSL配置字典,默认为None
112
141
  """
113
142
  self.username = username
@@ -362,10 +391,10 @@ class MySQLUploader:
362
391
  return f"{table_name}_{date_obj.year}_{date_obj.month:02d}"
363
392
  else:
364
393
  logger.error(sys._getframe().f_code.co_name, {
365
- "分表方式必须是 'year' 或 'month'": partition_by,
394
+ "分表方式必须是 'year' 或 'month' 或 'None'": partition_by,
366
395
  '表': table_name
367
396
  })
368
- raise ValueError("分表方式必须是 'year' 或 'month'")
397
+ raise ValueError("分表方式必须是 'year' 或 'month' 或 'None'")
369
398
 
370
399
  def _validate_identifier(self, identifier: str) -> str:
371
400
  """
@@ -611,10 +640,15 @@ class MySQLUploader:
611
640
  column_type_lower = column_type.lower()
612
641
 
613
642
  # 处理百分比值
614
- if str(value).strip().endswith('%'):
643
+ if isinstance(value, str) and value.strip().endswith('%'):
615
644
  try:
616
645
  # 移除百分号并转换为小数
617
- percent_value = float(value.strip().replace('%', ''))
646
+ percent_str = value.strip().replace('%', '')
647
+ # 处理科学计数法
648
+ if 'e' in percent_str.lower():
649
+ percent_value = float(percent_str)
650
+ else:
651
+ percent_value = float(percent_str)
618
652
  decimal_value = percent_value / 100
619
653
  return decimal_value
620
654
  except ValueError:
@@ -855,8 +889,8 @@ class MySQLUploader:
855
889
  data = data.replace({pd.NA: None}).to_dict('records')
856
890
  except Exception as e:
857
891
  logger.error(sys._getframe().f_code.co_name, {
858
- '数据转字典时发生错误': str(e),
859
- '数据': data,
892
+ '数据转字典时发生错误': str(e),
893
+ '数据': data,
860
894
  })
861
895
  raise ValueError(f"数据转字典时发生错误: {e}")
862
896
  elif isinstance(data, dict):
@@ -878,7 +912,7 @@ class MySQLUploader:
878
912
 
879
913
  # 获取数据中实际存在的列名
880
914
  data_columns = set()
881
- if data:
915
+ if data and len(data) > 0:
882
916
  data_columns = set(data[0].keys())
883
917
 
884
918
  # 过滤set_typ,只保留数据中存在的列
@@ -910,7 +944,6 @@ class MySQLUploader:
910
944
  if not allow_null:
911
945
  error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`"
912
946
  logger.error(error_msg)
913
-
914
947
  raise ValueError(error_msg)
915
948
  prepared_row[col_name] = None
916
949
  else:
@@ -949,7 +982,7 @@ class MySQLUploader:
949
982
 
950
983
  :param db_name: 数据库名
951
984
  :param table_name: 表名
952
- :param data: 要上传的数据
985
+ :param data: 要上传的数据,支持字典、字典列表或DataFrame格式
953
986
  :param set_typ: 列名和数据类型字典 {列名: 数据类型}
954
987
  :param primary_keys: 主键列列表,可选
955
988
  :param check_duplicate: 是否检查重复数据,默认为False
@@ -959,12 +992,11 @@ class MySQLUploader:
959
992
  :param partition_date_column: 用于分表的日期列名,默认为'日期'
960
993
  :param auto_create: 表不存在时是否自动创建,默认为True
961
994
  :param indexes: 需要创建索引的列列表,可选
962
- :param update_on_duplicate: 遇到重复数据时是否更新旧数据(默认为False)
995
+ :param update_on_duplicate: 遇到重复数据时是否更新旧数据,默认为False
963
996
  :param transaction_mode: 事务模式,可选值:
964
- - 'row' : 逐行提交事务(错误隔离性好)
965
- - 'batch' : 整批提交事务(性能最优)
966
- - 'hybrid' : 混合模式(每N行提交,平衡性能与安全性)
967
- 默认值为 'batch'
997
+ - 'row' : 逐行提交事务(错误隔离性好)
998
+ - 'batch' : 整批提交事务(性能最优)
999
+ - 'hybrid' : 混合模式(每N行提交,平衡性能与安全性)
968
1000
  :raises: 可能抛出各种验证和数据库相关异常
969
1001
  """
970
1002
  # upload_start = time.time()
@@ -1091,9 +1123,8 @@ class MySQLUploader:
1091
1123
  table_name: str,
1092
1124
  data: List[Dict],
1093
1125
  set_typ: Dict[str, str],
1094
- check_duplicate: bool = False,
1095
- duplicate_columns: Optional[List[str]] = None,
1096
- batch_size: int = 1000,
1126
+ check_duplicate: bool,
1127
+ duplicate_columns: Optional[List[str]],
1097
1128
  batch_id: Optional[str] = None,
1098
1129
  update_on_duplicate: bool = False,
1099
1130
  transaction_mode: str = "batch"
@@ -1105,16 +1136,14 @@ class MySQLUploader:
1105
1136
  :param table_name: 表名
1106
1137
  :param data: 要插入的数据列表
1107
1138
  :param set_typ: 列名和数据类型字典 {列名: 数据类型}
1108
- :param check_duplicate: 是否检查重复数据,默认为False
1139
+ :param check_duplicate: 是否检查重复数据
1109
1140
  :param duplicate_columns: 用于检查重复的列,可选
1110
- :param batch_size: 批量插入大小,默认为1000
1111
- :param update_on_duplicate: 遇到重复数据时是否更新旧数据(默认为False)
1112
1141
  :param batch_id: 批次ID用于日志追踪,可选
1142
+ :param update_on_duplicate: 遇到重复数据时是否更新旧数据,默认为False
1113
1143
  :param transaction_mode: 事务模式,可选值:
1114
- - 'row' : 逐行提交事务(错误隔离性好)
1115
- - 'batch' : 整批提交事务(性能最优)
1116
- - 'hybrid' : 混合模式(每N行提交,平衡性能与安全性)
1117
- 默认值为 'batch'
1144
+ - 'row' : 逐行提交事务(错误隔离性好)
1145
+ - 'batch' : 整批提交事务(性能最优)
1146
+ - 'hybrid' : 混合模式(每N行提交,平衡性能与安全性)
1118
1147
  """
1119
1148
  if not data:
1120
1149
  return
@@ -1133,7 +1162,8 @@ class MySQLUploader:
1133
1162
  total_inserted, total_skipped, total_failed = self._execute_batch_insert(
1134
1163
  db_name, table_name, data, set_typ,
1135
1164
  sql, check_duplicate, duplicate_columns,
1136
- batch_size, batch_id, transaction_mode
1165
+ batch_id, transaction_mode,
1166
+ update_on_duplicate
1137
1167
  )
1138
1168
 
1139
1169
  logger.info('插入完成', {
@@ -1168,16 +1198,18 @@ class MySQLUploader:
1168
1198
  VALUES ({placeholders})
1169
1199
  """
1170
1200
 
1171
- # # 情况2:不检查重复但允许更新
1172
- # if update_on_duplicate:
1173
- # update_clause = ", ".join([f"`{col}` = VALUES(`{col}`)"
1174
- # for col in columns])
1175
- # sql += f" ON DUPLICATE KEY UPDATE {update_clause}"
1201
+ # 情况2:不检查重复但允许更新
1202
+ if update_on_duplicate:
1203
+ update_clause = ", ".join([f"`{col}` = VALUES(`{col}`)"
1204
+ for col in columns])
1205
+ sql += f" ON DUPLICATE KEY UPDATE {update_clause}"
1176
1206
 
1177
1207
  return sql
1178
1208
 
1179
1209
  def _build_duplicate_check_sql(self, db_name, table_name, all_columns,
1180
1210
  duplicate_columns, update_on_duplicate, set_typ):
1211
+ if duplicate_columns is None:
1212
+ duplicate_columns = []
1181
1213
  duplicate_columns = [_item for _item in duplicate_columns if _item.lower() not in self.base_excute_col]
1182
1214
  safe_columns = [self._validate_identifier(col) for col in all_columns]
1183
1215
  placeholders = ','.join(['%s'] * len(safe_columns))
@@ -1286,11 +1318,25 @@ class MySQLUploader:
1286
1318
  sql: str,
1287
1319
  check_duplicate: bool,
1288
1320
  duplicate_columns: Optional[List[str]],
1289
- batch_size: int,
1290
1321
  batch_id: Optional[str],
1291
- transaction_mode: str
1322
+ transaction_mode: str,
1323
+ update_on_duplicate: bool = False
1292
1324
  ) -> Tuple[int, int, int]:
1293
1325
  """执行批量插入操作"""
1326
+
1327
+ def get_optimal_batch_size(total_rows: int) -> int:
1328
+ # 根据数据量调整批量大小
1329
+ if total_rows <= 100:
1330
+ return total_rows
1331
+ elif total_rows <= 1000:
1332
+ return 500
1333
+ elif total_rows <= 10000:
1334
+ return 1000
1335
+ else:
1336
+ return 2000
1337
+
1338
+ batch_size = get_optimal_batch_size(len(data))
1339
+
1294
1340
  # 获取所有列名(排除id列)
1295
1341
  all_columns = [col for col in set_typ.keys()
1296
1342
  if col.lower() != 'id']
@@ -1306,7 +1352,7 @@ class MySQLUploader:
1306
1352
  batch_inserted, batch_skipped, batch_failed = self._process_batch(
1307
1353
  conn, cursor, db_name, table_name, batch, all_columns,
1308
1354
  sql, check_duplicate, duplicate_columns, batch_id,
1309
- transaction_mode, i, len(data)
1355
+ transaction_mode, i, len(data), update_on_duplicate
1310
1356
  )
1311
1357
 
1312
1358
  # 更新总统计
@@ -1330,7 +1376,8 @@ class MySQLUploader:
1330
1376
  batch_id: Optional[str],
1331
1377
  transaction_mode: str,
1332
1378
  batch_index: int,
1333
- total_data_length: int
1379
+ total_data_length: int,
1380
+ update_on_duplicate: bool = False
1334
1381
  ) -> Tuple[int, int, int]:
1335
1382
  """处理单个批次的数据插入"""
1336
1383
  batch_inserted = 0
@@ -1344,7 +1391,8 @@ class MySQLUploader:
1344
1391
  result = self._process_single_row(
1345
1392
  db_name, table_name,
1346
1393
  cursor, row, all_columns, sql,
1347
- check_duplicate, duplicate_columns
1394
+ check_duplicate, duplicate_columns,
1395
+ update_on_duplicate
1348
1396
  )
1349
1397
  if result == 'inserted':
1350
1398
  batch_inserted += 1
@@ -1376,7 +1424,8 @@ class MySQLUploader:
1376
1424
  result = self._process_single_row(
1377
1425
  db_name, table_name,
1378
1426
  cursor, row, all_columns, sql,
1379
- check_duplicate, duplicate_columns
1427
+ check_duplicate, duplicate_columns,
1428
+ update_on_duplicate
1380
1429
  )
1381
1430
  if result == 'inserted':
1382
1431
  batch_inserted += 1
@@ -1400,7 +1449,6 @@ class MySQLUploader:
1400
1449
  '批次/当前行': f'{batch_id} {row_idx}/{len(batch)}',
1401
1450
  'error_type': type(e).__name__,
1402
1451
  '单行插入失败': str(e),
1403
- '数据类型': set_typ,
1404
1452
  '是否排重': check_duplicate,
1405
1453
  '排重列': duplicate_columns,
1406
1454
  '事务模式': transaction_mode,
@@ -1421,21 +1469,24 @@ class MySQLUploader:
1421
1469
  all_columns: List[str],
1422
1470
  sql: str,
1423
1471
  check_duplicate: bool,
1424
- duplicate_columns: Optional[List[str]]
1472
+ duplicate_columns: Optional[List[str]],
1473
+ update_on_duplicate: bool = False
1425
1474
  ) -> str:
1426
1475
  """处理单行数据插入"""
1427
1476
  try:
1428
1477
  # 准备参数
1429
1478
  row_values = [row.get(col) for col in all_columns]
1479
+
1480
+ # 确定排重列(排除id和更新时间列)
1481
+ dup_cols = duplicate_columns if duplicate_columns else [
1482
+ col for col in all_columns
1483
+ if col.lower() not in self.base_excute_col
1484
+ ]
1485
+
1430
1486
  if check_duplicate:
1431
- # 确定排重列(排除id和更新时间列)
1432
- dup_cols = duplicate_columns if duplicate_columns else [
1433
- col for col in all_columns
1434
- if col.lower() not in self.base_excute_col
1435
- ]
1436
-
1437
1487
  # 添加排重条件参数
1438
- row_values += [row.get(col) for col in dup_cols]
1488
+ dup_values = [row.get(col) for col in dup_cols]
1489
+ row_values.extend(dup_values)
1439
1490
 
1440
1491
  # logger.info(sql)
1441
1492
  # logger.info(row_values)
@@ -1459,6 +1510,10 @@ class MySQLUploader:
1459
1510
  def close(self):
1460
1511
  """
1461
1512
  关闭连接池并清理资源
1513
+
1514
+ 这个方法会安全地关闭数据库连接池,并清理相关资源。
1515
+ 建议结束时手动调用此方法。
1516
+
1462
1517
  :raises: 可能抛出关闭连接时的异常
1463
1518
  """
1464
1519
  try:
@@ -1486,16 +1541,24 @@ class MySQLUploader:
1486
1541
 
1487
1542
  :return: 连接池健康返回True,否则返回False
1488
1543
  """
1544
+ conn = None
1489
1545
  try:
1490
1546
  conn = self.pool.connection()
1491
1547
  conn.ping(reconnect=True)
1492
- conn.close()
1493
1548
  return True
1494
1549
  except Exception as e:
1495
1550
  logger.warning("连接池健康检查失败", {
1496
1551
  'error': str(e)
1497
1552
  })
1498
1553
  return False
1554
+ finally:
1555
+ if conn:
1556
+ try:
1557
+ conn.close()
1558
+ except Exception as e:
1559
+ logger.warning("关闭连接时出错", {
1560
+ 'error': str(e)
1561
+ })
1499
1562
 
1500
1563
  def retry_on_failure(max_retries=3, delay=1):
1501
1564
  def decorator(func):
@@ -1527,6 +1590,16 @@ class MySQLUploader:
1527
1590
 
1528
1591
 
1529
1592
  def main():
1593
+ """
1594
+ 示例代码:
1595
+
1596
+ 这个示例展示了如何:
1597
+ 1. 创建上传器实例
1598
+ 2. 定义数据表结构
1599
+ 3. 准备测试数据
1600
+ 4. 上传数据到数据库
1601
+ 5. 关闭连接
1602
+ """
1530
1603
  uploader = MySQLUploader(
1531
1604
  username='root',
1532
1605
  password='pw',
mdbq/redis/getredis.py CHANGED
@@ -1,8 +1,6 @@
1
1
  # -*- coding: UTF-8 –*-
2
2
  import os
3
- import sys
4
3
  import random
5
- import redis
6
4
  import pandas as pd
7
5
  import numpy as np
8
6
  import json
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: mdbq
3
- Version: 3.10.7
3
+ Version: 3.10.9
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1,18 +1,18 @@
1
1
  mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
- mdbq/__version__.py,sha256=luTjFBlvNtusrL5oiS9aezU8M7ZT0e3xyxokQP2F1Es,18
2
+ mdbq/__version__.py,sha256=HNkSJG3z_6qz_o8xClGa5PA9tG04IvFuLgr3lvdcfKM,18
3
3
  mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
4
- mdbq/aggregation/optimize.py,sha256=2oalzD9weZhDclUC22OLxYa8Zj7KnmsGUoUau_Jlyc4,19796
5
- mdbq/aggregation/query_data.py,sha256=5_OzjGR5Sq00q-EgAYmSE5V9i4Solw9y4hkldl4mvt8,179808
4
+ mdbq/aggregation/optimize.py,sha256=zC_w_aVYXwmvfF0Z8iSGMmv5vptF0rP-Dz5zmp0gXTU,19820
5
+ mdbq/aggregation/query_data.py,sha256=fdotW8qdAyDB13p7r3p6AGBkavcHnf6hIvSMtcS7vqE,179875
6
6
  mdbq/config/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
7
7
  mdbq/config/config.py,sha256=eaTfrfXQ65xLqjr5I8-HkZd_jEY1JkGinEgv3TSLeoQ,3170
8
8
  mdbq/log/__init__.py,sha256=Mpbrav0s0ifLL7lVDAuePEi1hJKiSHhxcv1byBKDl5E,15
9
- mdbq/log/mylogger.py,sha256=1c4ij0BlSeFvjLoglJCcFXkYfAUw3Zuh2HjMmRd5fa8,24495
9
+ mdbq/log/mylogger.py,sha256=07sstIeaIQUJXwpMwmxppRI7kW7QwZFnv4Rr3UDlyUs,24133
10
10
  mdbq/log/spider_logging.py,sha256=-ozWWEGm3HVv604ozs_OOvVwumjokmUPwbaodesUrPY,1664
11
11
  mdbq/mysql/__init__.py,sha256=A_DPJyAoEvTSFojiI2e94zP0FKtCkkwKP1kYUCSyQzo,11
12
- mdbq/mysql/deduplicator.py,sha256=brhX3eyE8-kn3nAYweKfBbAkXiNcyw_pL4CTyPqmPBg,21983
13
- mdbq/mysql/mysql.py,sha256=Fzaqbjg5g3HdNl50jInIrdurdzcgR2CCzdKLVImD1-Q,55339
14
- mdbq/mysql/s_query.py,sha256=X055aLRAgxVvueXx4NbfNjp6MyBI02_XBb1pTKw09L0,8660
15
- mdbq/mysql/uploader.py,sha256=Lw9lgzmJkr5ojHQ_VMy4z710ZJpIoH7pzn4zrpZokms,62371
12
+ mdbq/mysql/deduplicator.py,sha256=Ingkpaz7Wy6qHxDP4aiy_2c76dSyuIwPF8_pb5dYC48,22542
13
+ mdbq/mysql/mysql.py,sha256=Lfy9PsEdgmdRtcG_UUgegH3bFTJPhByTWkcAYl8G6m0,56788
14
+ mdbq/mysql/s_query.py,sha256=dlnrVJ3-Vp1Suv9CNbPxyYSRqRJUHjOpF39tb2F-wBc,10190
15
+ mdbq/mysql/uploader.py,sha256=3RzslC10pNIYm-0NASicvCHXH0zgUXx7uD1jE21z_OU,64677
16
16
  mdbq/other/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
17
17
  mdbq/other/download_sku_picture.py,sha256=YU8DxKMXbdeE1OOKEA848WVp62jYHw5O4tXTjUdq9H0,44832
18
18
  mdbq/other/otk.py,sha256=iclBIFbQbhlqzUbcMMoePXBpcP1eZ06ZtjnhcA_EbmE,7241
@@ -22,10 +22,10 @@ mdbq/pbix/__init__.py,sha256=Trtfaynu9RjoTyLLYBN2xdRxTvm_zhCniUkVTAYwcjo,24
22
22
  mdbq/pbix/pbix_refresh.py,sha256=JUjKW3bNEyoMVfVfo77UhguvS5AWkixvVhDbw4_MHco,2396
23
23
  mdbq/pbix/refresh_all.py,sha256=OBT9EewSZ0aRS9vL_FflVn74d4l2G00wzHiikCC4TC0,5926
24
24
  mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
25
- mdbq/redis/getredis.py,sha256=Uk8-cOWT0JU1qRyIVqdbYokSLvkDIAfcokmYj1ebw8k,24104
25
+ mdbq/redis/getredis.py,sha256=YHgCKO8mEsslwet33K5tGss-nrDDwPnOSlhA9iBu0jY,24078
26
26
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
27
27
  mdbq/spider/aikucun.py,sha256=YyPWa_nOH1zs8wgTDcgzn5w8szGKWPyWzmWMVIPkFnU,21638
28
- mdbq-3.10.7.dist-info/METADATA,sha256=bsu32knUObF_BeSlp2OYExxro_8H1ygHLX2ZMay9Bug,364
29
- mdbq-3.10.7.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
30
- mdbq-3.10.7.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
31
- mdbq-3.10.7.dist-info/RECORD,,
28
+ mdbq-3.10.9.dist-info/METADATA,sha256=ur4y78xMogVypcNrUAkxFs7JSyZTQPTMpX5YC1wzhCA,364
29
+ mdbq-3.10.9.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
30
+ mdbq-3.10.9.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
31
+ mdbq-3.10.9.dist-info/RECORD,,
File without changes