Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutils.py
More file actions
Latest commit
187 lines (160 loc) · 5.54 KB
/
Copy pathutils.py
File metadata and controls
187 lines (160 loc) · 5.54 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# -*- coding: utf-8 -*-
"""通用工具: PHP 风格格式化 / 时间格式化 / 带缓存的 HTTP 请求"""
importhashlib
importjson
importos
importre
importtime
importurllib.parse
importurllib.request
CACHE_DIRECTORY=os.path.join(os.path.dirname(os.path.abspath(__file__)), 'cache')
_FMT_RE=re.compile(
r"%(?:(\d+)\$)?([-+ 0']*)(\d+)?(?:\.(\d+))?(l?)([bcdeEfFgGosuxX%])"
)
def_format_one(arg, flags, width, precision, conv):
ifconv=='s':
s=''ifargisNoneelsestr(arg)
ifprecisionisnotNone:
s=s[:int(precision)]
ifwidth:
w=int(width)
if'-'inflags:
s=s.ljust(w)
elif'0'inflags:
s=s.rjust(w, '0')
else:
s=s.rjust(w)
returns
ifconvin'duxX':
try:
n=int(round(float(argor0)))
except (TypeError, ValueError):
n=0
spec=''
if'-'inflags:
spec+='<'
if'+'inflags:
spec+='+'
if'0'inflagsand'-'notinflags:
spec+='0'
ifwidth:
spec+=width
spec+= {'d': 'd', 'u': 'd', 'x': 'x', 'X': 'X'}[conv]
returnformat(n, spec)
ifconvin'eEfFgG':
try:
n=float(argor0)
except (TypeError, ValueError):
n=0.0
spec=''
if'-'inflags:
spec+='<'
if'+'inflags:
spec+='+'
if'0'inflagsand'-'notinflags:
spec+='0'
ifwidth:
spec+=width
spec+='.'+ (precisionifprecisionisnotNoneelse'6')
spec+=convifconvin'eEgG'else'f'
returnformat(n, spec)
return''ifargisNoneelsestr(arg)
defphp_vsprintf(fmt, args):
"""模拟 PHP vsprintf, 支持 %1$s 位置参数与 %.3lf 等格式"""
pos= [0]
defrepl(m):
argnum, flags, width, precision, _l, conv=m.groups()
ifconv=='%':
return'%'
ifargnumisnotNone:
idx=int(argnum) -1
else:
idx=pos[0]
pos[0] +=1
arg=args[idx] if0<=idx<len(args) elseNone
return_format_one(arg, flagsor'', width, precision, conv)
return_FMT_RE.sub(repl, fmt)
defbuild_string(layout, placeholders=None):
"""按 PHP vsprintf 风格拼接多行文本"""
placeholders=placeholdersor []
ifisinstance(layout, list):
joined='\n'.join(''ifxisNoneelsestr(x) forxinlayout)
returnphp_vsprintf(joined, placeholders).replace('\n\n', '\n')
returnphp_vsprintf(str(layout), placeholders)
defnumber_format(num, decimals=0):
"""模拟 PHP number_format, None 按 0 处理"""
try:
n=float(numor0)
except (TypeError, ValueError):
n=0.0
ifdecimals==0:
returnformat(int(round(n)), ',d')
returnformat(round(n, decimals), ',.{}f'.format(decimals))
defdiv(a, b, nd=3):
"""安全除法, 任一为 0/None 时返回 0"""
ifnotaornotb:
return0
returnround(a/b, nd)
_PHP_TO_STRFTIME= {
'Y': '%Y', 'm': '%m', 'd': '%d',
'H': '%H', 'i': '%M', 's': '%S',
}
defformat_time(timestamp, in_seconds=False, fmt='Y-m-d H:i', offset=0):
"""gmdate 风格时间格式化, None 按 0 处理"""
ts=timestampor0
secs=round(ts) ifin_secondselseround(ts/1000) +offset
strf=''.join(_PHP_TO_STRFTIME.get(c, c) forcinfmt)
returntime.strftime(strf, time.gmtime(secs))
defget_url(url, query=None, cache_expiration=0, timeout=15):
"""带文件缓存的 GET 请求, 返回 (body, status) 失败时 body 为 None"""
query=queryor {}
full_url=url+'?'+urllib.parse.urlencode(query)
os.makedirs(CACHE_DIRECTORY, exist_ok=True)
cache_file=os.path.join(CACHE_DIRECTORY, hashlib.md5(full_url.encode()).hexdigest())
ifos.path.isfile(cache_file) andtime.time() -os.path.getmtime(cache_file) <=cache_expiration:
withopen(cache_file, 'r', encoding='utf-8') asf:
returnf.read(), 200
headers= {'User-Agent': 'HypixelCheckPy/1.0'}
# 根据最新 api.hypixel.net 文档, API Key 通过 API-Key 请求头传递
if'key'inquery:
q=dict(query)
headers['API-Key'] =q.pop('key')
full_url_req=url+'?'+urllib.parse.urlencode(q)
else:
full_url_req=full_url
req=urllib.request.Request(full_url_req, headers=headers)
try:
withurllib.request.urlopen(req, timeout=timeout) asresp:
body=resp.read().decode('utf-8')
status=resp.status
excepturllib.error.HTTPErrorase:
returnNone, e.code
exceptException:
returnNone, 0
ifstatus==200:
withopen(cache_file, 'w', encoding='utf-8') asf:
f.write(body)
returnbody, 200
returnNone, status
defjson_get(data, path, default=None):
"""按 a.b.c 路径取 JSON 值"""
cur=data
forkeyinpath.split('.'):
ifisinstance(cur, dict) andkeyincur:
cur=cur[key]
else:
returndefault
returncur
defsize_format(byte):
units= ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
pos=0
b=float(byte)
whileb>=1024andpos<len(units) -1:
b/=1024
pos+=1
return'{} {}'.format(round(b, 2), units[pos])
defplain_string(formatted):
"""去除 Minecraft 颜色代码"""
ifformattedisNone:
returnNone
returnre.sub('§[0-9a-fk-or]', '', str(formatted))