Skip to content

Commit 2de1f4b

Browse files
committed
Merge branch 'dev' into 'master'
Dev See merge request server/openapi/openapi-python-sdk!164
2 parents 1eed854 + 82e6fd9 commit 2de1f4b

File tree

5 files changed

+118
-54
lines changed

5 files changed

+118
-54
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
## 2.3.3 (2023-03-10)
2+
### Modify
3+
- 支持多进程运行场景下的 token 刷新,需安装 pip install watchdog 已开启 token 文件监听
4+
### Fix
5+
- 修复日历接口
6+
7+
18
## 2.3.2 (2023-03-03)
29
### New
310
- PushClient长链接支持Protobuf,当前版本默认不开启,可通过在 PushClient 初始化时传人 `user_protobuf=True ` 开启,

tigeropen/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
55
@author: gaoan
66
"""
7-
__VERSION__ = '2.3.2'
7+
__VERSION__ = '2.3.3'

tigeropen/quote/request/model.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -835,6 +835,8 @@ def to_openapi_dict(self):
835835
if self.end_date:
836836
params['end_date'] = self.end_date
837837

838+
return params
839+
838840

839841
class MarketScannerParams(BaseParams):
840842
def __init__(self):

tigeropen/tiger_open_client.py

Lines changed: 58 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -25,16 +25,10 @@
2525
from tigeropen.common.util.signature_utils import get_sign_content, sign_with_rsa, verify_with_rsa
2626
from tigeropen.common.util.web_utils import do_post
2727

28-
try:
29-
from getmac import get_mac_address
30-
except ImportError:
31-
def get_mac_address():
32-
return ':'.join(("%012x" % uuid.getnode())[i:i + 2] for i in range(0, 12, 2))
3328

3429
LOG_FORMATTER = logging.Formatter('%(asctime)s %(name)s %(levelname)s: %(message)s')
3530
SKIP_RETRY_SERVICES = {PLACE_ORDER, CANCEL_ORDER, MODIFY_ORDER}
3631

37-
3832
_SCHEDULE_STATE = {'is_running': False}
3933

4034

@@ -43,6 +37,7 @@ class TigerOpenClient:
4337
client_config:客户端配置,包含tiger_id、应用私钥、老虎公钥等
4438
logger:日志对象,客户端执行信息会通过此日志对象输出
4539
"""
40+
4641
def __init__(self, client_config, logger=None):
4742
self.__config = client_config
4843
if not client_config.private_key:
@@ -63,62 +58,56 @@ def __init__(self, client_config, logger=None):
6358
"Connection": "Keep-Alive",
6459
"User-Agent": 'openapi-python-sdk-' + __VERSION__
6560
}
66-
self.__device_id = self.__get_device_id()
67-
self.__init_license()
68-
self.__refresh_server_info()
69-
if self.__config.token and self.__config.license:
70-
self.__schedule_thread()
61+
self._initialize()
62+
63+
def _initialize(self):
64+
if not self.__config.inited:
65+
self.__logger.info(f'sdk version: {self.__config.sdk_version}')
66+
self.__init_license()
67+
self.__refresh_server_info()
68+
if self.__config.token and self.__config.license:
69+
self.__schedule_thread()
70+
self.__config.inited = True
7171

7272
def __init_license(self):
73+
self.__logger.debug('init license')
7374
if self.__config.license is None and self.__config.enable_dynamic_domain:
7475
self.__config.license = self.query_license()
7576

7677
def __refresh_server_info(self):
78+
self.__logger.debug('init server info')
7779
self.__config.refresh_server_info()
7880

79-
80-
"""
81-
内部方法,从params中抽取公共参数
82-
"""
83-
8481
def __get_common_params(self, params):
82+
"""
83+
内部方法,从params中抽取公共参数
84+
"""
8585
common_params = dict()
8686
common_params[P_TIMESTAMP] = params[P_TIMESTAMP]
8787
common_params[P_TIGER_ID] = self.__config.tiger_id
8888
common_params[P_METHOD] = params[P_METHOD]
8989
common_params[P_CHARSET] = self.__config.charset
9090
common_params[P_VERSION] = params[P_VERSION] if params.get(P_VERSION) is not None else OPEN_API_SERVICE_VERSION
9191
common_params[P_SIGN_TYPE] = self.__config.sign_type
92-
common_params[P_DEVICE_ID] = self.__device_id
92+
common_params[P_DEVICE_ID] = self.__config._device_id
9393
if has_value(params, P_NOTIFY_URL):
9494
common_params[P_NOTIFY_URL] = params[P_NOTIFY_URL]
9595
return common_params
9696

