tencentcloud-sdk-python-common 3.0.1303__tar.gz → 3.1.3__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.
Files changed (33) hide show
  1. tencentcloud_sdk_python_common-3.1.3/PKG-INFO +48 -0
  2. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/setup.py +1 -0
  3. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/__init__.py +1 -1
  4. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/abstract_client.py +104 -67
  5. tencentcloud_sdk_python_common-3.1.3/tencentcloud/common/abstract_client_async.py +654 -0
  6. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/abstract_model.py +5 -1
  7. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/common_client.py +14 -15
  8. tencentcloud_sdk_python_common-3.1.3/tencentcloud/common/common_client_async.py +45 -0
  9. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/credential.py +151 -96
  10. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/exception/tencent_cloud_sdk_exception.py +1 -1
  11. tencentcloud_sdk_python_common-3.1.3/tencentcloud/common/http/request_async.py +62 -0
  12. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/profile/client_profile.py +36 -33
  13. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/profile/http_profile.py +20 -12
  14. tencentcloud_sdk_python_common-3.1.3/tencentcloud/common/retry.py +80 -0
  15. tencentcloud_sdk_python_common-3.1.3/tencentcloud/common/retry_async.py +87 -0
  16. tencentcloud_sdk_python_common-3.1.3/tencentcloud_sdk_python_common.egg-info/PKG-INFO +48 -0
  17. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud_sdk_python_common.egg-info/SOURCES.txt +5 -0
  18. tencentcloud_sdk_python_common-3.1.3/tencentcloud_sdk_python_common.egg-info/requires.txt +4 -0
  19. tencentcloud-sdk-python-common-3.0.1303/PKG-INFO +0 -45
  20. tencentcloud-sdk-python-common-3.0.1303/tencentcloud_sdk_python_common.egg-info/PKG-INFO +0 -45
  21. tencentcloud-sdk-python-common-3.0.1303/tencentcloud_sdk_python_common.egg-info/requires.txt +0 -1
  22. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/README.rst +0 -0
  23. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/setup.cfg +0 -0
  24. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/__init__.py +0 -0
  25. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/circuit_breaker.py +0 -0
  26. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/exception/__init__.py +0 -0
  27. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/http/__init__.py +0 -0
  28. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/http/pre_conn.py +0 -0
  29. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/http/request.py +0 -0
  30. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/profile/__init__.py +0 -0
  31. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud/common/sign.py +0 -0
  32. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud_sdk_python_common.egg-info/dependency_links.txt +0 -0
  33. {tencentcloud-sdk-python-common-3.0.1303 → tencentcloud_sdk_python_common-3.1.3}/tencentcloud_sdk_python_common.egg-info/top_level.txt +0 -0
