MainShortcuts2 2.2.4__tar.gz → 2.3.1__tar.gz
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.
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/PKG-INFO +2 -2
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/pyproject.toml +5 -2
- mainshortcuts2-2.3.1/src/MainShortcuts2/__main__.py +74 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/_module_info.py +1 -1
- mainshortcuts2-2.3.1/src/MainShortcuts2/api/base.py +35 -0
- mainshortcuts2-2.3.1/src/MainShortcuts2/api/gigachat.py +65 -0
- mainshortcuts2-2.3.1/src/MainShortcuts2/api/russian_trusted_root_ca_pem.crt +33 -0
- mainshortcuts2-2.3.1/src/MainShortcuts2/api/russian_trusted_sub_ca_pem.crt +41 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/core.py +1 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/file.py +25 -6
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/json.py +79 -14
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/path.py +3 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/term.py +6 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/utils.py +40 -3
- mainshortcuts2-2.2.4/src/MainShortcuts2/__main__.py +0 -3
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/README.md +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/__init__.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/advanced.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/cfg.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/dict.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/dir.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/list.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/proc.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/regex.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/special_chars.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/str.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/types.py +0 -0
- {mainshortcuts2-2.2.4 → mainshortcuts2-2.3.1}/src/MainShortcuts2/win.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: MainShortcuts2
|
|
3
|
-
Version: 2.
|
|
4
|
-
Summary: Сокращение и улучшение функций
|
|
3
|
+
Version: 2.3.1
|
|
4
|
+
Summary: Сокращение и улучшение функций + консольные утилиты
|
|
5
5
|
Home-page: https://github.com/MainPlay-TG/MainShortcuts2.py
|
|
6
6
|
Author: MainPlay TG
|
|
7
7
|
Author-email: xbox.roman6666666666@gmail.com
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
|
-
version = "2.
|
|
2
|
+
version = "2.3.1"
|
|
3
3
|
name = "MainShortcuts2"
|
|
4
|
-
description = "Сокращение и улучшение функций"
|
|
4
|
+
description = "Сокращение и улучшение функций + консольные утилиты"
|
|
5
5
|
authors = [ "MainPlay TG <xbox.roman6666666666@gmail.com>",]
|
|
6
6
|
readme = "README.md"
|
|
7
7
|
repository = "https://github.com/MainPlay-TG/MainShortcuts2.py"
|
|
@@ -11,6 +11,9 @@ packages = [
|
|
|
11
11
|
|
|
12
12
|
[tool.poetry.scripts]
|
|
13
13
|
ms2-import_example = "MainShortcuts2.__main__:import_example"
|
|
14
|
+
nano-json = "MainShortcuts2.__main__:nano_json"
|
|
15
|
+
nginx-reload = "MainShortcuts2.__main__:nginx_reload"
|
|
16
|
+
nginx-restart = "MainShortcuts2.__main__:nginx_restart"
|
|
14
17
|
|
|
15
18
|
[tool.poetry.dependencies]
|
|
16
19
|
python = "^3.6"
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
from MainShortcuts2 import ms
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def import_example():
|
|
7
|
+
argp = argparse.ArgumentParser("ms2-import_example", description="код для импорта MainShortcuts2")
|
|
8
|
+
argp.parse_args()
|
|
9
|
+
print("from MainShortcuts2 import ms")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def nano_json():
|
|
13
|
+
ms.utils.check_programs("nano")
|
|
14
|
+
import subprocess
|
|
15
|
+
argp = argparse.ArgumentParser("nano-json", description="форматирование JSON файлов и редактирование в GNU NANO")
|
|
16
|
+
argp.add_argument("files", nargs="+", help="пути к файлам JSON")
|
|
17
|
+
argp.add_argument("--nano-help", action="store_true", help="показать помощь nano")
|
|
18
|
+
argp.add_argument("-f", "--rcfile", help="использовать только этот файл для настройки nano")
|
|
19
|
+
argp.add_argument("-m", "--mode", choices=ms.json.MODES, default="p", help="режим сохранения редактирования")
|
|
20
|
+
args = argp.parse_args()
|
|
21
|
+
if args.nano_help:
|
|
22
|
+
return subprocess.call(["nano", "--help"])
|
|
23
|
+
nano_args = ["nano"]
|
|
24
|
+
if nano_args.rcfile:
|
|
25
|
+
nano_args += ["--rcfile", args.rcfile]
|
|
26
|
+
nano_args += args.files
|
|
27
|
+
for i in args.files:
|
|
28
|
+
try:
|
|
29
|
+
data = ms.json.read(i)
|
|
30
|
+
ms.json.write(i + ms.file.TMP_SUFFIX, data, ensure_ascii=False, mode="p")
|
|
31
|
+
ms.file.delete(i)
|
|
32
|
+
ms.file.move(i + ms.file.TMP_SUFFIX, i)
|
|
33
|
+
except Exception as err:
|
|
34
|
+
print(err, file=sys.stderr)
|
|
35
|
+
subprocess.call(nano_args)
|
|
36
|
+
for i in args.files:
|
|
37
|
+
try:
|
|
38
|
+
data = ms.json.read(i)
|
|
39
|
+
ms.json.write(i + ms.file.TMP_SUFFIX, data, mode=args.mode)
|
|
40
|
+
ms.file.delete(i)
|
|
41
|
+
ms.file.move(i + ms.file.TMP_SUFFIX, i)
|
|
42
|
+
except Exception as err:
|
|
43
|
+
print(err, file=sys.stderr)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _check_nginx() -> int:
|
|
47
|
+
import subprocess
|
|
48
|
+
with subprocess.Popen(["nginx", "-t"], stderr=subprocess.PIPE) as p:
|
|
49
|
+
code = p.wait()
|
|
50
|
+
if code != 0:
|
|
51
|
+
sys.stderr.buffer.write(p.stderr.read())
|
|
52
|
+
return code
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def nginx_reload():
|
|
56
|
+
ms.utils.check_programs("nginx")
|
|
57
|
+
import subprocess
|
|
58
|
+
argp = argparse.ArgumentParser("nginx-reload", description="проверка конфига и перезагрузка Nginx")
|
|
59
|
+
argp.parse_args()
|
|
60
|
+
code = _check_nginx()
|
|
61
|
+
if code != 0:
|
|
62
|
+
sys.exit(code)
|
|
63
|
+
sys.exit(subprocess.call(["nginx", "-s", "reload"]))
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def nginx_restart():
|
|
67
|
+
ms.utils.check_programs("nginx", "systemctl")
|
|
68
|
+
import subprocess
|
|
69
|
+
argp = argparse.ArgumentParser("nginx-reload", description="проверка конфига и перезапуск Nginx через systemctl")
|
|
70
|
+
argp.parse_args()
|
|
71
|
+
code = _check_nginx()
|
|
72
|
+
if code != 0:
|
|
73
|
+
sys.exit(code)
|
|
74
|
+
sys.exit(subprocess.call(["systemctl", "restart", "nginx"]))
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
name = "MainShortcuts2"
|
|
2
|
-
version = "2.
|
|
2
|
+
version = "2.3.1"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class Base:
|
|
5
|
+
def __init__(self, session: requests.Session = None):
|
|
6
|
+
self._headers = {}
|
|
7
|
+
self._http = requests.Session() if session is None else session
|
|
8
|
+
self._params = {}
|
|
9
|
+
self._url_data = {}
|
|
10
|
+
self._url = "https://example.com/api/{method}"
|
|
11
|
+
|
|
12
|
+
@property
|
|
13
|
+
def http(self) -> requests.Session:
|
|
14
|
+
return self._http
|
|
15
|
+
|
|
16
|
+
def _request(self, http_method: str, api_method: str, *, headers: dict[str, str] = None, params: dict[str, str] = None, raise_for_status: bool = True, url_data: dict[str, str] = None, **kw):
|
|
17
|
+
_headers = self._headers.copy()
|
|
18
|
+
_params = self._params.copy()
|
|
19
|
+
_url_data = self._url_data.copy()
|
|
20
|
+
_url_data["method"] = api_method
|
|
21
|
+
_headers.update({} if headers is None else headers)
|
|
22
|
+
_params.update({} if params is None else params)
|
|
23
|
+
_url_data.update({} if url_data is None else url_data)
|
|
24
|
+
kw["headers"] = _headers
|
|
25
|
+
kw["method"] = http_method
|
|
26
|
+
kw["params"] = _params
|
|
27
|
+
kw["url"] = self._url.format(**_url_data)
|
|
28
|
+
result = self.http.request(**kw)
|
|
29
|
+
if raise_for_status:
|
|
30
|
+
result.raise_for_status()
|
|
31
|
+
return result
|
|
32
|
+
|
|
33
|
+
def request(self, *args, **kwargs) -> requests.Response:
|
|
34
|
+
"""Отправить запрос к API"""
|
|
35
|
+
return self._request(*args, **kwargs)
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from .base import Base, requests
|
|
3
|
+
from datetime import timedelta
|
|
4
|
+
from time import time
|
|
5
|
+
CERT_PATH = os.path.dirname(__file__) + "/russian_trusted_root_ca_pem.crt"
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class GigaChat(Base):
|
|
9
|
+
def __init__(self, auth_data: str, client_id: str, *,
|
|
10
|
+
cert_path: str = None,
|
|
11
|
+
**kw):
|
|
12
|
+
Base.__init__(self, **kw)
|
|
13
|
+
self._access_token = {"expire_at": 0, "kw": None, "token": None}
|
|
14
|
+
self._auth_data: str = auth_data
|
|
15
|
+
self._client_id: str = client_id
|
|
16
|
+
self._url = "https://gigachat.devices.sberbank.ru/api/v1/{method}"
|
|
17
|
+
self.http.verify = CERT_PATH if cert_path is None else cert_path
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def access_token(self) -> str:
|
|
21
|
+
if True: # TODO Сделать проверку истёк ли токен доступа. Пока что каждый раз новый токен
|
|
22
|
+
if self._access_token["kw"] is None: # Если изменились данные для авторизации
|
|
23
|
+
kw = {}
|
|
24
|
+
kw["data"] = {}
|
|
25
|
+
kw["headers"] = {}
|
|
26
|
+
kw["stream"] = False
|
|
27
|
+
kw["url"] = "https://ngw.devices.sberbank.ru:9443/api/v2/oauth"
|
|
28
|
+
kw["data"]["scope"] = "GIGACHAT_API_PERS"
|
|
29
|
+
kw["headers"]["Authorization"] = "Basic: " + self.auth_data
|
|
30
|
+
kw["headers"]["Content-Type"] = "application/x-www-form-urlencoded"
|
|
31
|
+
kw["headers"]["RqUID"] = self.client_id
|
|
32
|
+
self._access_token["kw"] = kw
|
|
33
|
+
else:
|
|
34
|
+
kw = self._access_token["kw"]
|
|
35
|
+
with self.http.post(**kw) as resp: # Получить новый токен
|
|
36
|
+
resp.raise_for_status()
|
|
37
|
+
json = resp.json()
|
|
38
|
+
self._access_token["expire_at"] = json["expires_at"] / 1000
|
|
39
|
+
self._access_token["token"] = json["access_token"]
|
|
40
|
+
return self._access_token["token"]
|
|
41
|
+
|
|
42
|
+
@property
|
|
43
|
+
def auth_data(self) -> str:
|
|
44
|
+
return self._auth_data
|
|
45
|
+
|
|
46
|
+
@auth_data.setter
|
|
47
|
+
def auth_data(self, v: str):
|
|
48
|
+
self._access_token["kw"] = None
|
|
49
|
+
self._auth_data = v
|
|
50
|
+
|
|
51
|
+
@property
|
|
52
|
+
def client_id(self) -> str:
|
|
53
|
+
return self._client_id
|
|
54
|
+
|
|
55
|
+
@client_id.setter
|
|
56
|
+
def client_id(self, v: str):
|
|
57
|
+
self._access_token["kw"] = None
|
|
58
|
+
self._client_id = v
|
|
59
|
+
|
|
60
|
+
def request(self, http_method: str, api_method: str, **kw):
|
|
61
|
+
self._headers["Authorization"] = "Bearer " + self.access_token
|
|
62
|
+
kw["api_method"] = api_method
|
|
63
|
+
kw["http_method"] = http_method
|
|
64
|
+
return self._request(**kw).json()
|
|
65
|
+
# TODO Добавить методы
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIFwjCCA6qgAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
|
3
|
+
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
|
4
|
+
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
|
5
|
+
Q0EwHhcNMjIwMzAxMjEwNDE1WhcNMzIwMjI3MjEwNDE1WjBwMQswCQYDVQQGEwJS
|
|
6
|
+
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
|
7
|
+
YW5kIENvbW11bmljYXRpb25zMSAwHgYDVQQDDBdSdXNzaWFuIFRydXN0ZWQgUm9v
|
|
8
|
+
dCBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMfFOZ8pUAL3+r2n
|
|
9
|
+
qqE0Zp52selXsKGFYoG0GM5bwz1bSFtCt+AZQMhkWQheI3poZAToYJu69pHLKS6Q
|
|
10
|
+
XBiwBC1cvzYmUYKMYZC7jE5YhEU2bSL0mX7NaMxMDmH2/NwuOVRj8OImVa5s1F4U
|
|
11
|
+
zn4Kv3PFlDBjjSjXKVY9kmjUBsXQrIHeaqmUIsPIlNWUnimXS0I0abExqkbdrXbX
|
|
12
|
+
YwCOXhOO2pDUx3ckmJlCMUGacUTnylyQW2VsJIyIGA8V0xzdaeUXg0VZ6ZmNUr5Y
|
|
13
|
+
Ber/EAOLPb8NYpsAhJe2mXjMB/J9HNsoFMBFJ0lLOT/+dQvjbdRZoOT8eqJpWnVD
|
|
14
|
+
U+QL/qEZnz57N88OWM3rabJkRNdU/Z7x5SFIM9FrqtN8xewsiBWBI0K6XFuOBOTD
|
|
15
|
+
4V08o4TzJ8+Ccq5XlCUW2L48pZNCYuBDfBh7FxkB7qDgGDiaftEkZZfApRg2E+M9
|
|
16
|
+
G8wkNKTPLDc4wH0FDTijhgxR3Y4PiS1HL2Zhw7bD3CbslmEGgfnnZojNkJtcLeBH
|
|
17
|
+
BLa52/dSwNU4WWLubaYSiAmA9IUMX1/RpfpxOxd4Ykmhz97oFbUaDJFipIggx5sX
|
|
18
|
+
ePAlkTdWnv+RWBxlJwMQ25oEHmRguNYf4Zr/Rxr9cS93Y+mdXIZaBEE0KS2iLRqa
|
|
19
|
+
OiWBki9IMQU4phqPOBAaG7A+eP8PAgMBAAGjZjBkMB0GA1UdDgQWBBTh0YHlzlpf
|
|
20
|
+
BKrS6badZrHF+qwshzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzAS
|
|
21
|
+
BgNVHRMBAf8ECDAGAQH/AgEEMA4GA1UdDwEB/wQEAwIBhjANBgkqhkiG9w0BAQsF
|
|
22
|
+
AAOCAgEAALIY1wkilt/urfEVM5vKzr6utOeDWCUczmWX/RX4ljpRdgF+5fAIS4vH
|
|
23
|
+
tmXkqpSCOVeWUrJV9QvZn6L227ZwuE15cWi8DCDal3Ue90WgAJJZMfTshN4OI8cq
|
|
24
|
+
W9E4EG9wglbEtMnObHlms8F3CHmrw3k6KmUkWGoa+/ENmcVl68u/cMRl1JbW2bM+
|
|
25
|
+
/3A+SAg2c6iPDlehczKx2oa95QW0SkPPWGuNA/CE8CpyANIhu9XFrj3RQ3EqeRcS
|
|
26
|
+
AQQod1RNuHpfETLU/A2gMmvn/w/sx7TB3W5BPs6rprOA37tutPq9u6FTZOcG1Oqj
|
|
27
|
+
C/B7yTqgI7rbyvox7DEXoX7rIiEqyNNUguTk/u3SZ4VXE2kmxdmSh3TQvybfbnXV
|
|
28
|
+
4JbCZVaqiZraqc7oZMnRoWrXRG3ztbnbes/9qhRGI7PqXqeKJBztxRTEVj8ONs1d
|
|
29
|
+
WN5szTwaPIvhkhO3CO5ErU2rVdUr89wKpNXbBODFKRtgxUT70YpmJ46VVaqdAhOZ
|
|
30
|
+
D9EUUn4YaeLaS8AjSF/h7UkjOibNc4qVDiPP+rkehFWM66PVnP1Msh93tc+taIfC
|
|
31
|
+
EYVMxjh8zNbFuoc7fzvvrFILLe7ifvEIUqSVIC/AzplM/Jxw7buXFeGP1qVCBEHq
|
|
32
|
+
391d/9RAfaZ12zkwFsl+IKwE/OZxW8AHa9i1p4GO0YSNuczzEm4=
|
|
33
|
+
-----END CERTIFICATE-----
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
|
2
|
+
MIIHQjCCBSqgAwIBAgICEAIwDQYJKoZIhvcNAQELBQAwcDELMAkGA1UEBhMCUlUx
|
|
3
|
+
PzA9BgNVBAoMNlRoZSBNaW5pc3RyeSBvZiBEaWdpdGFsIERldmVsb3BtZW50IGFu
|
|
4
|
+
ZCBDb21tdW5pY2F0aW9uczEgMB4GA1UEAwwXUnVzc2lhbiBUcnVzdGVkIFJvb3Qg
|
|
5
|
+
Q0EwHhcNMjIwMzAyMTEyNTE5WhcNMjcwMzA2MTEyNTE5WjBvMQswCQYDVQQGEwJS
|
|
6
|
+
VTE/MD0GA1UECgw2VGhlIE1pbmlzdHJ5IG9mIERpZ2l0YWwgRGV2ZWxvcG1lbnQg
|
|
7
|
+
YW5kIENvbW11bmljYXRpb25zMR8wHQYDVQQDDBZSdXNzaWFuIFRydXN0ZWQgU3Vi
|
|
8
|
+
IENBMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA9YPqBKOk19NFymrE
|
|
9
|
+
wehzrhBEgT2atLezpduB24mQ7CiOa/HVpFCDRZzdxqlh8drku408/tTmWzlNH/br
|
|
10
|
+
HuQhZ/miWKOf35lpKzjyBd6TPM23uAfJvEOQ2/dnKGGJbsUo1/udKSvxQwVHpVv3
|
|
11
|
+
S80OlluKfhWPDEXQpgyFqIzPoxIQTLZ0deirZwMVHarZ5u8HqHetRuAtmO2ZDGQn
|
|
12
|
+
vVOJYAjls+Hiueq7Lj7Oce7CQsTwVZeP+XQx28PAaEZ3y6sQEt6rL06ddpSdoTMp
|
|
13
|
+
BnCqTbxW+eWMyjkIn6t9GBtUV45yB1EkHNnj2Ex4GwCiN9T84QQjKSr+8f0psGrZ
|
|
14
|
+
vPbCbQAwNFJjisLixnjlGPLKa5vOmNwIh/LAyUW5DjpkCx004LPDuqPpFsKXNKpa
|
|
15
|
+
L2Dm6uc0x4Jo5m+gUTVORB6hOSzWnWDj2GWfomLzzyjG81DRGFBpco/O93zecsIN
|
|
16
|
+
3SL2Ysjpq1zdoS01CMYxie//9zWvYwzI25/OZigtnpCIrcd2j1Y6dMUFQAzAtHE+
|
|
17
|
+
qsXflSL8HIS+IJEFIQobLlYhHkoE3avgNx5jlu+OLYe0dF0Ykx1PGNjbwqvTX37R
|
|
18
|
+
Cn32NMjlotW2QcGEZhDKj+3urZizp5xdTPZitA+aEjZM/Ni71VOdiOP0igbw6asZ
|
|
19
|
+
2fxdozZ1TnSSYNYvNATwthNmZysCAwEAAaOCAeUwggHhMBIGA1UdEwEB/wQIMAYB
|
|
20
|
+
Af8CAQAwDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTR4XENCy2BTm6KSo9MI7NM
|
|
21
|
+
XqtpCzAfBgNVHSMEGDAWgBTh0YHlzlpfBKrS6badZrHF+qwshzCBxwYIKwYBBQUH
|
|
22
|
+
AQEEgbowgbcwOwYIKwYBBQUHMAKGL2h0dHA6Ly9yb3N0ZWxlY29tLnJ1L2NkcC9y
|
|
23
|
+
b290Y2Ffc3NsX3JzYTIwMjIuY3J0MDsGCCsGAQUFBzAChi9odHRwOi8vY29tcGFu
|
|
24
|
+
eS5ydC5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNydDA7BggrBgEFBQcwAoYv
|
|
25
|
+
aHR0cDovL3JlZXN0ci1wa2kucnUvY2RwL3Jvb3RjYV9zc2xfcnNhMjAyMi5jcnQw
|
|
26
|
+
gbAGA1UdHwSBqDCBpTA1oDOgMYYvaHR0cDovL3Jvc3RlbGVjb20ucnUvY2RwL3Jv
|
|
27
|
+
b3RjYV9zc2xfcnNhMjAyMi5jcmwwNaAzoDGGL2h0dHA6Ly9jb21wYW55LnJ0LnJ1
|
|
28
|
+
L2NkcC9yb290Y2Ffc3NsX3JzYTIwMjIuY3JsMDWgM6Axhi9odHRwOi8vcmVlc3Ry
|
|
29
|
+
LXBraS5ydS9jZHAvcm9vdGNhX3NzbF9yc2EyMDIyLmNybDANBgkqhkiG9w0BAQsF
|
|
30
|
+
AAOCAgEARBVzZls79AdiSCpar15dA5Hr/rrT4WbrOfzlpI+xrLeRPrUG6eUWIW4v
|
|
31
|
+
Sui1yx3iqGLCjPcKb+HOTwoRMbI6ytP/ndp3TlYua2advYBEhSvjs+4vDZNwXr/D
|
|
32
|
+
anbwIWdurZmViQRBDFebpkvnIvru/RpWud/5r624Wp8voZMRtj/cm6aI9LtvBfT9
|
|
33
|
+
cfzhOaexI/99c14dyiuk1+6QhdwKaCRTc1mdfNQmnfWNRbfWhWBlK3h4GGE9JK33
|
|
34
|
+
Gk8ZS8DMrkdAh0xby4xAQ/mSWAfWrBmfzlOqGyoB1U47WTOeqNbWkkoAP2ys94+s
|
|
35
|
+
Jg4NTkiDVtXRF6nr6fYi0bSOvOFg0IQrMXO2Y8gyg9ARdPJwKtvWX8VPADCYMiWH
|
|
36
|
+
h4n8bZokIrImVKLDQKHY4jCsND2HHdJfnrdL2YJw1qFskNO4cSNmZydw0Wkgjv9k
|
|
37
|
+
F+KxqrDKlB8MZu2Hclph6v/CZ0fQ9YuE8/lsHZ0Qc2HyiSMnvjgK5fDc3TD4fa8F
|
|
38
|
+
E8gMNurM+kV8PT8LNIM+4Zs+LKEV8nqRWBaxkIVJGekkVKO8xDBOG/aN62AZKHOe
|
|
39
|
+
GcyIdu7yNMMRihGVZCYr8rYiJoKiOzDqOkPkLOPdhtVlgnhowzHDxMHND/E2WA5p
|
|
40
|
+
ZHuNM/m0TXt2wTTPL7JH2YC0gPz/BvvSzjksgzU5rLbRyUKQkgU=
|
|
41
|
+
-----END CERTIFICATE-----
|
|
@@ -5,6 +5,7 @@ from .core import ms
|
|
|
5
5
|
from .path import PATH_TYPES, path2str
|
|
6
6
|
from .types import NotAFileError
|
|
7
7
|
from typing import *
|
|
8
|
+
TMP_SUFFIX = ".MS2_TMP"
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def _check(path, **kw) -> str:
|
|
@@ -25,15 +26,24 @@ def read(path: PATH_TYPES, encoding: str = None, **kw) -> str:
|
|
|
25
26
|
return f.read()
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
def write(path: PATH_TYPES, data: str, encoding: str = None, mkdir: bool = False, **kw) -> int:
|
|
29
|
+
def write(path: PATH_TYPES, data: str, encoding: str = None, mkdir: bool = False, use_tmp_file: bool = None, **kw) -> int:
|
|
29
30
|
"""Записать текст в файл"""
|
|
31
|
+
file = _check(path)
|
|
32
|
+
if use_tmp_file is None:
|
|
33
|
+
use_tmp_file = ms.use_tmp_file
|
|
30
34
|
kw["encoding"] = ms.encoding if encoding is None else encoding
|
|
31
|
-
kw["file"] =
|
|
35
|
+
kw["file"] = file
|
|
32
36
|
kw["mode"] = "w"
|
|
33
37
|
if mkdir:
|
|
34
38
|
ms.dir.create(os.path.dirname(kw["file"]))
|
|
39
|
+
if use_tmp_file:
|
|
40
|
+
kw["file"] += TMP_SUFFIX
|
|
35
41
|
with builtins.open(**kw) as f:
|
|
36
|
-
|
|
42
|
+
result = f.write(data)
|
|
43
|
+
if use_tmp_file:
|
|
44
|
+
delete(file)
|
|
45
|
+
move(kw["file"], file)
|
|
46
|
+
return result
|
|
37
47
|
|
|
38
48
|
|
|
39
49
|
def load(path: PATH_TYPES, **kw) -> bytes:
|
|
@@ -44,14 +54,23 @@ def load(path: PATH_TYPES, **kw) -> bytes:
|
|
|
44
54
|
return f.read()
|
|
45
55
|
|
|
46
56
|
|
|
47
|
-
def save(path: PATH_TYPES, data: bytes, mkdir: bool = False, **kw) -> int:
|
|
57
|
+
def save(path: PATH_TYPES, data: bytes, mkdir: bool = False, use_tmp_file: bool = None, **kw) -> int:
|
|
48
58
|
"""Записать байты в файл"""
|
|
49
|
-
|
|
59
|
+
file = _check(path)
|
|
60
|
+
if use_tmp_file is None:
|
|
61
|
+
use_tmp_file = ms.use_tmp_file
|
|
62
|
+
kw["file"] = file
|
|
50
63
|
kw["mode"] = "wb"
|
|
51
64
|
if mkdir:
|
|
52
65
|
ms.dir.create(os.path.dirname(kw["file"]))
|
|
66
|
+
if use_tmp_file:
|
|
67
|
+
kw["file"] += TMP_SUFFIX
|
|
53
68
|
with builtins.open(**kw) as f:
|
|
54
|
-
|
|
69
|
+
result = f.write(data)
|
|
70
|
+
if use_tmp_file:
|
|
71
|
+
delete(file)
|
|
72
|
+
move(kw["file"], file)
|
|
73
|
+
return result
|
|
55
74
|
|
|
56
75
|
|
|
57
76
|
def copy(path: PATH_TYPES, dest: PATH_TYPES, **kw):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"""Работа с JSON"""
|
|
2
|
-
import
|
|
2
|
+
import atexit
|
|
3
3
|
import builtins
|
|
4
|
+
import json
|
|
4
5
|
from .core import ms
|
|
5
6
|
from .path import PATH_TYPES
|
|
6
7
|
from typing import *
|
|
@@ -9,6 +10,7 @@ try:
|
|
|
9
10
|
except Exception:
|
|
10
11
|
json5 = None
|
|
11
12
|
JSON_TYPES = Union[bool, dict, float, int, list, None, str]
|
|
13
|
+
MODES = ["c", "compress", "mainplay_tg", "mainplay", "max", "min", "mp_tg", "mp", "p", "pretty", "print", "zip"]
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def decode(text: str, *, like_json5: bool = False, **kw) -> JSON_TYPES:
|
|
@@ -26,18 +28,21 @@ def encode(data: JSON_TYPES, mode: str = "c", **kw):
|
|
|
26
28
|
mode = mode.lower()
|
|
27
29
|
if "force" in kw:
|
|
28
30
|
del kw["force"]
|
|
31
|
+
import warnings
|
|
32
|
+
warnings.warn("The argument 'force' is no longer used", DeprecationWarning)
|
|
29
33
|
if "sort" in kw:
|
|
30
34
|
kw["sort_keys"] = kw.pop("sort")
|
|
31
35
|
if mode in ["c", "compress", "min", "zip"]: # Сжатый
|
|
32
|
-
kw
|
|
33
|
-
kw
|
|
36
|
+
kw.setdefault("indent", None)
|
|
37
|
+
kw.setdefault("separators", [",", ":"])
|
|
34
38
|
if mode in ["p", "pretty", "max", "print"]: # Развёрнутый
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
39
|
+
kw.setdefault("indent", 2)
|
|
40
|
+
kw.setdefault("separators", [",", ": "])
|
|
41
|
+
if mode == "print":
|
|
42
|
+
kw.setdefault("ensure_ascii", False)
|
|
38
43
|
if mode in ["mp", "mp_tg", "mainplay", "mainplay_tg"]: # Стиль MainPlay TG
|
|
39
|
-
kw
|
|
40
|
-
kw
|
|
44
|
+
kw.setdefault("indent", 2)
|
|
45
|
+
kw.setdefault("separators", [",", ":"])
|
|
41
46
|
return json.dumps(**kw)
|
|
42
47
|
|
|
43
48
|
|
|
@@ -48,11 +53,7 @@ def print(data: JSON_TYPES, **kw):
|
|
|
48
53
|
if i in kw:
|
|
49
54
|
pr_kw[i] = kw.pop(i)
|
|
50
55
|
kw["data"] = data
|
|
51
|
-
kw
|
|
52
|
-
if not "ensure_ascii" in kw:
|
|
53
|
-
kw["ensure_ascii"] = False
|
|
54
|
-
if not "mode" in kw:
|
|
55
|
-
kw["mode"] = "p"
|
|
56
|
+
kw.setdefault("mode", "print")
|
|
56
57
|
builtins.print(encode(**kw), **pr_kw)
|
|
57
58
|
|
|
58
59
|
|
|
@@ -93,9 +94,73 @@ def write(path: PATH_TYPES, data: JSON_TYPES, **kw) -> int:
|
|
|
93
94
|
"""Прочитать JSON файл"""
|
|
94
95
|
f_kw = {}
|
|
95
96
|
kw["data"] = data
|
|
96
|
-
for i in ["encoding", "force"]:
|
|
97
|
+
for i in ["encoding", "force", "use_tmp_file"]:
|
|
97
98
|
if i in kw:
|
|
98
99
|
f_kw[i] = kw.pop(i)
|
|
99
100
|
f_kw["path"] = path
|
|
100
101
|
f_kw["data"] = encode(**kw)
|
|
101
102
|
return ms.file.write(**f_kw)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class JsonFile:
|
|
106
|
+
def __init__(self, path: str, autoload: bool = True, save_at_exit: bool = False, **kw):
|
|
107
|
+
atexit.register(self.__exit__)
|
|
108
|
+
self._data = None
|
|
109
|
+
self._loaded = False
|
|
110
|
+
self._path = None
|
|
111
|
+
self.load_kw = {}
|
|
112
|
+
self.save_kw = kw
|
|
113
|
+
self.path = ms.path.Path(path)
|
|
114
|
+
self.save_at_exit = save_at_exit
|
|
115
|
+
if "like_json5" in self.save_kw:
|
|
116
|
+
self.load_kw["like_json5"] = self.save_kw.pop("like_json5")
|
|
117
|
+
if autoload:
|
|
118
|
+
self.load()
|
|
119
|
+
|
|
120
|
+
def __contains__(self, k) -> bool:
|
|
121
|
+
return k in self.data
|
|
122
|
+
|
|
123
|
+
def __delitem__(self, k):
|
|
124
|
+
del self.data[k]
|
|
125
|
+
|
|
126
|
+
def __enter__(self):
|
|
127
|
+
return self
|
|
128
|
+
|
|
129
|
+
def __exit__(self, *a):
|
|
130
|
+
if self.save_at_exit:
|
|
131
|
+
self.save()
|
|
132
|
+
|
|
133
|
+
def __getitem__(self, k):
|
|
134
|
+
return self.data[k]
|
|
135
|
+
|
|
136
|
+
def __setitem__(self, k, v):
|
|
137
|
+
self.data[k] = v
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def path(self) -> ms.path.Path:
|
|
141
|
+
return self._path
|
|
142
|
+
|
|
143
|
+
@path.setter
|
|
144
|
+
def path(self, v):
|
|
145
|
+
self._path = ms.path.Path(v)
|
|
146
|
+
self.load_kw["path"] = self._path.path
|
|
147
|
+
self.save_kw["path"] = self._path.path
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def data(self):
|
|
151
|
+
if not self._loaded:
|
|
152
|
+
self.load()
|
|
153
|
+
return self._data
|
|
154
|
+
|
|
155
|
+
@data.setter
|
|
156
|
+
def data(self, v):
|
|
157
|
+
self._data = v
|
|
158
|
+
self._loaded = True
|
|
159
|
+
|
|
160
|
+
def load(self):
|
|
161
|
+
self._data = read(**self.load_kw)
|
|
162
|
+
self._loaded = True
|
|
163
|
+
|
|
164
|
+
def save(self):
|
|
165
|
+
self.save_kw["data"] = self.data
|
|
166
|
+
return write(**self.save_kw)
|
|
@@ -374,7 +374,7 @@ class OnlyOneInstance:
|
|
|
374
374
|
|
|
375
375
|
def _enter():
|
|
376
376
|
try:
|
|
377
|
-
if self.lock.
|
|
377
|
+
if os.path.exists(self.lock.path):
|
|
378
378
|
os.unlink(self.lock.path)
|
|
379
379
|
self.fd = os.open(self.lock.path, flags)
|
|
380
380
|
except OSError as err:
|
|
@@ -399,8 +399,8 @@ class OnlyOneInstance:
|
|
|
399
399
|
|
|
400
400
|
def _exit():
|
|
401
401
|
fcntl.lockf(self.fp, fcntl.LOCK_UN)
|
|
402
|
-
|
|
403
|
-
if self.lock.
|
|
402
|
+
self.fp.close()
|
|
403
|
+
if os.path.exists(self.lock.path):
|
|
404
404
|
os.unlink(self.lock.path)
|
|
405
405
|
self._enter = _enter
|
|
406
406
|
self._exit = _exit
|
|
@@ -430,5 +430,42 @@ class OnlyOneInstance:
|
|
|
430
430
|
pass
|
|
431
431
|
|
|
432
432
|
|
|
433
|
+
def multi_and(*values: bool) -> bool:
|
|
434
|
+
for i in values:
|
|
435
|
+
if not i:
|
|
436
|
+
return False
|
|
437
|
+
return True
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def multi_or(*values: bool) -> bool:
|
|
441
|
+
for i in values:
|
|
442
|
+
if i:
|
|
443
|
+
return True
|
|
444
|
+
return False
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def is_int(value: float) -> bool:
|
|
448
|
+
return value == int(value)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def get_self_module(__name__: str):
|
|
452
|
+
return sys.modules[__name__]
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
def check_programs(*progs: str, raise_error: bool = True) -> list[str]:
|
|
456
|
+
"""Проверить наличие программ в `$PATH` | `shutil`"""
|
|
457
|
+
from shutil import which
|
|
458
|
+
failed = []
|
|
459
|
+
for i in progs:
|
|
460
|
+
if which(i) is None:
|
|
461
|
+
if not i in failed:
|
|
462
|
+
failed.append(i)
|
|
463
|
+
failed.sort()
|
|
464
|
+
if raise_error:
|
|
465
|
+
if len(failed) > 0:
|
|
466
|
+
raise OSError("Failed to find programs " + (", ".join(failed)) + " in $PATH")
|
|
467
|
+
return failed
|
|
468
|
+
|
|
469
|
+
|
|
433
470
|
download_file = sync_download_file
|
|
434
471
|
request = sync_request
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|