gomyck-tools 1.2.5__py3-none-any.whl → 1.2.7__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.
- ctools/api_result.py +13 -7
- ctools/bottle_web_base.py +32 -9
- ctools/bottle_webserver.py +41 -20
- ctools/metrics.py +14 -14
- ctools/token.py +34 -0
- ctools/work_path.py +5 -2
- {gomyck_tools-1.2.5.dist-info → gomyck_tools-1.2.7.dist-info}/METADATA +3 -1
- {gomyck_tools-1.2.5.dist-info → gomyck_tools-1.2.7.dist-info}/RECORD +10 -9
- {gomyck_tools-1.2.5.dist-info → gomyck_tools-1.2.7.dist-info}/WHEEL +0 -0
- {gomyck_tools-1.2.5.dist-info → gomyck_tools-1.2.7.dist-info}/top_level.txt +0 -0
ctools/api_result.py
CHANGED
@@ -29,7 +29,7 @@ class R(object):
|
|
29
29
|
self.message = message
|
30
30
|
self.data = data
|
31
31
|
|
32
|
-
def
|
32
|
+
def _to_json_str(self):
|
33
33
|
return cjson.unify_to_str(cjson.dumps(self))
|
34
34
|
|
35
35
|
@staticmethod
|
@@ -37,13 +37,19 @@ class R(object):
|
|
37
37
|
return R(**cjson.loads(r_json))
|
38
38
|
|
39
39
|
@staticmethod
|
40
|
-
def ok(data=None, resp=Code.SUCCESS, msg=None):
|
41
|
-
|
40
|
+
def ok(data=None, resp=Code.SUCCESS, msg=None, to_json_str=True):
|
41
|
+
if not to_json_str:
|
42
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
43
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
42
44
|
|
43
45
|
@staticmethod
|
44
|
-
def fail(msg=None, resp=Code.FAIL, data=None):
|
45
|
-
|
46
|
+
def fail(msg=None, resp=Code.FAIL, data=None, to_json_str=True):
|
47
|
+
if not to_json_str:
|
48
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
49
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
46
50
|
|
47
51
|
@staticmethod
|
48
|
-
def error(msg=None, resp=Code.ERROR, data=None):
|
49
|
-
|
52
|
+
def error(msg=None, resp=Code.ERROR, data=None, to_json_str=True):
|
53
|
+
if not to_json_str:
|
54
|
+
return R(resp.code, msg if msg is not None else resp.message, data)
|
55
|
+
return R(resp.code, msg if msg is not None else resp.message, data)._to_json_str()
|
ctools/bottle_web_base.py
CHANGED
@@ -4,6 +4,8 @@ from functools import wraps
|
|
4
4
|
|
5
5
|
import bottle
|
6
6
|
from bottle import response, Bottle, request
|
7
|
+
|
8
|
+
from ctools import token
|
7
9
|
from ctools.dict_wrapper import DictWrapper
|
8
10
|
from ctools.api_result import R
|
9
11
|
from ctools.sys_log import flog as log
|
@@ -14,13 +16,15 @@ func_has_params = {}
|
|
14
16
|
class GlobalState:
|
15
17
|
lock = threading.Lock()
|
16
18
|
withOutLoginURI = [
|
17
|
-
'/
|
18
|
-
'/
|
19
|
+
'/',
|
20
|
+
'/index',
|
21
|
+
'/login'
|
19
22
|
]
|
20
23
|
allowRemoteCallURI = [
|
21
24
|
|
22
25
|
]
|
23
26
|
token = {}
|
27
|
+
interceptors = []
|
24
28
|
|
25
29
|
def init_app(context_path=None):
|
26
30
|
app = Bottle()
|
@@ -28,20 +32,21 @@ def init_app(context_path=None):
|
|
28
32
|
|
29
33
|
@app.hook('before_request')
|
30
34
|
def before_request():
|
31
|
-
|
32
|
-
|
35
|
+
for interceptor in GlobalState.interceptors:
|
36
|
+
res: R = interceptor['func']()
|
37
|
+
if res.code != 200: bottle.abort(res.code, res.message)
|
33
38
|
|
34
39
|
@app.error(401)
|
35
40
|
def unauthorized(error):
|
36
41
|
after_request()
|
37
|
-
response.status =
|
42
|
+
response.status = 401
|
38
43
|
log.error("系统未授权: {} {} {}".format(error.body, request.method, request.fullpath))
|
39
44
|
return R.error(resp=R.Code.cus_code(9999, "系统未授权! {}".format(error.body)))
|
40
45
|
|
41
46
|
@app.error(403)
|
42
47
|
def unauthorized(error):
|
43
48
|
after_request()
|
44
|
-
response.status =
|
49
|
+
response.status = 403
|
45
50
|
log.error("访问受限: {} {} {}".format(error.body, request.method, request.fullpath))
|
46
51
|
return R.error(resp=R.Code.cus_code(8888, "访问受限: {}".format(error.body)))
|
47
52
|
|
@@ -49,7 +54,7 @@ def init_app(context_path=None):
|
|
49
54
|
def cors_error(error):
|
50
55
|
if request.method == 'OPTIONS':
|
51
56
|
after_request()
|
52
|
-
response.status =
|
57
|
+
response.status = 405
|
53
58
|
return
|
54
59
|
log.error("请求方法错误: {} {} {}".format(error.status_line, request.method, request.fullpath))
|
55
60
|
return R.error(msg='请求方法错误: {}'.format(error.status_line))
|
@@ -57,7 +62,7 @@ def init_app(context_path=None):
|
|
57
62
|
@app.error(500)
|
58
63
|
def cors_error(error):
|
59
64
|
after_request()
|
60
|
-
response.status =
|
65
|
+
response.status = 500
|
61
66
|
log.error("系统发生错误: {} {} {}".format(error.body, request.method, request.fullpath))
|
62
67
|
return R.error(msg='系统发生错误: {}'.format(error.exception))
|
63
68
|
|
@@ -75,6 +80,14 @@ def enable_cors():
|
|
75
80
|
response.headers['Access-Control-Allow-Headers'] = request_headers if request_headers else ''
|
76
81
|
response.headers['Access-Control-Expose-Headers'] = '*'
|
77
82
|
|
83
|
+
# annotation
|
84
|
+
def before_intercept(order=0):
|
85
|
+
def decorator(func):
|
86
|
+
log.info("add before interceptor: {}".format(func.__name__))
|
87
|
+
GlobalState.interceptors.append({'order': order, 'func': func})
|
88
|
+
GlobalState.interceptors = sorted(GlobalState.interceptors, key=lambda x: x['order'])
|
89
|
+
return decorator
|
90
|
+
|
78
91
|
# annotation
|
79
92
|
def rule(key):
|
80
93
|
def return_func(func):
|
@@ -142,4 +155,14 @@ class FormDataParams:
|
|
142
155
|
except Exception:
|
143
156
|
return self.files[key]
|
144
157
|
|
145
|
-
|
158
|
+
# 通用的鉴权方法
|
159
|
+
def common_auth_verify(aes_key):
|
160
|
+
if request.path.startswith('/static') or request.path in GlobalState.withOutLoginURI:
|
161
|
+
return R.ok(to_json_str=False)
|
162
|
+
auth_token = request.get_header('Authorization')
|
163
|
+
if auth_token is None:
|
164
|
+
auth_token = request.get_cookie('Authorization')
|
165
|
+
payload = token.get_payload(auth_token, aes_key)
|
166
|
+
if payload:
|
167
|
+
return R.ok(to_json_str=False)
|
168
|
+
return R.error(resp=R.Code.cus_code(401, "未授权"), to_json_str=False)
|
ctools/bottle_webserver.py
CHANGED
@@ -2,18 +2,11 @@ import sys
|
|
2
2
|
from socketserver import ThreadingMixIn
|
3
3
|
from wsgiref.simple_server import WSGIServer, WSGIRequestHandler, make_server
|
4
4
|
|
5
|
-
from bottle import ServerAdapter, Bottle, template, static_file
|
5
|
+
from bottle import ServerAdapter, Bottle, template, static_file, abort
|
6
6
|
|
7
7
|
from ctools import sys_info
|
8
8
|
|
9
|
-
"""
|
10
|
-
app = bottle_web_base.init_app('子模块写 context_path, 主模块就不用写任何东西')
|
11
9
|
|
12
|
-
@app.get('/queryList')
|
13
|
-
@bottle_web_base.rule('DOC:DOWNLOAD')
|
14
|
-
def query_list(params):
|
15
|
-
print(123)
|
16
|
-
"""
|
17
10
|
|
18
11
|
"""
|
19
12
|
module_names = list(globals().keys())
|
@@ -31,10 +24,26 @@ def get_ws_modules():
|
|
31
24
|
"""
|
32
25
|
|
33
26
|
"""
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
app = bottle_web_base.init_app('子模块写 context_path, 主模块就不用写任何东西')
|
28
|
+
|
29
|
+
# 通用的鉴权方法
|
30
|
+
@bottle_web_base.before_intercept(0)
|
31
|
+
def token_check():
|
32
|
+
return bottle_web_base.common_auth_verify(aes_key)
|
33
|
+
|
34
|
+
@app.post('/login')
|
35
|
+
def login(params):
|
36
|
+
return R.ok(token.gen_token({'username': 'xxx'}, aes_key, 3600))
|
37
|
+
|
38
|
+
@app.get('/queryList')
|
39
|
+
@bottle_web_base.rule('DOC:DOWNLOAD')
|
40
|
+
def query_list(params):
|
41
|
+
print(123)
|
42
|
+
|
43
|
+
main_app = bottle_webserver.init_bottle() # 这里可以传 APP 当做主模块, 但是 context_path 就不好使了, 上下文必须是 /
|
44
|
+
main_app.mount(app.context_path, app)
|
45
|
+
main_app.set_index(r'轨迹点位压缩.html')
|
46
|
+
main_app.run()
|
38
47
|
"""
|
39
48
|
|
40
49
|
class CBottle:
|
@@ -49,20 +58,32 @@ class CBottle:
|
|
49
58
|
print('Click the link below to open the service homepage %s' % '\n \t\t http://localhost:%s \n \t\t http://%s:%s' % (self.port, sys_info.get_local_ipv4(), self.port), file=sys.stderr)
|
50
59
|
self.bottle.run(server=http_server, quiet=self.quiet)
|
51
60
|
|
52
|
-
def set_index(self,
|
61
|
+
def set_index(self, filename='index.html', root='./', **kwargs):
|
53
62
|
@self.bottle.route(['/', '/index'])
|
54
63
|
def index():
|
55
|
-
|
64
|
+
try:
|
65
|
+
return static_file(filename=filename, root=root, **kwargs)
|
66
|
+
except FileNotFoundError:
|
67
|
+
abort(404, "File not found...")
|
68
|
+
|
69
|
+
def set_template(self, root, **kwargs):
|
70
|
+
@self.bottle.route('/template/<filepath:path>')
|
71
|
+
def index(filepath):
|
72
|
+
template_path = f"{root}/{filepath}"
|
73
|
+
return template(template_path, **kwargs)
|
56
74
|
|
57
75
|
def set_static(self, root):
|
58
76
|
@self.bottle.route('/static/<filepath:path>')
|
59
77
|
def static(filepath):
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
78
|
+
try:
|
79
|
+
return static_file(filepath, root=root)
|
80
|
+
except FileNotFoundError:
|
81
|
+
abort(404, "File not found...")
|
82
|
+
|
83
|
+
def set_download(self, root):
|
84
|
+
@self.bottle.route('/download/<filepath:path>')
|
85
|
+
def download(filepath):
|
86
|
+
return static_file(filepath, root=root, download=True)
|
66
87
|
|
67
88
|
def mount(self, context_path, app, **kwargs):
|
68
89
|
self.bottle.mount(context_path, app, **kwargs)
|
ctools/metrics.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import os
|
2
2
|
import threading
|
3
|
-
import time
|
4
3
|
from enum import Enum
|
5
4
|
|
6
5
|
from prometheus_client import Counter, Gauge, Summary, Histogram, start_http_server
|
@@ -12,7 +11,7 @@ _metrics_port = 8011
|
|
12
11
|
_persistent_json = {}
|
13
12
|
_metrics_initial = {}
|
14
13
|
_lock = threading.Lock()
|
15
|
-
|
14
|
+
_metrics_persistent_path = os.path.join(work_path.get_user_work_path('metrics', True), 'persistent.json')
|
16
15
|
|
17
16
|
# 认证中间件
|
18
17
|
# @app.before_request
|
@@ -30,8 +29,7 @@ class MetricType(Enum):
|
|
30
29
|
HISTOGRAM = 'histogram'
|
31
30
|
|
32
31
|
class Metric:
|
33
|
-
def __init__(self, metric_type: MetricType, metric_key: str, metric_labels: [],
|
34
|
-
persistent: bool = False, buckets: [] = None, reset: bool = False, desc: str = ""):
|
32
|
+
def __init__(self, metric_type: MetricType, metric_key: str, metric_labels: [], persistent: bool = False, buckets: [] = None, reset: bool = False, desc: str = ""):
|
35
33
|
self.metric_type = metric_type
|
36
34
|
self.metric_key = metric_key
|
37
35
|
self.metric_labels = metric_labels
|
@@ -54,9 +52,8 @@ class Metric:
|
|
54
52
|
|
55
53
|
@call.once
|
56
54
|
def init(reset_persistent: bool = False):
|
57
|
-
|
58
|
-
|
59
|
-
with open(persistent_path, 'r') as persistent_file:
|
55
|
+
if os.path.exists(_metrics_persistent_path) and not reset_persistent:
|
56
|
+
with open(_metrics_persistent_path, 'r') as persistent_file:
|
60
57
|
global _persistent_json
|
61
58
|
try:
|
62
59
|
content = persistent_file.readline()
|
@@ -66,6 +63,7 @@ def init(reset_persistent: bool = False):
|
|
66
63
|
log.error('persistent.json is not valid json!!!!!')
|
67
64
|
_persistent_json = {}
|
68
65
|
_init_all_metrics()
|
66
|
+
_persistent_json = _persistent_json or {}
|
69
67
|
for key, item in _persistent_json.items():
|
70
68
|
metrics_key = key.split("-")[0]
|
71
69
|
if '_labels' in key or metrics_key not in _metrics_initial: continue
|
@@ -77,7 +75,7 @@ def init(reset_persistent: bool = False):
|
|
77
75
|
def persistent_metrics():
|
78
76
|
if _persistent_json and not _lock.locked():
|
79
77
|
log.info('begin persistent metrics to file...')
|
80
|
-
with open(
|
78
|
+
with open(_metrics_persistent_path, 'w') as persistent_file:
|
81
79
|
persistent_file.write(cjson.dumps(_persistent_json))
|
82
80
|
persistent_file.flush()
|
83
81
|
|
@@ -100,14 +98,16 @@ def opt(metric_key: str, label_values: [], metric_value: int):
|
|
100
98
|
if metric_entity.metric_type == MetricType.COUNTER or metric_entity.metric_type == MetricType.GAUGE:
|
101
99
|
if label_values is None or len(label_values) == 0:
|
102
100
|
if metric_entity.metric_type == MetricType.COUNTER and metric_entity.reset:
|
103
|
-
metric_entity.metric.reset()
|
104
|
-
metric_entity.
|
101
|
+
metric_entity.metric.labels('').reset()
|
102
|
+
if metric_entity.metric_type == MetricType.GAUGE and metric_entity.reset:
|
103
|
+
metric_entity.metric.labels('').set(0)
|
104
|
+
metric_entity.metric.labels('').inc(metric_value)
|
105
105
|
else:
|
106
106
|
if metric_entity.reset:
|
107
|
-
|
108
|
-
metric_entity.metric.
|
109
|
-
|
110
|
-
|
107
|
+
if metric_entity.metric_type == MetricType.COUNTER and metric_entity.reset:
|
108
|
+
metric_entity.metric.labels(*label_values).reset()
|
109
|
+
if metric_entity.metric_type == MetricType.GAUGE and metric_entity.reset:
|
110
|
+
metric_entity.metric.labels(*label_values).set(0)
|
111
111
|
metric_entity.metric.labels(*label_values).inc(metric_value)
|
112
112
|
else:
|
113
113
|
if label_values is None or len(label_values) == 0:
|
ctools/token.py
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# -*- coding: UTF-8 -*-
|
3
|
+
__author__ = 'haoyang'
|
4
|
+
__date__ = '2025/1/21 16:01'
|
5
|
+
|
6
|
+
import time
|
7
|
+
import jwt
|
8
|
+
from bottle import request
|
9
|
+
|
10
|
+
from ctools.dict_wrapper import DictWrapper
|
11
|
+
|
12
|
+
token_header = 'Authorization'
|
13
|
+
|
14
|
+
def gen_token(payload: {}, secret_key, expired: int=3600) -> str:
|
15
|
+
payload.update({'exp': time.time() + expired})
|
16
|
+
return jwt.encode(payload, secret_key, algorithm='HS256')
|
17
|
+
|
18
|
+
def get_payload(token, secret_key):
|
19
|
+
try:
|
20
|
+
payload = jwt.decode(token, secret_key, algorithms=['HS256'])
|
21
|
+
return DictWrapper(payload)
|
22
|
+
except Exception as e:
|
23
|
+
return None
|
24
|
+
|
25
|
+
def get_token(key):
|
26
|
+
return get_payload(request.get_header(token_header), key)
|
27
|
+
|
28
|
+
def is_valid(key):
|
29
|
+
return get_payload(request.get_header(token_header), key) is not None
|
30
|
+
|
31
|
+
# if __name__ == '__main__':
|
32
|
+
# token = gen_token({"xx": 123}, '123')
|
33
|
+
# xx = get_payload(token, '123')
|
34
|
+
# print(xx.xx)
|
ctools/work_path.py
CHANGED
@@ -33,13 +33,16 @@ def get_app_path(subPath: str = '') -> str:
|
|
33
33
|
return os.path.join(base_path, subPath)
|
34
34
|
|
35
35
|
|
36
|
-
def get_user_work_path(subPath: str = ''
|
36
|
+
def get_user_work_path(subPath: str = '', mkdir: bool = False):
|
37
37
|
"""
|
38
38
|
获取用户工作目录
|
39
39
|
:param subPath: 拼接的子路径
|
40
|
+
:param mkdir: 是否创建目录
|
40
41
|
:return: 用户工作目录
|
41
42
|
"""
|
42
|
-
|
43
|
+
path = os.path.join(os.path.expanduser("~"), subPath)
|
44
|
+
if mkdir and not os.path.exists(path): os.makedirs(path, exist_ok=True)
|
45
|
+
return path
|
43
46
|
|
44
47
|
|
45
48
|
def get_user_temp_path(subPath: str = '') -> str:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: gomyck-tools
|
3
|
-
Version: 1.2.
|
3
|
+
Version: 1.2.7
|
4
4
|
Summary: A ctools for python development by hao474798383
|
5
5
|
Home-page: https://blog.gomyck.com
|
6
6
|
Author: gomyck
|
@@ -28,5 +28,7 @@ Requires-Dist: fuzzywuzzy ~=0.18.0
|
|
28
28
|
Requires-Dist: pymysql ~=1.1.1
|
29
29
|
Requires-Dist: pyzipper ==0.3.6
|
30
30
|
Requires-Dist: prometheus-client ==0.21.1
|
31
|
+
Requires-Dist: paramiko ==3.5.0
|
32
|
+
Requires-Dist: pyjwt ==2.10.1
|
31
33
|
|
32
34
|
this package is for python development
|
@@ -1,11 +1,11 @@
|
|
1
1
|
ctools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
2
|
ctools/aes_tools.py,sha256=ylUgyhlx7bNCTGrbWEZVwCObzYdJTQtwEc4ZMidL2Fo,563
|
3
|
-
ctools/api_result.py,sha256=
|
3
|
+
ctools/api_result.py,sha256=UeQXI_zuZB-uY5qECTpz1fC7EGy82yGQqWMx20tyRTw,1572
|
4
4
|
ctools/application.py,sha256=DcuSt2m8cDuSftx6eKfJ5gA6_F9dDlzkj0K86EG4F7s,15884
|
5
5
|
ctools/b64.py,sha256=_BdhX3p3-MaSSlU2wivN5qPxQfacR3VRBr1WC456tU0,194
|
6
6
|
ctools/bashPath.py,sha256=BCN_EhYzqvwsxYso81omMNd3SbEociwSOyb9kLvu8V4,337
|
7
|
-
ctools/bottle_web_base.py,sha256=
|
8
|
-
ctools/bottle_webserver.py,sha256=
|
7
|
+
ctools/bottle_web_base.py,sha256=VUAw3GBpoamLDXQJZQScbNPWIysVexns7QcMV-kcLSM,5822
|
8
|
+
ctools/bottle_webserver.py,sha256=tZc9-By_kZgyfRKUSb7tCbEsa5d7X4V3Gcj80p9tb2Q,3489
|
9
9
|
ctools/bottle_websocket.py,sha256=wjsjKVE_tlon0hYfnzBT0Sis6yNPe318vKgTfzWYbfQ,1903
|
10
10
|
ctools/browser_element_tools.py,sha256=IFR_tWu5on0LxhuC_4yT6EOjwCsC-juIoU8KQRDqR7E,9952
|
11
11
|
ctools/call.py,sha256=BCr8wzt5qd70okv8IZn-9-EpjywleZgvA3u1vfZ_Kt8,1581
|
@@ -26,7 +26,7 @@ ctools/html_soup.py,sha256=rnr8M3gn3gQGo-wNaNFXDjdzp8AAkv9o4yqfIIfO-zw,1567
|
|
26
26
|
ctools/http_utils.py,sha256=dG26aci1_YxAyKwYqMKFw4wZAryLkDyvnQ3hURjB6Lk,768
|
27
27
|
ctools/images_tools.py,sha256=TapXYCPqC7GesgrALecxxa_ApuT_dxUG5fqQIJF2bNY,670
|
28
28
|
ctools/imgDialog.py,sha256=zFeyPmpnEn9Ih7-yuJJrePqW8Myj3jC9UYMTDk-umTs,1385
|
29
|
-
ctools/metrics.py,sha256=
|
29
|
+
ctools/metrics.py,sha256=xKy1uWqvg2BnS14dQxvs_qUQtMuVLlUKECSyErFnBtk,5247
|
30
30
|
ctools/mqtt_utils.py,sha256=ZWSZiiNJLLlkHF95S6LmRmkYt-iIL4K73VdN3b1HaHw,10702
|
31
31
|
ctools/obj.py,sha256=GYS1B8NyjtUIh0HlK9r8avC2eGbK2SJac4C1CGnfGhI,479
|
32
32
|
ctools/pacth.py,sha256=MJ9Du-J9Gv62y4cZKls1jKbl5a5kL2y9bD-gzYUCveQ,2604
|
@@ -44,14 +44,15 @@ ctools/string_tools.py,sha256=itK59W4Ed4rQzuyHuioNgDRUcBlfb4ZoZnwmS9cJxiI,1887
|
|
44
44
|
ctools/sys_info.py,sha256=NvKCuBlWHHiW4bDI4tYZUo3QusvODm1HlW6aAkrllnE,4248
|
45
45
|
ctools/sys_log.py,sha256=oqb1S41LosdeZxtogFVgDk8R4sjiHhUeYJLCzHd728E,2805
|
46
46
|
ctools/thread_pool.py,sha256=qb68ULHy1K7u3MC7WP49wDhmgUhgWazd9FRuFbClET4,925
|
47
|
+
ctools/token.py,sha256=NZSBGF3lJajJFLRIZoeXmpp8h5cKM0dAH2weySgeORc,882
|
47
48
|
ctools/upload_tools.py,sha256=sqe6K3ZWiyY58pFE5IO5mNaS1znnS7U4c4UqY8noED4,1068
|
48
49
|
ctools/win_canvas.py,sha256=PAxI4i1jalfree9d1YG4damjc2EzaHZrgHZCTgk2GiM,2530
|
49
50
|
ctools/win_control.py,sha256=35f9x_ijSyc4ZDkcT32e9ZIhr_ffNxadynrQfFuIdYo,3489
|
50
51
|
ctools/wordFill.py,sha256=dB1OLt6GLmWdkDV8H20VWbJmY4ggNNI8iHD1ocae2iM,875
|
51
52
|
ctools/word_fill.py,sha256=xeo-P4DOjQUqd-o9XL3g66wQrE2diUPGwFywm8TdVyw,18210
|
52
53
|
ctools/word_fill_entity.py,sha256=eX3G0Gy16hfGpavQSEkCIoKDdTnNgRRJrFvKliETZK8,985
|
53
|
-
ctools/work_path.py,sha256=
|
54
|
-
gomyck_tools-1.2.
|
55
|
-
gomyck_tools-1.2.
|
56
|
-
gomyck_tools-1.2.
|
57
|
-
gomyck_tools-1.2.
|
54
|
+
ctools/work_path.py,sha256=OmfYu-Jjg2huRY6Su8zJ_2EGFFhtBZFbobYTwbjJtG4,1817
|
55
|
+
gomyck_tools-1.2.7.dist-info/METADATA,sha256=uS-JVNCRR951EEMSzzMThWcONySUcNIj-lG-uJhF4M0,1108
|
56
|
+
gomyck_tools-1.2.7.dist-info/WHEEL,sha256=yQN5g4mg4AybRjkgi-9yy4iQEFibGQmlz78Pik5Or-A,92
|
57
|
+
gomyck_tools-1.2.7.dist-info/top_level.txt,sha256=-MiIH9FYRVKp1i5_SVRkaI-71WmF1sZSRrNWFU9ls3s,7
|
58
|
+
gomyck_tools-1.2.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|