maleo-foundation 0.1.32__py3-none-any.whl → 0.1.34__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.
@@ -1,17 +1,35 @@
1
1
  from typing import Optional
2
+ from maleo_foundation.enums import BaseEnums
3
+ from maleo_foundation.types import BaseTypes
2
4
  from maleo_foundation.utils.logging import GoogleCloudLogging, ClientLogger
3
5
 
4
6
  class ClientManager:
5
- def __init__(self, key:str, name:str, logs_dir:str, google_cloud_logging:Optional[GoogleCloudLogging] = None) -> None:
7
+ def __init__(
8
+ self,
9
+ key:str,
10
+ name:str,
11
+ logs_dir:str,
12
+ service_key:BaseTypes.OptionalString = None,
13
+ level:BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
14
+ google_cloud_logging:Optional[GoogleCloudLogging] = None
15
+ ) -> None:
6
16
  self._key = key
7
17
  self._name = name
8
18
  self._logs_dir = logs_dir
19
+ self._service_key = service_key
20
+ self._level = level
9
21
  self._google_cloud_logging = google_cloud_logging
10
22
  self._initialize_logger()
11
23
  self._logger.info("Initializing client manager")
12
24
 
13
25
  def _initialize_logger(self) -> None:
14
- self._logger = ClientLogger(logs_dir=self._logs_dir, client_key=self._key, google_cloud_logging=self._google_cloud_logging)
26
+ self._logger = ClientLogger(
27
+ logs_dir=self._logs_dir,
28
+ client_key=self._key,
29
+ service_key=self._service_key,
30
+ level=self._level,
31
+ google_cloud_logging=self._google_cloud_logging
32
+ )
15
33
 
16
34
  @property
17
35
  def key(self) -> str:
@@ -1,11 +1,24 @@
1
1
  import os
2
2
  from google.auth import default
3
3
  from google.oauth2 import service_account
4
+ from typing import Optional
5
+ from maleo_foundation.enums import BaseEnums
6
+ from maleo_foundation.types import BaseTypes
4
7
  from maleo_foundation.managers.client.base import ClientManager
8
+ from maleo_foundation.utils.logging import GoogleCloudLogging
5
9
 
6
10
  class GoogleClientManager(ClientManager):
7
- def __init__(self, key, name, logs_dir, google_cloud_logging=None, credentials_path=None) -> None:
8
- super().__init__(key, name, logs_dir, google_cloud_logging)
11
+ def __init__(
12
+ self,
13
+ key:str,
14
+ name:str,
15
+ logs_dir:str,
16
+ service_key:BaseTypes.OptionalString=None,
17
+ level:BaseEnums.LoggerLevel=BaseEnums.LoggerLevel.INFO,
18
+ google_cloud_logging:Optional[GoogleCloudLogging]=None,
19
+ credentials_path:BaseTypes.OptionalString=None
20
+ ) -> None:
21
+ super().__init__(key, name, logs_dir, service_key, level, google_cloud_logging)
9
22
  credentials_path = credentials_path or os.getenv("GOOGLE_CREDENTIALS_PATH")
10
23
  try:
11
24
  if credentials_path is not None:
@@ -2,16 +2,28 @@ from google.api_core import retry
2
2
  from google.api_core.exceptions import NotFound
3
3
  from google.cloud import secretmanager
4
4
  from typing import Optional
5
+ from maleo_foundation.enums import BaseEnums
6
+ from maleo_foundation.types import BaseTypes
7
+ from maleo_foundation.utils.logging import GoogleCloudLogging
5
8
  from .base import GoogleClientManager
6
9
 
7
10
  class GoogleSecretManager(GoogleClientManager):
8
- def __init__(self, logs_dir, google_cloud_logging=None, credentials_path=None) -> None:
11
+ def __init__(
12
+ self,
13
+ logs_dir:str,
14
+ service_key:BaseTypes.OptionalString = None,
15
+ level:BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
16
+ google_cloud_logging:Optional[GoogleCloudLogging] = None,
17
+ credentials_path:BaseTypes.OptionalString=None
18
+ ) -> None:
9
19
  self._key = "google-secret-manager"
10
20
  self._name = "GoogleSecretManager"
