ctm-python-client 2.3.7__py3-none-any.whl → 2.4.1__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.
aapi/bases.py CHANGED
@@ -2,6 +2,9 @@ import attrs
2
2
  import enum
3
3
  import typing
4
4
  import json
5
+ import random
6
+ from ctm_python_client.core.comm import Environment
7
+ from ctm_python_client.core.monitoring import RunMonitor
5
8
 
6
9
  class AAPIJob:
7
10
  pass
@@ -57,4 +60,45 @@ class AAPIObject:
57
60
 
58
61
  def as_dict(self):
59
62
  return attrs.asdict(self)
60
-
63
+
64
+ def run_on_demand(self, environment: Environment, inpath: str = f'run_on_demand{random.randint(100,999)}', controlm_server: str = None,
65
+ run_as: str = None, host: str = None, application: str = None, sub_application: str = None, skip_login: bool = False,
66
+ file_path: str = None, delete_afterwards: bool = True, open_in_browser: str = None) -> RunMonitor:
67
+ # Import circular dependency
68
+ from ctm_python_client.core.workflow import Workflow, WorkflowDefaults
69
+ from aapi import Folder
70
+
71
+ if (hasattr(self, '_type') and 'Job' in self._type) or (hasattr(self, 'job_list') and self.job_list is not None and len(self.job_list) > 0):
72
+ try:
73
+ on_demand_workflow = Workflow(
74
+ environment,
75
+ WorkflowDefaults(
76
+ controlm_server=controlm_server,
77
+ run_as=run_as,
78
+ host=host,
79
+ application=application,
80
+ sub_application=sub_application
81
+ )
82
+ )
83
+ if isinstance(self, Folder):
84
+ on_demand_workflow.add(self)
85
+ else:
86
+ on_demand_workflow.add(self, inpath=inpath)
87
+
88
+ on_demand_workflow.run_on_demand(
89
+ skip_login=skip_login,
90
+ file_path=file_path,
91
+ delete_afterwards=delete_afterwards,
92
+ open_in_browser=open_in_browser
93
+ )
94
+ except Exception as e:
95
+ if e.body:
96
+ errors = [err.get('message', '') + ' ' + err.get('item', '')
97
+ for err in json.loads(e.body)['errors']]
98
+ raise RuntimeError(f"AAPI request failed: {', '.join(errors)}")
99
+ else:
100
+ raise e
101
+ finally:
102
+ on_demand_workflow.clear_all()
103
+ else:
104
+ raise Exception('Run is not allowed for json without jobs')
@@ -3413,6 +3413,112 @@ class RunApi(object):
3413
3413
  collection_formats=collection_formats,
3414
3414
  )
3415
3415
 
