mdbq 4.1.13__py3-none-any.whl → 4.1.14__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 mdbq might be problematic. Click here for more details.

mdbq/__version__.py CHANGED
@@ -1 +1 @@
1
- VERSION = '4.1.13'
1
+ VERSION = '4.1.14'
mdbq/mysql/uploader.py CHANGED
@@ -1362,79 +1362,60 @@ class MySQLUploader:
1362
1362
  # 跳过id列,不允许外部传入id
1363
1363
  if (self.case_sensitive and col_name == 'id') or (not self.case_sensitive and col_name.lower() == 'id'):
1364
1364
  continue
1365
- # 对于自动时间戳字段,使用特殊标记让MySQL使用DEFAULT值
1365
+ # 对于自动时间戳字段,跳过处理,让MySQL自动处理
1366
1366
  col_type_lower = filtered_set_typ[col_name].lower()
1367
1367
  is_auto_timestamp = ('timestamp' in col_type_lower and 'current_timestamp' in col_type_lower and
1368
1368
  col_name in ['创建时间', '更新时间'])
1369
1369
 
1370
+ if is_auto_timestamp:
1371
+ # 自动时间戳字段完全跳过,不在INSERT语句中包含
1372
+ continue
1373
+
1370
1374
  if col_name not in row:
1371
1375
  # 对于缺失的列,使用None作为默认值,在_validate_value中会根据allow_null和列类型进行进一步处理
1372
- if is_auto_timestamp:
1373
- # 自动时间戳字段使用特殊标记
1374
- prepared_row[col_name] = 'DEFAULT'
1375
- else:
1376
- try:
1377
- prepared_row[col_name] = self._validate_value(None, filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
1378
- except ValueError as e:
1379
- if not allow_null:
1380
- # 如果不允许空值但验证失败,尝试使用兜底值
1381
- try:
1382
- fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, None)
1383
- if fallback_value is not None:
1384
- prepared_row[col_name] = fallback_value
1385
- logger.warning(f"行号:{row_idx} -> 缺失列: `{col_name}`, 使用兜底值: {fallback_value}", {'row': self._shorten_for_log(row)})
1386
- else:
1387
- error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`, 且不允许空值"
1388
- logger.error(error_msg, {'row': self._shorten_for_log(row)})
1389
- raise ValueError(error_msg)
1390
- except Exception:
1376
+ try:
1377
+ prepared_row[col_name] = self._validate_value(None, filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
1378
+ except ValueError as e:
1379
+ if not allow_null:
1380
+ # 如果不允许空值但验证失败,尝试使用兜底值
1381
+ try:
1382
+ fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, None)
1383
+ if fallback_value is not None:
1384
+ prepared_row[col_name] = fallback_value
1385
+ logger.warning(f"行号:{row_idx} -> 缺失列: `{col_name}`, 使用兜底值: {fallback_value}", {'row': self._shorten_for_log(row)})
1386
+ else:
1391
1387
  error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`, 且不允许空值"
1392
1388
  logger.error(error_msg, {'row': self._shorten_for_log(row)})
1393
1389
  raise ValueError(error_msg)
1394
- else:
1395
- prepared_row[col_name] = None
1390
+ except Exception:
1391
+ error_msg = f"行号:{row_idx} -> 缺失列: `{col_name}`, 且不允许空值"
1392
+ logger.error(error_msg, {'row': self._shorten_for_log(row)})
1393
+ raise ValueError(error_msg)
1394
+ else:
1395
+ prepared_row[col_name] = None
1396
1396
  else:
1397
- if is_auto_timestamp:
1398
- # 自动时间戳字段忽略用户传入的值,使用DEFAULT
1399
- prepared_row[col_name] = 'DEFAULT'
1400
- if row[col_name] is not None: # 如果用户传入了值,给出警告
1401
- logger.warning('忽略自动时间戳字段的用户传入值', {
1402
- '库': db_name,
1403
- '表': table_name,
1404
- '': col_name,
1405
- '用户值': row[col_name],
1406
- '原因': '将使用MySQL CURRENT_TIMESTAMP'
1407
- })
1408
- else:
1409
- try:
1410
- prepared_row[col_name] = self._validate_value(row[col_name], filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
1411
- except ValueError as e:
1412
- # 如果数据验证失败,检查是否为空值且不允许空值,尝试使用兜底值
1413
- original_value = row[col_name]
1414
- is_empty_original = (original_value is None or
1415
- original_value == '' or
1416
- (not isinstance(original_value, (list, dict)) and
1417
- pd.isna(original_value) if hasattr(pd, 'isna') else False))
1418
-
1419
- if is_empty_original and not allow_null:
1420
- try:
1421
- fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, original_value)
1422
- if fallback_value is not None:
1423
- prepared_row[col_name] = fallback_value
1424
- logger.warning(f"行:{row_idx}, 列:`{col_name}` -> 原值验证失败,使用兜底值: {fallback_value}", {
1425
- '原值': original_value,
1426
- '兜底值': fallback_value,
1427
- 'row': self._shorten_for_log(row)
1428
- })
1429
- else:
1430
- logger.error('数据验证失败', {
1431
- '列': col_name,
1432
- '行': row_idx,
1433
- '报错': str(e),
1434
- 'row': self._shorten_for_log(row),
1435
- })
1436
- raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
1437
- except Exception:
1397
+ # 处理用户传入的值
1398
+ try:
1399
+ prepared_row[col_name] = self._validate_value(row[col_name], filtered_set_typ[col_name], allow_null, db_name, table_name, col_name)
1400
+ except ValueError as e:
1401
+ # 如果数据验证失败,检查是否为空值且不允许空值,尝试使用兜底值
1402
+ original_value = row[col_name]
1403
+ is_empty_original = (original_value is None or
1404
+ original_value == '' or
1405
+ (not isinstance(original_value, (list, dict)) and
1406
+ pd.isna(original_value) if hasattr(pd, 'isna') else False))
1407
+
1408
+ if is_empty_original and not allow_null:
1409
+ try:
1410
+ fallback_value = self._get_fallback_value(filtered_set_typ[col_name].lower(), allow_null, db_name, table_name, col_name, original_value)
1411
+ if fallback_value is not None:
1412
+ prepared_row[col_name] = fallback_value
1413
+ logger.warning(f"行:{row_idx}, 列:`{col_name}` -> 原值验证失败,使用兜底值: {fallback_value}", {
1414
+ '原值': original_value,
1415
+ '兜底值': fallback_value,
1416
+ 'row': self._shorten_for_log(row)
1417
+ })
1418
+ else:
1438
1419
  logger.error('数据验证失败', {
1439
1420
  '列': col_name,
1440
1421
  '行': row_idx,
@@ -1442,7 +1423,7 @@ class MySQLUploader:
1442
1423
  'row': self._shorten_for_log(row),
1443
1424
  })
1444
1425
  raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
1445
- else:
1426
+ except Exception:
1446
1427
  logger.error('数据验证失败', {
1447
1428
  '列': col_name,
1448
1429
  '行': row_idx,
@@ -1450,6 +1431,14 @@ class MySQLUploader:
1450
1431
  'row': self._shorten_for_log(row),
1451
1432
  })
1452
1433
  raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
1434
+ else:
1435
+ logger.error('数据验证失败', {
1436
+ '列': col_name,
1437
+ '行': row_idx,
1438
+ '报错': str(e),
1439
+ 'row': self._shorten_for_log(row),
1440
+ })
1441
+ raise ValueError(f"行:{row_idx}, 列:`{col_name}`-> 报错: {str(e)}")
1453
1442
  prepared_data.append(prepared_row)
1454
1443
  return prepared_data, filtered_set_typ
1455
1444
 
@@ -1899,8 +1888,17 @@ class MySQLUploader:
1899
1888
  cached = self._prepared_statements.get(cache_key)
1900
1889
  if cached:
1901
1890
  return cached
1902
- # 获取所有列名(排除id
1903
- all_columns = [col for col in set_typ.keys() if col.lower() != 'id']
1891
+ # 获取所有列名(排除id和自动时间戳字段)
1892
+ all_columns = []
1893
+ for col in set_typ.keys():
1894
+ if col.lower() == 'id':
1895
+ continue
1896
+ # 检查是否是自动时间戳字段
1897
+ col_type_lower = set_typ[col].lower()
1898
+ is_auto_timestamp = ('timestamp' in col_type_lower and 'current_timestamp' in col_type_lower and
1899
+ col in ['创建时间', '更新时间'])
1900
+ if not is_auto_timestamp:
1901
+ all_columns.append(col)
1904
1902
  if not check_duplicate:
1905
1903
  sql = self._build_simple_insert_sql(db_name, table_name, all_columns,
1906
1904
  update_on_duplicate)
@@ -1956,51 +1954,19 @@ class MySQLUploader:
1956
1954
  return str(value)
1957
1955
  return value
1958
1956
 
1959
- def execute_single_row_with_defaults(row):
1960
- """处理单行插入,支持DEFAULT字段"""
1961
- has_defaults = any(row.get(col) == 'DEFAULT' for col in all_columns)
1962
-
1963
- if has_defaults:
1964
- # 分离普通字段和DEFAULT字段
1965
- regular_columns = []
1966
- regular_values = []
1967
- default_columns = []
1968
-
1969
- for col in all_columns:
1970
- val = row.get(col)
1971
- if val == 'DEFAULT':
1972
- default_columns.append(col)
1973
- else:
1974
- regular_columns.append(col)
1975
- regular_values.append(ensure_basic_type(val))
1976
-
1977
- # 构建INSERT ... SET语句
1978
- set_clauses = []
1979
- for col in regular_columns:
1980
- set_clauses.append(f"`{self._validate_identifier(col)}` = %s")
1981
- for col in default_columns:
1982
- set_clauses.append(f"`{self._validate_identifier(col)}` = DEFAULT")
1983
-
1984
- if set_clauses:
1985
- dynamic_sql = f"INSERT INTO `{db_name}`.`{table_name}` SET {', '.join(set_clauses)}"
1986
- if update_on_duplicate and regular_columns:
1987
- update_clauses = [f"`{self._validate_identifier(col)}` = VALUES(`{self._validate_identifier(col)}`)" for col in regular_columns]
1988
- if update_clauses:
1989
- dynamic_sql += f" ON DUPLICATE KEY UPDATE {', '.join(update_clauses)}"
1990
-
1991
- cursor.execute(dynamic_sql, regular_values)
1992
- return cursor.rowcount if cursor.rowcount is not None else 0
1993
- else:
1994
- # 没有DEFAULT字段,使用原有逻辑
1995
- values = [ensure_basic_type(row.get(col)) for col in all_columns]
1996
- if check_duplicate and not update_on_duplicate:
1997
- dup_cols = duplicate_columns if duplicate_columns else [col for col in all_columns if col.lower() not in self.base_excute_col]
1998
- values += [ensure_basic_type(row.get(col)) for col in dup_cols]
1999
- cursor.execute(sql, values)
2000
- return cursor.rowcount if cursor.rowcount is not None else 0
2001
1957
 
2002
1958
  batch_size = get_optimal_batch_size(len(data))
2003
- all_columns = [col for col in set_typ.keys() if col.lower() != 'id']
1959
+ # 排除id列和自动时间戳列
1960
+ all_columns = []
1961
+ for col in set_typ.keys():
1962
+ if col.lower() == 'id':
1963
+ continue
1964
+ # 检查是否是自动时间戳字段
1965
+ col_type_lower = set_typ[col].lower()
1966
+ is_auto_timestamp = ('timestamp' in col_type_lower and 'current_timestamp' in col_type_lower and
1967
+ col in ['创建时间', '更新时间'])
1968
+ if not is_auto_timestamp:
1969
+ all_columns.append(col)
2004
1970
  total_inserted = 0