11
21
  super().__init__(
12
22
  key=self._key,
13
23
  name=self._name,
14
24
  logs_dir=logs_dir,
25
+ service_key=service_key,
26
+ level=level,
15
27
  google_cloud_logging=google_cloud_logging,
16
28
  credentials_path=credentials_path
17
29
  )
@@ -1,17 +1,30 @@
1
1
  import os
2
2
  from datetime import timedelta
3
3
  from google.cloud.storage import Bucket, Client
4
+ from typing import Optional
5
+ from maleo_foundation.enums import BaseEnums
4
6
  from maleo_foundation.types import BaseTypes
7
+ from maleo_foundation.utils.logging import GoogleCloudLogging
5
8
  from .base import GoogleClientManager
6
9
 
7
10
  class GoogleCloudStorage(GoogleClientManager):
8
- def __init__(self, logs_dir, google_cloud_logging=None, credentials_path=None, bucket_name:BaseTypes.OptionalString=None) -> None:
11
+ def __init__(
12
+ self,
13
+ logs_dir:str,
14
+ service_key:BaseTypes.OptionalString = None,
15
+ level:BaseEnums.LoggerLevel = BaseEnums.LoggerLevel.INFO,
16
+ google_cloud_logging:Optional[GoogleCloudLogging] = None,
17
+ credentials_path:BaseTypes.OptionalString=None,
18
+ bucket_name:BaseTypes.OptionalString=None
19
+ ) -> None:
9
20
  self._key = "google-cloud-storage"
10
21
  self._name = "GoogleCloudStorage"
11
22
  super().__init__(
12
23
  key=self._key,
13
24
  name=self._name,
14
25
  logs_dir=logs_dir,
26
+ service_key=service_key,
27
+ level=level,
15
28
  google_cloud_logging=google_cloud_logging,
16
29
  credentials_path=credentials_path
17
30
  )
@@ -1,9 +1,10 @@
1
1
  import httpx
2
2
  from contextlib import asynccontextmanager
3
3
  from pydantic import BaseModel, Field
4
- from typing import AsyncGenerator
4
+ from typing import AsyncGenerator, Optional
5
+ from maleo_foundation.enums import BaseEnums
5
6
  from maleo_foundation.types import BaseTypes
6
- from maleo_foundation.utils.logging import ClientLogger
7
+ from maleo_foundation.utils.logging import GoogleCloudLogging, ClientLogger
7
8
  from maleo_foundation.managers.client.base import ClientManager
8
9
 
9
10
  class URL(BaseModel):
@@ -88,8 +89,17 @@ class ClientServices(BaseModel):
88
89
  arbitrary_types_allowed=True
89
90
 
90
91
  class MaleoClientManager(ClientManager):
91
- def __init__(self, key, name, logs_dir, google_cloud_logging = None, url:BaseTypes.OptionalString = None):
92
- super().__init__(key, name, logs_dir, google_cloud_logging)
92
+ def __init__(
93
+ self,
94
+ key:str,
95
+ name:str,
96
+ logs_dir:str,
97
+ service_key:BaseTypes.OptionalString=None,
98
+ level:BaseEnums.LoggerLevel=BaseEnums.LoggerLevel.INFO,
99
+ google_cloud_logging:Optional[GoogleCloudLogging]=None,
100
+ url:BaseTypes.OptionalString = None
101
+ ):
102
+ super().__init__(key, name, logs_dir, service_key, level, google_cloud_logging)
93
103
  self._url = url
94
104
 
95
105
  def _initialize_controllers(self) -> None:
@@ -14,7 +14,6 @@ from maleo_foundation.models.transfers.general.token import BaseTokenGeneralTran
14
14
  from maleo_foundation.models.transfers.parameters.token import BaseTokenParametersTransfers
15
15
  from maleo_foundation.managers.client.google.secret import GoogleSecretManager
16
16
  from maleo_foundation.managers.client.google.storage import GoogleCloudStorage
17
- from maleo_foundation.managers.client.http import HTTPClientManager
18
17
  from maleo_foundation.managers.client.maleo import MaleoClientManager
19
18
  from maleo_foundation.managers.db import DatabaseManager
