mdbq 3.10.7__py3-none-any.whl → 3.10.8__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/__version__.py +1 -1
- mdbq/log/mylogger.py +8 -19
- mdbq/mysql/uploader.py +125 -52
- {mdbq-3.10.7.dist-info → mdbq-3.10.8.dist-info}/METADATA +1 -1
- {mdbq-3.10.7.dist-info → mdbq-3.10.8.dist-info}/RECORD +7 -7
- {mdbq-3.10.7.dist-info → mdbq-3.10.8.dist-info}/WHEEL +0 -0
- {mdbq-3.10.7.dist-info → mdbq-3.10.8.dist-info}/top_level.txt +0 -0
mdbq/__version__.py
CHANGED
@@ -1 +1 @@
|
|
1
|
-
VERSION = '3.10.
|
1
|
+
VERSION = '3.10.8'
|
mdbq/log/mylogger.py
CHANGED
@@ -239,8 +239,9 @@ class MyLogger:
|
|
239
239
|
log_data['异常'] = self.formatException(record.exc_info)
|
240
240
|
|
241
241
|
# 过滤敏感信息
|
242
|
-
if hasattr(record, '过滤'
|
243
|
-
|
242
|
+
if hasattr(record, 'extra_data') and '过滤' in record.extra_data:
|
243
|
+
sensitive_fields = record.extra_data['过滤']
|
244
|
+
for field in sensitive_fields:
|
244
245
|
if field in log_data:
|
245
246
|
log_data[field] = '***'
|
246
247
|
if isinstance(log_data.get('message'), str):
|
@@ -447,8 +448,11 @@ class MyLogger:
|
|
447
448
|
try:
|
448
449
|
self._log_queue.put((level, message, extra), timeout=0.1)
|
449
450
|
except queue.Full:
|
450
|
-
#
|
451
|
-
|
451
|
+
# 队列满时降级为同步日志,添加队列状态信息到extra
|
452
|
+
if extra is None:
|
453
|
+
extra = {}
|
454
|
+
extra['queue_status'] = 'full'
|
455
|
+
self._sync_log(level, message, extra)
|
452
456
|
else:
|
453
457
|
self._sync_log(level, message, extra)
|
454
458
|
|
@@ -509,21 +513,6 @@ class MyLogger:
|
|
509
513
|
if not extra:
|
510
514
|
extra = {}
|
511
515
|
|
512
|
-
# # 获取完整的异常堆栈
|
513
|
-
# tb = exc_info.__traceback__
|
514
|
-
# while tb.tb_next:
|
515
|
-
# tb = tb.tb_next # 获取最内层的堆栈帧
|
516
|
-
#
|
517
|
-
# extra.update({
|
518
|
-
# 'module': tb.tb_frame.f_globals.get('__name__', ''),
|
519
|
-
# 'function': tb.tb_frame.f_code.co_name,
|
520
|
-
# 'line': tb.tb_lineno,
|
521
|
-
# 'file': tb.tb_frame.f_code.co_filename,
|
522
|
-
# '异常': str(exc_info),
|
523
|
-
# '类型': exc_info.__class__.__name__,
|
524
|
-
# '堆栈': self._format_traceback(exc_info)
|
525
|
-
# })
|
526
|
-
|
527
516
|
# 使用inspect获取调用栈
|
528
517
|
frame = inspect.currentframe()
|
529
518
|
try:
|
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
|
-
"""
|
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
|
643
|
+
if isinstance(value, str) and value.strip().endswith('%'):
|
615
644
|
try:
|
616
645
|
# 移除百分号并转换为小数
|
617
|
-
|
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
|
-
|
859
|
-
|
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:
|
995
|
+
:param update_on_duplicate: 遇到重复数据时是否更新旧数据,默认为False
|
963
996
|
:param transaction_mode: 事务模式,可选值:
|
964
|
-
|
965
|
-
|
966
|
-
|
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
|
1095
|
-
duplicate_columns: Optional[List[str]]
|
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:
|
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
|
-
|
1115
|
-
|
1116
|
-
|
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
|
-
|
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
|
-
#
|
1172
|
-
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
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
|
-
|
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',
|
@@ -1,18 +1,18 @@
|
|
1
1
|
mdbq/__init__.py,sha256=Il5Q9ATdX8yXqVxtP_nYqUhExzxPC_qk_WXQ_4h0exg,16
|
2
|
-
mdbq/__version__.py,sha256=
|
2
|
+
mdbq/__version__.py,sha256=3bjRR4HE9A875_CgBnallmc2aayagpc0BO_tqJqU6vE,18
|
3
3
|
mdbq/aggregation/__init__.py,sha256=EeDqX2Aml6SPx8363J-v1lz0EcZtgwIBYyCJV6CcEDU,40
|
4
4
|
mdbq/aggregation/optimize.py,sha256=2oalzD9weZhDclUC22OLxYa8Zj7KnmsGUoUau_Jlyc4,19796
|
5
5
|
mdbq/aggregation/query_data.py,sha256=5_OzjGR5Sq00q-EgAYmSE5V9i4Solw9y4hkldl4mvt8,179808
|
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=
|
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
12
|
mdbq/mysql/deduplicator.py,sha256=brhX3eyE8-kn3nAYweKfBbAkXiNcyw_pL4CTyPqmPBg,21983
|
13
13
|
mdbq/mysql/mysql.py,sha256=Fzaqbjg5g3HdNl50jInIrdurdzcgR2CCzdKLVImD1-Q,55339
|
14
14
|
mdbq/mysql/s_query.py,sha256=X055aLRAgxVvueXx4NbfNjp6MyBI02_XBb1pTKw09L0,8660
|
15
|
-
mdbq/mysql/uploader.py,sha256=
|
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
|
@@ -25,7 +25,7 @@ mdbq/redis/__init__.py,sha256=YtgBlVSMDphtpwYX248wGge1x-Ex_mMufz4-8W0XRmA,12
|
|
25
25
|
mdbq/redis/getredis.py,sha256=Uk8-cOWT0JU1qRyIVqdbYokSLvkDIAfcokmYj1ebw8k,24104
|
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.
|
29
|
-
mdbq-3.10.
|
30
|
-
mdbq-3.10.
|
31
|
-
mdbq-3.10.
|
28
|
+
mdbq-3.10.8.dist-info/METADATA,sha256=-hNRDByZy-pto3aGS3WGlOivKQCC8CpM5NHy5yd8VgI,364
|
29
|
+
mdbq-3.10.8.dist-info/WHEEL,sha256=jB7zZ3N9hIM9adW7qlTAyycLYW9npaWKLRzaoVcLKcM,91
|
30
|
+
mdbq-3.10.8.dist-info/top_level.txt,sha256=2FQ-uLnCSB-OwFiWntzmwosW3X2Xqsg0ewh1axsaylA,5
|
31
|
+
mdbq-3.10.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|