3416
+ def run_on_demand(self, definitions_file, annotation=None, **kwargs): # noqa: E501
3417
+ """Run a job on demand # noqa: E501
3418
+
3419
+ Run a job on demand # noqa: E501
3420
+ This method makes a synchronous HTTP request by default. To make an
3421
+ asynchronous HTTP request, please pass async_req=True
3422
+ >>> thread = api.run_on_demand(definitions_file, async_req=True)
3423
+ >>> result = thread.get()
3424
+
3425
+ :param async_req bool
3426
+ :param file definitions_file: A file that contains jobs definitions to be run on demand (required)
3427
+ :param file deploy_descriptor_file: Deploy Descriptor JSON file.
3428
+ :param annotation - user annotation
3429
+ :return: RunResult
3430
+ If the method is called asynchronously,
3431
+ returns the request thread.
3432
+ """
3433
+ kwargs['_return_http_data_only'] = True
3434
+ if kwargs.get('async_req'):
3435
+ return self.run_on_demand_with_http_info(definitions_file, annotation, **kwargs) # noqa: E501
3436
+ else:
3437
+ (data) = self.run_on_demand_with_http_info(definitions_file, annotation, **kwargs) # noqa: E501
3438
+ return data
3439
+
3440
+
3441
+ def run_on_demand_with_http_info(self, definitions_file, annotation, **kwargs): # noqa: E501
3442
+ """Run a job on demand # noqa: E501
3443
+
3444
+ Run a job on demand # noqa: E501
3445
+ This method makes a synchronous HTTP request by default. To make an
3446
+ asynchronous HTTP request, please pass async_req=True
3447
+ >>> thread = api.run_on_demand_with_http_info(definitions_file, async_req=True)
3448
+ >>> result = thread.get()
3449
+
3450
+ :param async_req bool
3451
+ :param file definitions_file: A file that contains jobs definitions to be run on demand (required)
3452
+ :param file deploy_descriptor_file: Deploy Descriptor JSON file.
3453
+ :param annotation - user annotation
3454
+ :return: RunResult
3455
+ If the method is called asynchronously,
3456
+ returns the request thread.
3457
+ """
3458
+
3459
+ all_params = ['definitions_file', 'deploy_descriptor_file'] # noqa: E501
3460
+ all_params.append('async_req')
3461
+ all_params.append('_return_http_data_only')
3462
+ all_params.append('_preload_content')
3463
+ all_params.append('_request_timeout')
3464
+
3465
+ params = locals()
3466
+ for key, val in six.iteritems(params['kwargs']):
3467
+ if key not in all_params:
3468
+ raise TypeError(
3469
+ "Got an unexpected keyword argument '%s'"
3470
+ " to method run_on_demand" % key
3471
+ )
3472
+ params[key] = val
3473
+ del params['kwargs']
3474
+ # verify the required parameter 'definitions_file' is set
3475
+ if self.api_client.client_side_validation and ('definitions_file' not in params or
3476
+ params['definitions_file'] is None): # noqa: E501
3477
+ raise ValueError("Missing the required parameter `definitions_file` when calling `run_on_demand`") # noqa: E501
3478
+
3479
+ collection_formats = {}
3480
+
3481
+ path_params = {}
3482
+
3483
+ query_params = []
3484
+
3485
+ header_params = annotation.get_annotation() if annotation else {}
3486
+
3487
+ form_params = []
3488
+ local_var_files = {}
3489
+ if 'definitions_file' in params:
3490
+ local_var_files['definitionsFile'] = params['definitions_file'] # noqa: E501
3491
+ if 'deploy_descriptor_file' in params:
3492
+ local_var_files['deployDescriptorFile'] = params['deploy_descriptor_file'] # noqa: E501
3493
+
3494
+ body_params = None
3495
+ # HTTP header `Accept`
3496
+ header_params['Accept'] = self.api_client.select_header_accept(
3497
+ ['application/json']) # noqa: E501
3498
+
3499
+ # HTTP header `Content-Type`
3500
+ header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501
3501
+ ['multipart/form-data']) # noqa: E501
3502
+
3503
+ # Authentication setting
3504
+ auth_settings = ['ApiKeyAuth', 'Bearer'] # noqa: E501
3505
+
3506
+ return self.api_client.call_api(
3507
+ '/run/ondemand', 'POST',
3508
+ path_params,
3509
+ query_params,
3510
+ header_params,
3511
+ body=body_params,
3512
+ post_params=form_params,
3513
+ files=local_var_files,
3514
+ response_type='RunResult', # noqa: E501
3515
+ auth_settings=auth_settings,
3516
+ async_req=params.get('async_req'),
3517
+ _return_http_data_only=params.get('_return_http_data_only'),
3518
+ _preload_content=params.get('_preload_content', True),
3519
+ _request_timeout=params.get('_request_timeout'),
3520
+ collection_formats=collection_formats)
3521
+
3416
3522
  def run_now(self, job_id, **kwargs): # noqa: E501