20
19
  from maleo_foundation.managers.middleware import MiddlewareConfigurations, MiddlewareLoggers, MiddlewareManager
@@ -40,7 +39,6 @@ class Settings(BaseSettings):
40
39
  PUBLIC_KEY_PATH:str = Field("/keys/maleo-public-key.pem", description="Maleo's public key path")
41
40
  KEY_PASSWORD:str = Field(..., description="Maleo key's password")
42
41
  CONFIGURATIONS_PATH:str = Field(..., description="Service's configuration file path")
43
- GCS_BUCKET_NAME:str = Field(..., description="Google cloud storage (GCS)'s bucket name")
44
42
 
45
43
  class Keys(BaseModel):
46
44
  password:str = Field(..., description="Key's password")
@@ -90,6 +88,12 @@ class DatabaseConfigurations(BaseModel):
90
88
  def url(self) -> str:
91
89
  return f"postgresql://{self.username}:{self.password}@{self.host}:{self.port}/{self.database}"
92
90
 
91
+ class GoogleCloudStorageConfigurations(BaseModel):
92
+ bucket_name:str = Field(..., description="Bucket's name")
93
+
94
+ class GoogleClientConfigurations(BaseModel):
95
+ storage:GoogleCloudStorageConfigurations = Field(..., description="Google cloud storage client configurations")
96
+
93
97
  class MaleoClientConfiguration(BaseModel):
94
98
  key:str = Field(..., description="Client's key")
95
99
  name:str = Field(..., description="Client's name")
@@ -113,8 +117,12 @@ class MaleoClientConfigurations(BaseModel):
113
117
  arbitrary_types_allowed=True
114
118
 
115
119
  class ClientConfigurations(BaseModel):
120
+ google:GoogleClientConfigurations = Field(..., description="Google client's configurations")
116
121
  maleo:MaleoClientConfigurations = Field(..., description="Maleo client's configurations")
117
122
 
123
+ class Config:
124
+ arbitrary_types_allowed=True
125
+
118
126
  class Configurations(BaseModel):
119
127
  service:ServiceConfigurations = Field(..., description="Service's configurations")
120
128
  middleware:MiddlewareConfigurations = Field(..., description="Middleware's configurations")
@@ -175,7 +183,6 @@ class MaleoClientManagers(BaseModel):
175
183
 
176
184
  class ClientManagers(BaseModel):
177
185
  google:GoogleClientManagers = Field(..., description="Google client's managers")
178
- http:Type[HTTPClientManager] = Field(..., description="HTTP client's manager")
179
186
  maleo:MaleoClientManagers = Field(..., description="Maleo client's managers")
180
187
 
181
188
  class Config:
@@ -314,30 +321,36 @@ class ServiceManager:
314
321
 
315
322
  def _initialize_clients(self) -> None:
316
323
  #* Initialize google clients
317
- secret = GoogleSecretManager(**self._log_config.model_dump())
318
- storage = GoogleCloudStorage(**self._log_config.model_dump())
324
+ secret = GoogleSecretManager(
325
+ **self._log_config.model_dump(),
326
+ service_key=self._configs.service.key,
327
+ credentials_path=self._settings.GOOGLE_CREDENTIALS_PATH
328
+ )
329
+ storage = GoogleCloudStorage(
330
+ **self._log_config.model_dump(),
331
+ service_key=self._configs.service.key,
332
+ credentials_path=self._settings.GOOGLE_CREDENTIALS_PATH,
333
+ bucket_name=self._configs.client.google.storage.bucket_name
334
+ )
319
335
  self._google_clients = GoogleClientManagers(secret=secret, storage=storage)
320
- #* Initialize http clients
321
- self._http_client = HTTPClientManager
322
- self._http_client.initialize()
323
336
  #* Initialize maleo clients
324
337
  self._maleo_clients = MaleoClientManagers()
325
338
  for client in self._maleo_client_manager_classes.__class__.__annotations__:
326
339
  client_cls = getattr(self._maleo_client_manager_classes, client)
327
340
  if client_cls is not None and issubclass(client_cls, MaleoClientManager):
328
341
  cfg:MaleoClientConfiguration = getattr(self._configs.client.maleo, client)
