esd-services-api-client 2.6.0__py3-none-any.whl → 2.6.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.
@@ -17,4 +17,4 @@
17
17
  Root index.
18
18
  """
19
19
 
20
- __version__ = "2.6.0"
20
+ __version__ = "2.6.1"
@@ -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,61 @@ 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
+
40
+ @final
41
+ class BootstrapLoggerFactory:
42
+ """
43
+ Bootstrap logger provisioner.
44
+ Bootstrap loggers do not use enriched properties since they are initialized before payload is deserialized.
45
+ """
46
+
47
+ def __init__(self):
48
+ self._log_handlers = [
49
+ StreamHandler(),
50
+ ]
51
+ if "NEXUS__DATADOG_LOGGER_CONFIGURATION" in os.environ:
52
+ self._log_handlers.append(
53
+ DataDogApiHandler(
54
+ **json.loads(os.getenv("NEXUS__DATADOG_LOGGER_CONFIGURATION"))
55
+ )
56
+ )
57
+
58
+ def create_logger(self, request_id: str, algorithm_name: str) -> LoggerInterface:
59
+ """
60
+ Creates an async-safe logger for the provided class name.
61
+ """
62
+ return create_async_logger(
63
+ logger_type=BootstrapLogger.__class__,
64
+ log_handlers=self._log_handlers,
65
+ min_log_level=LogLevel(os.getenv("NEXUS__LOG_LEVEL", "INFO")),
66
+ global_tags={
67
+ "request_id": request_id,
68
+ "algorithm": algorithm_name,
69
+ },
70
+ )
71
+
72
+
32
73
  @final
33
74
  class LoggerFactory:
34
75
  """
35
76
  Async logger provisioner.
36
77
  """
37
78
 
38
- def __init__(self):
79
+ def __init__(
80
+ self,
81
+ fixed_template: dict[str, dict[str, str]] | None = None,
82
+ fixed_template_delimiter: str = None,
83
+ global_tags: dict[str, str] | None = None,
84
+ ):
85
+ self._global_tags = global_tags
86
+ self._fixed_template = fixed_template
87
+ self._fixed_template_delimiter = fixed_template_delimiter or ", "
39
88
  self._log_handlers = [
40
89
  StreamHandler(),
41
90
  ]
@@ -45,12 +94,20 @@ class LoggerFactory:
45
94
  **json.loads(os.getenv("NEXUS__DATADOG_LOGGER_CONFIGURATION"))
46
95
  )
47
96
  )
97
+ if "NEXUS__LOGGER_FIXED_TEMPLATE" in os.environ:
98
+ self._fixed_template = self._fixed_template | json.loads(
99
+ os.getenv("NEXUS__LOGGER_FIXED_TEMPLATE")
100
+ )
101
+
102
+ if "NEXUS__LOGGER_FIXED_TEMPLATE_DELIMITER" in os.environ:
103
+ self._fixed_template_delimiter = (
104
+ self._fixed_template_delimiter
105
+ or os.getenv("NEXUS__LOGGER_FIXED_TEMPLATE")
106
+ )
48
107
 
49
108
  def create_logger(
50
109
  self,
51
- logger_type: Type[TLogger],
52
- fixed_template: Optional[Dict[str, Dict[str, str]]] = None,
53
- fixed_template_delimiter=", ",
110
+ logger_type: type[TLogger],
54
111
  ) -> LoggerInterface:
55
112
  """
56
113
  Creates an async-safe logger for the provided class name.
@@ -59,6 +116,7 @@ class LoggerFactory:
59
116
  logger_type=logger_type,
60
117
  log_handlers=self._log_handlers,
61
118
  min_log_level=LogLevel(os.getenv("NEXUS__LOG_LEVEL", "INFO")),
62
- fixed_template=fixed_template,
63
- fixed_template_delimiter=fixed_template_delimiter,
119
+ fixed_template=self._fixed_template,
120
+ fixed_template_delimiter=self._fixed_template_delimiter,
121
+ global_tags=self._global_tags,
64
122
  )
@@ -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,36 @@ 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
+ """
219
+ Adds a log `tagger` and a log `enricher` to be used with injected logger.
220
+ A log `tagger` will add key-value tags to each emitted log message, and those tags can be inferred from the payload and entrypoint arguments.
221
+ A log `enricher` will add additional static templated content to log messages, and render those templates using payload properties entrypoint argyments.
222
+ """
223
+ self._log_tagger = tagger
224
+ self._log_enricher = enricher
225
+ self._log_enrichment_delimiter = delimiter
226
+ return self
227
+
180
228
  def with_module(self, module: Type[Module]) -> "Nexus":
