esd-services-api-client 2.6.0__py3-none-any.whl → 2.6.1a149.dev13__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.
@@ -17,4 +17,4 @@
17
17
  Root index.
18
18
  """
19
19
 
20
- __version__ = "2.6.0"
20
+ __version__ = "0.0.0"
@@ -0,0 +1 @@
1
+ __version__ = 'v2.6.1a149.dev13'
@@ -20,7 +20,11 @@ import os
20
20
  from functools import reduce
21
21
  from typing import Optional, Iterator, final
22
22
 
23
- from adapta.security.clients import AzureClient
23
+ try:
24
+ from adapta.security.clients import AzureClient
25
+ except ImportError:
26
+ pass
27
+
24
28
  from adapta.utils import session_with_retries
25
29
  from requests import Session, Response
26
30
 
@@ -22,7 +22,7 @@ import socketserver
22
22
  import threading
23
23
  from dataclasses import dataclass
24
24
  from http.server import ThreadingHTTPServer, BaseHTTPRequestHandler
25
- from typing import Dict, Optional, Any
25
+ from typing import Optional, Any
26
26
 
27
27
  import pandas
28
28
  from adapta.metrics import MetricsProvider
@@ -30,6 +30,7 @@ from adapta.storage.query_enabled_store import QueryEnabledStore
30
30
  from dataclasses_json import DataClassJsonMixin
31
31
  from injector import inject
32
32
 
33
+ from esd_services_api_client.crystal import CrystalEntrypointArguments
33
34
  from esd_services_api_client.nexus.abstractions.algrorithm_cache import InputCache
34
35
  from esd_services_api_client.nexus.abstractions.logger_factory import LoggerFactory
35
36
  from esd_services_api_client.nexus.abstractions.nexus_object import AlgorithmResult
@@ -292,6 +293,15 @@ async def main():
292
293
  Mock HTTP Server
293
294
  :return:
294
295
  """
296
+ def tags_from_payload(payload: MyAlgorithmPayload, _: CrystalEntrypointArguments) -> dict[str, str]:
297
+ return {
298
+ "test_tag": str(payload.x)
299
+ }
300
+ def enrich_from_payload(payload: MyAlgorithmPayload2, run_args: CrystalEntrypointArguments) -> dict[str, dict[str, str]]:
301
+ return {
302
+ "(value of y:{y})": {"y": payload.y},
303
+ "(request_id:{request_id})": {"request_id": run_args.request_id}
304
+ }
295
305
  with ThreadingHTTPServer(("localhost", 9876), MockRequestHandler) as server:
296
306
  server_thread = threading.Thread(target=server.serve_forever)
297
307
  server_thread.daemon = True
@@ -306,6 +316,7 @@ async def main():
306
316
  .on_complete(ObjectiveAnalytics)
307
317
  .inject_configuration(MyAlgorithmConfiguration)
308
318
  .inject_payload(MyAlgorithmPayload, MyAlgorithmPayload2)
319
+ .with_log_enricher(tags_from_payload, enrich_from_payload)
309
320
  )
310
321
 
311
322
  await nexus.activate()
@@ -19,8 +19,9 @@
19
19
 
20
20
  import json
21
21
  import os
22
+ from abc import ABC
22
23
  from logging import StreamHandler
23
- from typing import final, Type, TypeVar, Optional, Dict
24
+ from typing import final, TypeVar
24
25
 
25
26
  from adapta.logs import LoggerInterface, create_async_logger
26
27
  from adapta.logs.handlers.datadog_api_handler import DataDogApiHandler
@@ -29,13 +30,63 @@ from adapta.logs.models import LogLevel
29
30
  TLogger = TypeVar("TLogger") # pylint: disable=C0103:
30
31
 
31
32
 