3417
3523
  """Bypass scheduling cretirias and start the job # noqa: E501
3418
3524
 
@@ -2723,6 +2723,112 @@ class RunApi(object):
2723
2723
  _request_timeout=params.get('_request_timeout'),
2724
2724
  collection_formats=collection_formats)
2725
2725
 
2726
+ def run_on_demand(self, definitions_file, annotation=None, **kwargs): # noqa: E501
2727
+ """Run a job on demand # noqa: E501
2728
+
2729
+ Run a job on demand # noqa: E501
2730
+ This method makes a synchronous HTTP request by default. To make an
2731
+ asynchronous HTTP request, please pass async_req=True
2732
+ >>> thread = api.run_on_demand(definitions_file, async_req=True)
2733
+ >>> result = thread.get()
2734
+
2735
+ :param async_req bool
2736
+ :param file definitions_file: A file that contains jobs definitions to be run on demand (required)
2737
+ :param file deploy_descriptor_file: Deploy Descriptor JSON file.
2738
+ :param annotation - user annotation
2739
+ :return: RunResult
2740
+ If the method is called asynchronously,
2741
+ returns the request thread.
2742
+ """
2743
+ kwargs['_return_http_data_only'] = True
2744
+ if kwargs.get('async_req'):
2745
+ return self.run_on_demand_with_http_info(definitions_file, annotation, **kwargs) # noqa: E501
2746
+ else:
2747
+ (data) = self.run_on_demand_with_http_info(definitions_file, annotation, **kwargs) # noqa: E501
2748
+ return data
2749
+
2750
+
2751
+ def run_on_demand_with_http_info(self, definitions_file, annotation, **kwargs): # noqa: E501
2752
+ """Run a job on demand # noqa: E501
2753
+
2754
+ Run a job on demand # noqa: E501
2755
+ This method makes a synchronous HTTP request by default. To make an
2756
+ asynchronous HTTP request, please pass async_req=True
2757
+ >>> thread = api.run_on_demand_with_http_info(definitions_file, async_req=True)
2758
+ >>> result = thread.get()
2759
+
2760
+ :param async_req bool
2761
+ :param file definitions_file: A file that contains jobs definitions to be run on demand (required)
2762
+ :param file deploy_descriptor_file: Deploy Descriptor JSON file.
2763
+ :param annotation - user annotation
2764
+ :return: RunResult
2765
+ If the method is called asynchronously,
2766
+ returns the request thread.
2767
+ """
2768
+
2769
+ all_params = ['definitions_file', 'deploy_descriptor_file'] # noqa: E501
2770
+ all_params.append('async_req')
2771
+ all_params.append('_return_http_data_only')
2772
+ all_params.append('_preload_content')
2773
+ all_params.append('_request_timeout')
2774
+
2775
+ params = locals()
2776
+ for key, val in six.iteritems(params['kwargs']):
2777
+ if key not in all_params:
2778
+ raise TypeError(
2779
+ "Got an unexpected keyword argument '%s'"
2780
+ " to method run_on_demand" % key
2781
+ )
2782
+ params[key] = val
2783
+ del params['kwargs']
2784
+ # verify the required parameter 'definitions_file' is set
2785
+ if self.api_client.client_side_validation and ('definitions_file' not in params or
2786
+ params['definitions_file'] is None): # noqa: E501
2787
+ raise ValueError("Missing the required parameter `definitions_file` when calling `run_on_demand`") # noqa: E501
2788
+
2789
+ collection_formats = {}
2790
+
2791
+ path_params = {}
2792
+
2793
+ query_params = []
2794
+
2795
+ header_params = annotation.get_annotation() if annotation else {}
2796
+
2797
+ form_params = []
2798
+ local_var_files = {}
2799
+ if 'definitions_file' in params:
2800
+ local_var_files['definitionsFile'] = params['definitions_file'] # noqa: E501
2801
+ if 'deploy_descriptor_file' in params:
2802
+ local_var_files['deployDescriptorFile'] = params['deploy_descriptor_file'] # noqa: E501
2803
+
2804
+ body_params = None
2805
+ # HTTP header `Accept`
2806
+ header_params['Accept'] = self.api_client.select_header_accept(
2807
+ ['application/json']) # noqa: E501
2808
+
2809
+ # HTTP header `Content-Type`
2810
+ header_params['Content-Type'] = self.api_client.select_header_content_type( # noqa: E501
2811
+ ['multipart/form-data']) # noqa: E501
2812
+
2813
+ # Authentication setting
2814
+ auth_settings = ['ApiKeyAuth', 'Bearer'] # noqa: E501
2815
+
2816
+ return self.api_client.call_api(
2817
+ '/run/ondemand', 'POST',
2818
+ path_params,
2819
+ query_params,
2820
+ header_params,
2821
+ body=body_params,
2822
+ post_params=form_params,
2823
+ files=local_var_files,
2824
+ response_type='RunResult', # noqa: E501
2825
+ auth_settings=auth_settings,
2826
+ async_req=params.get('async_req'),
2827
+ _return_http_data_only=params.get('_return_http_data_only'),
2828
+ _preload_content=params.get('_preload_content', True),
2829
+ _request_timeout=params.get('_request_timeout'),
2830
+ collection_formats=collection_formats)
2831
+
2726
2832
  def run_now(self, job_id, **kwargs): # noqa: E501
2727
2833
  """Bypass scheduling cretirias and start the job # noqa: E501
2728
2834
 
@@ -1,2 +1,2 @@
1
- __version__ = '2.3.7'
1
+ __version__ = '2.4.1'
2
2
  __author__ = 'BMC Software'