@@ -0,0 +1,48 @@
1
+ Metadata-Version: 2.1
2
+ Name: tencentcloud-sdk-python-common
3
+ Version: 3.1.3
4
+ Summary: Tencent Cloud Common SDK for Python
5
+ Home-page: https://github.com/TencentCloud/tencentcloud-sdk-python
6
+ Author: Tencent Cloud
7
+ Maintainer-email: tencentcloudapi@tencent.com
8
+ License: Apache License 2.0
9
+ Platform: any
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: Apache Software License
13
+ Classifier: Programming Language :: Python
14
+ Classifier: Programming Language :: Python :: 2.7
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.6
17
+ Classifier: Programming Language :: Python :: 3.7
18
+ Requires-Dist: requests>=2.16.0
19
+ Provides-Extra: async
20
+ Requires-Dist: httpx>=0.22.0; extra == "async"
21
+
22
+ ============================
23
+ Tencent Cloud SDK for Python
24
+ ============================
25
+
26
+ Tencent Cloud Python Common SDK is the official software development kit, which allows Python developers to write software that makes use of Tencent Cloud services like CVM and CBS.
27
+ The SDK works on Python versions:
28
+
29
+ * 2.7 and greater, including 3.x
30
+
31
+ Quick Start
32
+ -----------
33
+
34
+ First, install the library:
35
+
36
+ .. code-block:: sh
37
+
38
+ $ pip install tencentcloud-sdk-python-common
39
+ $ pip install tencentcloud-sdk-python-common
40
+
41
+ or download source code from github and install:
42
+
43
+ .. code-block:: sh
44
+
45
+ $ git clone https://github.com/tencentcloud/tencentcloud-sdk-python.git
46
+ $ cd tencentcloud-sdk-python
47
+ $ python package.py --components common common
48
+
@@ -9,6 +9,7 @@ ROOT = os.path.dirname(__file__)
9
9
  setup(
10
10
  name='tencentcloud-sdk-python-common',
11
11
  install_requires=["requests>=2.16.0"],
12
+ extras_require={"async": ["httpx>=0.22.0"]},
12
13
  version=tencentcloud.__version__,
13
14
  description='Tencent Cloud Common SDK for Python',
14
15
  long_description=open('README.rst').read(),
@@ -14,4 +14,4 @@
14
14
  # limitations under the License.
15
15
 
16
16
 
17
- __version__ = '3.0.1303'
17
+ __version__ = '3.1.3'
@@ -28,8 +28,10 @@ import logging.handlers
28
28
 
29
29
  try:
30
30
  from urllib.parse import urlencode
31
+ from urllib.parse import urlparse
31
32
  except ImportError:
32
33
  from urllib import urlencode
34
+ from urlparse import urlparse
33
35
 
34
36
  import tencentcloud
35
37
  from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
@@ -39,6 +41,7 @@ from tencentcloud.common.http.request import RequestInternal
39
41
  from tencentcloud.common.profile.client_profile import ClientProfile, RegionBreakerProfile
40
42
  from tencentcloud.common.sign import Sign
41
43
  from tencentcloud.common.circuit_breaker import CircuitBreaker
44
+ from tencentcloud.common.retry import NoopRetryer
42
45
 
43
46
  warnings.filterwarnings("ignore", module="tencentcloud", category=UserWarning)
44
47
 
@@ -132,11 +135,11 @@ class AbstractClient(object):
132
135
  elif self.profile.signMethod == "TC3-HMAC-SHA256" or options.get("IsMultipart") is True:
133
136
  self._build_req_with_tc3_signature(action, params, req_inter, options)
134
137
  elif self.profile.signMethod in ("HmacSHA1", "HmacSHA256"):
135
- self._build_req_with_old_signature(action, params, req_inter)
138
+ self._build_req_with_old_signature(action, params, req_inter, options)
136
139
  else:
137
140
  raise TencentCloudSDKException("ClientError", "Invalid signature method.")
138
141
 
139
- def _build_req_with_old_signature(self, action, params, req):
142
+ def _build_req_with_old_signature(self, action, params, req, options):
140
143
  params = copy.deepcopy(self._fix_params(params))
141
144
  params['Action'] = action[0].upper() + action[1:]
142
145
  params['RequestClient'] = self.request_client
@@ -144,14 +147,16 @@ class AbstractClient(object):
144
147
  params['Timestamp'] = int(time.time())
145
148
  params['Version'] = self._apiVersion
146
149
 
150
+ cred_secret_id, cred_secret_key, cred_token = self.credential.get_credential_info()
151
+
147
152
  if self.region:
148
153
  params['Region'] = self.region
149
154
 
150
- if self.credential.token:
151
- params['Token'] = self.credential.token
155
+ if cred_token:
156
+ params['Token'] = cred_token
152
157
 
153
- if self.credential.secret_id:
154
- params['SecretId'] = self.credential.secret_id
158
+ if cred_secret_id:
159
+ params['SecretId'] = cred_secret_id
155
160
 
156
161
  if self.profile.signMethod:
157
162
  params['SignatureMethod'] = self.profile.signMethod
@@ -159,8 +164,8 @@ class AbstractClient(object):
159
164
  if self.profile.language:
160
165
  params['Language'] = self.profile.language
161
166
 
162
- signInParam = self._format_sign_string(params)
163
- params['Signature'] = Sign.sign(str(self.credential.secret_key),
167
+ signInParam = self._format_sign_string(params, options)
168
+ params['Signature'] = Sign.sign(str(cred_secret_key),
164
169
  str(signInParam),
165
170
  str(self.profile.signMethod))
166
171
 
@@ -184,8 +189,9 @@ class AbstractClient(object):
184
189
  raise SDKError("ClientError",
185
190
  "Invalid request method GET for multipart.")
186
191
 
187
- endpoint = self._get_endpoint()
192
+ endpoint = self._get_endpoint(options=options)
188
193
  timestamp = int(time.time())
194
+ cred_secret_id, cred_secret_key, cred_token = self.credential.get_credential_info()
189
195
  req.header["Host"] = endpoint
190
196
  req.header["X-TC-Action"] = action[0].upper() + action[1:]
191
197
  req.header["X-TC-RequestClient"] = self.request_client
@@ -195,8 +201,8 @@ class AbstractClient(object):
195
201
  req.header["X-TC-Content-SHA256"] = "UNSIGNED-PAYLOAD"
196
202
  if self.region:
197
203
  req.header['X-TC-Region'] = self.region
198
- if self.credential.token:
199
- req.header['X-TC-Token'] = self.credential.token
204
+ if cred_token:
205
+ req.header['X-TC-Token'] = cred_token
200
206
  if self.profile.language:
201
207
  req.header['X-TC-Language'] = self.profile.language
202
208
 
@@ -212,13 +218,13 @@ class AbstractClient(object):
212
218
 
213
219
  service = self._service
214
220
  date = datetime.utcfromtimestamp(timestamp).strftime('%Y-%m-%d')
215
- signature = self._get_tc3_signature(params, req, date, service, options)
221
+ signature = self._get_tc3_signature(params, req, date, service, cred_secret_key, options)
216
222
 
217
223
  auth = "TC3-HMAC-SHA256 Credential=%s/%s/%s/tc3_request, SignedHeaders=content-type;host, Signature=%s" % (
218
- self.credential.secret_id, date, service, signature)
224
+ cred_secret_id, date, service, signature)
219
225
  req.header["Authorization"] = auth
220
226
 
221
- def _get_tc3_signature(self, params, req, date, service, options=None):
227
+ def _get_tc3_signature(self, params, req, date, service, secret_key, options=None):
222
228
  options = options or {}
223
229
  canonical_uri = req.uri
224
230
  canonical_querystring = ""
@@ -255,8 +261,7 @@ class AbstractClient(object):
255
261
  req.header["X-TC-Timestamp"],
256
262
  credential_scope,
257
263
  digest)