329
- client_instance = client_cls(**cfg.model_dump(), **self._log_config.model_dump())
342
+ client_instance = client_cls(
343
+ **cfg.model_dump(),
344
+ **self._log_config.model_dump(),
345
+ service_key=self._configs.service.key
346
+ )
330
347
  setattr(self._maleo_clients, client, client_instance)
331
- self._clients = ClientManagers(google=self._google_clients, http=self._http_client, maleo=self._maleo_clients)
348
+ self._clients = ClientManagers(google=self._google_clients, maleo=self._maleo_clients)
332
349
 
333
350
  @property
334
351
  def google_clients(self) -> GoogleClientManagers:
335
352
  return self._google_clients
336
353
 
337
- @property
338
- def http_client(self) -> Type[HTTPClientManager]:
339
- return self._http_client
340
-
341
354
  @property
342
355
  def maleo_clients(self) -> MaleoClientManagers:
343
356
  return self._maleo_clients
@@ -400,7 +413,6 @@ class ServiceManager:
400
413
  if self._clients is not None:
401
414
  self._clients.google.storage.dispose()
402
415
  self._clients.google.secret.dispose()
403
- await self._clients.http.dispose()
404
416
  for client in self._maleo_clients.__class__.__annotations__:
405
417
  client_instance = getattr(self._maleo_clients, client)
406
418
  if client_instance is not None and isinstance(client_instance, MaleoClientManager):
@@ -18,22 +18,22 @@ class Backend(AuthenticationBackend):
18
18
  async def authenticate(self, conn:HTTPConnection) -> Tuple[Credentials, User]:
19
19
  client_ip = extract_client_ip(conn)
20
20
  if "Authorization" not in conn.headers:
21
- self._logger.info(f"Authentication - Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Header did not contain authorization")
21
+ self._logger.info(f"Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Header did not contain authorization")
22
22
  return Credentials(), User(authenticated=False)
23
23
 
24
24
  auth = conn.headers["Authorization"]
25
25
  scheme, token = auth.split()
26
26
  if scheme != 'Bearer':
27
- self._logger.info(f"Authentication - Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Authorization scheme is not Bearer")
27
+ self._logger.info(f"Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Authorization scheme is not Bearer")
28
28
  return Credentials(), User(authenticated=False)
29
29
 
30
30
  decode_token_parameters = BaseTokenParametersTransfers.Decode(key=self._key, token=token)
31
31
  decode_token_result = BaseTokenService.decode(parameters=decode_token_parameters)
32
32
  if not decode_token_result.success:
33
- self._logger.error(f"Authentication - Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Failed decoding authorization token")
33
+ self._logger.error(f"Request | IP: {client_ip} | URL: {conn.url.path} - Result | General: Failed decoding authorization token")
34
34
  return Credentials(token=token), User(authenticated=False)
35
35
 
36
- self._logger.info(f"Authentication - Request | IP: {client_ip} | URL: {conn.url.path} - Result | Username: {decode_token_result.data.u_u} | Email: {decode_token_result.data.u_e}")
36
+ self._logger.info(f"Request | IP: {client_ip} | URL: {conn.url.path} - Result | Username: {decode_token_result.data.u_u} | Email: {decode_token_result.data.u_e}")
37
37
  return Credentials(token=token), User(authenticated=True, username=decode_token_result.data.u_u, email=decode_token_result.data.u_e)
38
38
 
39
39
  def add_authentication_middleware(app:FastAPI, logger:MiddlewareLogger, key:str) -> None:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: maleo_foundation
3
- Version: 0.1.32
3
+ Version: 0.1.34
4
4
  Summary: Foundation package for Maleo
5
5
  Author-email: Agra Bima Yuda <agra@nexmedis.com>
6
6
  License: MIT
@@ -32,17 +32,16 @@ maleo_foundation/expanded_types/token.py,sha256=4fRTJw6W5MYq71NksNrWNi7qYHQ4_lQw
32
32
  maleo_foundation/managers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
33
33
  maleo_foundation/managers/db.py,sha256=Jf0w-9JOAG5X2quDxqqw6d2WUIX7MYGdlPGxWP0rxHs,4699