2005
1971
  total_skipped = 0
2006
1972
  total_failed = 0
@@ -2009,72 +1975,51 @@ class MySQLUploader:
2009
1975
  if transaction_mode == 'batch':
2010
1976
  for i in range(0, len(data), batch_size):
2011
1977
  batch = data[i:i + batch_size]
2012
- # 检查是否有DEFAULT字段,如果有则需要特殊处理
2013
- has_default_fields = any(row.get(col) == 'DEFAULT' for row in batch for col in all_columns)
2014
-
2015
- if has_default_fields:
2016
- # 对于包含DEFAULT字段的情况,逐行处理
2017
- for row in batch:
2018
- try:
2019
- affected = execute_single_row_with_defaults(row)
2020
- if update_on_duplicate:
2021
- total_inserted += 1
2022
- else:
2023
- if affected > 0:
2024
- total_inserted += 1
2025
- else:
2026
- total_skipped += 1
2027
- except pymysql.err.IntegrityError:
2028
- total_skipped += 1
2029
- except Exception as e:
2030
- total_failed += 1
2031
- logger.error('单行插入失败', {
2032
- '库': db_name,
2033
- '表': table_name,
2034
- '错误': str(e)
2035
- })
1978
+ # 使用批量插入逻辑
1979
+ values_list = []
1980
+ for row in batch:
1981
+ values = [ensure_basic_type(row.get(col)) for col in all_columns]
1982
+ if check_duplicate and not update_on_duplicate:
1983
+ dup_cols = duplicate_columns if duplicate_columns else [col for col in all_columns if col.lower() not in self.base_excute_col]
1984
+ values += [ensure_basic_type(row.get(col)) for col in dup_cols]
1985
+ values_list.append(values)
1986
+ try:
1987
+ cursor.executemany(sql, values_list)
2036
1988
  conn.commit()
2037
- else:
2038
- # 没有DEFAULT字段,使用原有逻辑
2039
- values_list = []
2040
- for row in batch:
2041
- values = [ensure_basic_type(row.get(col)) for col in all_columns]
2042
- if check_duplicate and not update_on_duplicate:
2043
- dup_cols = duplicate_columns if duplicate_columns else [col for col in all_columns if col.lower() not in self.base_excute_col]
2044
- values += [ensure_basic_type(row.get(col)) for col in dup_cols]
2045
- values_list.append(values)
2046
- try:
2047
- cursor.executemany(sql, values_list)
2048
- conn.commit()
2049
- # batch模式下,affected_rows表示实际影响的行数
2050
- # 如果update_on_duplicate为True,则affected_rows包含更新的行数
2051
- # 如果update_on_duplicate为False,则affected_rows只包含插入的行数
2052
- affected = cursor.rowcount if cursor.rowcount is not None else 0
2053
- if update_on_duplicate:
2054
- # 当启用更新时,affected_rows包含插入和更新的行数
2055
- # 我们需要区分插入和更新的行数
2056
- # 由于无法准确区分,我们假设所有行都是插入的
2057
- total_inserted += len(batch)
2058
- else:
2059
- # 当不启用更新时,affected_rows只包含插入的行数
2060
- total_inserted += affected
2061
- total_skipped += len(batch) - affected
2062
- except pymysql.err.IntegrityError as e:
2063
- conn.rollback()
2064
- # 在唯一约束冲突时,所有行都被跳过
2065
- total_skipped += len(batch)
2066
- logger.debug('批量插入唯一约束冲突,全部跳过', {'库': db_name, '表': table_name, '错误': str(e)})
2067
- except Exception as e:
2068
- conn.rollback()
2069
- total_failed += len(batch)
2070
- logger.error('批量插入失败', {'库': db_name, '表': table_name, '错误': str(e)})
1989
+ # 在batch模式下,affected_rows表示实际影响的行数
1990
+ # 如果update_on_duplicate为True,则affected_rows包含更新的行数
1991
+ # 如果update_on_duplicate为False,则affected_rows只包含插入的行数
1992
+ affected = cursor.rowcount if cursor.rowcount is not None else 0
1993
+ if update_on_duplicate:
1994
+ # 当启用更新时,affected_rows包含插入和更新的行数
1995
+ # 我们需要区分插入和更新的行数
1996
+ # 由于无法准确区分,我们假设所有行都是插入的
1997
+ total_inserted += len(batch)
1998
+ else:
1999
+ # 当不启用更新时,affected_rows只包含插入的行数
2000
+ total_inserted += affected
2001
+ total_skipped += len(batch) - affected
2002
+ except pymysql.err.IntegrityError as e:
2003
+ conn.rollback()
2004
+ # 在唯一约束冲突时,所有行都被跳过
2005
+ total_skipped += len(batch)
2006
+ logger.debug('批量插入唯一约束冲突,全部跳过', {'库': db_name, '表': table_name, '错误': str(e)})
2007
+ except Exception as e:
2008
+ conn.rollback()
2009
+ total_failed += len(batch)
2010
+ logger.error('批量插入失败', {'库': db_name, '表': table_name, '错误': str(e)})
2071
2011
  elif transaction_mode == 'hybrid':
