pyegeria 0.3.2__py3-none-any.whl → 0.3.4__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.
- pyegeria/__init__.py +1 -1
- pyegeria/automated_curation_omvs.py +78 -56
- pyegeria/core_omag_server_config.py +14 -21
- pyegeria/exceptions.py +382 -0
- pyegeria/full_omag_server_config.py +0 -1
- pyegeria/glossary_omvs.py +14 -2
- pyegeria/gov_engine.py +1 -8
- pyegeria/governance_author.py +2 -0
- pyegeria/my_profile_omvs.py +1 -1
- pyegeria/platform_services.py +2 -39
- pyegeria/registered_info.py +46 -9
- pyegeria/server_operations.py +2 -2
- pyegeria-0.3.4.data/scripts/engine_action_status.py +145 -0
- pyegeria-0.3.4.data/scripts/find_todos.py +152 -0
- pyegeria-0.3.4.data/scripts/glossary_view.py +135 -0
- pyegeria-0.3.4.data/scripts/gov_engine_status.py +120 -0
- pyegeria-0.3.4.data/scripts/integration_daemon_status.py +130 -0
- pyegeria-0.3.4.data/scripts/list_asset_types.py +114 -0
- pyegeria-0.3.4.data/scripts/multi-server_status.py +123 -0
- pyegeria-0.3.4.data/scripts/my_todos.py +162 -0
- pyegeria-0.3.4.data/scripts/open_todos.py +140 -0
- pyegeria-0.3.4.data/scripts/server_status.py +105 -0
- pyegeria-0.3.4.data/scripts/server_status_widget.py +93 -0
- pyegeria-0.3.4.data/scripts/view_my_profile.py +140 -0
- {pyegeria-0.3.2.dist-info → pyegeria-0.3.4.dist-info}/METADATA +1 -1
- pyegeria-0.3.4.dist-info/RECORD +34 -0
- pyegeria-0.3.2.dist-info/RECORD +0 -21
- {pyegeria-0.3.2.dist-info → pyegeria-0.3.4.dist-info}/LICENSE +0 -0
- {pyegeria-0.3.2.dist-info → pyegeria-0.3.4.dist-info}/WHEEL +0 -0
- {pyegeria-0.3.2.dist-info → pyegeria-0.3.4.dist-info}/top_level.txt +0 -0
pyegeria/__init__.py
CHANGED
@@ -26,7 +26,7 @@ if disable_ssl_warnings:
|
|
26
26
|
|
27
27
|
from ._exceptions import (InvalidParameterException, PropertyServerException, UserNotAuthorizedException,
|
28
28
|
print_exception_response)
|
29
|
-
from .utils import print_response, body_slimmer
|
29
|
+
from .utils import print_response, body_slimmer, wrap_text
|
30
30
|
from ._client import Client
|
31
31
|
from .automated_curation_omvs import AutomatedCuration
|
32
32
|
from .core_omag_server_config import CoreServerConfig
|
@@ -6,29 +6,34 @@ Copyright Contributors to the ODPi Egeria project.
|
|
6
6
|
|
7
7
|
"""
|
8
8
|
import asyncio
|
9
|
-
import json
|
10
9
|
from datetime import datetime
|
11
|
-
|
10
|
+
|
11
|
+
from httpx import Response
|
12
12
|
|
13
13
|
from pyegeria import Client, max_paging_size, body_slimmer
|
14
|
-
from ._validators import validate_name, validate_guid, validate_search_string
|
15
14
|
from pyegeria._exceptions import (
|
16
15
|
InvalidParameterException,
|
17
16
|
)
|
17
|
+
from ._validators import validate_name, validate_guid, validate_search_string
|
18
18
|
|
19
19
|
|
20
20
|
class AutomatedCuration(Client):
|
21
21
|
""" Set up and maintain automation services in Egeria.
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
22
|
+
|
23
|
+
Attributes:
|
24
|
+
server_name : str
|
25
|
+
The name of the View Server to use.
|
26
|
+
platform_url : str
|
27
|
+
URL of the server platform to connect to
|
28
|
+
user_id : str
|
29
|
+
The identity of the user calling the method - this sets a default optionally used by the methods
|
30
|
+
when the user doesn't pass the user_id on a method call.
|
31
|
+
user_pwd: str
|
32
|
+
The password associated with the user_id. Defaults to None
|
33
|
+
verify_flag: bool
|
34
|
+
Flag to indicate if SSL Certificates should be verified in the HTTP requests.
|
35
|
+
Defaults to False.
|
36
|
+
|
32
37
|
"""
|
33
38
|
|
34
39
|
def __init__(
|
@@ -92,7 +97,7 @@ class AutomatedCuration(Client):
|
|
92
97
|
|
93
98
|
url = f"{self.platform_url}/servers/{server}/api/open-metadata/automated-curation/catalog-templates/new-element"
|
94
99
|
response = await self._async_make_request("POST", url, body)
|
95
|
-
return response.json().get("guid","GUID failed to be returned")
|
100
|
+
return response.json().get("guid", "GUID failed to be returned")
|
96
101
|
|
97
102
|
def create_element_from_template(self, body: dict, server: str = None) -> str:
|
98
103
|
""" Create a new metadata element from a template. Async version.
|
@@ -145,8 +150,8 @@ class AutomatedCuration(Client):
|
|
145
150
|
)
|
146
151
|
return response
|
147
152
|
|
148
|
-
async def _async_create_kafka_server_element_from_template(self, kafka_server:str, host_name: str, port: str,
|
149
|
-
server:str = None) -> str:
|
153
|
+
async def _async_create_kafka_server_element_from_template(self, kafka_server: str, host_name: str, port: str,
|
154
|
+
server: str = None) -> str:
|
150
155
|
""" Create a Kafka server element from a template. Async version.
|
151
156
|
|
152
157
|
Parameters
|
@@ -210,7 +215,6 @@ class AutomatedCuration(Client):
|
|
210
215
|
)
|
211
216
|
return response
|
212
217
|
|
213
|
-
|
214
218
|
async def _async_create_postgres_server_element_from_template(self, postgres_server: str, host_name: str, port: str,
|
215
219
|
db_user: str, db_pwd: str, server: str = None) -> str:
|
216
220
|
""" Create a Postgres server element from a template. Async version.
|
@@ -247,7 +251,7 @@ class AutomatedCuration(Client):
|
|
247
251
|
"serverName": postgres_server,
|
248
252
|
"hostIdentifier": host_name,
|
249
253
|
"portNumber": port,
|
250
|
-
"databaseUserId"
|
254
|
+
"databaseUserId": db_user,
|
251
255
|
"databasePassword": db_pwd
|
252
256
|
}
|
253
257
|
}
|
@@ -285,14 +289,14 @@ class AutomatedCuration(Client):
|
|
285
289
|
"""
|
286
290
|
loop = asyncio.get_event_loop()
|
287
291
|
response = loop.run_until_complete(
|
288
|
-
self._async_create_postgres_server_element_from_template(postgres_server,host_name,
|
289
|
-
port, db_user,db_pwd,server)
|
292
|
+
self._async_create_postgres_server_element_from_template(postgres_server, host_name,
|
293
|
+
port, db_user, db_pwd, server)
|
290
294
|
)
|
291
295
|
return response
|
292
296
|
|
293
297
|
#
|
294
|
-
# Engine Actions
|
295
|
-
#
|
298
|
+
# Engine Actions
|
299
|
+
#
|
296
300
|
async def _async_get_engine_actions(self, server: str = None, start_from: int = 0,
|
297
301
|
page_size: int = max_paging_size) -> list:
|
298
302
|
""" Retrieve the engine actions that are known to the server. Async version.
|
@@ -562,16 +566,19 @@ class AutomatedCuration(Client):
|
|
562
566
|
name : str
|
563
567
|
The name of the engine action to retrieve.
|
564
568
|
server : str, optional
|
565
|
-
|
569
|
+
The name of the server to retrieve the engine action from. If not provided, the default server specified in
|
570
|
+
the instance will be used.
|
566
571
|
start_from : int, optional
|
567
572
|
The index to start retrieving engine actions from. If not provided, the default value will be used.
|
568
573
|
page_size : int, optional
|
569
|
-
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
574
|
+
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
575
|
+
maximum paging size will be used.
|
570
576
|
|
571
577
|
Returns
|
572
578
|
-------
|
573
579
|
list of dict | str
|
574
|
-
A list of dictionaries representing the retrieved engine actions, or "no actions" if no engine actions were
|
580
|
+
A list of dictionaries representing the retrieved engine actions, or "no actions" if no engine actions were
|
581
|
+
found with the given name.
|
575
582
|
Raises:
|
576
583
|
------
|
577
584
|
InvalidParameterException
|
@@ -604,16 +611,19 @@ class AutomatedCuration(Client):
|
|
604
611
|
name : str
|
605
612
|
The name of the engine action to retrieve.
|
606
613
|
server : str, optional
|
607
|
-
The name of the server to retrieve the engine action from. If not provided, the default server specified in
|
614
|
+
The name of the server to retrieve the engine action from. If not provided, the default server specified in
|
615
|
+
the instance will be used.
|
608
616
|
start_from : int, optional
|
609
617
|
The index to start retrieving engine actions from. If not provided, the default value will be used.
|
610
618
|
page_size : int, optional
|
611
|
-
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
619
|
+
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
620
|
+
maximum paging size will be used.
|
612
621
|
|
613
622
|
Returns
|
614
623
|
-------
|
615
624
|
list of dict | str
|
616
|
-
A list of dictionaries representing the retrieved engine actions, or "no actions" if no engine actions were
|
625
|
+
A list of dictionaries representing the retrieved engine actions, or "no actions" if no engine actions were
|
626
|
+
found with the given name.
|
617
627
|
Raises:
|
618
628
|
------
|
619
629
|
InvalidParameterException
|
@@ -745,9 +755,9 @@ class AutomatedCuration(Client):
|
|
745
755
|
)
|
746
756
|
return response
|
747
757
|
|
748
|
-
#
|
749
|
-
# Governance action processes
|
750
|
-
#
|
758
|
+
#
|
759
|
+
# Governance action processes
|
760
|
+
#
|
751
761
|
|
752
762
|
async def _async_get_governance_action_process_by_guid(self, process_guid: str, server: str = None) -> dict | str:
|
753
763
|
""" Retrieve the governance action process metadata element with the supplied unique identifier. Async Version.
|
@@ -863,7 +873,7 @@ class AutomatedCuration(Client):
|
|
863
873
|
"""
|
864
874
|
loop = asyncio.get_event_loop()
|
865
875
|
response = loop.run_until_complete(
|
866
|
-
self.
|
876
|
+
self._async_gov_action_process_graph(process_guid, server)
|
867
877
|
)
|
868
878
|
return response
|
869
879
|
|
@@ -877,11 +887,13 @@ class AutomatedCuration(Client):
|
|
877
887
|
name : str
|
878
888
|
The name of the engine action to retrieve.
|
879
889
|
server : str, optional
|
880
|
-
The name of the server to retrieve the engine action from. If not provided, the default server specified
|
890
|
+
The name of the server to retrieve the engine action from. If not provided, the default server specified
|
891
|
+
in the instance will be used.
|
881
892
|
start_from : int, optional
|
882
893
|
The index to start retrieving engine actions from. If not provided, the default value will be used.
|
883
894
|
page_size : int, optional
|
884
|
-
The maximum number of engine actions to retrieve in a single request. If not provided, the default
|
895
|
+
The maximum number of engine actions to retrieve in a single request. If not provided, the default
|
896
|
+
global maximum paging size will be used.
|
885
897
|
|
886
898
|
Returns
|
887
899
|
-------
|
@@ -916,11 +928,13 @@ class AutomatedCuration(Client):
|
|
916
928
|
name : str
|
917
929
|
The name of the engine action to retrieve.
|
918
930
|
server : str, optional
|
919
|
-
The name of the server to retrieve the engine action from. If not provided, the default server specified
|
931
|
+
The name of the server to retrieve the engine action from. If not provided, the default server specified
|
932
|
+
in the instance will be used.
|
920
933
|
start_from : int, optional
|
921
934
|
The index to start retrieving engine actions from. If not provided, the default value will be used.
|
922
935
|
page_size : int, optional
|
923
|
-
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
936
|
+
The maximum number of engine actions to retrieve in a single request. If not provided, the default global
|
937
|
+
maximum paging size will be used.
|
924
938
|
|
925
939
|
Returns
|
926
940
|
-------
|
@@ -1041,8 +1055,8 @@ class AutomatedCuration(Client):
|
|
1041
1055
|
|
1042
1056
|
loop = asyncio.get_event_loop()
|
1043
1057
|
response = loop.run_until_complete(
|
1044
|
-
self.
|
1045
|
-
|
1058
|
+
self._async_find_gov_action_processes(search_string, server, starts_with, ends_with, ignore_case,
|
1059
|
+
start_from, page_size)
|
1046
1060
|
)
|
1047
1061
|
return response
|
1048
1062
|
|
@@ -1117,6 +1131,8 @@ class AutomatedCuration(Client):
|
|
1117
1131
|
- unique name of the requesting governance service (if initiated by a governance engine)
|
1118
1132
|
orig_engine_name: str
|
1119
1133
|
- optional unique name of the governance engine (if initiated by a governance engine).
|
1134
|
+
server: str, optional
|
1135
|
+
- if not specified, the server from the class instance will be used
|
1120
1136
|
|
1121
1137
|
Returns
|
1122
1138
|
-------
|
@@ -1202,7 +1218,8 @@ class AutomatedCuration(Client):
|
|
1202
1218
|
action_type_name: str
|
1203
1219
|
The name of the governance action type to retrieve.
|
1204
1220
|
server: str, optional
|
1205
|
-
The name of the server. If None, will use the default server specified in the instance
|
1221
|
+
The name of the server. If None, will use the default server specified in the instance
|
1222
|
+
will be used.
|
1206
1223
|
Returns:
|
1207
1224
|
-------
|
1208
1225
|
dict: The JSON representation of the governance action type element.
|
@@ -1369,8 +1386,10 @@ class AutomatedCuration(Client):
|
|
1369
1386
|
return response
|
1370
1387
|
|
1371
1388
|
async def _async_initiate_gov_action_type(self, action_type_qualified_name: str, request_source_guids: [str],
|
1372
|
-
action_targets: list, start_time: datetime
|
1373
|
-
|
1389
|
+
action_targets: list, start_time: datetime = None,
|
1390
|
+
request_parameters: dict = None,
|
1391
|
+
orig_service_name: str = None, orig_engine_name: str = None,
|
1392
|
+
server: str = None) -> str:
|
1374
1393
|
""" Using the named governance action type as a template, initiate an engine action. Async version.
|
1375
1394
|
|
1376
1395
|
Parameters
|
@@ -1389,6 +1408,8 @@ class AutomatedCuration(Client):
|
|
1389
1408
|
- unique name of the requesting governance service (if initiated by a governance engine)
|
1390
1409
|
orig_engine_name: str
|
1391
1410
|
- optional unique name of the governance engine (if initiated by a governance engine).
|
1411
|
+
server : str, optional
|
1412
|
+
- The name of the server. If None, will use the default server specified in the instance will be used.
|
1392
1413
|
|
1393
1414
|
Returns
|
1394
1415
|
-------
|
@@ -1439,6 +1460,8 @@ class AutomatedCuration(Client):
|
|
1439
1460
|
- unique name of the requesting governance service (if initiated by a governance engine)
|
1440
1461
|
orig_engine_name: str
|
1441
1462
|
- optional unique name of the governance engine (if initiated by a governance engine).
|
1463
|
+
server : str, optional
|
1464
|
+
- The name of the server. If None, will use the default server specified in the instance will be used.
|
1442
1465
|
|
1443
1466
|
Returns
|
1444
1467
|
-------
|
@@ -1458,7 +1481,7 @@ class AutomatedCuration(Client):
|
|
1458
1481
|
)
|
1459
1482
|
return response
|
1460
1483
|
|
1461
|
-
async def _async_initiate_postgres_server_survey(self, postgres_server_guid: str,
|
1484
|
+
async def _async_initiate_postgres_server_survey(self, postgres_server_guid: str, server: str = None) -> str:
|
1462
1485
|
server = self.server_name if server is None else server
|
1463
1486
|
url = (f"{self.platform_url}/servers/{server}/api/open-metadata/automated-curation/governance-action-types/"
|
1464
1487
|
f"initiate")
|
@@ -1467,9 +1490,9 @@ class AutomatedCuration(Client):
|
|
1467
1490
|
"class": "InitiateGovernanceActionTypeRequestBody",
|
1468
1491
|
"governanceActionTypeQualifiedName": "Egeria:GovernanceActionType:2adeb8f1-0f59-4970-b6f2-6cc25d4d2402survey-postgres-server",
|
1469
1492
|
"actionTargets": [{
|
1470
|
-
"class"
|
1471
|
-
"actionTargetName"
|
1472
|
-
"actionTargetGUID"
|
1493
|
+
"class": "NewActionTarget",
|
1494
|
+
"actionTargetName": "serverToSurvey",
|
1495
|
+
"actionTargetGUID": postgres_server_guid
|
1473
1496
|
}]
|
1474
1497
|
}
|
1475
1498
|
response = await self._async_make_request("POST", url, body)
|
@@ -1507,7 +1530,7 @@ class AutomatedCuration(Client):
|
|
1507
1530
|
def initiate_file_folder_survey(self, file_folder_guid: str, server: str = None) -> str:
|
1508
1531
|
loop = asyncio.get_event_loop()
|
1509
1532
|
response = loop.run_until_complete(
|
1510
|
-
self._async_initiate_file_folder_survey(
|
1533
|
+
self._async_initiate_file_folder_survey(file_folder_guid, server)
|
1511
1534
|
)
|
1512
1535
|
return response
|
1513
1536
|
|
@@ -1536,14 +1559,13 @@ class AutomatedCuration(Client):
|
|
1536
1559
|
)
|
1537
1560
|
return response
|
1538
1561
|
|
1539
|
-
async def run_gov_action_postgres_server_survey(self, postgres_server: str, host_name: str, port: str,):
|
1562
|
+
async def run_gov_action_postgres_server_survey(self, postgres_server: str, host_name: str, port: str, ):
|
1540
1563
|
pass
|
1541
1564
|
# New combo process to do
|
1542
1565
|
# run a process the creates the postgres server catalog entry, runs the server survey
|
1543
1566
|
# creates a survey report
|
1544
1567
|
# adds a to-do list element when done
|
1545
1568
|
|
1546
|
-
|
1547
1569
|
async def _async_initiate_engine_action(self, qualified_name: str, domain_identifier: int, display_name: str,
|
1548
1570
|
description: str, request_source_guids: str, action_targets: str,
|
1549
1571
|
received_guards: [str], start_time: datetime, gov_engine_name: str,
|
@@ -1928,8 +1950,8 @@ class AutomatedCuration(Client):
|
|
1928
1950
|
|
1929
1951
|
loop = asyncio.get_event_loop()
|
1930
1952
|
loop.run_until_complete(
|
1931
|
-
self.
|
1932
|
-
|
1953
|
+
self._async_remove_catalog_target(integ_connector_guid, metadata_element_guid,
|
1954
|
+
server)
|
1933
1955
|
)
|
1934
1956
|
return
|
1935
1957
|
|
@@ -2081,7 +2103,7 @@ class AutomatedCuration(Client):
|
|
2081
2103
|
return response
|
2082
2104
|
|
2083
2105
|
async def _async_find_technology_types(self, search_string: str = "*", server: str = None, start_from: int = 0,
|
2084
|
-
page_size: int = max_paging_size, starts_with: bool=False,
|
2106
|
+
page_size: int = max_paging_size, starts_with: bool = False,
|
2085
2107
|
ends_with: bool = False, ignore_case: bool = True) -> list | str:
|
2086
2108
|
""" Retrieve the list of technology types that contain the search string. Async version.
|
2087
2109
|
|
@@ -2127,7 +2149,7 @@ class AutomatedCuration(Client):
|
|
2127
2149
|
ends_with_s = str(ends_with).lower()
|
2128
2150
|
ignore_case_s = str(ignore_case).lower()
|
2129
2151
|
validate_name(search_string)
|
2130
|
-
if search_string== "*":
|
2152
|
+
if search_string == "*":
|
2131
2153
|
search_string = ""
|
2132
2154
|
|
2133
2155
|
url = (f"{self.platform_url}/servers/{server}/api/open-metadata/automated-curation/technology-types/"
|
@@ -2139,7 +2161,7 @@ class AutomatedCuration(Client):
|
|
2139
2161
|
return response.json().get("elements", "no tech found")
|
2140
2162
|
|
2141
2163
|
def find_technology_types(self, type_name: str = "*", server: str = None, start_from: int = 0,
|
2142
|
-
page_size: int = max_paging_size, starts_with: bool=False,
|
2164
|
+
page_size: int = max_paging_size, starts_with: bool = False,
|
2143
2165
|
ends_with: bool = False, ignore_case: bool = True) -> list | str:
|
2144
2166
|
""" Retrieve the list of technology types that contain the search string. Async version.
|
2145
2167
|
|
@@ -2168,7 +2190,7 @@ class AutomatedCuration(Client):
|
|
2168
2190
|
loop = asyncio.get_event_loop()
|
2169
2191
|
response = loop.run_until_complete(
|
2170
2192
|
self._async_find_technology_types(type_name, server, start_from,
|
2171
|
-
page_size, starts_with,ends_with, ignore_case)
|
2193
|
+
page_size, starts_with, ends_with, ignore_case)
|
2172
2194
|
)
|
2173
2195
|
return response
|
2174
2196
|
|
@@ -2183,6 +2205,6 @@ class AutomatedCuration(Client):
|
|
2183
2205
|
|
2184
2206
|
if __name__ == "__main__":
|
2185
2207
|
p = AutomatedCuration("meow", "https://127.0.0.1:9443", "garygeeke", verify_flag=False)
|
2186
|
-
response = p.
|
2187
|
-
out = response.json()
|
2208
|
+
response = p.get_active_engine_actions()
|
2209
|
+
out = response.json()
|
2188
2210
|
print(out)
|
@@ -24,31 +24,24 @@ from pyegeria._validators import (
|
|
24
24
|
class CoreServerConfig(Client):
|
25
25
|
"""
|
26
26
|
CoreServerConfig is a class that extends the Client class. It provides methods to configure and interact with access
|
27
|
-
|
27
|
+
services in the OMAG server.
|
28
28
|
|
29
|
-
|
29
|
+
Attributes:
|
30
30
|
|
31
|
-
|
32
|
-
|
31
|
+
server_name: str
|
32
|
+
The name of the OMAG server to configure.
|
33
|
+
platform_url : str
|
34
|
+
URL of the server platform to connect to
|
35
|
+
user_id : str
|
36
|
+
The identity of the user calling the method - this sets a default optionally used by the methods
|
37
|
+
when the user doesn't pass the user_id on a method call.
|
38
|
+
user_pwd: str
|
39
|
+
The password associated with the user_id. Defaults to None
|
40
|
+
verify_flag: bool
|
41
|
+
Flag to indicate if SSL Certificates should be verified in the HTTP requests.
|
42
|
+
Defaults to False.
|
33
43
|
|
34
|
-
- get_configured_access_services(server_name: str = None) -> dict:
|
35
|
-
Returns the list of access services that are configured for this server.
|
36
44
|
|
37
|
-
- configure_all_access_services(server_name: str = None) -> None:
|
38
|
-
Enables all access services that are registered with this server platform.
|
39
|
-
|
40
|
-
- configure_all_access_services_no_topics(server_name: str = None) -> None:
|
41
|
-
Configures all access services for the specified server with no cohort/Event Bus.
|
42
|
-
|
43
|
-
- clear_all_access_services(server_name: str = None) -> None:
|
44
|
-
Disables the access services. This removes all configuration for the access services and disables the
|
45
|
-
enterprise repository services.
|
46
|
-
|
47
|
-
- get_access_service_config(access_service_name: str, server_name: str = None) -> dict:
|
48
|
-
Retrieves the config for an access service.
|
49
|
-
|
50
|
-
- configure_access_service(access_service_name: str, server_name: str = None) -> None:
|
51
|
-
Enables a single access service.
|
52
45
|
"""
|
53
46
|
|
54
47
|
def __init__(
|