258
-
259
- return Sign.sign_tc3(self.credential.secret_key, date, service, string2sign)
264
+ return Sign.sign_tc3(secret_key, date, service, string2sign)
260
265
 
261
266
  def _build_req_without_signature(self, action, params, req, options=None):
262
267
  content_type = self._default_content_type
@@ -275,7 +280,7 @@ class AbstractClient(object):
275
280
  raise SDKError("ClientError",
276
281
  "Invalid request method GET for multipart.")
277
282
 
278
- endpoint = self._get_endpoint()
283
+ endpoint = self._get_endpoint(options=options)
279
284
  timestamp = int(time.time())
280
285
  req.header["Host"] = endpoint
281
286
  req.header["X-TC-Action"] = action[0].upper() + action[1:]
@@ -332,20 +337,23 @@ class AbstractClient(object):
332
337
  logger.debug("GetResponse: %s", ResponsePrettyFormatter(resp_inter))
333
338
  raise TencentCloudSDKException("ServerNetworkError", resp_inter.content)
334
339
 
335
- def _format_sign_string(self, params):
340
+ def _format_sign_string(self, params, options=None):
336
341
  formatParam = {}
337
342
  for k in params:
338
343
  formatParam[k.replace('_', '.')] = params[k]
339
344
  strParam = '&'.join('%s=%s' % (k, formatParam[k]) for k in sorted(formatParam))
340
- msg = '%s%s%s?%s' % (self.profile.httpProfile.reqMethod, self._get_endpoint(), self._requestPath, strParam)
345
+ msg = '%s%s%s?%s' % (
346
+ self.profile.httpProfile.reqMethod, self._get_endpoint(options=options), self._requestPath, strParam)
341
347
  return msg
342
348
 
343
349
  def _get_service_domain(self):
344
350
  rootDomain = self.profile.httpProfile.rootDomain
345
351
  return self._service + "." + rootDomain
346
352
 
347
- def _get_endpoint(self):
353
+ def _get_endpoint(self, options=None):
348
354
  endpoint = self.profile.httpProfile.endpoint
355
+ if not endpoint and options:
356
+ endpoint = urlparse(options.get("Endpoint", "")).hostname
349
357
  if endpoint is None:
350
358
  endpoint = self._get_service_domain()
351
359
  return endpoint
@@ -389,6 +397,9 @@ class AbstractClient(object):
389
397
  colon_idx = line.find(':')
390
398
  key = line[:colon_idx]
391
399
  val = line[colon_idx + 1:]
400
+ # If value starts with a U+0020 SPACE character, remove it from value.
401
+ if val and val[0] == " ":
402
+ val = val[1:]
392
403
  if key == 'data':
393
404
  # The spec allows for multiple data fields per event, concatenated them with "\n".
394
405
  if 'data' not in e:
@@ -416,7 +427,7 @@ class AbstractClient(object):
416
427
  headers["X-TC-TraceId"] = str(uuid.uuid4())