2072
2012
  hybrid_n = 100 # 可配置
2073
2013
  for i in range(0, len(data), hybrid_n):
2074
2014
  batch = data[i:i + hybrid_n]
2075
2015
  for row in batch:
2076
2016
  try:
2077
- affected = execute_single_row_with_defaults(row)
2017
+ values = [ensure_basic_type(row.get(col)) for col in all_columns]
2018
+ if check_duplicate and not update_on_duplicate:
2019
+ dup_cols = duplicate_columns if duplicate_columns else [col for col in all_columns if col.lower() not in self.base_excute_col]
2020
+ values += [ensure_basic_type(row.get(col)) for col in dup_cols]
2021
+ cursor.execute(sql, values)
2022
+ affected = cursor.rowcount if cursor.rowcount is not None else 0
2078
2023
  if update_on_duplicate:
2079
2024
  # 当启用更新时,affected_rows包含插入和更新的行数
2080
2025
  # 假设所有行都是插入的,因为无法区分插入和更新
@@ -2097,7 +2042,12 @@ class MySQLUploader:
2097
2042
  else: # row模式
2098
2043
  for row in data:
2099
2044
  try:
2100
- affected = execute_single_row_with_defaults(row)
2045
+ values = [ensure_basic_type(row.get(col)) for col in all_columns]
2046
+ if check_duplicate and not update_on_duplicate:
2047
+ dup_cols = duplicate_columns if duplicate_columns else [col for col in all_columns if col.lower() not in self.base_excute_col]
2048
+ values += [ensure_basic_type(row.get(col)) for col in dup_cols]
2049
+ cursor.execute(sql, values)
2050
+ affected = cursor.rowcount if cursor.rowcount is not None else 0
2101
2051
  if update_on_duplicate:
2102
2052
  # 当启用更新时,affected_rows包含插入和更新的行数
2103
2053
  # 假设所有行都是插入的,因为无法区分插入和更新
@@ -2737,9 +2687,9 @@ class MySQLUploader:
2737
2687
 
2738
2688
  # 使用MySQL的CURRENT_TIMESTAMP功能,按固定顺序添加时间戳列
2739
2689
  # 创建时间:插入时自动设置,更新时不变
2740
- updated_set_typ[created_col] = 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP'
2690
+ updated_set_typ[created_col] = 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP'
2741
2691
  # 更新时间:插入和更新时都自动设置为当前时间
2742
- updated_set_typ[updated_col] = 'TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
2692
+ updated_set_typ[updated_col] = 'TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'
2743
2693
 
2744
2694
  # 处理DataFrame格式的数据
2745
2695
  if hasattr(data, 'shape') and hasattr(data, 'columns'):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mdbq