@@ -67,8 +67,7 @@ class WorkflowDefaults:
67
67
  run_as: str = attrs.field(default=None, metadata={'applyon': [
68
68
  'Folder', 'SimpleFolder', 'SubFolder', 'Job']})
69
69
  host: str = attrs.field(default=None, metadata={'applyon': ['Job']})
70
- when: Job.When = attrs.field(default=None, metadata={
71
- 'applyon': ['Folder', 'Job']})
70
+ when: Job.When = attrs.field(default=None, metadata={'applyon': ['Folder', 'Job']})
72
71
  application: str = attrs.field(default=None, metadata={'applyon': [
73
72
  'Folder', 'SimpleFolder', 'SubFolder', 'Job']})
74
73
  sub_application: str = attrs.field(default=None, metadata={'applyon': [
@@ -129,7 +128,7 @@ class BaseWorkflow(AbstractWorkflow):
129
128
  pass
130
129
 
131
130
  if inpath:
132
- if isinstance(obj, SubFolder) or isinstance(obj, Job):
131
+ if isinstance(obj, SubFolder) or isinstance(obj, Job) or isinstance(obj, AAPIJob):
133
132
  try:
134
133
  folder = self.get(inpath)
135
134
  except Exception as e:
@@ -138,7 +137,7 @@ class BaseWorkflow(AbstractWorkflow):
138
137
  else:
139
138
  raise e
140
139
  if folder:
141
- if isinstance(obj, Job):
140
+ if isinstance(obj, Job) or isinstance(obj, AAPIJob):
142
141
  folder.job_list.append(obj)
143
142
  else:
144
143
  folder.sub_folder_list.append(obj)
@@ -146,7 +145,7 @@ class BaseWorkflow(AbstractWorkflow):
146
145
  # Folder does not exist, create folder
147
146
  folder, parent_folder = self.create_folder_hierarchy(inpath)
148
147
  self._apply_defaults_for_folder(folder)
149
- if isinstance(obj, Job):
148
+ if isinstance(obj, Job) or isinstance(obj, AAPIJob):
150
149
  parent_folder.job_list.append(obj)
151
150
  else:
152
151
  parent_folder.sub_folder_list.append(obj)
@@ -408,13 +407,40 @@ class Workflow(BaseWorkflow):
408
407
 
409
408
  try:
410
409
  res = self.aapiclient.run_api.run_jobs(fpath.resolve())
411
- # return res
412
410
  run_ = RunMonitor(res.run_id, self.aapiclient, monitor_page_uri= res.monitor_page_uri)
413
- if open_in_browser:
411
+ if open_in_browser:
414
412
  run_.open_in_browser()
415
413
  return run_
416
414
  except Exception as e:
417
- raise e
415
+ errors = [err.get('message', '') + ' ' + err.get('item', '')
416
+ for err in json.loads(e.body)['errors']]
417
+ raise RuntimeError(f"AAPI request failed: {', '.join(errors)}")
418
+ finally:
419
+ if delete_afterwards:
420
+ fpath.unlink()
421
+
422
+ def run_on_demand(self, skip_login: bool = False, file_path: str = None, delete_afterwards: bool = True, open_in_browser=False) -> RunMonitor:
423
+ if not skip_login:
424
+ self.aapiclient.authenticate()
425
+
426
+ if not file_path:
427
+ file_path = f'{tempfile.gettempdir()}/temp_{random.randint(1000,9999)}.json'
428
+
429
+ fpath = pathlib.Path(file_path)
430
+
431
+ with open(fpath.resolve(), 'w') as f:
432
+ self.dump_json(f)
433
+
434
+ try:
435
+ res = self.aapiclient.run_api.run_on_demand(fpath.resolve())
436
+ run_ = RunMonitor(res.run_id, self.aapiclient, monitor_page_uri= res.monitor_page_uri)
437
+ if open_in_browser:
438
+ run_.open_in_browser()
439
+ return run_
440
+ except Exception as e:
441
+ errors = [err.get('message', '') + ' ' + err.get('item', '')
442
+ for err in json.loads(e.body)['errors']]
443
+ raise RuntimeError(f"AAPI request failed: {', '.join(errors)}")
418
444
  finally:
419
445
  if delete_afterwards:
420
446
  fpath.unlink()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ctm-python-client
3
- Version: 2.3.7
3
+ Version: 2.4.1
4
4
  Summary: Python Workflows for Control-M
5
5
  Author: BMC Software
6
6
  License: BSD 3-Clause
@@ -3,7 +3,7 @@ aapi/action.py,sha256=te1lr64tU7854Oy4lUjQgh1EIlZzO82ulA-NnfiK9ko,1622
3
3
  aapi/addevents.py,sha256=maZH3TpP1C1usvM2qee4DNl1PUA6IpnAWUKki1WGUlg,793
4
4
  aapi/ai.py,sha256=3mZu6I8PSbnr0v6AKxZ3-OA8RbdHlI55qCeaqlF3iXA,2094
5
5
  aapi/basefolder.py,sha256=l6wHCwCr-RkJIaDQGzV22bbHk6DQWiNpKHKmOe0x-I4,14191
6
- aapi/bases.py,sha256=isZW33dtSWJ2A7fdUQNumUB0_lXk8VtDYLHZb1cA_jw,1952
6
+ aapi/bases.py,sha256=BW4W4slph3fbA4uY0ubAp-kCyBal_RVIa2Gl4CA1lzg,4164
7
7
  aapi/businessfield.py,sha256=T11iWMIp8BtQCCrkR3glp0E7dKOLI00qO3lKT6_Ngdw,373
8
8
  aapi/businessparameter.py,sha256=Ee8NBDM4TDA2wx7T2lsj1yaNRJ-Pt5FADPCwx8qa4JU,3372
9
9
  aapi/calendar.py,sha256=C6mAQhypnSs2IG1CKRbjTekLdo87AHJ5GM_a3HofF3c,9322
@@ -87,7 +87,7 @@ clients/ctm_api_client/api/config_api.py,sha256=uoWYF7hS_vHUMAWZWpncbwhZw4a3ehUO
87
87
  clients/ctm_api_client/api/deploy_api.py,sha256=SfOADZT87KtgWKnxqo6C8XrL_QjGdy6rf-iFwyvC0rU,100862
88
88
  clients/ctm_api_client/api/provision_api.py,sha256=vSKrVGMpwP5_Tn8JtEWdLcyfWJs2bNnywBSiin0r6k8,45868
89
89
  clients/ctm_api_client/api/reporting_api.py,sha256=PN0lhmNLuTMV73EvQo1MNHdAq2ka9FaJKA2yuMGcok4,16864
90
- clients/ctm_api_client/api/run_api.py,sha256=7xV6H0abzdM4vBE3PvoTpROAe7q0UUn8-DjFpPyeVTM,153524
90
+ clients/ctm_api_client/api/run_api.py,sha256=KcogHTqwlHkqgs0Zznom_qS-UuRD16m5VjMkk4vmPb4,158136
91
91
  clients/ctm_api_client/api/session_api.py,sha256=lM1O-r-zFjV687Y7OjItK5-XLVFTzCmN18N0_cZTEyU,12005
92
92
  clients/ctm_api_client/models/__init__.py,sha256=VP4dbGWWe6V45cEhxBtVV-6SUOdDTCt-S0NXdiuCdhs,26274
93
93
  clients/ctm_api_client/models/actions_auth_record.py,sha256=GNFj9AGdNHfiZhKimjv2_C_CvA_SL0qx1oQs8tNxKXQ,24898
@@ -409,7 +409,7 @@ clients/ctm_saas_client/api/config_api.py,sha256=JaWStV4EOptbZfSXLsvrVxYOgGJ6JcH
409
409
  clients/ctm_saas_client/api/deploy_api.py,sha256=WRZ-JJLSgn6sQRKGCnkSTCVStJcF2NJLHzwG9pYgwOE,90990
410
410
  clients/ctm_saas_client/api/provision_api.py,sha256=CZpb84Nr43fccJiAfJQaIdULz8jGG57d88eOvrz2q0U,4826
411
411
  clients/ctm_saas_client/api/reporting_api.py,sha256=uQZ1uDxmkeH1nLYuTFUf-S9C9mgiL2moqRTMJpbwqE0,12476
412
- clients/ctm_saas_client/api/run_api.py,sha256=QH2bQllLlc2Cude8puPZXqR-VTgw0Btd5-TwK0dtCdk,131542
412
+ clients/ctm_saas_client/api/run_api.py,sha256=Xi2GyfXhmdfAeWHjzJNwvfT_uI8zix3Rv2b3V3c3bew,136154
413
413
  clients/ctm_saas_client/api/usage_api.py,sha256=4KnU1HSn4Zx61Qqd8niCSXFPVnfPAkv_rOaAkHUEdGA,4173
414
414
  clients/ctm_saas_client/models/__init__.py,sha256=s0u_kaVbJMgHUifVoA6Fv2JZ1QheYS_yINoXwY_0tDY,26099
415
415
  clients/ctm_saas_client/models/actions_auth_record.py,sha256=4sj40sUgg51abuO_1-6dKRnb0Ze3dc_kZmAwDwrxa-4,21362
@@ -728,18 +728,18 @@ clients/ctm_saas_client/models/workspace_folder.py,sha256=Uxn61e8LzGzceVV6TBofuy
728
728
  clients/ctm_saas_client/models/workspace_folders.py,sha256=rpVIXVw8Fh_9W-mfuDEDClC0vYTStKQo_8zgGcGaZu8,3455
729
729
  clients/ctm_saas_client/models/zoo_keeper.py,sha256=qYqn4P4CaKqLAKSxBoJvwxm1OGUcS8cmy9BW8Ul7maI,8790
730
730
  clients/ctm_saas_client/models/zos_template_data.py,sha256=RF7S-oR4rmbwc_WJDebk2SLvkAku4uxd_a59Cq_DzVM,18096
731
- ctm_python_client/__init__.py,sha256=-eZ0f6luAq4x-O3DO2KG4XTaGZfxd8mqaF52EkETsP4,49
731
+ ctm_python_client/__init__.py,sha256=kBOH7JmRxvqzB5r3C90YEJWsMNhEAeR1u3wwU954ZiQ,49
732
732
  ctm_python_client/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
733
733
  ctm_python_client/core/comm.py,sha256=33NSUm-dI9jo7jwQft76KSxridmQsjuX-0eNbQSH5Fo,6726
734
734
  ctm_python_client/core/credential.py,sha256=UDN1Q8IYf-ufiXUr-aHm1MDu9bVX8J0Hk8BhvlmYfKk,1009
735
735
  ctm_python_client/core/monitoring.py,sha256=62fbSTEGFpHcBrXK8R8cWejq6DlrmdaNumXbOaDa3HY,12043
736
- ctm_python_client/core/workflow.py,sha256=Tx-IoB3rQxOQTtw6M0NEsj5HRgWG8IAGIkTsCZwCx3Q,15018
736
+ ctm_python_client/core/workflow.py,sha256=eA4gqbPEF9PqoYr3FXrcsbYuXRd8PbFkBzjNA0VOXes,16283
737
737
  ctm_python_client/ext/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
738
738
  ctm_python_client/ext/autogen.py,sha256=Oo5UZ2HKIuMh98e2FDb9GN9dChVe1mt3kcaaOaqBNPk,6671
739
739
  ctm_python_client/ext/credential.py,sha256=2AdNAPtquq9ApH41pWi8Zt8mhlh9VNWVFXeLCNrxOfA,469
740
740
  ctm_python_client/ext/viz.py,sha256=oVY7DYP0H-nCGRQIb1nZgspc8DQdsS8yWUCAl5x_dwg,1672
741
- ctm_python_client-2.3.7.dist-info/LICENSE,sha256=L9eFhIgJK0TNYFm9I7tDUR1CDI8muWkBJvzpbkGCVh8,1525
742
- ctm_python_client-2.3.7.dist-info/METADATA,sha256=2QMTNMGbGRwtyJYsoBuDJpmPPZHrcBYA7ffwrY2oVrg,2921
743
- ctm_python_client-2.3.7.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
744
- ctm_python_client-2.3.7.dist-info/top_level.txt,sha256=Dz3aBWbGmLA3lZcNMUAWX_Yhdw0FdplhI52YBid8Oe0,31
745
- ctm_python_client-2.3.7.dist-info/RECORD,,
741
+ ctm_python_client-2.4.1.dist-info/LICENSE,sha256=L9eFhIgJK0TNYFm9I7tDUR1CDI8muWkBJvzpbkGCVh8,1525
742
+ ctm_python_client-2.4.1.dist-info/METADATA,sha256=D4-dep8hh0bk59q0zQsp5rl18ymC0lKGsAMcF4W1CKY,2921
743
+ ctm_python_client-2.4.1.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
744
+ ctm_python_client-2.4.1.dist-info/top_level.txt,sha256=Dz3aBWbGmLA3lZcNMUAWX_Yhdw0FdplhI52YBid8Oe0,31
745
+ ctm_python_client-2.4.1.dist-info/RECORD,,