417
428
  if not self.profile.disable_region_breaker:
418
429
  return self._call_with_region_breaker(action, params, options, headers)
419
- req = RequestInternal(self._get_endpoint(),
430
+ req = RequestInternal(self._get_endpoint(options=options),
420
431
  self.profile.httpProfile.reqMethod,
421
432
  self._requestPath,
422
433
  header=headers)
@@ -428,14 +439,19 @@ class AbstractClient(object):
428
439
  return self.request.send_request(req)
429
440
 
430
441
  def call(self, action, params, options=None, headers=None):
431
- resp = self._call(action, params, options, headers)
432
- self._check_status(resp)
433
- self._check_error(resp)
434
- logger.debug("GetResponse: %s", ResponsePrettyFormatter(resp))
435
- return resp.content
442
+
443
+ def _call_once():
444
+ resp = self._call(action, params, options, headers)
445
+ self._check_status(resp)
446
+ self._check_error(resp)
447
+ logger.debug("GetResponse: %s", ResponsePrettyFormatter(resp))
448
+ return resp
449
+
450
+ retryer = self.profile.retryer or NoopRetryer()
451
+ return retryer.send_request(_call_once).content
436
452
 
437
453
  def _call_with_region_breaker(self, action, params, options=None, headers=None):
438
- endpoint = self._get_endpoint()
454
+ endpoint = self._get_endpoint(options=options)
439
455
  generation, need_break = self.circuit_breaker.before_requests()
440
456
  if need_break:
441
457
  endpoint = self._service + "." + self.profile.region_breaker_profile.backup_endpoint
@@ -461,33 +477,34 @@ class AbstractClient(object):
461
477
  self._check_error(resp)
462
478
  return resp.content
463
479
 
464
- def call_octet_stream(self, action, headers, body):
465
- """
466
- Invoke API with application/ocet-stream content-type.
480
+ def call_octet_stream(self, action, headers, body, options=None):
481
+ """Invoke API with application/ocet-stream content-type.
467
482
 
468
483
  Note:
469
484
  1. only specific API can be invoked in such manner.
470
485
  2. only TC3-HMAC-SHA256 signature method can be specified.
471
486
  3. only POST request method can be specified
472
487
 
473
- :type action: str
474
488
  :param action: Specific API action name.
475
- :type headers: dict
489
+ :type action: str
476
490
  :param headers: Header parameters for this API.
477
- :type body: bytes
491
+ :type headers: dict
478
492
  :param body: Bytes of requested body
493
+ :type body: bytes
479
494
  """
480
495
  if self.profile.signMethod != "TC3-HMAC-SHA256":
481
496
  raise SDKError("ClientError", "Invalid signature method.")
482
497
  if self.profile.httpProfile.reqMethod != "POST":
483
498
  raise SDKError("ClientError", "Invalid request method.")
484
499
 
485
- req = RequestInternal(self._get_endpoint(),
500
+ if not options:
501
+ options = {}
502
+ req = RequestInternal(self._get_endpoint(options=options),
486
503
  self.profile.httpProfile.reqMethod,
487
504
  self._requestPath,
488
505
  header=headers)
489
506
  req.data = body
490
- options = {"IsOctetStream": True}
507
+ options["IsOctetStream"] = True
491
508
  self._build_req_inter(action, None, req, options)
492
509
 
493
510
  resp = self.request.send_request(req)
@@ -496,35 +513,59 @@ class AbstractClient(object):
496
513
  return json.loads(resp.content)
497
514
 
498
515
  def call_json(self, action, params, headers=None, options=None):
499
- """
500
- Call api with json object and return with json object.
516
+ """Call api with json object and return with json object.
501
517
 
502
- :type action: str
503
518
  :param action: api name e.g. ``DescribeInstances``
519
+ :type action: str
520
+ :param params: Request parameters of this action
504
521
  :type params: dict
505
- :param params: params with this action
522
+ :param headers: Request headers, like {"X-TC-TraceId": "ffe0c072-8a5d-4e17-8887-a8a60252abca"}
506
523
  :type headers: dict
507
- :param headers: request header, like {"X-TC-TraceId": "ffe0c072-8a5d-4e17-8887-a8a60252abca"}
524
+ :param options: Request options, like {"SkipSign": False, "IsMultipart": False, "IsOctetStream": False, "BinaryParams": []}
508
525
  :type options: dict
509
- :param options: request options, like {"SkipSign": False, "IsMultipart": False, "IsOctetStream": False, "BinaryParams": []}
510
526
  """
511
- resp = self._call(action, params, options, headers)
512
- self._check_status(resp)
513
- self._check_error(resp)
514
- logger.debug("GetResponse: %s", ResponsePrettyFormatter(resp))
515
- return json.loads(resp.content)
527
+
528
+ def _call_once():
529
+ resp = self._call(action, params, options, headers)
530
+ self._check_status(resp)
531
+ self._check_error(resp)
532
+ logger.debug("GetResponse: %s", ResponsePrettyFormatter(resp))
533
+ return resp
534
+
535
+ retryer = self.profile.retryer or NoopRetryer()
536
+ return json.loads(retryer.send_request(_call_once).content)
516
537
 
517
538
  def call_sse(self, action, params, headers=None, options=None):
518
- resp = self._call(action, params, options, headers)
519
- self._check_status(resp)
520
- self._check_error(resp)
521
- return self._process_response_sse(resp)
539
+ """Call api with json object and return with sse event.
540
+
541
+ :param action: api name e.g. ``ChatCompletions``
542
+ :type action: str
543
+ :param params: Request parameters of this action
544
+ :type params: dict
545
+ :param headers: Request headers, like {"X-TC-TraceId": "ffe0c072-8a5d-4e17-8887-a8a60252abca"}
546
+ :type headers: dict
547
+ :param options: Request options, like {"SkipSign": False, "IsMultipart": False, "IsOctetStream": False, "BinaryParams": []}
548
+ :type options: dict
549
+ """
550
+
551
+ def _call_once():
552
+ resp = self._call(action, params, options, headers)
553
+ self._check_status(resp)
554
+ self._check_error(resp)
555
+ return resp
556
+
557
+ retryer = self.profile.retryer or NoopRetryer()
558
+ return self._process_response_sse(retryer.send_request(_call_once))
522
559
 
523
560
  def _call_and_deserialize(self, action, params, resp_type, headers=None, options=None):
524
- resp = self._call(action, params, options, headers)
525
- self._check_status(resp)
526
- self._check_error(resp)
527
- return self._process_response(resp, resp_type)
561
+ def _call_once():
562
+ resp = self._call(action, params, options, headers)
563
+ self._check_status(resp)
564
+ self._check_error(resp)
565
+ return resp
566
+
567
+ retryer = self.profile.retryer or NoopRetryer()
568
+ return self._process_response(retryer.send_request(_call_once), resp_type)
528
569
 
529
570
  def _process_response(self, resp, resp_type):
530
571
  if resp.headers.get('Content-Type') == "text/event-stream":
@@ -535,15 +576,14 @@ class AbstractClient(object):
535
576
  return self._process_response_json(resp, resp_type)
536
577
 
537
578
  def set_stream_logger(self, stream=None, level=logging.DEBUG, log_format=None):
538
- """
539
- Add a stream handler
579
+ """Add a stream handler
540
580
 
541
- :type stream: IO[str]
542
581
  :param stream: e.g. ``sys.stdout`` ``sys.stdin`` ``sys.stderr``
543
- :type level: int
582
+ :type stream: IO[str]
544
583
  :param level: Logging level, e.g. ``logging.INFO``
545
- :type log_format: str
584
+ :type level: int
546
585
  :param log_format: Log message format
586
+ :type log_format: str
547
587
  """
548
588
  log = logging.getLogger(LOGGER_NAME)
549
589
  log.setLevel(level)
@@ -556,15 +596,14 @@ class AbstractClient(object):
556
596
  log.addHandler(sh)
557
597
 
558
598
  def set_file_logger(self, file_path, level=logging.DEBUG, log_format=None):
559
- """
560
- Add a file handler
599
+ """Add a file handler
561
600
 
562
- :type file_path: str
563
601
  :param file_path: path of log file
564
- :type level: int
602
+ :type file_path: str
565
603
  :param level: Logging level, e.g. ``logging.INFO``
566
- :type log_format: str
604
+ :type level: int
567
605
  :param log_format: Log message format
606
+ :type log_format: str
568
607
  """
569
608
  log = logging.getLogger(LOGGER_NAME)
570
609
  log.setLevel(level)
@@ -578,9 +617,7 @@ class AbstractClient(object):
578
617
  log.addHandler(fh)
579
618
 
580
619
  def set_default_logger(self):
581
- """
582
- Set default log handler
583
- """
620
+ """Set default log handler"""
584
621
  log = logging.getLogger(LOGGER_NAME)
585
622
  log.handlers = []
586
623
  logger.addHandler(EmptyHandler())