3
- Version: 4.1.13
3
+ Version: 4.1.14
4
4
  Home-page: https://pypi.org/project/mdbq
5
5
  Author: xigua,
6
6
  Author-email: 2587125111@qq.com
@@ -1,5 +1,5 @@
1
1
  mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
2
- mdbq/__version__.py,sha256=ekxTFUNsVfy6qrG8u7tQM8OlFtSVSQTGZsMd31HqV9I,18
2
+ mdbq/__version__.py,sha256=zj2uAJqL04jV1wMjxAyT-2T4sjHxXiGCOfNFXJfgjz0,18
3
3
  mdbq/auth/__init__.py,sha256=pnPMAt63sh1B6kEvmutUuro46zVf2v2YDAG7q-jV_To,24
4
4
  mdbq/auth/auth_backend.py,sha256=iLN7AqiSq7fQgFtNtge_TIlVOR1hrCSZXH6oId6uGX4,116924
5
5
  mdbq/auth/crypto.py,sha256=fcZRFCnrKVVdWDUx_zds51ynFYwS9DBvJOrRQVldrfM,15931
@@ -15,7 +15,7 @@ mdbq/mysql/deduplicator.py,sha256=tzLIm9K9S0lGLlVTI0dDQVYpWX796XCuyufmw1lU26Y,73
15
15
  mdbq/mysql/mysql.py,sha256=pDg771xBugCMSTWeskIFTi3pFLgaqgyG3smzf-86Wn8,56772
16
16
  mdbq/mysql/s_query.py,sha256=N2xHJf2CiUXjXIVBemdst-wamIP3908EGAJOFG13fCU,50475
17
17
  mdbq/mysql/unique_.py,sha256=MaztT-WIyEQUs-OOYY4pFulgHVcXR1BfCy3QUz0XM_U,21127
18
- mdbq/mysql/uploader.py,sha256=0ZnC7KxF1y5-UlCPyrdg-MHpVm_NTHdZ6yY2ytxG8Io,136520
18
+ mdbq/mysql/uploader.py,sha256=5zpvcQpa1BAtFKkVF-EQYXSieeF2hHoZQq6lmEaEnjY,133474
19
19
  mdbq/other/__init__.py,sha256=jso1oHcy6cJEfa7udS_9uO5X6kZLoPBF8l3wCYmr5dM,18
20
20
  mdbq/other/download_sku_picture.py,sha256=X66sVdvVgzoNzmgVJyPtd7bjEvctEKtLPblEPF65EWc,46940
21
21
  mdbq/other/error_handler.py,sha256=4p5haAXSY-P78stp4Xwo_MwAngWYqyKj5ogWIuYXMeY,12631
@@ -35,7 +35,7 @@ mdbq/route/routes.py,sha256=QVGfTvDgu0CpcKCvk1ra74H8uojgqTLUav1fnVAqLEA,29433
35
35
  mdbq/selenium/__init__.py,sha256=AKzeEceqZyvqn2dEDoJSzDQnbuENkJSHAlbHAD0u0ZI,10
36
36
  mdbq/selenium/get_driver.py,sha256=1NTlVUE6QsyjTrVVVqTO2LOnYf578ccFWlWnvIXGtic,20903
37
37
  mdbq/spider/__init__.py,sha256=RBMFXGy_jd1HXZhngB2T2XTvJqki8P_Fr-pBcwijnew,18
38
- mdbq-4.1.13.dist-info/METADATA,sha256=GJ4JaVRELhrQqlakZeZ_C2swBtLhhVXsd9ens555dck,364
39
- mdbq-4.1.13.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
- mdbq-4.1.13.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
41
- mdbq-4.1.13.dist-info/RECORD,,
38
+ mdbq-4.1.14.dist-info/METADATA,sha256=KTtSRhxxSKyWNWlEvHI7YIhD6XDneGbuEw3Qa1ku5EE,364
39
+ mdbq-4.1.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ mdbq-4.1.14.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
41
+ mdbq-4.1.14.dist-info/RECORD,,
File without changes