kailash 0.4.1__py3-none-any.whl → 0.5.0__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.
kailash/nodes/api/http.py CHANGED
@@ -17,6 +17,11 @@ from pydantic import BaseModel
17
17
  from kailash.nodes.base import Node, NodeParameter, register_node
18
18
  from kailash.nodes.base_async import AsyncNode
19
19
  from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
20
+ from kailash.utils.resource_manager import (
21
+ AsyncResourcePool,
22
+ ResourcePool,
23
+ managed_resource,
24
+ )
20
25
 
21
26
 
22
27
  class HTTPMethod(str, Enum):
@@ -55,6 +60,23 @@ class HTTPResponse(BaseModel):
55
60
  url: str
56
61
 
57
62
 
63
+ # Global connection pool for HTTP sessions
64
+ _http_session_pool = ResourcePool(
65
+ factory=lambda: requests.Session(),
66
+ max_size=20,
67
+ timeout=30.0,
68
+ cleanup=lambda session: session.close(),
69
+ )
70
+
71
+ # Global async connection pool for aiohttp sessions
72
+ _async_http_session_pool = AsyncResourcePool(
73
+ factory=lambda: aiohttp.ClientSession(),
74
+ max_size=20,
75
+ timeout=30.0,
76
+ cleanup=lambda session: asyncio.create_task(session.close()),
77
+ )
78
+
79
+
58
80
  @register_node()
59
81
  class HTTPRequestNode(Node):
60
82
  """
@@ -520,8 +542,11 @@ class HTTPRequestNode(Node):
520
542
 
521
543
  try:
522
544
  start_time = time.time()
523
- response = self.session.request(method=method.value, **request_kwargs)
524
- response_time = (time.time() - start_time) * 1000 # Convert to ms
545
+
546
+ # Use connection pool for efficient resource management
547
+ with _http_session_pool.acquire() as session:
548
+ response = session.request(method=method.value, **request_kwargs)
549
+ response_time = (time.time() - start_time) * 1000 # Convert to ms
525
550
 
526
551
  # Log response if enabled
527
552
  if log_requests:
@@ -681,7 +706,6 @@ class AsyncHTTPRequestNode(AsyncNode):
681
706
  Same as HTTPRequestNode
682
707
  """
683
708
  super().__init__(**kwargs)
684
- self._session = None # Will be created when needed
685
709
 
686
710
  def get_parameters(self) -> dict[str, NodeParameter]:
687
711
  """Define the parameters this node accepts.
@@ -829,10 +853,6 @@ class AsyncHTTPRequestNode(AsyncNode):
829
853
  if rate_limit_delay > 0:
830
854
  await asyncio.sleep(rate_limit_delay)
831
855
 
832
- # Create session if needed
833
- if self._session is None:
834
- self._session = aiohttp.ClientSession()
835
-
836
856
  # Prepare request kwargs
837
857
  request_kwargs = {
838
858
  "url": url,
@@ -870,72 +890,76 @@ class AsyncHTTPRequestNode(AsyncNode):
870
890
  try:
871
891
  start_time = time.time()
872
892
 
873
- async with self._session.request(
874
- method=method.value, **request_kwargs
875
- ) as response:
876
- response_time = (time.time() - start_time) * 1000 # Convert to ms
877
-
878
- # Get content type
879
- content_type = response.headers.get("Content-Type", "")
880
-
881
- # Log response if enabled
882
- if log_requests:
883
- self.logger.info(f"Response: {response.status}")
884
- self.logger.info(f"Headers: {dict(response.headers)}")
885
- text_preview = await response.text()
886
- self.logger.info(f"Body: {text_preview[:500]}...")
887
-
888
- # Determine response format
889
- actual_format = response_format
890
- if actual_format == ResponseFormat.AUTO:
891
- if "application/json" in content_type:
892
- actual_format = ResponseFormat.JSON
893
- elif "text/" in content_type:
894
- actual_format = ResponseFormat.TEXT
895
- else:
896
- actual_format = ResponseFormat.BINARY
897
-
898
- # Parse response
899
- try:
900
- if actual_format == ResponseFormat.JSON:
901
- content = await response.json()
902
- elif actual_format == ResponseFormat.TEXT:
903
- content = await response.text()
904
- elif actual_format == ResponseFormat.BINARY:
905
- content = await response.read()
906
- else:
893
+ # Use connection pool for efficient resource management
894
+ async with _async_http_session_pool.acquire() as session:
895
+ async with session.request(
896
+ method=method.value, **request_kwargs
897
+ ) as response:
898
+ response_time = (
899
+ time.time() - start_time
900
+ ) * 1000 # Convert to ms
901
+
902
+ # Get content type
903
+ content_type = response.headers.get("Content-Type", "")
904
+
905
+ # Log response if enabled
906
+ if log_requests:
907
+ self.logger.info(f"Response: {response.status}")
908
+ self.logger.info(f"Headers: {dict(response.headers)}")
909
+ text_preview = await response.text()
910
+ self.logger.info(f"Body: {text_preview[:500]}...")
911
+
912
+ # Determine response format
913
+ actual_format = response_format
914
+ if actual_format == ResponseFormat.AUTO:
915
+ if "application/json" in content_type:
916
+ actual_format = ResponseFormat.JSON
917
+ elif "text/" in content_type:
918
+ actual_format = ResponseFormat.TEXT
919
+ else:
920
+ actual_format = ResponseFormat.BINARY
921
+
922
+ # Parse response
923
+ try:
924
+ if actual_format == ResponseFormat.JSON:
925
+ content = await response.json()
926
+ elif actual_format == ResponseFormat.TEXT:
927
+ content = await response.text()
928
+ elif actual_format == ResponseFormat.BINARY:
929
+ content = await response.read()
930
+ else:
931
+ content = await response.text() # Fallback to text
932
+ except Exception as e:
933
+ self.logger.warning(
934
+ f"Failed to parse response as {actual_format}: {str(e)}"
935
+ )
907
936
  content = await response.text() # Fallback to text
908
- except Exception as e:
909
- self.logger.warning(
910
- f"Failed to parse response as {actual_format}: {str(e)}"
911
- )
912
- content = await response.text() # Fallback to text
913
-
914
- # Create response object
915
- http_response = HTTPResponse(
916
- status_code=response.status,
917
- headers=dict(response.headers),
918
- content_type=content_type,
919
- content=content,
920
- response_time_ms=response_time,
921
- url=str(response.url),
922
- ).model_dump()
923
-
924
- # Return results
925
- success = 200 <= response.status < 300
926
-
927
- result = {
928
- "response": http_response,
929
- "status_code": response.status,
930
- "success": success,
931
- }
932
-
933
- if not success:
934
- result["recovery_suggestions"] = self._get_recovery_suggestions(
935
- response.status
936
- )
937
937
 
938
- return result
938
+ # Create response object
939
+ http_response = HTTPResponse(
940
+ status_code=response.status,
941
+ headers=dict(response.headers),
942
+ content_type=content_type,
943
+ content=content,
944
+ response_time_ms=response_time,
945
+ url=str(response.url),
946
+ ).model_dump()
947
+
948
+ # Return results
949
+ success = 200 <= response.status < 300
950
+
951
+ result = {
952
+ "response": http_response,
953
+ "status_code": response.status,
954
+ "success": success,
955
+ }
956
+
957
+ if not success:
958
+ result["recovery_suggestions"] = (
959
+ self._get_recovery_suggestions(response.status)
960
+ )
961
+
962
+ return result
939
963
 
940
964
  except (TimeoutError, aiohttp.ClientError) as e:
941
965
  self.logger.warning(f"Async request failed: {str(e)}")