33
+ @final
34
+ class BootstrapLogger(LoggerInterface, ABC):
35
+ """
36
+ Dummy class to separate bootstrap logging from core app loggers
37
+ """
38
+
39
+ pass
40
+
41
+
42
+ @final
43
+ class BootstrapLoggerFactory:
44
+ """
45
+ Bootstrap logger provisioner.
46
+ Bootstrap loggers do not use enriched properties since they are initialized before payload is deserialized.
47
+ """
48
+
49
+ def __init__(self):
50
+ self._log_handlers = [
51
+ StreamHandler(),
52
+ ]
53
+ if "NEXUS__DATADOG_LOGGER_CONFIGURATION" in os.environ:
54
+ self._log_handlers.append(
55
+ DataDogApiHandler(
56
+ **json.loads(os.getenv("NEXUS__DATADOG_LOGGER_CONFIGURATION"))
57
+ )
58
+ )
59
+
60
+ def create_logger(self, request_id: str, algorithm_name: str) -> LoggerInterface:
61
+ """
62
+ Creates an async-safe logger for the provided class name.
63
+ """
64
+ return create_async_logger(
65
+ logger_type=BootstrapLogger.__class__,
66
+ log_handlers=self._log_handlers,
67
+ min_log_level=LogLevel(os.getenv("NEXUS__LOG_LEVEL", "INFO")),
68
+ global_tags={
69
+ "request_id": request_id,
70
+ "algorithm": algorithm_name,
71
+ },
72
+ )
73
+
74
+
32
75
  @final
33
76
  class LoggerFactory:
34
77
  """
35
78
  Async logger provisioner.
36
79
  """
37
80
 
38
- def __init__(self):
81
+ def __init__(
82
+ self,
83
+ fixed_template: dict[str, dict[str, str]] | None = None,
84
+ fixed_template_delimiter: str = None,
85
+ global_tags: dict[str, str] | None = None,
86
+ ):
87
+ self._global_tags = global_tags
88
+ self._fixed_template = fixed_template
89
+ self._fixed_template_delimiter = fixed_template_delimiter or ", "
39
90
  self._log_handlers = [
40
91
  StreamHandler(),
41
92
  ]
@@ -45,12 +96,20 @@ class LoggerFactory:
45
96
  **json.loads(os.getenv("NEXUS__DATADOG_LOGGER_CONFIGURATION"))
46
97
  )
47
98
  )
99
+ if "NEXUS__LOGGER_FIXED_TEMPLATE" in os.environ:
100
+ self._fixed_template = self._fixed_template | json.loads(
101
+ os.getenv("NEXUS__LOGGER_FIXED_TEMPLATE")
102
+ )
103
+
104
+ if "NEXUS__LOGGER_FIXED_TEMPLATE_DELIMITER" in os.environ:
105
+ self._fixed_template_delimiter = (
106
+ self._fixed_template_delimiter
107
+ or os.getenv("NEXUS__LOGGER_FIXED_TEMPLATE")
108
+ )
48
109
 
49
110
  def create_logger(
50
111
  self,
51
- logger_type: Type[TLogger],
52
- fixed_template: Optional[Dict[str, Dict[str, str]]] = None,
53
- fixed_template_delimiter=", ",
112
+ logger_type: type[TLogger],
54
113
  ) -> LoggerInterface:
55
114
  """
56
115
  Creates an async-safe logger for the provided class name.
@@ -59,6 +118,7 @@ class LoggerFactory:
59
118
  logger_type=logger_type,
60
119
  log_handlers=self._log_handlers,
61
120
  min_log_level=LogLevel(os.getenv("NEXUS__LOG_LEVEL", "INFO")),
62
- fixed_template=fixed_template,
63
- fixed_template_delimiter=fixed_template_delimiter,
121
+ fixed_template=self._fixed_template,
122
+ fixed_template_delimiter=self._fixed_template_delimiter,
123
+ global_tags=self._global_tags,
64
124
  )
@@ -23,7 +23,7 @@ import platform
23
23
  import signal
24
24
  import sys
25
25
  import traceback
26
- from typing import final, Type, Optional
26
+ from typing import final, Type, Optional, Callable
27
27
 
28
28
  import backoff
29
29
  import urllib3.exceptions
@@ -41,7 +41,10 @@ from esd_services_api_client.crystal import (
41
41
  AlgorithmRunResult,
42
42
  CrystalEntrypointArguments,
43
43
  )
44
- from esd_services_api_client.nexus.abstractions.logger_factory import LoggerFactory
44
+ from esd_services_api_client.nexus.abstractions.logger_factory import (
45
+ LoggerFactory,
46
+ BootstrapLoggerFactory,
47
+ )
45
48
  from esd_services_api_client.nexus.abstractions.nexus_object import AlgorithmResult
46
49
  from esd_services_api_client.nexus.algorithms import (
47
50
  BaselineAlgorithm,
@@ -119,6 +122,21 @@ class Nexus:
119
122
  self._algorithm_run_task: Optional[asyncio.Task] = None
120
123
  self._on_complete_tasks: list[type[UserTelemetryRecorder]] = []
121
124
  self._payload_types: list[type[AlgorithmPayload]] = []
125
+ self._log_enricher: Callable[
126
+ [
127
+ AlgorithmPayload,
128
+ CrystalEntrypointArguments,
129
+ ],
130
+ dict[str, dict[str, str]],
131
+ ] | None = None
132
+ self._log_tagger: Callable[
133
+ [
134
+ AlgorithmPayload,
135
+ CrystalEntrypointArguments,
136
+ ],
137
+ dict[str, str],
138
+ ] | None = None
139
+ self._log_enrichment_delimiter: str = ", "
122
140
 
123
141
  attach_signal_handlers()
124
142
 
@@ -177,6 +195,31 @@ class Nexus:
177
195
 
178
196
  return self
179
197
 
198
+ def with_log_enricher(
199
+ self,
200
+ tagger: Callable[
201
+ [
202
+ AlgorithmPayload,
203
+ CrystalEntrypointArguments,
204
+ ],
205
+ dict[str, str],
206
+ ]
207
+ | None,
208
+ enricher: Callable[
209
+ [
210
+ AlgorithmPayload,
211
+ CrystalEntrypointArguments,
212
+ ],
213
+ dict[str, dict[str, str]],
214
+ ]
215
+ | None = None,
216
+ delimiter: str = ", ",
217
+ ) -> "Nexus":
218
+ self._log_tagger = tagger
219
+ self._log_enricher = enricher
220
+ self._log_enrichment_delimiter = delimiter
221
+ return self
222
+
180
223
  def with_module(self, module: Type[Module]) -> "Nexus":
181
224
  """
182
225
  Adds a (custom) DI module into the DI container.
@@ -260,11 +303,14 @@ class Nexus:
260
303
 
261
304
  self._injector = Injector(self._configurator.injection_binds)
262
305
 
