kevin-toolbox-dev 1.4.6__py3-none-any.whl → 1.4.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.
- kevin_toolbox/__init__.py +2 -2
- kevin_toolbox/{developing → computer_science/algorithm}/decorator/__init__.py +2 -1
- kevin_toolbox/computer_science/algorithm/decorator/retry.py +62 -0
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/__init__.py +1 -0
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/multi_process_execute.py +109 -0
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/multi_thread_execute.py +50 -29
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/utils/__init__.py +15 -0
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/utils/wrapper_with_timeout_1.py +69 -0
- kevin_toolbox/computer_science/algorithm/parallel_and_concurrent/utils/wrapper_with_timeout_2.py +76 -0
- kevin_toolbox/computer_science/algorithm/registration/__init__.py +1 -0
- kevin_toolbox/computer_science/algorithm/registration/serializer_for_registry_execution.py +82 -0
- kevin_toolbox/computer_science/data_structure/executor.py +2 -2
- kevin_toolbox/data_flow/core/cache/cache_manager_for_iterator.py +1 -1
- kevin_toolbox/data_flow/file/json_/write_json.py +36 -3
- kevin_toolbox/env_info/variable_/env_vars_parser.py +17 -2
- kevin_toolbox/nested_dict_list/serializer/backends/_json_.py +2 -2
- kevin_toolbox/nested_dict_list/serializer/variable.py +14 -2
- kevin_toolbox/nested_dict_list/serializer/write.py +2 -0
- kevin_toolbox/network/__init__.py +10 -0
- kevin_toolbox/network/download_file.py +120 -0
- kevin_toolbox/network/fetch_content.py +55 -0
- kevin_toolbox/network/fetch_metadata.py +64 -0
- kevin_toolbox/network/get_response.py +50 -0
- kevin_toolbox/network/variable.py +6 -0
- kevin_toolbox/patches/for_logging/build_logger.py +1 -1
- kevin_toolbox/patches/for_matplotlib/common_charts/__init__.py +45 -0
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_bars.py +63 -22
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_confusion_matrix.py +67 -20
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_distribution.py +66 -17
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_from_record.py +21 -0
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_lines.py +63 -19
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_scatters.py +61 -12
- kevin_toolbox/patches/for_matplotlib/common_charts/plot_scatters_matrix.py +57 -14
- kevin_toolbox/patches/for_matplotlib/common_charts/utils/__init__.py +3 -0
- kevin_toolbox/patches/for_matplotlib/common_charts/utils/get_output_path.py +15 -0
- kevin_toolbox/patches/for_matplotlib/common_charts/utils/save_plot.py +11 -0
- kevin_toolbox/patches/for_matplotlib/common_charts/utils/save_record.py +34 -0
- kevin_toolbox/patches/for_matplotlib/variable.py +20 -0
- kevin_toolbox_dev-1.4.8.dist-info/METADATA +86 -0
- {kevin_toolbox_dev-1.4.6.dist-info → kevin_toolbox_dev-1.4.8.dist-info}/RECORD +43 -25
- kevin_toolbox_dev-1.4.6.dist-info/METADATA +0 -76
- /kevin_toolbox/{developing → computer_science/algorithm}/decorator/restore_original_work_path.py +0 -0
- {kevin_toolbox_dev-1.4.6.dist-info → kevin_toolbox_dev-1.4.8.dist-info}/WHEEL +0 -0
- {kevin_toolbox_dev-1.4.6.dist-info → kevin_toolbox_dev-1.4.8.dist-info}/top_level.txt +0 -0
@@ -26,10 +26,24 @@ class Env_Vars_Parser:
|
|
26
26
|
def __call__(self, *args, **kwargs):
|
27
27
|
return self.parse(*args, **kwargs)
|
28
28
|
|
29
|
-
def parse(self, text):
|
29
|
+
def parse(self, text, **kwargs):
|
30
30
|
"""
|
31
31
|
解释并替换
|
32
|
+
|
33
|
+
参数:
|
34
|
+
default: 默认之。
|
35
|
+
当有设定时,若无法解释则返回该值。
|
36
|
+
否则,若无法解释将报错。
|
32
37
|
"""
|
38
|
+
if "default" in kwargs:
|
39
|
+
try:
|
40
|
+
return self.__parse(text=text)
|
41
|
+
except:
|
42
|
+
return kwargs["default"]
|
43
|
+
else:
|
44
|
+
return self.__parse(text=text)
|
45
|
+
|
46
|
+
def __parse(self, text):
|
33
47
|
temp_ls = []
|
34
48
|
for it in self.split_string(text=text):
|
35
49
|
if isinstance(it, str):
|
@@ -85,4 +99,5 @@ class Env_Vars_Parser:
|
|
85
99
|
|
86
100
|
if __name__ == '__main__':
|
87
101
|
env_vars_parser = Env_Vars_Parser()
|
88
|
-
print(env_vars_parser.split_string("666/123${:VAR}/afasf/${/xxx.../xxx.json:111:222}336"))
|
102
|
+
# print(env_vars_parser.split_string("666/123${:VAR}/afasf/${/xxx.../xxx.json:111:222}336"))
|
103
|
+
print(env_vars_parser.parse("${KVT_PATCHES:for_matplotlib:common_charts:font_settings:for_non-windows-platform}"))
|
@@ -35,8 +35,8 @@ class Json_(Backend_Base):
|
|
35
35
|
"""
|
36
36
|
if id(var) != self.w_cache_s["id_"]:
|
37
37
|
try:
|
38
|
-
self.w_cache_s["content"] = json_.write(content=var, file_path=None,
|
39
|
-
|
38
|
+
self.w_cache_s["content"] = json_.write(content=var, file_path=None, b_use_suggested_converter=True,
|
39
|
+
output_format=kwargs.get("output_format", "pretty_printed"))
|
40
40
|
except:
|
41
41
|
self.w_cache_s["content"], self.w_cache_s["id_"] = None, None
|
42
42
|
self.w_cache_s["able"] = False
|
@@ -3,6 +3,18 @@ from kevin_toolbox.computer_science.algorithm.registration import Registry
|
|
3
3
|
|
4
4
|
SERIALIZER_BACKEND = Registry(uid="SERIALIZER_BACKEND")
|
5
5
|
|
6
|
+
# 导入时的默认过滤规则
|
7
|
+
ignore_s = [
|
8
|
+
{
|
9
|
+
"func": lambda _, __, path: os.path.basename(path) in ["temp", "test", "__pycache__",
|
10
|
+
"_old_version"],
|
11
|
+
"scope": ["root", "dirs"]
|
12
|
+
},
|
13
|
+
]
|
14
|
+
|
6
15
|
# 从 kevin_toolbox/nested_dict_list/serializer/backends 下收集被注册的 backend
|
7
|
-
SERIALIZER_BACKEND.collect_from_paths(
|
8
|
-
|
16
|
+
SERIALIZER_BACKEND.collect_from_paths(
|
17
|
+
path_ls=[os.path.join(os.path.dirname(__file__), "backends"), ],
|
18
|
+
ignore_s=ignore_s,
|
19
|
+
b_execute_now=False
|
20
|
+
)
|
@@ -250,6 +250,8 @@ def write(var, output_dir, settings=None, traversal_mode=Traversal_Mode.BFS, b_p
|
|
250
250
|
if not os.path.exists(tgt_path):
|
251
251
|
for_os.copy(src=src_path, dst=tgt_path, remove_dst_if_exists=True)
|
252
252
|
|
253
|
+
return tgt_path
|
254
|
+
|
253
255
|
|
254
256
|
def _judge_processed_or_not(processed_s, name):
|
255
257
|
"""
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import urllib3
|
2
|
+
from urllib3.exceptions import InsecureRequestWarning
|
3
|
+
|
4
|
+
# 禁用不安全请求警告(例如当 verify=False 时)
|
5
|
+
urllib3.disable_warnings(InsecureRequestWarning)
|
6
|
+
|
7
|
+
from .get_response import get_response
|
8
|
+
from .fetch_metadata import fetch_metadata
|
9
|
+
from .fetch_content import fetch_content
|
10
|
+
from .download_file import download_file
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import os
|
2
|
+
import time
|
3
|
+
from tqdm import tqdm
|
4
|
+
from kevin_toolbox.network import fetch_metadata, fetch_content, get_response
|
5
|
+
from kevin_toolbox.patches.for_os.path import replace_illegal_chars
|
6
|
+
from kevin_toolbox.patches import for_os
|
7
|
+
from kevin_toolbox.nested_dict_list import get_hash
|
8
|
+
|
9
|
+
default_option_func_s = {
|
10
|
+
"hash_name": lambda url, _, option_s: {"hash_name": get_hash(option_s["name"], length=12)},
|
11
|
+
"hash_url": lambda url, _, option_s: {"hash_url": get_hash(url, length=12)},
|
12
|
+
"timestamp": lambda *arg, **kwargs: {"timestamp": f'{time.time()}'},
|
13
|
+
"legalized_name": lambda url, _, option_s: {
|
14
|
+
"legalized_name": replace_illegal_chars(file_name=option_s["name"], b_is_path=False)}
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
def download_file(
|
19
|
+
output_dir, url=None, response=None, chunk_size=1024 * 10,
|
20
|
+
file_name=None, file_name_format="{legalized_name:.100}{suffix}", format_option_generate_func_ls=None,
|
21
|
+
b_allow_overwrite=False, b_display_progress=False, **kwargs
|
22
|
+
):
|
23
|
+
"""
|
24
|
+
下载文件
|
25
|
+
支持以下高级功能:
|
26
|
+
1. 自动识别文件类型并命名。
|
27
|
+
2. 多次重试。
|
28
|
+
3. TODO:断点续传(待实现)。
|
29
|
+
|
30
|
+
参数:
|
31
|
+
output_dir: <path> 文件保存的目录
|
32
|
+
url: <str> 下载的 URL 地址。
|
33
|
+
response: 响应。
|
34
|
+
以上两个参数只需要指定其一即可,建议使用后者。
|
35
|
+
chunk_size: <int> 采用分块下载时,块的大小
|
36
|
+
默认为 1024 * 10
|
37
|
+
file_name: <str> 文件名
|
38
|
+
默认为 None,此时将根据 file_name_format 自动生成名字。
|
39
|
+
file_name_format: <str> 保存的文件的命名方式。
|
40
|
+
基本结构为: '{<part_0>}...{<part_1>}...'
|
41
|
+
其中 {} 内将根据 part 指定的选项进行自动填充。目前支持以下几种选项:
|
42
|
+
- "name" 文件名(不含后缀)。
|
43
|
+
- "suffix" 后缀。
|
44
|
+
- "timestamp" 下载的时间戳。
|
45
|
+
- "hash_name" 文件名的hash值。
|
46
|
+
- "legalized_name" 经过合法化处理的文件名(对其中特殊符号进行了替换)。
|
47
|
+
- "hash_url" url的hash值。
|
48
|
+
!!注意:
|
49
|
+
"name" 该选项由于其可能含有 : 和 / 等特殊符号,当以其作为文件名时,可能会引发错误。
|
50
|
+
因此对于 windows 用户,请慎重使用该选项,对于 mac 和 linux 用户,同样也不建议使用该选项。
|
51
|
+
相较而言,"legalized_name" 是一个更好的选择。
|
52
|
+
"hash_name" 和 "hash_url" 有极低但非0的可能会发生 hash 碰撞。
|
53
|
+
综合而言:
|
54
|
+
建议使用 "legalized_name" 和 "suffix" 以及 "timestamp" 的组合。
|
55
|
+
高级设置:
|
56
|
+
1. 如果想限制文件名中某部分的长度(避免文件名过长在某些系统下引发报错),应该如何做?
|
57
|
+
本命名方式兼容 str.format() 语法,比如你可以通过 {name:.10} 来限制名字的长度不大于10个字符。
|
58
|
+
2. 如果已有的选项无法满足你的需求,如何新增选项?
|
59
|
+
本函数支持通过设置 format_option_generate_func_ls 来补充或者覆盖默认选项。
|
60
|
+
默认值为:
|
61
|
+
'{legalized_name:.100}{suffix}'
|
62
|
+
format_option_generate_func_ls: <list of callable> 函数列表,将使用这些函数的结果来对 file_name_format 中的选项进行补充或者覆盖。
|
63
|
+
函数需要接受 url, response, option_s(已有的选项键值对) 三个参数,并返回一个包含选项名和选项值的 dict。
|
64
|
+
默认为 None
|
65
|
+
b_allow_overwrite: <boolean> 是否允许覆盖已有文件。
|
66
|
+
b_display_progress: <boolean> 显示进度条。
|
67
|
+
|
68
|
+
|
69
|
+
返回:
|
70
|
+
文件完整路径(下载成功)或空字符串(失败)
|
71
|
+
"""
|
72
|
+
global default_option_func_s
|
73
|
+
assert url is not None or response is not None
|
74
|
+
if url is not None:
|
75
|
+
response = response or get_response(url=url, **kwargs)
|
76
|
+
assert response is not None
|
77
|
+
output_dir = os.path.expanduser(output_dir)
|
78
|
+
#
|
79
|
+
metadata_s = fetch_metadata(url=url, response=response, default_name="", default_suffix="")
|
80
|
+
if file_name is None:
|
81
|
+
option_s = metadata_s.copy()
|
82
|
+
for k, func in default_option_func_s.items():
|
83
|
+
if k in file_name_format:
|
84
|
+
option_s.update(func(url, response, option_s))
|
85
|
+
if isinstance(format_option_generate_func_ls, (list, tuple,)):
|
86
|
+
for func in format_option_generate_func_ls:
|
87
|
+
assert callable(func)
|
88
|
+
option_s.update(func(url, response, option_s))
|
89
|
+
file_name = file_name_format.format(**option_s)
|
90
|
+
#
|
91
|
+
os.makedirs(output_dir, exist_ok=True)
|
92
|
+
#
|
93
|
+
file_path = os.path.join(output_dir, file_name)
|
94
|
+
if os.path.exists(file_path):
|
95
|
+
if b_allow_overwrite:
|
96
|
+
for_os.remove(path=file_path, ignore_errors=True)
|
97
|
+
else:
|
98
|
+
raise FileExistsError(f"target {file_path} already exists")
|
99
|
+
|
100
|
+
if metadata_s["content_length"] and b_display_progress:
|
101
|
+
pbar = tqdm(total=metadata_s["content_length"], unit="B", unit_scale=True, desc="下载进度")
|
102
|
+
else:
|
103
|
+
pbar = None
|
104
|
+
|
105
|
+
with open(file_path, "wb") as f:
|
106
|
+
for chunk in fetch_content(response=response, chunk_size=chunk_size):
|
107
|
+
if chunk:
|
108
|
+
f.write(chunk)
|
109
|
+
if pbar is not None:
|
110
|
+
pbar.update(len(chunk))
|
111
|
+
|
112
|
+
return file_path
|
113
|
+
|
114
|
+
|
115
|
+
# 示例用法
|
116
|
+
if __name__ == "__main__":
|
117
|
+
url_ = "https://i.pinimg.com/736x/28/6a/b1/286ab1eb816dc59a1c72374c75645d80.jpg"
|
118
|
+
output_dir = r'./temp/123'
|
119
|
+
downloaded_file = download_file(url=url_, output_dir=output_dir, file_name="233.jpg", b_allow_overwrite=True,
|
120
|
+
b_display_progress=True, chunk_size=100)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
import warnings
|
2
|
+
from kevin_toolbox.network import get_response
|
3
|
+
|
4
|
+
|
5
|
+
def fetch_content(url=None, response=None, decoding=None, chunk_size=None, **kwargs):
|
6
|
+
"""
|
7
|
+
从 URL/response 中获取内容
|
8
|
+
|
9
|
+
参数:
|
10
|
+
url: <str> 请求的 URL 地址。
|
11
|
+
response: 响应。
|
12
|
+
以上两个参数只需要指定其一即可,建议使用后者。
|
13
|
+
decoding: <str> 响应内容的解码方式
|
14
|
+
默认为 None,返回原始的字节流。
|
15
|
+
一些常用的可选值:
|
16
|
+
"utf-8"
|
17
|
+
chunk_size: <int> 采用分块方式读取内容时,块的大小
|
18
|
+
默认为 None,此时不使用分块读取,而直接读取所有内容
|
19
|
+
注意!当 chunk_size 非 None 时,decoding 将失效
|
20
|
+
|
21
|
+
返回:
|
22
|
+
当 chunk_size 为 None 时:
|
23
|
+
str: 请求成功后的响应内容。如果 decoding 为 None,则返回 bytes 类型数据。
|
24
|
+
当 chunk_size 非 None 时:
|
25
|
+
读取内容的生成器。
|
26
|
+
"""
|
27
|
+
assert url is not None or response is not None
|
28
|
+
if url is not None:
|
29
|
+
response = response or get_response(url=url, **kwargs)
|
30
|
+
assert response is not None
|
31
|
+
|
32
|
+
if chunk_size is None:
|
33
|
+
content = response.data
|
34
|
+
if decoding:
|
35
|
+
content = content.decode(decoding)
|
36
|
+
return content
|
37
|
+
else:
|
38
|
+
if decoding:
|
39
|
+
warnings.warn(f'当 chunk_size 非 None 时,decoding 参数将失效。')
|
40
|
+
return __generator(response, chunk_size)
|
41
|
+
|
42
|
+
|
43
|
+
def __generator(response, chunk_size):
|
44
|
+
while True:
|
45
|
+
chunk = response.read(chunk_size)
|
46
|
+
if not chunk:
|
47
|
+
break
|
48
|
+
yield chunk
|
49
|
+
|
50
|
+
|
51
|
+
if __name__ == "__main__":
|
52
|
+
url_ = "https://i.pinimg.com/736x/28/6a/b1/286ab1eb816dc59a1c72374c75645d80.jpg" # "https://www.google.com/"
|
53
|
+
print(len(fetch_content(url=url_, decoding=None)))
|
54
|
+
for i, j in enumerate(fetch_content(url=url_, chunk_size=50000)):
|
55
|
+
print(i, len(j))
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import os
|
2
|
+
import re
|
3
|
+
import time
|
4
|
+
import mimetypes
|
5
|
+
from urllib.parse import quote
|
6
|
+
from urllib.parse import urlsplit, unquote
|
7
|
+
from kevin_toolbox.network import get_response
|
8
|
+
|
9
|
+
|
10
|
+
def fetch_metadata(url=None, response=None, default_suffix=".bin", default_name=None, **kwargs):
|
11
|
+
"""
|
12
|
+
从 URL/response 中获取文件名、后缀(扩展名)、大小等元信息
|
13
|
+
|
14
|
+
参数:
|
15
|
+
url: <str> 请求的 URL 地址。
|
16
|
+
response: 响应。
|
17
|
+
以上两个参数建议同时指定。
|
18
|
+
default_suffix: <str> 默认后缀。
|
19
|
+
default_name: <str> 默认文件名。
|
20
|
+
默认为 None,表示使用当前时间戳作为默认文件名。
|
21
|
+
只有从 URL/response 中无法获取出 suffix 和 name 时才会使用上面的默认值作为填充
|
22
|
+
|
23
|
+
返回:
|
24
|
+
dict with keys ['content_length', 'content_type', 'content_disp', 'suffix', 'name']
|
25
|
+
"""
|
26
|
+
assert url is not None or response is not None
|
27
|
+
if url is not None:
|
28
|
+
response = response or get_response(url=url, **kwargs)
|
29
|
+
assert response is not None
|
30
|
+
default_name = f'{time.time()}' if default_name is None else default_name
|
31
|
+
|
32
|
+
metadata_s = {"content_length": None, "content_type": None, "content_disp": None, "suffix": None, "name": None}
|
33
|
+
name, suffix = None, None
|
34
|
+
# 尝试直接从url中获取文件名和后缀
|
35
|
+
if url is not None:
|
36
|
+
url = quote(url, safe='/:?=&')
|
37
|
+
basename = unquote(os.path.basename(urlsplit(url).path))
|
38
|
+
name, suffix = os.path.splitext(basename)
|
39
|
+
# 尝试从更加可信的响应头中获取文件名和后缀
|
40
|
+
content_length = response.headers.get("Content-Length", None)
|
41
|
+
metadata_s["content_length"] = int(content_length) if content_length and content_length.isdigit() else None
|
42
|
+
#
|
43
|
+
content_type = response.headers.get("Content-Type", None)
|
44
|
+
metadata_s["content_type"] = content_type
|
45
|
+
if content_type:
|
46
|
+
suffix = mimetypes.guess_extension(content_type.split(";")[0].strip()) or suffix
|
47
|
+
#
|
48
|
+
content_disp = response.headers.get("Content-Disposition", None)
|
49
|
+
metadata_s["content_disp"] = content_disp
|
50
|
+
if content_disp:
|
51
|
+
temp_ls = re.findall('filename="([^"]+)"', content_disp)
|
52
|
+
if temp_ls:
|
53
|
+
name, temp = os.path.splitext(temp_ls[0])
|
54
|
+
suffix = temp or suffix
|
55
|
+
metadata_s["name"] = name or default_name
|
56
|
+
metadata_s["suffix"] = suffix or default_suffix
|
57
|
+
|
58
|
+
return metadata_s
|
59
|
+
|
60
|
+
|
61
|
+
# 示例用法
|
62
|
+
if __name__ == "__main__":
|
63
|
+
url_ = "https://i.pinimg.com/736x/28/6a/b1/286ab1eb816dc59a1c72374c75645d80.jpg" # "https://www.google.com/"
|
64
|
+
print(fetch_metadata(url=url_))
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import urllib3
|
2
|
+
from urllib.parse import quote
|
3
|
+
from kevin_toolbox.computer_science.algorithm.decorator import retry
|
4
|
+
from kevin_toolbox.network.variable import DEFAULT_HEADERS
|
5
|
+
|
6
|
+
# 全局 PoolManager 实例,设置 cert_reqs='CERT_NONE' 可关闭 SSL 证书验证
|
7
|
+
http = urllib3.PoolManager(cert_reqs='CERT_NONE')
|
8
|
+
|
9
|
+
|
10
|
+
def get_response(url, data=None, headers=None, method=None, retries=3, delay=0.5, b_verbose=False, stream=True,
|
11
|
+
**kwargs):
|
12
|
+
"""
|
13
|
+
获取 url 的响应
|
14
|
+
|
15
|
+
参数:
|
16
|
+
url: <str> 请求的 URL 地址。
|
17
|
+
data: <bytes, optional> 请求发送的数据,如果需要传递数据,必须是字节类型。
|
18
|
+
headers: <dict> 请求头字典。
|
19
|
+
默认为 DEFAULT_HEADERS。
|
20
|
+
method: <str> HTTP 请求方法,如 "GET", "POST" 等。
|
21
|
+
retries: <int> 重试次数
|
22
|
+
默认重试3次
|
23
|
+
delay: <int/float> 每次重试前等待的秒数。
|
24
|
+
默认0.5秒
|
25
|
+
b_verbose: <boolean> 进行多次重试时是否打印详细日志信息。
|
26
|
+
默认为 False。
|
27
|
+
|
28
|
+
返回:
|
29
|
+
响应。 urllib3.response.HTTPResponse object
|
30
|
+
"""
|
31
|
+
headers = headers or DEFAULT_HEADERS
|
32
|
+
|
33
|
+
url = quote(url, safe='/:?=&')
|
34
|
+
worker = retry(retries=retries, delay=delay, logger="default" if b_verbose else None)(func=__worker)
|
35
|
+
response = worker(url, data, headers, method, stream)
|
36
|
+
return response
|
37
|
+
|
38
|
+
|
39
|
+
def __worker(url, data, headers, method, stream):
|
40
|
+
method = method if method is not None else "GET"
|
41
|
+
response = http.request(method, url, body=data, headers=headers, preload_content=not stream)
|
42
|
+
if response.status >= 400:
|
43
|
+
raise Exception(f"HTTP 请求失败,状态码:{response.status}")
|
44
|
+
return response
|
45
|
+
|
46
|
+
|
47
|
+
if __name__ == "__main__":
|
48
|
+
url_ = "https://i.pinimg.com/736x/28/6a/b1/286ab1eb816dc59a1c72374c75645d80.jpg" # "https://www.google.com/"
|
49
|
+
a = get_response(url=url_, b_verbose=True)
|
50
|
+
print(a)
|
@@ -49,7 +49,7 @@ def build_logger(name, handler_ls, level=logging.DEBUG,
|
|
49
49
|
# 输出到控制台
|
50
50
|
handler = logging.StreamHandler()
|
51
51
|
else:
|
52
|
-
raise ValueError(f'unexpected target {target}')
|
52
|
+
raise ValueError(f'unexpected target {details["target"]}')
|
53
53
|
handler.setLevel(details.get("level", level))
|
54
54
|
handler.setFormatter(logging.Formatter(details.get("formatter", formatter)))
|
55
55
|
# 添加到logger中
|
@@ -1,6 +1,51 @@
|
|
1
|
+
# 在非 windows 系统下,尝试自动下载中文字体,并尝试自动设置字体
|
2
|
+
import sys
|
3
|
+
|
4
|
+
if not sys.platform.startswith("win"):
|
5
|
+
import os
|
6
|
+
from kevin_toolbox.env_info.variable_ import env_vars_parser
|
7
|
+
|
8
|
+
font_setting_s = dict(
|
9
|
+
b_auto_download=True,
|
10
|
+
download_url="https://drive.usercontent.google.com/download?id=1wd-a4-AwAXkr7mHmB9BAIcGpAcqMrbqg&export=download&authuser=0",
|
11
|
+
save_path="~/.kvt_data/fonts/SimHei.ttf"
|
12
|
+
)
|
13
|
+
font_setting_s.update()
|
14
|
+
for k, v in list(font_setting_s.items()):
|
15
|
+
font_setting_s[k] = env_vars_parser.parse(
|
16
|
+
text=f"${{KVT_PATCHES:for_matplotlib:common_charts:font_settings:for_non-windows-platform:{k}}}",
|
17
|
+
default=v
|
18
|
+
)
|
19
|
+
save_path = os.path.expanduser(font_setting_s["save_path"])
|
20
|
+
|
21
|
+
if font_setting_s["b_auto_download"] and not os.path.isfile(save_path):
|
22
|
+
from kevin_toolbox.network import download_file
|
23
|
+
|
24
|
+
print(f'检测到当前系统非 Windows 系统,尝试自动下载中文字体...')
|
25
|
+
download_file(
|
26
|
+
output_dir=os.path.dirname(save_path),
|
27
|
+
file_name=os.path.basename(save_path),
|
28
|
+
url=font_setting_s["download_url"],
|
29
|
+
b_display_progress=True
|
30
|
+
)
|
31
|
+
|
32
|
+
if os.path.isfile(save_path):
|
33
|
+
import matplotlib.font_manager as fm
|
34
|
+
import matplotlib.pyplot as plt
|
35
|
+
|
36
|
+
# 注册字体
|
37
|
+
fm.fontManager.addfont(save_path)
|
38
|
+
# 获取字体名称
|
39
|
+
font_name = fm.FontProperties(fname=save_path).get_name()
|
40
|
+
|
41
|
+
# 全局设置默认字体
|
42
|
+
plt.rcParams['font.family'] = font_name
|
43
|
+
plt.rcParams['axes.unicode_minus'] = False # 解决负号 '-' 显示为方块的问题
|
44
|
+
|
1
45
|
from .plot_lines import plot_lines
|
2
46
|
from .plot_scatters import plot_scatters
|
3
47
|
from .plot_distribution import plot_distribution
|
4
48
|
from .plot_bars import plot_bars
|
5
49
|
from .plot_scatters_matrix import plot_scatters_matrix
|
6
50
|
from .plot_confusion_matrix import plot_confusion_matrix
|
51
|
+
from .plot_from_record import plot_from_record
|
@@ -1,20 +1,62 @@
|
|
1
|
-
import os
|
2
|
-
import copy
|
3
|
-
from kevin_toolbox.computer_science.algorithm import for_seq
|
4
1
|
import matplotlib.pyplot as plt
|
5
|
-
from kevin_toolbox.
|
2
|
+
from kevin_toolbox.computer_science.algorithm import for_seq
|
3
|
+
from kevin_toolbox.patches.for_matplotlib.common_charts.utils import save_plot, save_record, get_output_path
|
4
|
+
from kevin_toolbox.patches.for_matplotlib.variable import COMMON_CHARTS
|
5
|
+
|
6
|
+
__name = ":common_charts:plot_bars"
|
7
|
+
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
@COMMON_CHARTS.register(name=__name)
|
10
|
+
def plot_bars(data_s, title, x_name, output_dir=None, output_path=None, **kwargs):
|
11
|
+
"""
|
12
|
+
绘制条形图
|
10
13
|
|
14
|
+
参数:
|
15
|
+
data_s: <dict> 数据。
|
16
|
+
形如 {<data_name>: <data list>, ...} 的字典
|
17
|
+
title: <str> 绘图标题。
|
18
|
+
x_name: <str> 以哪个 data_name 作为 x 轴。
|
19
|
+
其余数据视为需要被绘制的数据点。
|
20
|
+
例子: data_s={"step":[...], "acc_top1":[...], "acc_top3":[...]}
|
21
|
+
当 x_name="step" 时,将会以 step 为 x 轴绘制 acc_top1 和 acc_top3 的 bar 图。
|
22
|
+
x_label: <str> x 轴的标签名称。
|
23
|
+
默认与指定的 x_name 相同。
|
24
|
+
y_label: <str> y 轴的标签名称。
|
25
|
+
默认为 "value"。
|
26
|
+
output_dir: <str or None> 图片输出目录。
|
27
|
+
output_path: <str or None> 图片输出路径。
|
28
|
+
以上两个只需指定一个即可,同时指定时以后者为准。
|
29
|
+
当只有 output_dir 被指定时,将会以 title 作为图片名。
|
30
|
+
若同时不指定,则直接调用 plt.show() 显示图像,而不进行保存。
|
31
|
+
在保存为文件时,若文件名中存在路径不适宜的非法字符将会被进行替换。
|
11
32
|
|
12
|
-
|
13
|
-
|
33
|
+
其他可选参数:
|
34
|
+
dpi: <int> 保存图像的分辨率。
|
35
|
+
默认为 200。
|
36
|
+
suffix: <str> 图片保存后缀。
|
37
|
+
目前支持的取值有 ".png", ".jpg", ".bmp",默认为第一个。
|
38
|
+
b_generate_record: <boolean> 是否保存函数参数为档案。
|
39
|
+
默认为 False,当设置为 True 时将会把函数参数保存成 [output_path].record.tar。
|
40
|
+
后续可以使用 plot_from_record() 函数或者 Serializer_for_Registry_Execution 读取该档案,并进行修改和重新绘制。
|
41
|
+
该参数仅在 output_dir 和 output_path 非 None 时起效。
|
42
|
+
|
43
|
+
返回值:
|
44
|
+
若 output_dir 非 None,则返回图像保存的文件路径。
|
45
|
+
"""
|
46
|
+
assert x_name in data_s and len(data_s) >= 2
|
14
47
|
paras = {
|
15
|
-
"
|
48
|
+
"x_label": f'{x_name}',
|
49
|
+
"y_label": "value",
|
50
|
+
"dpi": 200,
|
51
|
+
"suffix": ".png",
|
52
|
+
"b_generate_record": False
|
16
53
|
}
|
17
54
|
paras.update(kwargs)
|
55
|
+
#
|
56
|
+
_output_path = get_output_path(output_path=output_path, output_dir=output_dir, title=title, **kwargs)
|
57
|
+
save_record(_func=plot_bars, _name=__name, _output_path=_output_path if paras["b_generate_record"] else None,
|
58
|
+
**paras)
|
59
|
+
data_s = data_s.copy()
|
18
60
|
|
19
61
|
plt.clf()
|
20
62
|
#
|
@@ -26,29 +68,28 @@ def plot_bars(data_s, title, x_name, y_label=None, output_dir=None, **kwargs):
|
|
26
68
|
else:
|
27
69
|
plt.bar([j + 0.1 for j in range(len(x_all_ls))], y_ls, width=0.2, align='center', label=k)
|
28
70
|
|
29
|
-
plt.xlabel(
|
30
|
-
plt.ylabel(
|
71
|
+
plt.xlabel(paras["x_label"])
|
72
|
+
plt.ylabel(paras["y_label"])
|
31
73
|
temp = for_seq.flatten_list([list(i) for i in data_s.values()])
|
32
74
|
y_min, y_max = min(temp), max(temp)
|
33
75
|
plt.ylim(max(min(y_min, 0), y_min - (y_max - y_min) * 0.2), y_max + (y_max - y_min) * 0.1)
|
34
|
-
plt.xticks(list(range(len(x_all_ls))), labels=x_all_ls)
|
76
|
+
plt.xticks(list(range(len(x_all_ls))), labels=x_all_ls)
|
35
77
|
plt.title(f'{title}')
|
36
78
|
# 显示图例
|
37
79
|
plt.legend()
|
38
80
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
else:
|
43
|
-
os.makedirs(output_dir, exist_ok=True)
|
44
|
-
output_path = os.path.join(output_dir, f'{replace_illegal_chars(title)}.png')
|
45
|
-
plt.savefig(output_path, dpi=paras["dpi"])
|
46
|
-
return output_path
|
81
|
+
save_plot(plt=plt, output_path=_output_path, dpi=paras["dpi"], suffix=paras["suffix"])
|
82
|
+
|
83
|
+
return _output_path
|
47
84
|
|
48
85
|
|
49
86
|
if __name__ == '__main__':
|
87
|
+
import os
|
88
|
+
|
50
89
|
plot_bars(data_s={
|
51
90
|
'a': [1.5, 2, 3, 4, 5],
|
52
91
|
'b': [5, 4, 3, 2, 1],
|
53
92
|
'c': [1, 2, 3, 4, 5]},
|
54
|
-
title='
|
93
|
+
title='test_plot_bars', x_name='a', output_dir=os.path.join(os.path.dirname(__file__), "temp"),
|
94
|
+
b_generate_record=True
|
95
|
+
)
|