34
34
  maleo_foundation/managers/middleware.py,sha256=7CDXPMb28AR7J72TWOeKFxOlMypKezEtO9mr53a88B0,4032
35
- maleo_foundation/managers/service.py,sha256=FVsMitDat1UYIsEz0S1HGHQhoP9Oj4AlcjIF1zyKmjM,20026
35
+ maleo_foundation/managers/service.py,sha256=un1rJA9YdFK-8QRDQPurBIsHtRlmaVd2Z0-Lzr2o7_c,20392
36
36
  maleo_foundation/managers/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
- maleo_foundation/managers/client/base.py,sha256=K8AFV2MTZrC1jbTqxkx7eedmxodJirNIjoRXGGcppy4,1022
38
- maleo_foundation/managers/client/http.py,sha256=dWFZlG1z4TERYBITReR5oSrlzvdhh2EtztVnsU8gCeA,2712
39
- maleo_foundation/managers/client/maleo.py,sha256=f0_znaUjW4m-I55BPi6rtQNphFRVyybq774b7q59TUw,3951
37
+ maleo_foundation/managers/client/base.py,sha256=lYREmEoTLShlPPXOKKiAopjefJ8nIWHCi7IvWkXXKeY,1465
38
+ maleo_foundation/managers/client/maleo.py,sha256=NibYGdiN3EXUw5nx-tL48QAZym14GcA4BDZihGJNQ-g,4254
40
39
  maleo_foundation/managers/client/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
41
- maleo_foundation/managers/client/google/base.py,sha256=1lrGoyGnYW5Xq05bVXbKqnsqqmFPnsqZCBPK5-DINDA,1037
42
- maleo_foundation/managers/client/google/secret.py,sha256=bWR_3Xl_wUFqj4M48w8ZE692U7PQKX9ap0ndDJviV80,3074
43
- maleo_foundation/managers/client/google/storage.py,sha256=041iSSaFubnZrP-pS8MiLVtOgeLQ591Iz64JekZ5K7o,2350
40
+ maleo_foundation/managers/client/google/base.py,sha256=7jCnzkBN-F8xJEfP5eopuyfRBNyOdzImVgBTsQ5CGZ4,1472
41
+ maleo_foundation/managers/client/google/secret.py,sha256=RhWxjh_NeUZPV3iQDQBGrIUN85G5wgLahAuLRAefQ9M,3505
42
+ maleo_foundation/managers/client/google/storage.py,sha256=sWxyyUVcInIedxIgcG0orLwO1ziSyRmVht1IHSgu9UM,2772
44
43
  maleo_foundation/middlewares/__init__.py,sha256=bqE2EIFC3rWcR2AwFPR0fk2kSFfeTRzgA24GbnuT5RA,3697
45
- maleo_foundation/middlewares/authentication.py,sha256=y67sWz6FLl6NAtUWyK4mxC661dsBeKsDYRH3o7L_ZFQ,3188
44
+ maleo_foundation/middlewares/authentication.py,sha256=j1IcNnsfVVgDL4eVPP3LARjh5rKHm2KUEfS3E3j7snc,3120
46
45
  maleo_foundation/middlewares/base.py,sha256=3OaB5F57F3epKNieoAgarYM6PimoUdUl3DgpadtVuqs,11743
47
46
  maleo_foundation/middlewares/cors.py,sha256=9uvBvY2N6Vxa9RP_YtESxcWo6Doi6uS0lzAG9iLY7Uc,2288
48
47
  maleo_foundation/models/__init__.py,sha256=AaKehO7c1HyKhoTGRmNHDddSeBXkW-_YNrpOGBu8Ms8,246
@@ -83,7 +82,7 @@ maleo_foundation/utils/logging.py,sha256=MwvZmZSA8SIdfq-knEvpYIgqnSpHcyHrZY9TVHW
83
82
  maleo_foundation/utils/query.py,sha256=ODQ3adOYQNj5E2cRW9ytbjBz56nEDcnfq8mQ6YZbCCM,4375
84
83
  maleo_foundation/utils/formatter/__init__.py,sha256=iKf5YCbEdg1qKnFHyKqqcQbqAqEeRUf8mhI3v3dQoj8,78