181
229
  """
182
230
  Adds a (custom) DI module into the DI container.
@@ -260,11 +308,14 @@ class Nexus:
260
308
 
261
309
  self._injector = Injector(self._configurator.injection_binds)
262
310
 
263
- root_logger: LoggerInterface = self._injector.get(LoggerFactory).create_logger(
264
- logger_type=self.__class__,
311
+ bootstrap_logger: LoggerInterface = self._injector.get(
312
+ BootstrapLoggerFactory
313
+ ).create_logger(
314
+ request_id=self._run_args.request_id,
315
+ algorithm_name=os.getenv("CRYSTAL__ALGORITHM_NAME"),
265
316
  )
266
317
 
267
- root_logger.start()
318
+ bootstrap_logger.start()
268
319
 
269
320
  for payload_type in self._payload_types:
270
321
  try:
@@ -272,10 +323,31 @@ class Nexus:
272
323
  self._injector.binder.bind(
273
324
  payload.__class__, to=payload, scope=singleton
274
325
  )
326
+ logger_factory = LoggerFactory(
327
+ fixed_template=None
328
+ if not self._log_enricher
329
+ else self._log_enricher(payload, self._run_args),
330
+ fixed_template_delimiter=self._log_enrichment_delimiter,
331
+ global_tags=self._log_tagger(payload, self._run_args),
332
+ )
333
+ # bind app-level LoggerFactory now
334
+ self._injector.binder.bind(
335
+ logger_factory.__class__,
336
+ to=logger_factory,
337
+ scope=singleton,
338
+ )
275
339
  except BaseException as ex: # pylint: disable=broad-except
276
- root_logger.error("Error reading algorithm payload", ex)
340
+ bootstrap_logger.error("Error reading algorithm payload", ex)
277
341
  sys.exit(1)
278
342
 
343
+ bootstrap_logger.stop()
344
+
345
+ root_logger: LoggerInterface = self._injector.get(LoggerFactory).create_logger(
346
+ logger_type=self.__class__,
347
+ )
348
+
349
+ root_logger.start()
350
+
279
351
  algorithm: BaselineAlgorithm = self._injector.get(self._algorithm_class)
280
352
  telemetry_recorder: TelemetryRecorder = self._injector.get(TelemetryRecorder)
281
353
 
@@ -300,7 +372,7 @@ class Nexus:
300
372
  root_logger.error(
301
373
  "Algorithm {algorithm} run failed on Nexus version {version}",
302
374
  ex,
303
- algorithm=self._algorithm_class,
375
+ algorithm=algorithm.__class__.__name__,
304
376
  version=__version__,
305
377
  )
306
378
 
@@ -311,8 +383,8 @@ class Nexus:
311
383
 
312
384
  # record telemetry
313
385
  root_logger.info(
314
- "Recording telemetry for the run {request_id}",
315
- request_id=self._run_args.request_id,
386
+ "Recording telemetry for the run {run_id}",
387
+ run_id=self._run_args.request_id,
316
388
  )
317
389
  async with telemetry_recorder as recorder:
318
390
  await recorder.record(
@@ -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.1
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,4 @@
1
- esd_services_api_client/__init__.py,sha256=BBAYNMvljwWCMfrNwUz0XsUcC8i6XtJbMZus9tS5Fls,648
1
+ esd_services_api_client/__init__.py,sha256=FJBUxZX7h17fr_sUs01bd-fSxy6eQ6zLaqD-pb5bvRo,648
2
2
  esd_services_api_client/beast/__init__.py,sha256=zNhXcHSP5w4P9quM1XP4oXVJEccvC_VScG41TZ0GzZ8,723
3
3
  esd_services_api_client/beast/v3/__init__.py,sha256=FtumtInoDyCCRE424Llqv8QZLRuwXzj-smyfu1od1nc,754
4
4
  esd_services_api_client/beast/v3/_connector.py,sha256=VqxiCzJWKERh42aZAIphzmOEG5cdOcKM0DQzG7eQ_-8,11479
@@ -7,19 +7,19 @@ esd_services_api_client/boxer/README.md,sha256=-MAhYUPvmEMcgx_lo_2PlH_gZI1lndGv8
7
7
  esd_services_api_client/boxer/__init__.py,sha256=yDEcfL-9q0H91UWr-al-1iMJtVLv-oIXky6hUmFZs0g,785
8
8
  esd_services_api_client/boxer/_auth.py,sha256=q9vU4agH-s6HPpVTEdkXFA_RvO92hogOfjurfjHBmBg,7445
9
9
  esd_services_api_client/boxer/_base.py,sha256=LMKDArELZlWVKJH10coUFSSN8MFH6KfHCBncEDjsiIU,981
10
- esd_services_api_client/boxer/_connector.py,sha256=gUplRNAxcOoZfMPee0ZUJmhbzYmreys9cMeIMHmIyKM,8677
10
+ esd_services_api_client/boxer/_connector.py,sha256=UsZnAUSXT9BeUAU8eoezEaADrqz8ab-jSp4MVa-e3eM,8716
11
11
  esd_services_api_client/boxer/_models.py,sha256=ursQIR_c9jcVfRKc0LH1OuVL2KFD6kqojyGBadRfRPI,1702
12
12
  esd_services_api_client/common/__init__.py,sha256=L-cEW1mVbnTJLCLG5V6Ucw7zBgx1zf0t1bYcQC1heyw,603
13
13
  esd_services_api_client/crystal/__init__.py,sha256=oeyJjdQ9EpTnIq6XnjPq5v0DWPdHqi4uEfRIcD1mNZA,792
14
14
  esd_services_api_client/crystal/_api_versions.py,sha256=GHbmV_5lP9fP72TZE0j_ZeQSeJjMRcRaBRxNJbz-MWQ,837
15
15
  esd_services_api_client/crystal/_connector.py,sha256=0CqBpPahozAnentM6ZZrH84s_D7SoWCShr5_cRQGMX8,13110
16
16
  esd_services_api_client/crystal/_models.py,sha256=j8SEbwp_iAKjn06i-0f8npmhQhs2YJlmQ3eHwc2TwQE,4366
17
- esd_services_api_client/nexus/README.md,sha256=2-o9TeOYsVlUm72LsI6HUVaVx6ndYblRVDE7ncosYNQ,10032
17
+ esd_services_api_client/nexus/README.md,sha256=hNFV_fzAGrG6_1yXE3o_GuLTKIWEia2FfedMh-Sg0ew,10619
18
18
  esd_services_api_client/nexus/__init__.py,sha256=sOgKKq3_LZGbLmQMtMS7lDw2hv027qownTmNIRV0BB8,627
19
19
  esd_services_api_client/nexus/abstractions/__init__.py,sha256=sOgKKq3_LZGbLmQMtMS7lDw2hv027qownTmNIRV0BB8,627
20
20
  esd_services_api_client/nexus/abstractions/algrorithm_cache.py,sha256=6GevJJ7mf1c_PImhKQ_4_6n652VyHlgK_12LNidirxs,3644
21
21
  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
22
+ esd_services_api_client/nexus/abstractions/logger_factory.py,sha256=oJStrOPir8J18E3ALhUTCQrj3rbjo2yuYrnjpDwLQg8,3947
23
23
  esd_services_api_client/nexus/abstractions/nexus_object.py,sha256=rLE42imCVGE6Px4Yu6X6C4b69gA1grK-7Md_SuCLN2Q,3115
24
24
  esd_services_api_client/nexus/abstractions/socket_provider.py,sha256=Rwa_aPErI4Es5AdyCd3EoGze7mg2D70u8kuc2UGEBaI,1729
25
25
  esd_services_api_client/nexus/algorithms/__init__.py,sha256=v4rPDf36r6AaHi_3K8isBKYU_fG8ct3w14KpUg2XRgg,976
@@ -32,8 +32,8 @@ esd_services_api_client/nexus/algorithms/recursive.py,sha256=uaCCl4q-st_KqbcmkdO
32
32
  esd_services_api_client/nexus/configurations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  esd_services_api_client/nexus/configurations/algorithm_configuration.py,sha256=eE7diX2PATCGkmqhvFOcZwXrr6vns4fqnJGmgNvhhZM,1091
34
34
  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
35
+ esd_services_api_client/nexus/core/app_core.py,sha256=Lmkpq7Hy74EfMOi5vtjD82PpY7Lb84RVbXN5b82BxKs,15462
36
+ esd_services_api_client/nexus/core/app_dependencies.py,sha256=1m7Oc9JCXyrU0qVZekYhpbXLQo20aQPQ-Bf8zEdjtqI,8704
37
37
  esd_services_api_client/nexus/core/serializers.py,sha256=Vk9FaEeDHXx3S7rPlYoWzsOcN6gzLzemsrjq6ytfaI0,2217
38
38
  esd_services_api_client/nexus/exceptions/__init__.py,sha256=feN33VdqB5-2bD9aJesJl_OlsKrNNo3hZCnQgKuaU9k,696
39
39
  esd_services_api_client/nexus/exceptions/_nexus_error.py,sha256=QvtY38mNoIA6t26dUN6UIsaPfljhtVNsbQVS7ksMb-Q,895
@@ -50,7 +50,7 @@ esd_services_api_client/nexus/modules/mlflow_module.py,sha256=d4y8XetGF37md4dEpE
50
50
  esd_services_api_client/nexus/telemetry/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
51
51
  esd_services_api_client/nexus/telemetry/recorder.py,sha256=-shxk2vYDTafJ0U3CzkHxIHBQtxVJ1ZNI4ST0p1YV2g,4801
52
52
  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,,
53
+ esd_services_api_client-2.6.1.dist-info/LICENSE,sha256=0gS6zXsPp8qZhzi1xaGCIYPzb_0e8on7HCeFJe8fOpw,10693
54
+ esd_services_api_client-2.6.1.dist-info/METADATA,sha256=Y893K15f-VzKzAKxxs66BGwf8sCkGvMHe0hm_T3VL6Y,1058
55
+ esd_services_api_client-2.6.1.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
56
+ esd_services_api_client-2.6.1.dist-info/RECORD,,