263
- root_logger: LoggerInterface = self._injector.get(LoggerFactory).create_logger(
264
- logger_type=self.__class__,
306
+ bootstrap_logger: LoggerInterface = self._injector.get(
307
+ BootstrapLoggerFactory
308
+ ).create_logger(
309
+ request_id=self._run_args.request_id,
310
+ algorithm_name=os.getenv("CRYSTAL__ALGORITHM_NAME"),
265
311
  )
266
312
 
267
- root_logger.start()
313
+ bootstrap_logger.start()
268
314
 
269
315
  for payload_type in self._payload_types:
270
316
  try:
@@ -272,10 +318,30 @@ class Nexus:
272
318
  self._injector.binder.bind(
273
319
  payload.__class__, to=payload, scope=singleton
274
320
  )
321
+ # bind app-level LoggerFactory now
322
+ self._injector.binder.bind(
323
+ LoggerFactory.__class__,
324
+ to=LoggerFactory(
325
+ fixed_template=None
326
+ if not self._log_enricher
327
+ else self._log_enricher(payload, self._run_args),
328
+ fixed_template_delimiter=self._log_enrichment_delimiter,
329
+ global_tags=self._log_tagger(payload, self._run_args),
330
+ ),
331
+ scope=singleton,
332
+ )
275
333
  except BaseException as ex: # pylint: disable=broad-except
276
- root_logger.error("Error reading algorithm payload", ex)
334
+ bootstrap_logger.error("Error reading algorithm payload", ex)
277
335
  sys.exit(1)
278
336
 
337
+ bootstrap_logger.stop()
338
+
339
+ root_logger: LoggerInterface = self._injector.get(LoggerFactory).create_logger(
340
+ logger_type=self.__class__,
341
+ )
342
+
343
+ root_logger.start()
344
+
279
345
  algorithm: BaselineAlgorithm = self._injector.get(self._algorithm_class)
280
346
  telemetry_recorder: TelemetryRecorder = self._injector.get(TelemetryRecorder)
281
347
 
@@ -300,7 +366,7 @@ class Nexus:
300
366
  root_logger.error(
301
367
  "Algorithm {algorithm} run failed on Nexus version {version}",
302
368
  ex,
303
- algorithm=self._algorithm_class,
369
+ algorithm=algorithm.__class__.__name__,
304
370
  version=__version__,
305
371
  )
306
372
 
@@ -30,7 +30,9 @@ from injector import Module, singleton, provider
30
30
 
31
31
  from esd_services_api_client.crystal import CrystalConnector
32
32
  from esd_services_api_client.nexus.abstractions.algrorithm_cache import InputCache
33
- from esd_services_api_client.nexus.abstractions.logger_factory import LoggerFactory
33
+ from esd_services_api_client.nexus.abstractions.logger_factory import (
34
+ BootstrapLoggerFactory,
35
+ )
34
36
  from esd_services_api_client.nexus.abstractions.socket_provider import (
35
37
  ExternalSocketProvider,
36
38
  )
@@ -72,18 +74,18 @@ class MetricsModule(Module):
72
74
 
73
75
 
74
76
  @final
75
- class LoggerFactoryModule(Module):
77
+ class BootstrapLoggerFactoryModule(Module):
76
78
  """
77
79
  Logger factory module.
78
80
  """
79
81
 
80
82
  @singleton
81
83
  @provider
82
- def provide(self) -> LoggerFactory:
84
+ def provide(self) -> BootstrapLoggerFactory:
83
85
  """
84
86
  DI factory method.
85
87
  """
86
- return LoggerFactory()
88
+ return BootstrapLoggerFactory()
87
89
 
88
90
 
89
91
  @final
@@ -234,6 +236,7 @@ class ServiceConfigurator:
234
236
 
235
237
  def __init__(self):
236
238
  self._injection_binds = [
239
+ BootstrapLoggerFactoryModule(),
237
240
  MetricsModule(),
238
241
  CrystalReceiverClientModule(),
239
242
  QueryEnabledStoreModule(),
@@ -1,21 +1,19 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: esd-services-api-client
3
- Version: 2.6.0
3
+ Version: 2.6.1a149.dev13
4
4
  Summary: Python clients for ESD services
5
5
  License: Apache 2.0
6
6
  Author: ECCO Sneaks & Data
7
7
  Author-email: esdsupport@ecco.com
8
8
  Maintainer: GZU
9
9
  Maintainer-email: gzu@ecco.com
10
- Requires-Python: >=3.9,<3.12
10
+ Requires-Python: >=3.11,<3.12
11
11
  Classifier: License :: Other/Proprietary License
12
12
  Classifier: Programming Language :: Python :: 3
13
- Classifier: Programming Language :: Python :: 3.9
14
- Classifier: Programming Language :: Python :: 3.10
15
13
  Classifier: Programming Language :: Python :: 3.11
16
14
  Provides-Extra: azure
17
15
  Provides-Extra: nexus
18
- Requires-Dist: adapta[azure,datadog,storage] (>=3.2,<4.0)
16
+ Requires-Dist: adapta[datadog,storage] (>=3.3,<4.0)
19
17
  Requires-Dist: azure-identity (>=1.7,<1.8) ; extra == "azure"
20
18
  Requires-Dist: dataclasses-json (>=0.6.0,<0.7.0)
21
19
  Requires-Dist: httpx (>=0.27.0,<0.28.0) ; extra == "nexus"
@@ -1,4 +1,5 @@
1
- esd_services_api_client/__init__.py,sha256=BBAYNMvljwWCMfrNwUz0XsUcC8i6XtJbMZus9tS5Fls,648
1
+ esd_services_api_client/__init__.py,sha256=4LskDwFuAFMOjHtN3_-71G_VZ4MNfjMJ7wX2cHYxV-0,648
2
+ esd_services_api_client/_version.py,sha256=zSy81LuxG0DWm6XYDC7hrCVd4Ot5x1i0YMn25b22RTk,33
2
3
  esd_services_api_client/beast/__init__.py,sha256=zNhXcHSP5w4P9quM1XP4oXVJEccvC_VScG41TZ0GzZ8,723
3
4
  esd_services_api_client/beast/v3/__init__.py,sha256=FtumtInoDyCCRE424Llqv8QZLRuwXzj-smyfu1od1nc,754
4
5
  esd_services_api_client/beast/v3/_connector.py,sha256=VqxiCzJWKERh42aZAIphzmOEG5cdOcKM0DQzG7eQ_-8,11479
@@ -7,19 +8,19 @@ esd_services_api_client/boxer/README.md,sha256=-MAhYUPvmEMcgx_lo_2PlH_gZI1lndGv8
7
8
  esd_services_api_client/boxer/__init__.py,sha256=yDEcfL-9q0H91UWr-al-1iMJtVLv-oIXky6hUmFZs0g,785
8
9
  esd_services_api_client/boxer/_auth.py,sha256=q9vU4agH-s6HPpVTEdkXFA_RvO92hogOfjurfjHBmBg,7445
9
10
  esd_services_api_client/boxer/_base.py,sha256=LMKDArELZlWVKJH10coUFSSN8MFH6KfHCBncEDjsiIU,981
10
- esd_services_api_client/boxer/_connector.py,sha256=gUplRNAxcOoZfMPee0ZUJmhbzYmreys9cMeIMHmIyKM,8677
11
+ esd_services_api_client/boxer/_connector.py,sha256=UsZnAUSXT9BeUAU8eoezEaADrqz8ab-jSp4MVa-e3eM,8716
11
12
  esd_services_api_client/boxer/_models.py,sha256=ursQIR_c9jcVfRKc0LH1OuVL2KFD6kqojyGBadRfRPI,1702
12
13
  esd_services_api_client/common/__init__.py,sha256=L-cEW1mVbnTJLCLG5V6Ucw7zBgx1zf0t1bYcQC1heyw,603
13
14
  esd_services_api_client/crystal/__init__.py,sha256=oeyJjdQ9EpTnIq6XnjPq5v0DWPdHqi4uEfRIcD1mNZA,792
14
15
  esd_services_api_client/crystal/_api_versions.py,sha256=GHbmV_5lP9fP72TZE0j_ZeQSeJjMRcRaBRxNJbz-MWQ,837
15
16
  esd_services_api_client/crystal/_connector.py,sha256=0CqBpPahozAnentM6ZZrH84s_D7SoWCShr5_cRQGMX8,13110
16
17
  esd_services_api_client/crystal/_models.py,sha256=j8SEbwp_iAKjn06i-0f8npmhQhs2YJlmQ3eHwc2TwQE,4366
17
- esd_services_api_client/nexus/README.md,sha256=2-o9TeOYsVlUm72LsI6HUVaVx6ndYblRVDE7ncosYNQ,10032
18
+ esd_services_api_client/nexus/README.md,sha256=hNFV_fzAGrG6_1yXE3o_GuLTKIWEia2FfedMh-Sg0ew,10619
18
19
  esd_services_api_client/nexus/__init__.py,sha256=sOgKKq3_LZGbLmQMtMS7lDw2hv027qownTmNIRV0BB8,627
19
20
  esd_services_api_client/nexus/abstractions/__init__.py,sha256=sOgKKq3_LZGbLmQMtMS7lDw2hv027qownTmNIRV0BB8,627
20
21
  esd_services_api_client/nexus/abstractions/algrorithm_cache.py,sha256=6GevJJ7mf1c_PImhKQ_4_6n652VyHlgK_12LNidirxs,3644
21
22
  esd_services_api_client/nexus/abstractions/input_object.py,sha256=RUKnhekuZwd_RVvnLGAxHa4wYDFJf6wEwWQI9f-o0lM,1761
22
- esd_services_api_client/nexus/abstractions/logger_factory.py,sha256=9biONvCqNrP__yrmeRkoDL05TMA5v-LyrcKwgiKG59U,2019
23
+ esd_services_api_client/nexus/abstractions/logger_factory.py,sha256=agkmveLTMwsDx5XdG9TNTEHaFi0leQF3MVSwOUn66os,3957
23
24
  esd_services_api_client/nexus/abstractions/nexus_object.py,sha256=rLE42imCVGE6Px4Yu6X6C4b69gA1grK-7Md_SuCLN2Q,3115
24
25
  esd_services_api_client/nexus/abstractions/socket_provider.py,sha256=Rwa_aPErI4Es5AdyCd3EoGze7mg2D70u8kuc2UGEBaI,1729
25
26
  esd_services_api_client/nexus/algorithms/__init__.py,sha256=v4rPDf36r6AaHi_3K8isBKYU_fG8ct3w14KpUg2XRgg,976
@@ -32,8 +33,8 @@ esd_services_api_client/nexus/algorithms/recursive.py,sha256=uaCCl4q-st_KqbcmkdO
32
33
  esd_services_api_client/nexus/configurations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
34
  esd_services_api_client/nexus/configurations/algorithm_configuration.py,sha256=eE7diX2PATCGkmqhvFOcZwXrr6vns4fqnJGmgNvhhZM,1091
34
35
  esd_services_api_client/nexus/core/__init__.py,sha256=sOgKKq3_LZGbLmQMtMS7lDw2hv027qownTmNIRV0BB8,627
35
- esd_services_api_client/nexus/core/app_core.py,sha256=JlVn8kkw26blK1FtqNtwNxkjKv9XLCFYAPAHUOzW5Mc,12986
36
- esd_services_api_client/nexus/core/app_dependencies.py,sha256=tA18Smbiv1HjgXnpCHnigrR2mTmIeZawPKxMH0pezIU,8615
36
+ esd_services_api_client/nexus/core/app_core.py,sha256=y1yDJ-l366pmn_L0zqMgfM37hw82Z6xe5g9-MTYayvM,15028
37
+ esd_services_api_client/nexus/core/app_dependencies.py,sha256=1m7Oc9JCXyrU0qVZekYhpbXLQo20aQPQ-Bf8zEdjtqI,8704
37
38
  esd_services_api_client/nexus/core/serializers.py,sha256=Vk9FaEeDHXx3S7rPlYoWzsOcN6gzLzemsrjq6ytfaI0,2217
38
39
  esd_services_api_client/nexus/exceptions/__init__.py,sha256=feN33VdqB5-2bD9aJesJl_OlsKrNNo3hZCnQgKuaU9k,696
39
40
  esd_services_api_client/nexus/exceptions/_nexus_error.py,sha256=QvtY38mNoIA6t26dUN6UIsaPfljhtVNsbQVS7ksMb-Q,895
@@ -50,7 +51,7 @@ esd_services_api_client/nexus/modules/mlflow_module.py,sha256=d4y8XetGF37md4dEpE
50
51
  esd_services_api_client/nexus/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
52
  esd_services_api_client/nexus/telemetry/recorder.py,sha256=-shxk2vYDTafJ0U3CzkHxIHBQtxVJ1ZNI4ST0p1YV2g,4801
52
53
  esd_services_api_client/nexus/telemetry/user_telemetry_recorder.py,sha256=NgnjSY64nEx1kslkdomENC6vMqkEOpfXggJXwm-NyFs,5163
53
- esd_services_api_client-2.6.0.dist-info/LICENSE,sha256=0gS6zXsPp8qZhzi1xaGCIYPzb_0e8on7HCeFJe8fOpw,10693
54
- esd_services_api_client-2.6.0.dist-info/METADATA,sha256=9vAceRIDIKylVuNKpmSDzMmovD3cf6QoNoh4Z627sGw,1164
55
- esd_services_api_client-2.6.0.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
56
- esd_services_api_client-2.6.0.dist-info/RECORD,,
54
+ esd_services_api_client-2.6.1a149.dev13.dist-info/LICENSE,sha256=0gS6zXsPp8qZhzi1xaGCIYPzb_0e8on7HCeFJe8fOpw,10693
55
+ esd_services_api_client-2.6.1a149.dev13.dist-info/METADATA,sha256=8KhP0SVOEq0_CsZyjsuKqvV8kqv9pP08UNHiSXoVOZM,1068
56
+ esd_services_api_client-2.6.1a149.dev13.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
57
+ esd_services_api_client-2.6.1a149.dev13.dist-info/RECORD,,