85
84
  maleo_foundation/utils/formatter/case.py,sha256=TmvvlfzGdC_omMTB5vAa40TZBxQ3hnr-SYeo0M52Rlg,1352
86
- maleo_foundation-0.1.32.dist-info/METADATA,sha256=kJKuK_VSe2qGOGE39crL6srneNL_X1LMUBOs_TTtCZ8,3190
87
- maleo_foundation-0.1.32.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
88
- maleo_foundation-0.1.32.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
89
- maleo_foundation-0.1.32.dist-info/RECORD,,
85
+ maleo_foundation-0.1.34.dist-info/METADATA,sha256=YqGfviHpmvcCTYS6y5BCzZ1Z5ulF6WPYpUFpcCbs3WM,3190
86
+ maleo_foundation-0.1.34.dist-info/WHEEL,sha256=0CuiUZ_p9E4cD6NyLD6UG80LBXYyiSYZOKDm5lp32xk,91
87
+ maleo_foundation-0.1.34.dist-info/top_level.txt,sha256=_iBos3F_bhEOdjOnzeiEYSrCucasc810xXtLBXI8cQc,17
88
+ maleo_foundation-0.1.34.dist-info/RECORD,,
@@ -1,82 +0,0 @@
1
- import httpx
2
- from contextlib import asynccontextmanager
3
- from pydantic import BaseModel, Field
4
- from typing import AsyncGenerator, Optional
5
- from maleo_foundation.managers.client.base import ClientManager
6
-
7
- class HTTPClientManager:
8
- _client:Optional[httpx.AsyncClient] = None
9
-
10
- @classmethod
11
- def initialize(cls) -> None:
12
- """Initialize the HTTP client if not already initialized."""
13
- if cls._client is None:
14
- cls._client = httpx.AsyncClient()
15
-
16
- @classmethod
17
- async def _client_handler(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
18
- """Reusable generator for client handling."""
19
- if cls._client is None:
20
- raise RuntimeError("Client has not been initialized. Call initialize first.")
21
- yield cls._client
22
-
23
- @classmethod
24
- async def inject_client(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
25
- return cls._client_handler()
26
-
27
- @classmethod
28
- @asynccontextmanager
29
- async def get_client(cls) -> AsyncGenerator[httpx.AsyncClient, None]:
30
- """
31
- Async context manager for manual HTTP client handling.
32
- Supports `async with HTTPClientManager.get() as client:`
33
- """
34
- async for client in cls._client_handler():
35
- yield client
36
-
37
- @classmethod
38
- def get_base_url(cls) -> str:
39
- raise NotImplementedError()
40
-
41
- @classmethod
42
- async def dispose(cls) -> None:
43
- """Dispose of the HTTP client and release any resources."""
44
- if cls._client is not None:
45
- await cls._client.aclose()
46
- cls._client = None
47
-
48
- class URL(BaseModel):
49
- base:str = Field(..., description="Base URL")
50
-
51
- @property
52
- def api(self) -> str:
53
- return f"{self.base}/api"
54
-
55
- class HTTPClientManagerV2(ClientManager):
56
- def __init__(self, key, name, logs_dir, google_cloud_logging = None):
57
- super().__init__(key, name, logs_dir, google_cloud_logging)
58
- self._client = httpx.AsyncClient()
59
-
60
- async def _client_handler(self) -> AsyncGenerator[httpx.AsyncClient, None]:
61
- """Reusable generator for client handling."""
62
- yield self._client
63
-
64
- async def inject_client(self) -> AsyncGenerator[httpx.AsyncClient, None]:
65
- return self._client_handler()
66
-
67
- @asynccontextmanager
68
- async def get_client(self) -> AsyncGenerator[httpx.AsyncClient, None]:
69
- """
70
- Async context manager for manual HTTP client handling.
71
- Supports `async with HTTPClientManager.get() as client:`
72
- """
73
- async for client in self._client_handler():
74
- yield client
75
-
76
- @property
77
- def client(self) -> httpx.AsyncClient:
78
- return self._client
79
-
80
- @property
81
- def url(self) -> URL:
82
- raise NotImplementedError()