97-
@staticmethod
98-
def __get_device_id():
97+
def __remove_common_params(self, params):
9998
"""
100-
获取mac地址作为device_id
101-
:return:
99+
内部方法,从params中移除公共参数
102100
"""
103-
try:
104-
return get_mac_address()
105-
except:
106-
return None
107-
108-
"""
109-
内部方法,从params中移除公共参数
110-
"""
111-
def __remove_common_params(self, params):
112101
if not params:
113102
return
114103
for k in COMMON_PARAM_KEYS:
115104
if k in params:
116105
params.pop(k)
117106

118-
"""
119-
内部方法,通过请求request对象构造请求查询字符串和业务参数
120-
"""
121107
def __prepare_request(self, request, url=''):
108+
"""
109+
内部方法,通过请求request对象构造请求查询字符串和业务参数
110+
"""
122111
THREAD_LOCAL.logger = self.__logger
123112
params = request.get_params()
124113
params[P_TIMESTAMP] = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
@@ -139,11 +128,10 @@ def __prepare_request(self, request, url=''):
139128

140129
return all_params
141130

142-
"""
143-
内部方法,解析请求返回结果并做验签
144-
"""
145-
146131
def __parse_response(self, response_str, timestamp=None):
132+
"""
133+
内部方法,解析请求返回结果并做验签
134+
"""
147135
response_str = response_str.decode(self.__config.charset)
148136
if THREAD_LOCAL.logger:
149137
THREAD_LOCAL.logger.debug('[' + THREAD_LOCAL.uuid + ']response:' + response_str)
@@ -181,10 +169,10 @@ def _get_retry_deco(self, service):
181169
jitter=None)
182170
return None
183171

184-
"""
185-
执行接口请求
186-
"""
187172
def execute(self, request, url=None):
173+
"""
174+
执行接口请求
175+
"""
188176
if self.__config.token:
189177
self._update_header()
190178
if url is None:
@@ -196,7 +184,7 @@ def execute(self, request, url=None):
196184
retry_deco = self._get_retry_deco(request._method)
197185
if retry_deco is not None:
198186
response = retry_deco(do_post)(url, query_string, self.__headers, params, self.__config.timeout,
199-
self.__config.charset)
187+
self.__config.charset)
200188
else:
201189
response = do_post(url, query_string, self.__headers, params, self.__config.timeout,
202190
self.__config.charset)
@@ -234,10 +222,11 @@ def query_token(self):
234222
return None
235223

236224
def refresh_token(self):
225+
self.__config.token = self.__config.load_token()
237226
new_token = self.query_token()
238227
if new_token:
239228
self.__logger.info(f"refresh token, old:{self.__config.token}, new:{new_token}")
240-
self.__config.load_or_store_token(new_token)
229+
self.__config.store_token(new_token)
241230

242231
def __token_refresh_task(self):
243232
try:
@@ -249,13 +238,38 @@ def __token_refresh_task(self):
249238
def __schedule_thread(self):
250239
if not _SCHEDULE_STATE['is_running'] and self.__config.token_refresh_duration != 0:
251240
_SCHEDULE_STATE['is_running'] = True
252-
self.__logger.info('Starting schedule thread...')
241+
self.__logger.info('Starting token refresh thread...')
253242
daemon = RepeatTimer(self.__config.token_check_interval, self.__token_refresh_task)
254243
daemon.daemon = True
255244
daemon.start()
245+
self.__monitor_token()
246+
247+
def __monitor_token(self):
248+
"""对于多进程运行的情况,需监控token文件变动并加载,防止另一个进程刷新token后导致本进程token失效.
249+
需安装watchdog实现此功能: pip install watchdog"""
250+
try:
251+
from watchdog.observers import Observer
252+
from watchdog.events import FileSystemEventHandler
253+
except ImportError:
254+
return
255+
256+
def on_modified(event):
257+
file_token = self.__config.load_token()
258+
if file_token != self.__config.token:
259+
self.__logger.info(f'load token from changed token file, old: {self.__config.token}, '
260+
f'new:{file_token}')
261+
self.__config.token = file_token
262+
263+
event_handler = FileSystemEventHandler()
264+
event_handler.on_modified = on_modified
265+
observer = Observer()
266+
observer.schedule(event_handler, self.__config.get_token_path(), recursive=True)
267+
observer.daemon = True
268+
observer.start()
269+
self.__logger.info('Starting token monitor thread...')
256270

