sycommon-python-lib 0.1.56b14__py3-none-any.whl → 0.1.56b15__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.
- sycommon/tools/snowflake.py +105 -152
- {sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/METADATA +1 -1
- {sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/RECORD +6 -6
- {sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/WHEEL +0 -0
- {sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/entry_points.txt +0 -0
- {sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/top_level.txt +0 -0
sycommon/tools/snowflake.py
CHANGED
|
@@ -10,213 +10,191 @@ import psutil
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class ClassProperty:
|
|
13
|
-
"""
|
|
14
|
-
自定义类属性描述符,替代 @classmethod + @property 的废弃写法
|
|
15
|
-
支持通过 类.属性 的方式访问,无需实例化
|
|
16
|
-
"""
|
|
13
|
+
"""支持通过 类.属性 的方式访问,无需实例化"""
|
|
17
14
|
|
|
18
15
|
def __init__(self, func):
|
|
19
16
|
self.func = func
|
|
20
17
|
|
|
21
18
|
def __get__(self, instance: Any, cls: Type) -> str:
|
|
22
|
-
# 调用传入的函数,并传入类本身作为第一个参数
|
|
23
19
|
return self.func(cls)
|
|
24
20
|
|
|
25
21
|
|
|
26
22
|
class Snowflake:
|
|
27
|
-
"""
|
|
28
|
-
|
|
23
|
+
"""
|
|
24
|
+
雪花算法生成器(高并发终极优化版 - 修复语法错误)
|
|
25
|
+
|
|
26
|
+
终极优化点:
|
|
27
|
+
1. 解决溢出死锁:当序列号溢出时,释放锁自旋等待,避免阻塞其他线程。
|
|
28
|
+
2. 锁内原子更新:确保时钟回拨判断与状态更新的原子性。
|
|
29
|
+
3. 移除 sleep:全流程无休眠,纯自旋保证极致吞吐。
|
|
30
|
+
"""
|
|
29
31
|
START_TIMESTAMP = 1388534400000 # 2014-01-01 00:00:00
|
|
30
32
|
SEQUENCE_BITS = 12
|
|
31
33
|
MACHINE_ID_BITS = 10
|
|
32
34
|
MAX_MACHINE_ID = (1 << MACHINE_ID_BITS) - 1 # 0~1023
|
|
33
|
-
MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1
|
|
35
|
+
MAX_SEQUENCE = (1 << SEQUENCE_BITS) - 1 # 4095
|
|
34
36
|
MACHINE_ID_SHIFT = SEQUENCE_BITS
|
|
35
37
|
TIMESTAMP_SHIFT = SEQUENCE_BITS + MACHINE_ID_BITS
|
|
36
|
-
CLOCK_BACKWARD_THRESHOLD = 5
|
|
37
|
-
_MAX_JAVA_LONG = 9223372036854775807
|
|
38
|
+
CLOCK_BACKWARD_THRESHOLD = 5
|
|
39
|
+
_MAX_JAVA_LONG = 9223372036854775807
|
|
38
40
|
|
|
39
|
-
# 类级别的单例实例(线程安全)
|
|
40
41
|
_instance = None
|
|
41
42
|
_instance_lock = threading.Lock()
|
|
42
43
|
|
|
43
44
|
def __init__(self, machine_id: Optional[int] = None):
|
|
44
|
-
"""
|
|
45
|
-
初始化:优先使用传入的machine_id,否则自动从K8s环境获取
|
|
46
|
-
:param machine_id: 手动指定机器ID(None则自动计算)
|
|
47
|
-
"""
|
|
48
|
-
# 前置校验:确保雪花ID不会超过Java Long最大值
|
|
49
45
|
self._validate_timestamp_range()
|
|
50
|
-
|
|
51
|
-
# 自动计算K8s环境下的machine_id
|
|
52
46
|
if machine_id is None:
|
|
53
47
|
machine_id = self._get_k8s_machine_id()
|
|
54
|
-
|
|
55
|
-
# 校验machine_id合法性
|
|
56
48
|
if not (0 <= machine_id <= self.MAX_MACHINE_ID):
|
|
57
49
|
raise ValueError(f"机器ID必须在0~{self.MAX_MACHINE_ID}之间")
|
|
58
50
|
|
|
59
|
-
# 初始化核心参数
|
|
60
51
|
self.machine_id = machine_id
|
|
61
52
|
self.last_timestamp = -1
|
|
62
53
|
self.sequence = 0
|
|
63
54
|
self.lock = threading.Lock()
|
|
64
55
|
|
|
65
56
|
def _validate_timestamp_range(self):
|
|
66
|
-
"""校验当前时间戳是否在雪花ID支持的范围内,避免超过Java Long最大值"""
|
|
67
57
|
max_support_timestamp = self.START_TIMESTAMP + \
|
|
68
58
|
(1 << (64 - self.TIMESTAMP_SHIFT)) - 1
|
|
69
59
|
current_timestamp = self._get_current_timestamp()
|
|
70
60
|
if current_timestamp > max_support_timestamp:
|
|
71
|
-
raise RuntimeError(
|
|
72
|
-
f"当前时间戳({current_timestamp})超过雪花ID支持的最大时间戳({max_support_timestamp}),"
|
|
73
|
-
f"请调整START_TIMESTAMP或减少TIMESTAMP_SHIFT位数"
|
|
74
|
-
)
|
|
61
|
+
raise RuntimeError(f"当前时间戳({current_timestamp})超过支持范围")
|
|
75
62
|
|
|
76
63
|
def _get_k8s_machine_id(self) -> int:
|
|
77
|
-
"""
|
|
78
|
-
从K8s环境自动计算唯一machine_id(无公网依赖,多层兜底,降低重复风险):
|
|
79
|
-
优先级:POD_NAME > POD_IP > 容器内网IP(psutil读取) > 容器主机名 > 进程+时间+随机数(最终兜底)
|
|
80
|
-
"""
|
|
81
|
-
# 1. 优先读取K8s内置的POD_NAME(默认注入,优先级最高)
|
|
82
64
|
pod_name = environ.get("POD_NAME")
|
|
83
65
|
if pod_name:
|
|
84
66
|
return self._hash_to_machine_id(pod_name)
|
|
85
|
-
|
|
86
|
-
# 2. 读取POD_IP(手动配置downwardAPI后必存在)
|
|
87
67
|
pod_ip = environ.get("POD_IP")
|
|
88
68
|
if pod_ip:
|
|
89
69
|
return self._hash_to_machine_id(pod_ip)
|
|
90
|
-
|
|
91
|
-
# 3. 兜底1:读取本机网卡获取内网IP(替换netifaces,使用psutil)
|
|
92
70
|
try:
|
|
93
71
|
local_ip = self._get_local_internal_ip()
|
|
94
72
|
if local_ip:
|
|
95
73
|
return self._hash_to_machine_id(local_ip)
|
|
96
74
|
except Exception:
|
|
97
75
|
pass
|
|
98
|
-
|
|
99
|
-
# 4. 兜底2:获取容器主机名(K8s中默认等于Pod名称,保证唯一)
|
|
100
76
|
hostname = socket.gethostname()
|
|
101
77
|
if hostname:
|
|
102
78
|
return self._hash_to_machine_id(hostname)
|
|
103
|
-
|
|
104
|
-
# 5. 最终兜底:增加熵值(进程ID+毫秒时间戳+随机数),大幅降低重复概率
|
|
105
79
|
fallback_text = f"{os.getpid()}_{int(time.time()*1000)}_{random.randint(0, 100000)}"
|
|
106
80
|
return self._hash_to_machine_id(fallback_text)
|
|
107
81
|
|
|
108
82
|
def _get_local_internal_ip(self) -> Optional[str]:
|
|
109
|
-
"""
|
|
110
|
-
使用psutil读取本机网卡信息,获取非回环的内网IP(跨平台兼容,过滤lo/lo0等回环网卡)
|
|
111
|
-
:return: 内网IP字符串,失败返回None
|
|
112
|
-
"""
|
|
113
83
|
try:
|
|
114
|
-
# 遍历所有网卡接口
|
|
115
84
|
net_if_addrs = psutil.net_if_addrs()
|
|
116
85
|
for interface_name, addrs in net_if_addrs.items():
|
|
117
|
-
# 过滤回环/虚拟网卡(兼容lo、lo0、lo1、Loopback、virtual等)
|
|
118
86
|
if (interface_name.lower().startswith("lo")
|
|
119
|
-
or interface_name.lower() in ["loopback", "virtual"]):
|
|
87
|
+
or interface_name.lower() in ["loopback", "virtual", "docker", "veth"]):
|
|
120
88
|
continue
|
|
121
|
-
# 遍历该网卡的所有地址,优先返回第一个非回环IPv4
|
|
122
89
|
for addr in addrs:
|
|
123
90
|
if addr.family == psutil.AF_INET:
|
|
124
91
|
ip = addr.address
|
|
125
|
-
if ip and not ip.startswith('127.'):
|
|
92
|
+
if ip and not ip.startswith('127.') and not ip.startswith('0.'):
|
|
126
93
|
return ip
|
|
127
94
|
return None
|
|
128
95
|
except Exception:
|
|
129
|
-
# psutil调用失败,降级到纯内置方法
|
|
130
96
|
return self._get_local_ip_fallback()
|
|
131
97
|
|
|
132
98
|
def _get_local_ip_fallback(self) -> Optional[str]:
|
|
133
|
-
"""
|
|
134
|
-
增强版降级方案:纯Python内置方法,多维度获取内网IP(无第三方依赖)
|
|
135
|
-
"""
|
|
136
|
-
# 方案1:socket绑定内网地址(避免访问公网)
|
|
137
99
|
try:
|
|
138
100
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
139
|
-
s.connect(("
|
|
101
|
+
s.connect(("10.0.0.1", 80))
|
|
140
102
|
local_ip = s.getsockname()[0]
|
|
141
103
|
s.close()
|
|
142
|
-
if not local_ip.startswith('127.'):
|
|
104
|
+
if not local_ip.startswith('127.') and not local_ip.startswith('0.'):
|
|
143
105
|
return local_ip
|
|
144
106
|
except Exception:
|
|
145
107
|
pass
|
|
146
|
-
|
|
147
|
-
# 方案2:遍历所有本地IP(通过hostname解析)
|
|
148
108
|
try:
|
|
149
109
|
hostname = socket.gethostname()
|
|
150
110
|
ip_list = socket.gethostbyname_ex(hostname)[2]
|
|
151
111
|
for ip in ip_list:
|
|
152
|
-
if not ip.startswith('127.'):
|
|
112
|
+
if not ip.startswith('127.') and not ip.startswith('0.'):
|
|
153
113
|
return ip
|
|
154
114
|
except Exception:
|
|
155
115
|
pass
|
|
156
|
-
|
|
157
116
|
return None
|
|
158
117
|
|
|
159
118
|
def _hash_to_machine_id(self, text: str) -> int:
|
|
160
|
-
"""将字符串哈希后取模,得到0~1023的machine_id(保证分布均匀)"""
|
|
161
119
|
hash_bytes = hashlib.md5(text.encode("utf-8")).digest()
|
|
162
|
-
|
|
163
|
-
return hash_int % self.MAX_MACHINE_ID
|
|
120
|
+
return int.from_bytes(hash_bytes[:4], byteorder="big") % (self.MAX_MACHINE_ID + 1)
|
|
164
121
|
|
|
165
122
|
def _get_current_timestamp(self) -> int:
|
|
166
|
-
"""获取当前毫秒级时间戳"""
|
|
167
123
|
return int(time.time() * 1000)
|
|
168
124
|
|
|
169
|
-
def _wait_next_millisecond(self, current_timestamp: int) -> int:
|
|
170
|
-
"""等待直到下一个毫秒,避免序列耗尽"""
|
|
171
|
-
while current_timestamp <= self.last_timestamp:
|
|
172
|
-
current_timestamp = self._get_current_timestamp()
|
|
173
|
-
return current_timestamp
|
|
174
|
-
|
|
175
125
|
def generate_id(self) -> int:
|
|
176
|
-
"""
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
raise RuntimeError(
|
|
184
|
-
f"时钟回拨检测:当前时间戳({current_timestamp}) < 上一次时间戳({self.last_timestamp}),"
|
|
185
|
-
f"差值{time_diff}ms(阈值{self.CLOCK_BACKWARD_THRESHOLD}ms)"
|
|
186
|
-
)
|
|
187
|
-
# 轻微回拨:等待时钟追上
|
|
188
|
-
current_timestamp = self._wait_next_millisecond(current_timestamp)
|
|
126
|
+
"""
|
|
127
|
+
生成雪花ID(高并发优化版)
|
|
128
|
+
使用 while True 循环来处理序列号溢出时的重试逻辑
|
|
129
|
+
"""
|
|
130
|
+
while True:
|
|
131
|
+
# 1. 快速获取当前时间
|
|
132
|
+
current_timestamp = self._get_current_timestamp()
|
|
189
133
|
|
|
190
|
-
|
|
191
|
-
if current_timestamp != self.last_timestamp:
|
|
192
|
-
with self.lock:
|
|
193
|
-
self.last_timestamp = current_timestamp
|
|
194
|
-
self.sequence = 0
|
|
195
|
-
else:
|
|
134
|
+
# 2. 加锁,保证状态更新的原子性
|
|
196
135
|
with self.lock:
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
136
|
+
# 读取当前状态(快照)
|
|
137
|
+
last_timestamp = self.last_timestamp
|
|
138
|
+
sequence = self.sequence
|
|
139
|
+
|
|
140
|
+
# 2.1 处理时钟回拨
|
|
141
|
+
time_diff = last_timestamp - current_timestamp
|
|
142
|
+
if time_diff > 0:
|
|
143
|
+
if time_diff > self.CLOCK_BACKWARD_THRESHOLD:
|
|
144
|
+
# 大幅回拨:直接“借用”未来1ms
|
|
145
|
+
current_timestamp = last_timestamp + 1
|
|
146
|
+
else:
|
|
147
|
+
# 微小回拨:锁内自旋等待追上(通常很快)
|
|
148
|
+
while current_timestamp <= last_timestamp:
|
|
149
|
+
current_timestamp = self._get_current_timestamp()
|
|
150
|
+
|
|
151
|
+
# 2.2 计算序列号
|
|
152
|
+
if current_timestamp == last_timestamp:
|
|
153
|
+
# 同一毫秒内
|
|
154
|
+
sequence = (sequence + 1) & self.MAX_SEQUENCE
|
|
155
|
+
if sequence == 0:
|
|
156
|
+
# 【关键优化】:序列号溢出!
|
|
157
|
+
# 此时必须等待下一毫秒。为了不阻塞其他线程,我们:
|
|
158
|
+
# 1. 更新 last_timestamp 标记进入新毫秒 (可选,但这里直接 release 更安全)
|
|
159
|
+
# 2. 释放锁
|
|
160
|
+
# 3. 锁外自旋
|
|
161
|
+
# 4. 下一轮循环重新抢锁
|
|
162
|
+
|
|
163
|
+
self.lock.release() # 手动释放锁
|
|
164
|
+
|
|
165
|
+
# 锁外自旋等待下一毫秒
|
|
166
|
+
while current_timestamp <= last_timestamp:
|
|
167
|
+
current_timestamp = self._get_current_timestamp()
|
|
168
|
+
|
|
169
|
+
# 等待到了新毫秒,进入下一轮循环(continue 默认行为),重新抢锁竞争
|
|
170
|
+
continue
|
|
171
|
+
|
|
172
|
+
elif current_timestamp > last_timestamp:
|
|
173
|
+
# 时间推进,重置序列号
|
|
174
|
+
sequence = 0
|
|
201
175
|
self.last_timestamp = current_timestamp
|
|
176
|
+
self.sequence = sequence
|
|
177
|
+
else:
|
|
178
|
+
# current < last 的情况通常已被回拨逻辑处理
|
|
179
|
+
pass
|
|
180
|
+
|
|
181
|
+
# 2.4 生成 ID
|
|
182
|
+
snowflake_id = (
|
|
183
|
+
((current_timestamp - self.START_TIMESTAMP)
|
|
184
|
+
<< self.TIMESTAMP_SHIFT)
|
|
185
|
+
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
186
|
+
| sequence
|
|
187
|
+
)
|
|
202
188
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
| (self.machine_id << self.MACHINE_ID_SHIFT)
|
|
207
|
-
| self.sequence
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
# 最终校验:确保不超过Java Long最大值
|
|
211
|
-
if snowflake_id > self._MAX_JAVA_LONG:
|
|
212
|
-
raise RuntimeError(
|
|
213
|
-
f"生成的雪花ID({snowflake_id})超过Java Long最大值({self._MAX_JAVA_LONG})")
|
|
189
|
+
# 更新全局状态(如果是同一毫秒)
|
|
190
|
+
if current_timestamp == self.last_timestamp:
|
|
191
|
+
self.sequence = sequence
|
|
214
192
|
|
|
215
|
-
|
|
193
|
+
# 成功生成 ID,退出循环
|
|
194
|
+
return snowflake_id
|
|
216
195
|
|
|
217
196
|
@staticmethod
|
|
218
197
|
def parse_id(snowflake_id: int) -> dict:
|
|
219
|
-
"""解析雪花ID,返回生成时间、机器ID、序列等信息"""
|
|
220
198
|
from datetime import datetime
|
|
221
199
|
sequence = snowflake_id & Snowflake.MAX_SEQUENCE
|
|
222
200
|
machine_id = (snowflake_id >>
|
|
@@ -225,7 +203,6 @@ class Snowflake:
|
|
|
225
203
|
Snowflake.START_TIMESTAMP
|
|
226
204
|
generate_time = datetime.fromtimestamp(
|
|
227
205
|
timestamp / 1000).strftime("%Y-%m-%d %H:%M:%S.%f")[:-3]
|
|
228
|
-
|
|
229
206
|
return {
|
|
230
207
|
"snowflake_id": snowflake_id,
|
|
231
208
|
"generate_time": generate_time,
|
|
@@ -236,65 +213,41 @@ class Snowflake:
|
|
|
236
213
|
|
|
237
214
|
@classmethod
|
|
238
215
|
def next_id(cls) -> str:
|
|
239
|
-
"""
|
|
240
|
-
生成雪花ID(线程安全单例模式,避免重复创建实例,锁内完成所有初始化)
|
|
241
|
-
:return: 雪花ID字符串
|
|
242
|
-
"""
|
|
243
216
|
if cls._instance is None:
|
|
244
217
|
with cls._instance_lock:
|
|
245
218
|
if cls._instance is None:
|
|
246
|
-
# 锁内初始化,避免多线程重复计算machine_id
|
|
247
219
|
cls._instance = cls()
|
|
248
220
|
return str(cls._instance.generate_id())
|
|
249
221
|
|
|
250
222
|
@ClassProperty
|
|
251
223
|
def id(cls) -> str:
|
|
252
|
-
"""
|
|
253
|
-
直接通过 `Snowflake.id` 属性生成雪花ID(兼容Python 3.11+)
|
|
254
|
-
:return: 雪花ID字符串
|
|
255
|
-
"""
|
|
256
224
|
return cls.next_id()
|
|
257
225
|
|
|
258
226
|
|
|
259
227
|
if __name__ == "__main__":
|
|
260
|
-
print("=== 生产级雪花算法ID生成测试 ===")
|
|
261
|
-
# 1. 基础生成测试
|
|
262
|
-
id1 = Snowflake.id
|
|
263
|
-
id2 = Snowflake.id
|
|
264
|
-
id3 = Snowflake.id
|
|
265
|
-
print(f"生成ID1: {id1}")
|
|
266
|
-
print(f"生成ID2: {id2}")
|
|
267
|
-
print(f"生成ID3: {id3}")
|
|
268
|
-
print(f"ID是否唯一: {len({id1, id2, id3}) == 3}")
|
|
269
|
-
|
|
270
|
-
# 2. 解析ID信息
|
|
271
|
-
print("\n=== 雪花ID解析 ===")
|
|
272
|
-
parse_info = Snowflake.parse_id(int(id3))
|
|
273
|
-
for key, value in parse_info.items():
|
|
274
|
-
print(f"{key}: {value}")
|
|
275
|
-
|
|
276
|
-
# 3. 批量唯一性验证(10000个ID)
|
|
277
|
-
print("\n=== 批量唯一性验证(10000个)===")
|
|
278
|
-
id_set = set()
|
|
279
|
-
duplicate_count = 0
|
|
280
|
-
for i in range(10000):
|
|
281
|
-
snow_id = Snowflake.id
|
|
282
|
-
if snow_id in id_set:
|
|
283
|
-
duplicate_count += 1
|
|
284
|
-
id_set.add(snow_id)
|
|
285
|
-
print(f"总生成数量: 10000")
|
|
286
|
-
print(f"唯一ID数量: {len(id_set)}")
|
|
287
|
-
print(f"重复ID数量: {duplicate_count}")
|
|
288
|
-
print(f"机器ID: {Snowflake._instance.machine_id}")
|
|
289
|
-
|
|
290
|
-
# 4. 高并发测试
|
|
291
228
|
import concurrent.futures
|
|
292
|
-
|
|
293
|
-
|
|
229
|
+
|
|
230
|
+
print("=== 高并发终极版 Snowflake 性能测试 ===")
|
|
231
|
+
count = 100000
|
|
232
|
+
workers = 100
|
|
233
|
+
|
|
234
|
+
ids = []
|
|
235
|
+
start_time = time.perf_counter()
|
|
236
|
+
|
|
237
|
+
def task():
|
|
238
|
+
return Snowflake.id
|
|
239
|
+
|
|
294
240
|
with concurrent.futures.ThreadPoolExecutor(max_workers=100) as executor:
|
|
295
|
-
futures = [executor.submit(
|
|
241
|
+
futures = [executor.submit(task) for _ in range(count)]
|
|
296
242
|
for future in concurrent.futures.as_completed(futures):
|
|
297
|
-
|
|
298
|
-
|
|
243
|
+
ids.append(future.result())
|
|
244
|
+
|
|
245
|
+
end_time = time.perf_counter()
|
|
246
|
+
duration = end_time - start_time
|
|
299
247
|
|
|
300
|
-
|
|
248
|
+
unique_count = len(set(ids))
|
|
249
|
+
print(f"生成数量: {len(ids)}")
|
|
250
|
+
print(f"唯一ID数量: {unique_count}")
|
|
251
|
+
print(f"是否有重复: {'是 ❌' if unique_count != len(ids) else '否 ✅'}")
|
|
252
|
+
print(f"总耗时: {duration:.4f} 秒")
|
|
253
|
+
print(f"吞吐量 (QPS): {len(ids) / duration:,.2f}")
|
|
@@ -77,10 +77,10 @@ sycommon/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
77
77
|
sycommon/tools/docs.py,sha256=OPj2ETheuWjXLyaXtaZPbwmJKfJaYXV5s4XMVAUNrms,1607
|
|
78
78
|
sycommon/tools/env.py,sha256=Ah-tBwG2C0_hwLGFebVQgKdWWXCjTzBuF23gCkLHYy4,2437
|
|
79
79
|
sycommon/tools/merge_headers.py,sha256=HV_i52Q-9se3SP8qh7ZGYl8bP7Fxtal4CGVkyMwEdM8,4373
|
|
80
|
-
sycommon/tools/snowflake.py,sha256=
|
|
80
|
+
sycommon/tools/snowflake.py,sha256=MUUkBeCm3Tp9SLBU6oNjLsIPAGfn7OmhrR9YvWWhYm8,9569
|
|
81
81
|
sycommon/tools/timing.py,sha256=OiiE7P07lRoMzX9kzb8sZU9cDb0zNnqIlY5pWqHcnkY,2064
|
|
82
|
-
sycommon_python_lib-0.1.
|
|
83
|
-
sycommon_python_lib-0.1.
|
|
84
|
-
sycommon_python_lib-0.1.
|
|
85
|
-
sycommon_python_lib-0.1.
|
|
86
|
-
sycommon_python_lib-0.1.
|
|
82
|
+
sycommon_python_lib-0.1.56b15.dist-info/METADATA,sha256=JGFO7mcs8XAlW3izcvAQEpESd-pK275n-FqUzl1FJfA,7302
|
|
83
|
+
sycommon_python_lib-0.1.56b15.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
84
|
+
sycommon_python_lib-0.1.56b15.dist-info/entry_points.txt,sha256=q_h2nbvhhmdnsOUZEIwpuoDjaNfBF9XqppDEmQn9d_A,46
|
|
85
|
+
sycommon_python_lib-0.1.56b15.dist-info/top_level.txt,sha256=98CJ-cyM2WIKxLz-Pf0AitWLhJyrfXvyY8slwjTXNuc,17
|
|
86
|
+
sycommon_python_lib-0.1.56b15.dist-info/RECORD,,
|
|
File without changes
|
{sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/entry_points.txt
RENAMED
|
File without changes
|
{sycommon_python_lib-0.1.56b14.dist-info → sycommon_python_lib-0.1.56b15.dist-info}/top_level.txt
RENAMED
|
File without changes
|