jtt-utils 1.0.0__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.
- jtt-utils-1.0.0/PKG-INFO +21 -0
- jtt-utils-1.0.0/README.md +72 -0
- jtt-utils-1.0.0/jtt_tm_utils/__init__.py +12 -0
- jtt-utils-1.0.0/jtt_tm_utils/consul_handle.py +245 -0
- jtt-utils-1.0.0/jtt_tm_utils/kong.py +177 -0
- jtt-utils-1.0.0/jtt_tm_utils/log.py +3 -0
- jtt-utils-1.0.0/jtt_tm_utils/sync_basedata.py +266 -0
- jtt-utils-1.0.0/jtt_tm_utils/timeutil.py +23 -0
- jtt-utils-1.0.0/jtt_utils.egg-info/PKG-INFO +21 -0
- jtt-utils-1.0.0/jtt_utils.egg-info/SOURCES.txt +14 -0
- jtt-utils-1.0.0/jtt_utils.egg-info/dependency_links.txt +1 -0
- jtt-utils-1.0.0/jtt_utils.egg-info/not-zip-safe +1 -0
- jtt-utils-1.0.0/jtt_utils.egg-info/top_level.txt +1 -0
- jtt-utils-1.0.0/setup.cfg +4 -0
- jtt-utils-1.0.0/setup.py +74 -0
jtt-utils-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: jtt-utils
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: jtt common tools
|
|
5
|
+
Home-page: UNKNOWN
|
|
6
|
+
Author: candyabc
|
|
7
|
+
Author-email: hfcandyabc@163.com
|
|
8
|
+
License: maxwin
|
|
9
|
+
Platform: any
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
19
|
+
|
|
20
|
+
jtt common tools
|
|
21
|
+
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
## D:\work\mwwork\JTT\jtt-tm-util
|
|
2
|
+
#### jtt 常用工具
|
|
3
|
+
*
|
|
4
|
+
|
|
5
|
+
### 设计
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
### 设定
|
|
9
|
+
*
|
|
10
|
+
|
|
11
|
+
###产生分发包
|
|
12
|
+
``
|
|
13
|
+
python setup.py sdist
|
|
14
|
+
twine upload dist/*
|
|
15
|
+
``
|
|
16
|
+
### 运行
|
|
17
|
+
* python setup.py sdist build
|
|
18
|
+
* python setup.py install
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
### 修改记录
|
|
22
|
+
* 2020/03/23
|
|
23
|
+
```text
|
|
24
|
+
v 0.0.4
|
|
25
|
+
修改consul reader 增加read_service 的key参数,向上兼容
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
* 2020/05/27
|
|
29
|
+
```text
|
|
30
|
+
v 0.0.5
|
|
31
|
+
修改consul reader 增加service_as_url向上兼容
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
* 2020/06/11
|
|
36
|
+
```text
|
|
37
|
+
v 0.0.6
|
|
38
|
+
|
|
39
|
+
修改sync_basedata 增加read employee
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
* 2020/06/17
|
|
43
|
+
```text
|
|
44
|
+
v 0.0.7
|
|
45
|
+
|
|
46
|
+
修改sync_basedata 的bug
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
* 2023/03/16
|
|
50
|
+
```text
|
|
51
|
+
v 0.1.0
|
|
52
|
+
|
|
53
|
+
fix update line_config bug。
|
|
54
|
+
```
|
|
55
|
+
* 2023/03/17
|
|
56
|
+
```text
|
|
57
|
+
v 0.1.1
|
|
58
|
+
|
|
59
|
+
kong register add host param
|
|
60
|
+
```
|
|
61
|
+
* 2023/04/20
|
|
62
|
+
```text
|
|
63
|
+
v 0.1.2
|
|
64
|
+
sync basedata 同步支持 key_type='set' 方式
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
* 2023/04/20
|
|
68
|
+
```text
|
|
69
|
+
v 0.1.3
|
|
70
|
+
fix : consul read datebase driver 的错误
|
|
71
|
+
add read_dbdriver func
|
|
72
|
+
```
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
__version__ ='1.0.0'
|
|
2
|
+
__author__ = 'candyabc'
|
|
3
|
+
__copyright__ = 'candyabc'
|
|
4
|
+
__email__ = 'hfcandyabc@163.com'
|
|
5
|
+
__license__ = 'maxwin'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from .sync_basedata import data_manager
|
|
9
|
+
|
|
10
|
+
from .consul_handle import consul_reader
|
|
11
|
+
from .kong import Kong
|
|
12
|
+
# __all__=("JttTaskManager","JttTask")
|
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
from consul import Consul
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
import collections
|
|
6
|
+
|
|
7
|
+
from .log import logger
|
|
8
|
+
|
|
9
|
+
CService =collections.namedtuple('CService','ip port agent_ip serviceid')
|
|
10
|
+
CRedisService = collections.namedtuple('CRedisService','ip port db')
|
|
11
|
+
CDatabaseService = collections.namedtuple('CDatabaseService','dbdriver ip port dbname user pwd')
|
|
12
|
+
# CMongoService = collections.namedtuple('CMongoService','host port username password authSource')
|
|
13
|
+
def cservice_to_address(service):
|
|
14
|
+
return '%s:%s' % (service.ip,service.port)
|
|
15
|
+
|
|
16
|
+
class ConsulAgent:
|
|
17
|
+
def __init__(self,node_name,ip,services=None):
|
|
18
|
+
self.node_name = node_name
|
|
19
|
+
self.ip = ip
|
|
20
|
+
self._services = services
|
|
21
|
+
|
|
22
|
+
def combine_address(self,port):
|
|
23
|
+
return '%s:%s' %(self.ip,port)
|
|
24
|
+
|
|
25
|
+
def combine_service_id(self,name,port):
|
|
26
|
+
return '%s:%s:%s' %(self.node_name,name,port)
|
|
27
|
+
|
|
28
|
+
class ConsulReader():
|
|
29
|
+
def __init__(self):
|
|
30
|
+
self._consul =None
|
|
31
|
+
self._agent =None
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def consul(self):
|
|
35
|
+
if self._consul is None:
|
|
36
|
+
self._consul =Consul()
|
|
37
|
+
return self._consul
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def agent(self):
|
|
41
|
+
if self._agent is None:
|
|
42
|
+
try:
|
|
43
|
+
agent_self = self.consul.agent.self()
|
|
44
|
+
self._agent = ConsulAgent(agent_self['Member'].get('Name'),
|
|
45
|
+
agent_self['Member'].get('Addr'),
|
|
46
|
+
self.consul.catalog.services()[1])
|
|
47
|
+
|
|
48
|
+
except Exception as e:
|
|
49
|
+
logger.error('读取consul 服务失败,error:%s' % (e))
|
|
50
|
+
|
|
51
|
+
sys.exit(-1)
|
|
52
|
+
|
|
53
|
+
return self._agent
|
|
54
|
+
|
|
55
|
+
def read_key(self,key,raise_error=False):
|
|
56
|
+
v =self.consul.kv.get(key)[1]
|
|
57
|
+
if v is None:
|
|
58
|
+
if raise_error:
|
|
59
|
+
raise Exception('consul: not has key %s' % key)
|
|
60
|
+
else:
|
|
61
|
+
sys.exit('consul: not has key %s' % key)
|
|
62
|
+
return v['Value'].decode()
|
|
63
|
+
|
|
64
|
+
def read_key_default(self,key,default):
|
|
65
|
+
try:
|
|
66
|
+
return self.read_key(key,True)
|
|
67
|
+
except:
|
|
68
|
+
return default
|
|
69
|
+
|
|
70
|
+
def put_key(self,key,value,**args):
|
|
71
|
+
self.consul.kv.put(key,value,**args)
|
|
72
|
+
|
|
73
|
+
def read_services(self,service_name,tag,raise_error=False,key =None,**args):
|
|
74
|
+
try:
|
|
75
|
+
srvs = self.consul.catalog.service(service_name, tag=tag,**args)[1]
|
|
76
|
+
|
|
77
|
+
if key is not None:
|
|
78
|
+
port = self.read_key('%s_%s_%s' %(service_name,tag,key),raise_error=raise_error)
|
|
79
|
+
else:
|
|
80
|
+
port =None
|
|
81
|
+
re =[]
|
|
82
|
+
for srv in srvs or []:
|
|
83
|
+
re.append(CService(ip=srv['ServiceAddress'],
|
|
84
|
+
port=port or srv['ServicePort'],
|
|
85
|
+
agent_ip=srv['Address'],
|
|
86
|
+
serviceid = srv['ServiceID']))
|
|
87
|
+
return re
|
|
88
|
+
except Exception as e:
|
|
89
|
+
logger.error('consul:%s,%s error:%s' % (service_name,tag,str(e)))
|
|
90
|
+
self._consul =None
|
|
91
|
+
if raise_error:
|
|
92
|
+
raise Exception('consul:%s,%s, error:%s' % (service_name,tag,str(e)))
|
|
93
|
+
else:
|
|
94
|
+
sys.exit('consul:%s,%s, error:%s' % (service_name,tag,str(e)))
|
|
95
|
+
|
|
96
|
+
def read_service(self,service_name,tag,raise_error=False,key =None,**args):
|
|
97
|
+
srvs = self.read_services(service_name,tag,raise_error,key,**args)
|
|
98
|
+
if len(srvs) <= 0:
|
|
99
|
+
logger.error('consul:%s,%s error:config is not exists' % (service_name,tag))
|
|
100
|
+
if raise_error:
|
|
101
|
+
raise Exception('consul:%s,%s error:config is not exists' % (service_name,tag))
|
|
102
|
+
else:
|
|
103
|
+
sys.exit('consul:%s,%s error:config is not exists' % (service_name,tag))
|
|
104
|
+
return srvs[0]
|
|
105
|
+
|
|
106
|
+
def read_service_as_address(self,service_name,tag,raise_error=False,key =None,**args):
|
|
107
|
+
srv = self.read_service(service_name,tag,raise_error,key,**args)
|
|
108
|
+
return cservice_to_address(srv)
|
|
109
|
+
|
|
110
|
+
def read_redis(self,tag,as_slave=False):
|
|
111
|
+
try:
|
|
112
|
+
if as_slave:
|
|
113
|
+
agent_self = self.consul.agent.self()
|
|
114
|
+
member = agent_self['Member']
|
|
115
|
+
node_name = member.get('NodeName')
|
|
116
|
+
srv = self.read_service('redis', tag, raise_error=True, near=node_name)
|
|
117
|
+
else:
|
|
118
|
+
srv = self.read_service('redis',tag,raise_error=True)
|
|
119
|
+
|
|
120
|
+
try:
|
|
121
|
+
db = self.read_key('redis_%s_db' % tag)
|
|
122
|
+
except:
|
|
123
|
+
db =0
|
|
124
|
+
|
|
125
|
+
return CRedisService(srv.ip,srv.port,db)
|
|
126
|
+
|
|
127
|
+
except Exception as e:
|
|
128
|
+
logger.error('consul:redis error:%s' % str(e))
|
|
129
|
+
sys.exit('consul:redis error:%s' % str(e))
|
|
130
|
+
|
|
131
|
+
def redis_as_url(self,tag,**args):
|
|
132
|
+
credis = self.read_redis(tag,**args)
|
|
133
|
+
return 'redis://%s:%s/%s' % (credis.ip,credis.port,credis.db)
|
|
134
|
+
|
|
135
|
+
def service_as_url(self,service_name,tag,raise_error=False,key =None,protocol ='http',**args):
|
|
136
|
+
service = self.read_service(service_name,tag,raise_error,key,**args)
|
|
137
|
+
return '%s://%s' % (protocol,cservice_to_address(service))
|
|
138
|
+
|
|
139
|
+
def kafka_servers(self,tag=None):
|
|
140
|
+
srvs =self.read_services('kafka',tag=tag)
|
|
141
|
+
if len(srvs)<=0:
|
|
142
|
+
logger.error('consul not set kafka')
|
|
143
|
+
sys.exit('consul not set kafka')
|
|
144
|
+
addresses = [cservice_to_address(srv) for srv in srvs]
|
|
145
|
+
return ','.join(addresses)
|
|
146
|
+
|
|
147
|
+
def read_dbdriver(self,dbname,**args):
|
|
148
|
+
return self.read_key('%s_dbdriver' % dbname,**args)
|
|
149
|
+
|
|
150
|
+
def read_database(self,dbname,dbapi =None):
|
|
151
|
+
dbsrv =self.read_service('database',dbname)
|
|
152
|
+
driver_key = self.read_key('%s_dbdriver' % dbname)
|
|
153
|
+
if dbapi ==None:
|
|
154
|
+
if driver_key =='mssql':
|
|
155
|
+
dbapi ='pymssql'
|
|
156
|
+
elif driver_key =='mysql':
|
|
157
|
+
dbapi ='pymysql'
|
|
158
|
+
else:
|
|
159
|
+
raise Exception(f'not set dbapi driver for {driver_key}')
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
# if dbapi is None:
|
|
163
|
+
# db_driver = 'mssql+pymssql' if driver_key=='mssql' else 'mysql+mysqldb'
|
|
164
|
+
# else:
|
|
165
|
+
db_driver = '%s+%s' %(driver_key,dbapi)
|
|
166
|
+
|
|
167
|
+
return CDatabaseService(db_driver,
|
|
168
|
+
dbsrv.ip,
|
|
169
|
+
dbsrv.port,
|
|
170
|
+
self.read_key('%s_db_name'% dbname),
|
|
171
|
+
self.read_key('%s_login_user' % dbname),
|
|
172
|
+
self.read_key('%s_login_pw' % dbname))
|
|
173
|
+
|
|
174
|
+
#dbdriver ip port dbname user pwd
|
|
175
|
+
def read_database_keys(self,dbname):
|
|
176
|
+
return {'dbdriver' : self.read_key('%s_dbdriver' % dbname),
|
|
177
|
+
'dbname':self.read_key('%s_db_name' % dbname),
|
|
178
|
+
'user': self.read_key('%s_login_user' % dbname),
|
|
179
|
+
'pwd': self.read_key('%s_login_pw' % dbname)}
|
|
180
|
+
|
|
181
|
+
def database_as_url(self,dbname,**args):
|
|
182
|
+
dbsrv = self.read_database(dbname,**args)
|
|
183
|
+
return "{dbdriver}://{us}:{pw}@{host}:{port}/{name}".format(
|
|
184
|
+
dbdriver =dbsrv.dbdriver,us=dbsrv.user,pw=dbsrv.pwd,
|
|
185
|
+
host=dbsrv.ip,port=dbsrv.port,name=dbsrv.dbname)
|
|
186
|
+
|
|
187
|
+
def database_as_json(self,dbname):
|
|
188
|
+
dbsrv = self.read_database(dbname)
|
|
189
|
+
return {'host':dbsrv.ip,
|
|
190
|
+
'port':dbsrv.port,
|
|
191
|
+
'db':dbsrv.dbname,
|
|
192
|
+
'user':dbsrv.user,
|
|
193
|
+
'password':dbsrv.pwd}
|
|
194
|
+
|
|
195
|
+
def read_mongo(self,dbname):
|
|
196
|
+
dbsrv = self.read_service('mongo', dbname)
|
|
197
|
+
return {'host':dbsrv.ip,
|
|
198
|
+
'port':dbsrv.port,
|
|
199
|
+
'username':self.read_key('mongo_%s_user' % dbname),
|
|
200
|
+
'password':self.read_key('mongo_%s_pw' % dbname),
|
|
201
|
+
'authSource': dbname}
|
|
202
|
+
|
|
203
|
+
def mongo_as_url(self,dbname):
|
|
204
|
+
dbconfig = self.read_mongo(dbname)
|
|
205
|
+
return 'mongodb://{username}:{password}@{host}:{port}/{authSource}'.format(**dbconfig)
|
|
206
|
+
|
|
207
|
+
def register_service(self,name,port,host=None,tags =None,check=None,token=None,service_id=None):
|
|
208
|
+
if host is None:
|
|
209
|
+
host = self.agent.ip
|
|
210
|
+
try:
|
|
211
|
+
if service_id is None:
|
|
212
|
+
service_id = self.agent.combine_service_id( name, port )
|
|
213
|
+
|
|
214
|
+
ret= self.consul.agent.service.register(name, service_id=service_id, address=host,
|
|
215
|
+
port=port, tags=tags, check=check, token=token)
|
|
216
|
+
if ret:
|
|
217
|
+
logger.info('註冊%s(%s) 到consul成功!' % (name, service_id))
|
|
218
|
+
else:
|
|
219
|
+
logger.error('注册 %s(%s) 服务失败' % (name, service_id))
|
|
220
|
+
except Exception as e:
|
|
221
|
+
logger.error('注册 %s(%s) 服务失败,error:%s' % (name,service_id,str(e)))
|
|
222
|
+
|
|
223
|
+
sys.exit('注册 %s(%s)服务失败,error:%s' % (name,service_id, e))
|
|
224
|
+
|
|
225
|
+
def deregister_service(self,name,port,service_id=None):
|
|
226
|
+
try:
|
|
227
|
+
if service_id is None:
|
|
228
|
+
service_id=self.agent.combine_service_id( name, port )
|
|
229
|
+
|
|
230
|
+
ret= self.consul.agent.service.deregister(service_id)
|
|
231
|
+
|
|
232
|
+
if ret:
|
|
233
|
+
logger.info('删除%s(%s)成功!' % (name, service_id))
|
|
234
|
+
else:
|
|
235
|
+
logger.error('删除%s(%s)服务失败!' % (name, service_id))
|
|
236
|
+
except Exception as e:
|
|
237
|
+
logger.error('删除 %s(%s)服务失败,error:%s' % (name,service_id, str(e)))
|
|
238
|
+
|
|
239
|
+
sys.exit('删除 %s(%s)服务失败,error:%s' % (name,service_id, e))
|
|
240
|
+
|
|
241
|
+
def catalog_register(self):
|
|
242
|
+
self.consul.catalog.register()
|
|
243
|
+
consul_reader = ConsulReader()
|
|
244
|
+
|
|
245
|
+
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
from .consul_handle import consul_reader
|
|
2
|
+
from .log import logger
|
|
3
|
+
import requests
|
|
4
|
+
import sys
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Kong:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
|
|
10
|
+
self.admin_uri = consul_reader.service_as_url('kong',tag='admin')
|
|
11
|
+
srv = consul_reader.read_service('kong',tag='kong')
|
|
12
|
+
self.ip = srv.ip
|
|
13
|
+
self.port = srv.port
|
|
14
|
+
|
|
15
|
+
def register_upstream_service(self,upstream_name,port, base_path,auth=None,host =None,**args):
|
|
16
|
+
if host is None:
|
|
17
|
+
address = consul_reader.agent.combine_address(port)
|
|
18
|
+
else:
|
|
19
|
+
address ='%s:%s' %(host,port)
|
|
20
|
+
self.register_upstream(upstream_name,address,**args)
|
|
21
|
+
self.register_service(upstream_name,base_path,auth)
|
|
22
|
+
|
|
23
|
+
def register_service(self,upstream_name,base_path, auth=None):
|
|
24
|
+
'''
|
|
25
|
+
:param kong_admin: 用于注册服务
|
|
26
|
+
:param service_host: 本地服务的host,用于设定upstream_url
|
|
27
|
+
:param service: 服务api,如:rightmanage/v1.0
|
|
28
|
+
:param kong_uris: 空的host uris,为空时值为service,如:rightmanage/v1.0
|
|
29
|
+
:param auth: 认证类型,可为str或list,如:'jwt',或:['jwt','key']
|
|
30
|
+
:param health: 如果service_host 是upstreams,则可以注册health
|
|
31
|
+
:return:
|
|
32
|
+
'''
|
|
33
|
+
''''
|
|
34
|
+
# 注册 服务API
|
|
35
|
+
resp = requests.post('http://{kong}/apis/'.format(kong=KONG),
|
|
36
|
+
json={'name': 'rightmanage_v1.0',
|
|
37
|
+
'uris': '/rightmanage/v1.0',
|
|
38
|
+
'upstream_url': 'http://{server}/rightmanage/v1.0'.format(server=SERVER)})
|
|
39
|
+
print('注册 服务API', resp.text)
|
|
40
|
+
|
|
41
|
+
resp = requests.post('http://{kong}/apis/rightmanage_v1.0/plugins'.format(kong=KONG),
|
|
42
|
+
json={"name": "jwt"})
|
|
43
|
+
print('注册服务API JWT', resp.text)
|
|
44
|
+
'''
|
|
45
|
+
api_url = '{kong}/apis/'.format(kong=self.admin_uri)
|
|
46
|
+
# 如果kong_uris有值,则需要用它注册
|
|
47
|
+
api_name = base_path.replace('/','_').replace('.','_').strip('_')
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
upstream_url = 'http://{upstream_name}{base_path}'.format(upstream_name=upstream_name,
|
|
51
|
+
base_path=base_path)
|
|
52
|
+
try:
|
|
53
|
+
# 删除旧的api
|
|
54
|
+
resp = requests.delete('{kong}/apis/{api_name}'.format(kong=self.admin_uri,
|
|
55
|
+
api_name=api_name))
|
|
56
|
+
logger.info('del api(%s) %s,%s '% (api_name,resp.status_code,resp.text))
|
|
57
|
+
resp = requests.post(api_url,
|
|
58
|
+
json={'name': api_name,
|
|
59
|
+
'uris': base_path,
|
|
60
|
+
'upstream_url': upstream_url,
|
|
61
|
+
'preserve_host': True})
|
|
62
|
+
logger.info('注册%s服务 to kong 成功,%s '% (base_path, resp.text))
|
|
63
|
+
except Exception as e:
|
|
64
|
+
logger.error('注册 %s服务失败,error:%s' % (base_path, e))
|
|
65
|
+
|
|
66
|
+
sys.exit(-1)
|
|
67
|
+
if auth:
|
|
68
|
+
if isinstance(auth, str):
|
|
69
|
+
auth =[auth]
|
|
70
|
+
|
|
71
|
+
if 'jwt' in auth:
|
|
72
|
+
auth_data = {"name": "jwt",
|
|
73
|
+
"config.uri_param_names": "jwt",
|
|
74
|
+
"config.claims_to_verify": "exp",
|
|
75
|
+
"config.key_claim_name": "iss",
|
|
76
|
+
"config.secret_is_base64": "false",
|
|
77
|
+
"config.cookie_names": "sessionid"
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
elif 'key' in auth:
|
|
81
|
+
auth_data = {"name": "key-auth",
|
|
82
|
+
"config.key_names": "apikey"
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
else:
|
|
87
|
+
raise Exception('不支持的auth(%s)' % auth)
|
|
88
|
+
|
|
89
|
+
resp = requests.post('{kong}/apis/{api_name}/plugins'.format(kong=self.admin_uri,
|
|
90
|
+
api_name=api_name),
|
|
91
|
+
json=auth_data)
|
|
92
|
+
if resp.status_code != 201:
|
|
93
|
+
# 在0.12.1之前的kong 不支持config.cookie_names
|
|
94
|
+
if 'jwt' in auth:
|
|
95
|
+
auth_data.pop('config.cookie_names')
|
|
96
|
+
resp = requests.post('{kong}/apis/{api_name}/plugins'.format(kong=self.admin_uri,
|
|
97
|
+
api_name=api_name),
|
|
98
|
+
json=auth_data)
|
|
99
|
+
if resp.status_code == 201:
|
|
100
|
+
logger.info('注册%s服务API %s 成功,%s' % (base_path,auth, resp.text))
|
|
101
|
+
else:
|
|
102
|
+
logger.error('注册%s服务API %s 失败,%s,%s' % (base_path,auth, resp.status_code, resp.text))
|
|
103
|
+
|
|
104
|
+
def register_upstream(self,upstream_name,service_address,health =None,weight=100):
|
|
105
|
+
'''
|
|
106
|
+
|
|
107
|
+
:param upstream_name: monitor-srv
|
|
108
|
+
:param service_host: ip:port
|
|
109
|
+
:param weight: 100
|
|
110
|
+
:param health: 如: ordermng/v1.0/health, realtime-srv/v1.0/health
|
|
111
|
+
:return:
|
|
112
|
+
'''
|
|
113
|
+
''''
|
|
114
|
+
# create an upstream
|
|
115
|
+
$ curl -X POST http://kong:8001/upstreams \
|
|
116
|
+
--data "name=address.v1.service"
|
|
117
|
+
|
|
118
|
+
# add two targets to the upstream
|
|
119
|
+
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
|
|
120
|
+
--data "target=192.168.101.75:80"
|
|
121
|
+
--data "weight=100"
|
|
122
|
+
$ curl -X POST http://kong:8001/upstreams/address.v1.service/targets \
|
|
123
|
+
--data "target=192.168.100.76:80"
|
|
124
|
+
--data "weight=50"
|
|
125
|
+
|
|
126
|
+
# create a Service targeting the Blue upstream
|
|
127
|
+
$ curl -X POST http://kong:8001/apis/ \
|
|
128
|
+
--data "name=service-api" \
|
|
129
|
+
--data "uris=/aa"
|
|
130
|
+
--data "upstream_url=http://address.v1.service"
|
|
131
|
+
'''
|
|
132
|
+
upstream_api_url = '{kong}/upstreams/'.format(kong=self.admin_uri)
|
|
133
|
+
# add upstream
|
|
134
|
+
try:
|
|
135
|
+
'''
|
|
136
|
+
healthchecks.active.http_path - 在向目标发出HTTP GET请求时应该使用的路径。默认值是“/”。
|
|
137
|
+
healthchecks.active.timeout - 用于探测的HTTP GET请求的连接超时限制。默认值是1秒。
|
|
138
|
+
healthchecks.active.concurrency - 在主动健康检查中并发检查的目标数量。
|
|
139
|
+
你还需要为运行的探针指定间隔的值:
|
|
140
|
+
|
|
141
|
+
healthchecks.active.healthy.interval - 健康目标的主动健康检查间隔时间(以秒为单位)。0的值表明不执行对健康目标的主动探测。
|
|
142
|
+
healthchecks.active.unhealthy.interval - 对不健康目标的主动健康检查间隔时间(以秒为单位)。0值表示不应该执行不健康目标的主动探测。
|
|
143
|
+
这允许您调整主动健康检查的行为,无论您是否希望探测健康和不健康的目标在相同的时间间隔内运行,或者一个比另一个更频繁。
|
|
144
|
+
|
|
145
|
+
最后,您需要配置Kong应该如何解释探头,通过设置健康计数器上的各种阈值,一旦到达,就会触发状态变化。计数器阈值字段是:
|
|
146
|
+
|
|
147
|
+
healthchecks.active.healthy.successes - 在主动探测中成功的数量(由healthchecks.active.healthy.http_statuses定义)来确认目标的健康
|
|
148
|
+
healthchecks.active.unhealthy.tcp_failures - 在主动探测中TCP故障的数量,以确认目标是不健康的。
|
|
149
|
+
healthchecks.active.unhealthy.timeouts - 在主动探测中超时的数量,以确认目标是不健康的。
|
|
150
|
+
healthchecks.active.unhealthy.http_failures - 在主动探测中出现的HTTP故障数量(由healthchecks.active.healthy.http_statuses定义)来确认目标是不健康的。
|
|
151
|
+
'''
|
|
152
|
+
upstream = {'name': upstream_name,
|
|
153
|
+
'healthchecks.active.http_path': health or '/',
|
|
154
|
+
'healthchecks.active.healthy.interval': 0 if health is None else 5,
|
|
155
|
+
'healthchecks.active.healthy.successes': 1,
|
|
156
|
+
'healthchecks.active.unhealthy.interval': 5,
|
|
157
|
+
'healthchecks.active.unhealthy.tcp_failures': 1,
|
|
158
|
+
'healthchecks.active.unhealthy.timeouts': 3,
|
|
159
|
+
'healthchecks.active.unhealthy.http_failures': 1
|
|
160
|
+
}
|
|
161
|
+
resp = requests.post(upstream_api_url, json=upstream)
|
|
162
|
+
if resp.status_code == 409:
|
|
163
|
+
resp = requests.patch(upstream_api_url + upstream_name, json=upstream)
|
|
164
|
+
logger.info('add %s upstream to kong 成功,%s,%s' % (upstream_name,resp.status_code,resp.text))
|
|
165
|
+
except Exception as e:
|
|
166
|
+
logger.error('add %s upstream失败,error:%s' % (upstream_name, e))
|
|
167
|
+
sys.exit(-1)
|
|
168
|
+
targets_api_url = '{kong}/upstreams/{upstream_name}/targets'.format(kong=self.admin_uri,
|
|
169
|
+
upstream_name=upstream_name)
|
|
170
|
+
try:
|
|
171
|
+
resp = requests.post(targets_api_url,
|
|
172
|
+
json={'target': service_address, 'weight': weight})
|
|
173
|
+
logger.info('add %s target to kong 成功,%s,%s' % (upstream_name,resp.status_code,resp.text))
|
|
174
|
+
except Exception as e:
|
|
175
|
+
logger.error('add target for %s失败,error:%s' % (upstream_name, e))
|
|
176
|
+
|
|
177
|
+
sys.exit(-1)
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import json
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from collections import defaultdict
|
|
5
|
+
from .log import logger
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
DEFAULT_NAMES=[('vehicle','carid,dvrid1,fleetid,vtid'),
|
|
9
|
+
'accredituser']
|
|
10
|
+
|
|
11
|
+
UPDATE_ASSOCIATIONS={'vehicle':['vehicle.','device.']}
|
|
12
|
+
|
|
13
|
+
NullObj ={}
|
|
14
|
+
|
|
15
|
+
def data_isnull(data):
|
|
16
|
+
return data ==NullObj
|
|
17
|
+
|
|
18
|
+
async def get_last_modifytime(rds_r):
|
|
19
|
+
# 有可能新旧同步程序同时运行,需取最新变更值
|
|
20
|
+
async def get_last_time(rds_key):
|
|
21
|
+
try:
|
|
22
|
+
last_mdy_time =await rds_r.hget(rds_key, 'lastmodifytime')
|
|
23
|
+
except Exception as e:
|
|
24
|
+
logger.error('redis error:%s'%str(e))
|
|
25
|
+
last_mdy_time = None
|
|
26
|
+
last_mdy_time = last_mdy_time.decode()\
|
|
27
|
+
if last_mdy_time is not None else '20010101010101010101'
|
|
28
|
+
return datetime.strptime(last_mdy_time, '%Y%m%d%H%M%S%f')
|
|
29
|
+
|
|
30
|
+
return await get_last_time('sync_record')
|
|
31
|
+
|
|
32
|
+
async def read_data_as_dict(rds,rds_key,fields=None):
|
|
33
|
+
if fields is None:
|
|
34
|
+
data = await rds.hgetall(rds_key)
|
|
35
|
+
if data:
|
|
36
|
+
return {k.decode(): v.decode() for k, v in data.items()}
|
|
37
|
+
else:
|
|
38
|
+
data = await rds.hmget(rds_key, *fields)
|
|
39
|
+
if data:
|
|
40
|
+
obj = {k: v.decode() for (k, v) in zip(fields, data) if v is not None}
|
|
41
|
+
return obj
|
|
42
|
+
return NullObj
|
|
43
|
+
|
|
44
|
+
class SyncBasedataManage():
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self._tasks =[]
|
|
47
|
+
self._redis =None
|
|
48
|
+
self.mappings ={}
|
|
49
|
+
self.names =[]
|
|
50
|
+
self.cache =defaultdict(dict)
|
|
51
|
+
# self.sync_task =None
|
|
52
|
+
|
|
53
|
+
# self.clear_all()
|
|
54
|
+
|
|
55
|
+
def update_associations(self,key):
|
|
56
|
+
re =[key]
|
|
57
|
+
conditions = UPDATE_ASSOCIATIONS.get(key)
|
|
58
|
+
if conditions:
|
|
59
|
+
for condition in conditions:
|
|
60
|
+
re+=list(filter(lambda name:name.startswith(condition),self.names))
|
|
61
|
+
|
|
62
|
+
return re
|
|
63
|
+
|
|
64
|
+
def config(self,redis,names=DEFAULT_NAMES,loop=None):
|
|
65
|
+
def make_func(name):
|
|
66
|
+
def _method(self,key):
|
|
67
|
+
return self.get_bdobj(name,key )
|
|
68
|
+
|
|
69
|
+
return _method
|
|
70
|
+
|
|
71
|
+
self._redis =redis
|
|
72
|
+
for cname in names:
|
|
73
|
+
if type(cname)==tuple:
|
|
74
|
+
name, fields = cname
|
|
75
|
+
self.mappings[name]=fields.split(',')
|
|
76
|
+
|
|
77
|
+
else:
|
|
78
|
+
name =cname
|
|
79
|
+
self.names.append(name)
|
|
80
|
+
_method_name = 'get_%s' % name.replace('.', '_')
|
|
81
|
+
if not hasattr(self.__class__,_method_name):
|
|
82
|
+
_method = make_func(name)
|
|
83
|
+
setattr(self.__class__, _method_name, _method)
|
|
84
|
+
# self.names =names
|
|
85
|
+
if loop is not None:
|
|
86
|
+
self._tasks.append(loop.create_task(self.sync_data()))
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def rds(self):
|
|
90
|
+
assert self._redis is not None, '%s :please config redis first.' % self.__class__.__name__
|
|
91
|
+
return self._redis
|
|
92
|
+
|
|
93
|
+
def clear_all(self):
|
|
94
|
+
self.cache.clear()
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
async def sync_data(self):
|
|
98
|
+
last_modifytime =await get_last_modifytime(self.rds)
|
|
99
|
+
logger.info('sync_data get last modifytime:%s' % last_modifytime)
|
|
100
|
+
# last_modifytime =last_modifytime+timedelta(microseconds=-1)
|
|
101
|
+
# 避免频繁重启时,频繁访问redis
|
|
102
|
+
await asyncio.sleep(5)
|
|
103
|
+
|
|
104
|
+
while True:
|
|
105
|
+
try:
|
|
106
|
+
logger.debug('SyncBasedataManage.sync_data')
|
|
107
|
+
last_modifytime_local =await get_last_modifytime(self.rds)
|
|
108
|
+
if last_modifytime_local > last_modifytime:
|
|
109
|
+
updatedatas = await self.rds.zrangebyscore('bd_updatekeys',int(last_modifytime.timestamp()), float('inf'))
|
|
110
|
+
for ud in updatedatas:
|
|
111
|
+
ud_key = ud.decode().split('_{')[0] ##可能有'bdupd.line_config.%s_{"time": %s的情况,导致split到错误位置
|
|
112
|
+
if ud_key.startswith('bdupd.'):
|
|
113
|
+
[name,key] = ud_key.split('.')[-2:]
|
|
114
|
+
if name =='basedata' and key =='recreateall':
|
|
115
|
+
logger.info('ready recreatecall')
|
|
116
|
+
handled = self.custom_reload()
|
|
117
|
+
if not handled:
|
|
118
|
+
self.clear_all()
|
|
119
|
+
|
|
120
|
+
else:
|
|
121
|
+
if name =='line_config': # line.config的 update name 是line_config 所以要调整一下
|
|
122
|
+
name = 'line.config'
|
|
123
|
+
elif name =='vehiclegroup_vehicles':
|
|
124
|
+
name ='vehiclegroup.vehicles'
|
|
125
|
+
logger.info(f'notify update {name} {key}')
|
|
126
|
+
bd_names = self.update_associations(name)
|
|
127
|
+
for bd_name in bd_names:
|
|
128
|
+
handled =self.custom_reload_item(bd_name,key)
|
|
129
|
+
if not handled :
|
|
130
|
+
self.remove_bdobj(bd_name,key)
|
|
131
|
+
|
|
132
|
+
last_modifytime = last_modifytime_local
|
|
133
|
+
except Exception as e:
|
|
134
|
+
logger.error('sync_basedata error:%s' % str(e))
|
|
135
|
+
finally:
|
|
136
|
+
await asyncio.sleep(20)
|
|
137
|
+
def custom_reload(self):
|
|
138
|
+
return False
|
|
139
|
+
|
|
140
|
+
def custom_reload_item(self,name,key):
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
async def get_bdobj(self,name,key,key_type='hash'):
|
|
144
|
+
assert name in self.names,'not set config %s' % name
|
|
145
|
+
key = str(key)
|
|
146
|
+
obj =(self.cache.get(name) or {}).get(key)
|
|
147
|
+
# obj = self.cache[name].get(key)
|
|
148
|
+
if obj is None:
|
|
149
|
+
logger.info(f"get bdobj from redis {name},{key}")
|
|
150
|
+
rds_key = 'bd:%s:%s' % (name, key)
|
|
151
|
+
fields =self.mappings.get(name)
|
|
152
|
+
obj =None
|
|
153
|
+
if key_type =='hash':
|
|
154
|
+
obj = await read_data_as_dict(self.rds,rds_key,fields)
|
|
155
|
+
elif key_type =='string':
|
|
156
|
+
data = await self.rds.get(rds_key)
|
|
157
|
+
if data is not None:
|
|
158
|
+
try:
|
|
159
|
+
obj = json.loads(data.decode())
|
|
160
|
+
except Exception as e:
|
|
161
|
+
logger.error('key:%s load error:%s' %(rds_key,str(e)))
|
|
162
|
+
elif key_type =='set':
|
|
163
|
+
data = await self.rds.smembers(rds_key)
|
|
164
|
+
if data is not None:
|
|
165
|
+
try:
|
|
166
|
+
obj =[item.decode() for item in data]
|
|
167
|
+
except Exception as e:
|
|
168
|
+
logger.error('key:%s load error:%s' % (rds_key, str(e)))
|
|
169
|
+
else:
|
|
170
|
+
logger.error('key:%s not support key_type:%s' %(rds_key,key_type))
|
|
171
|
+
if data_isnull(obj):
|
|
172
|
+
logger.warning('the %s(%s) is not exist!' % (name, key))
|
|
173
|
+
obj =None
|
|
174
|
+
# if self.cache.get(name) is None:
|
|
175
|
+
# self.cache[name] = {}
|
|
176
|
+
self.add_bdobj(name,key,obj)
|
|
177
|
+
return obj
|
|
178
|
+
return obj
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def add_bdobj(self,name,key,obj):
|
|
182
|
+
self.cache[name][key] = obj
|
|
183
|
+
|
|
184
|
+
def remove_bdobj(self,name,key):
|
|
185
|
+
if self.cache.get(name):
|
|
186
|
+
try:
|
|
187
|
+
logger.info(f'remove {name} {key}')
|
|
188
|
+
self.cache[name].pop(key)
|
|
189
|
+
except:
|
|
190
|
+
pass
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
async def event_config(self,carid,evcode):
|
|
194
|
+
config = await self.get_vehicle_config(carid)
|
|
195
|
+
if config is not None:
|
|
196
|
+
data = config.get('event_params_%s' % evcode)
|
|
197
|
+
if data:
|
|
198
|
+
return json.loads(data)
|
|
199
|
+
return NullObj
|
|
200
|
+
|
|
201
|
+
async def get_e_stop(self,imsi):
|
|
202
|
+
return await self.get_bdobj('estop',imsi,'string')
|
|
203
|
+
|
|
204
|
+
async def get_vehicletype(self,id):
|
|
205
|
+
return await self.get_bdobj('vehicletype',id,'string')
|
|
206
|
+
|
|
207
|
+
class MapSyncManager(SyncBasedataManage):
|
|
208
|
+
def __init__(self,has_map_dvrid=False):
|
|
209
|
+
super().__init__()
|
|
210
|
+
self.has_map_dvrid =has_map_dvrid
|
|
211
|
+
self.dvrid_carid_map ={}
|
|
212
|
+
# self.has_dvrid = has_map_dvrid
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def add_bdobj(self,name,key,obj):
|
|
216
|
+
super().add_bdobj(name,key,obj)
|
|
217
|
+
if self.has_map_dvrid and name =='vehicle' and obj is not None and obj.get('dvrid1') is not None:
|
|
218
|
+
self.dvrid_carid_map[obj['dvrid1']]=key
|
|
219
|
+
|
|
220
|
+
def remove_bdobj(self,name,key):
|
|
221
|
+
if self.has_map_dvrid and name == 'vehicle':
|
|
222
|
+
obj = self.cache[name].get(key)
|
|
223
|
+
if obj is not None and obj.get('dvrid1') is not None:
|
|
224
|
+
try:
|
|
225
|
+
self.dvrid_carid_map.pop(obj['dvrid1'])
|
|
226
|
+
except:
|
|
227
|
+
pass
|
|
228
|
+
|
|
229
|
+
super().remove_bdobj(name, key)
|
|
230
|
+
|
|
231
|
+
def clear_all(self):
|
|
232
|
+
super().clear_all()
|
|
233
|
+
self.dvrid_carid_map.clear()
|
|
234
|
+
|
|
235
|
+
def custom_reload(self):
|
|
236
|
+
|
|
237
|
+
vt_keys = self.cache['vehicle'].keys()
|
|
238
|
+
|
|
239
|
+
self.clear_all()
|
|
240
|
+
|
|
241
|
+
for key in vt_keys:
|
|
242
|
+
asyncio.ensure_future(self.get_bdobj('vehicle',key))
|
|
243
|
+
|
|
244
|
+
return True
|
|
245
|
+
|
|
246
|
+
def custom_reload_item(self,name,key):
|
|
247
|
+
# logger.info(f'custom_reload_item{name},{key}')
|
|
248
|
+
self.remove_bdobj(name,key)
|
|
249
|
+
if name =='vehicle' and self.has_map_dvrid:
|
|
250
|
+
asyncio.ensure_future(self.get_bdobj(name,key))
|
|
251
|
+
return True
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
# managers={'default':SyncBasedataManage(),
|
|
255
|
+
# 'map_dvrid1':MapSyncManager(True)}
|
|
256
|
+
# def get_manager():
|
|
257
|
+
# import os
|
|
258
|
+
# mode = os.getenv('SYNC_MANAGER','default')
|
|
259
|
+
# if mode =='default':
|
|
260
|
+
# return SyncBasedataManage()
|
|
261
|
+
# elif mode =='map_dvrid1':
|
|
262
|
+
# return MapSyncManager(True)
|
|
263
|
+
|
|
264
|
+
data_manager=MapSyncManager()
|
|
265
|
+
|
|
266
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
def linux_timestamp():
|
|
3
|
+
return int(datetime.now().timestamp())
|
|
4
|
+
|
|
5
|
+
def timestamp_ms():
|
|
6
|
+
return datetime.now().timestamp()*1000
|
|
7
|
+
|
|
8
|
+
def time_to_microsec_str(d=None):
|
|
9
|
+
if d is None:
|
|
10
|
+
d = datetime.now()
|
|
11
|
+
return d.strftime('%Y%m%d%H%M%S%f')
|
|
12
|
+
|
|
13
|
+
def time_to_sql_str(d=None):
|
|
14
|
+
if d is None:
|
|
15
|
+
d = datetime.now()
|
|
16
|
+
|
|
17
|
+
return d.strftime('%Y-%m-%d %H:%M:%S')
|
|
18
|
+
|
|
19
|
+
def date_to_sql_str(d=None):
|
|
20
|
+
if d is None:
|
|
21
|
+
d = datetime.now()
|
|
22
|
+
|
|
23
|
+
return d.strftime('%Y-%m-%d')
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: jtt-utils
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: jtt common tools
|
|
5
|
+
Home-page: UNKNOWN
|
|
6
|
+
Author: candyabc
|
|
7
|
+
Author-email: hfcandyabc@163.com
|
|
8
|
+
License: maxwin
|
|
9
|
+
Platform: any
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Web Environment
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: BSD License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.5
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.6
|
|
19
|
+
|
|
20
|
+
jtt common tools
|
|
21
|
+
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
setup.cfg
|
|
3
|
+
setup.py
|
|
4
|
+
jtt_tm_utils/__init__.py
|
|
5
|
+
jtt_tm_utils/consul_handle.py
|
|
6
|
+
jtt_tm_utils/kong.py
|
|
7
|
+
jtt_tm_utils/log.py
|
|
8
|
+
jtt_tm_utils/sync_basedata.py
|
|
9
|
+
jtt_tm_utils/timeutil.py
|
|
10
|
+
jtt_utils.egg-info/PKG-INFO
|
|
11
|
+
jtt_utils.egg-info/SOURCES.txt
|
|
12
|
+
jtt_utils.egg-info/dependency_links.txt
|
|
13
|
+
jtt_utils.egg-info/not-zip-safe
|
|
14
|
+
jtt_utils.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
jtt_tm_utils
|
jtt-utils-1.0.0/setup.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
from setuptools import setup, find_packages
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def fpath(name):
|
|
12
|
+
return os.path.join(os.path.dirname(__file__), name)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def read(fname):
|
|
16
|
+
return open(fpath(fname)).read()
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# def desc():
|
|
20
|
+
# info = read('README.rst')
|
|
21
|
+
# try:
|
|
22
|
+
# return info + '\n\n' + read('doc/changelog.rst')
|
|
23
|
+
# except IOError:
|
|
24
|
+
# return info
|
|
25
|
+
|
|
26
|
+
# grep flask_admin/__init__.py since python 3.x cannot import it before using 2to3
|
|
27
|
+
file_text = read(fpath('jtt_tm_utils/__init__.py'))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def grep(attrname):
|
|
31
|
+
pattern = r"{0}\W*=\W*'([^']+)'".format(attrname)
|
|
32
|
+
strval, = re.findall(pattern, file_text)
|
|
33
|
+
return strval
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# entry_points = """
|
|
37
|
+
# [console_scripts]
|
|
38
|
+
# pycli = mw_uml_generator.cli:cli
|
|
39
|
+
# """
|
|
40
|
+
install_requires = [
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
setup(
|
|
44
|
+
name='jtt-utils',
|
|
45
|
+
version=grep('__version__'),
|
|
46
|
+
url='',
|
|
47
|
+
license=grep('__license__'),
|
|
48
|
+
author=grep('__author__'),
|
|
49
|
+
author_email=grep('__email__'),
|
|
50
|
+
description='jtt common tools',
|
|
51
|
+
long_description='jtt common tools',
|
|
52
|
+
packages=find_packages(),
|
|
53
|
+
# entry_points=entry_points,
|
|
54
|
+
include_package_data=True,
|
|
55
|
+
zip_safe=False,
|
|
56
|
+
platforms='any',
|
|
57
|
+
install_requires=install_requires,
|
|
58
|
+
|
|
59
|
+
tests_require=[
|
|
60
|
+
],
|
|
61
|
+
classifiers=[
|
|
62
|
+
'Development Status :: 4 - Beta',
|
|
63
|
+
'Environment :: Web Environment',
|
|
64
|
+
'Intended Audience :: Developers',
|
|
65
|
+
'License :: OSI Approved :: BSD License',
|
|
66
|
+
'Operating System :: OS Independent',
|
|
67
|
+
'Programming Language :: Python',
|
|
68
|
+
'Topic :: Software Development :: Libraries :: Python Modules',
|
|
69
|
+
'Programming Language :: Python :: 3.5',
|
|
70
|
+
'Programming Language :: Python :: 3.6',
|
|
71
|
+
]
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|