257271

258272
class RepeatTimer(Timer):
259273
def run(self):
260274
while not self.finished.wait(self.interval):
261-
self.function(*self.args, **self.kwargs)
275+
self.function(*self.args, **self.kwargs)

tigeropen/tiger_open_config.py

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import logging
1010
import os
1111
import time
12+
import uuid
1213

1314
from jproperties import Properties
1415
from pytz import timezone
@@ -57,6 +58,12 @@
5758
TOKEN_REFRESH_DURATION = 24 * 60 * 60 # seconds
5859
TOKEN_CHECK_INTERVAL = 5 * 60 # seconds
5960

61+
try:
62+
from getmac import get_mac_address
63+
except ImportError:
64+
def get_mac_address():
65+
return ':'.join(("%012x" % uuid.getnode())[i:i + 2] for i in range(0, 12, 2))
66+
6067

6168
class TigerOpenClientConfig:
6269
def __init__(self, sandbox_debug=None, enable_dynamic_domain=True, props_path='.'):
@@ -91,17 +98,18 @@ def __init__(self, sandbox_debug=None, enable_dynamic_domain=True, props_path='.
9198
self._quote_server_url = SERVER_URL
9299
self._socket_host_port = SOCKET_HOST_PORT
93100

101+
self._device_id = self.__get_device_id()
102+
94103
self.log_level = None
95104
self.log_path = None
96105
self.retry_max_time = 60
97106
self.retry_max_tries = 5
98107
self.props_path = props_path
99-
self.token = None
100108
# token 刷新间隔周期, 单位秒
101-
self.token_refresh_duration = TOKEN_REFRESH_DURATION
109+
self._token_refresh_duration = TOKEN_REFRESH_DURATION
102110
self.token_check_interval = TOKEN_CHECK_INTERVAL
103111
self._load_props()
104-
self.load_or_store_token()
112+
self._token = self.load_token()
105113

106114
self.domain_conf = dict()
107115
self.enable_dynamic_domain = enable_dynamic_domain
@@ -113,6 +121,7 @@ def __init__(self, sandbox_debug=None, enable_dynamic_domain=True, props_path='.
113121
if self.enable_dynamic_domain:
114122
self.domain_conf = self.query_domains()
115123
self.refresh_server_info()
124+
self.inited = False
116125

117126
@property
118127
def tiger_id(self):
@@ -246,6 +255,28 @@ def token(self):
246255
def token(self, value):
247256
self._token = value
248257

258+
@property
259+
def token_refresh_duration(self):
260+
return self._token_refresh_duration
261+
262+
@token_refresh_duration.setter
263+
def token_refresh_duration(self, value):
264+
if value and value < 30:
265+
# 最短刷新间隔为 10 s
266+
value = 30
267+
self._token_refresh_duration = value
268+
269+
@staticmethod
270+
def __get_device_id():
271+
"""
272+
获取mac地址作为device_id
273+
:return:
274+
"""
275+
try:
276+
return get_mac_address()
277+
except:
278+
return None
279+
249280
def _get_props_path(self, filename):
250281
if self.props_path is not None:
251282
if os.path.isdir(self.props_path):
@@ -277,8 +308,22 @@ def _load_props(self):
277308
except Exception as e:
278309
logging.error(e, exc_info=True)
279310

280-
def load_or_store_token(self, token=None):
281-
full_path = self._get_props_path(DEFAULT_TOKEN_FILE)
311+
def get_token_path(self):
312+
return self._get_props_path(DEFAULT_TOKEN_FILE)
313+
314+
def load_token(self):
315+
full_path = self.get_token_path()
316+
if full_path and os.path.exists(full_path):
317+
try:
318+
p = Properties()
319+
with open(full_path, "rb") as f:
320+
p.load(f, "utf-8")
321+
return getattr(p.get('token'), 'data', '')
322+
except Exception as e:
323+
logging.error(e, exc_info=True)
324+
325+
def store_token(self, token):
326+
full_path = self.get_token_path()
282327
if full_path and os.path.exists(full_path):
283328
try:
284329
p = Properties()
@@ -287,10 +332,6 @@ def load_or_store_token(self, token=None):
287332
self.token = token
288333
p['token'] = token
289334
p.store(f, encoding='utf-8')
290-
else:
291-
p.load(f, "utf-8")
292-
if not self.token:
293-
self.token = getattr(p.get('token'), 'data', '')
294335
except Exception as e:
295336
logging.error(e, exc_info=True)
296337

0 commit comments

Comments
 (0)