kailash 0.6.4__py3-none-any.whl → 0.6.5__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.
@@ -203,42 +203,47 @@ class MCPServerBase(ABC):
203
203
  try:
204
204
  # Try independent FastMCP package first (when available)
205
205
  from fastmcp import FastMCP
206
+
206
207
  self._mcp = FastMCP(self.name)
207
208
  except ImportError:
208
209
  logger.warning("FastMCP not available, using fallback mode")
209
210
  # Use same fallback as MCPServer
210
211
  self._mcp = self._create_fallback_server()
211
-
212
+
212
213
  def _create_fallback_server(self):
213
214
  """Create a fallback server when FastMCP is not available."""
215
+
214
216
  class FallbackMCPServer:
215
217
  def __init__(self, name: str):
216
218
  self.name = name
217
219
  self._tools = {}
218
220
  self._resources = {}
219
221
  self._prompts = {}
220
-
222
+
221
223
  def tool(self, *args, **kwargs):
222
224
  def decorator(func):
223
225
  self._tools[func.__name__] = func
224
226
  return func
227
+
225
228
  return decorator
226
-
229
+
227
230
  def resource(self, uri):
228
231
  def decorator(func):
229
232
  self._resources[uri] = func
230
233
  return func
234
+
231
235
  return decorator
232
-
236
+
233
237
  def prompt(self, name):
234
238
  def decorator(func):
235
239
  self._prompts[name] = func
236
240
  return func
241
+
237
242
  return decorator
238
-
243
+
239
244
  def run(self, **kwargs):
240
245
  raise NotImplementedError("FastMCP not available")
241
-
246
+
242
247
  return FallbackMCPServer(self.name)
243
248
 
244
249
  def start(self):
@@ -503,6 +508,7 @@ class MCPServer:
503
508
  try:
504
509
  # Try independent FastMCP package first (when available)
505
510
  from fastmcp import FastMCP
511
+
506
512
  self._mcp = FastMCP(self.name)
507
513
  logger.info(f"Initialized FastMCP server: {self.name}")
508
514
  except ImportError as e1:
@@ -510,6 +516,7 @@ class MCPServer:
510
516
  try:
511
517
  # Fallback to official MCP FastMCP (when fixed)
512
518
  from mcp.server import FastMCP
519
+
513
520
  self._mcp = FastMCP(self.name)
514
521
  logger.info(f"Initialized official FastMCP server: {self.name}")
515
522
  except ImportError as e2:
@@ -517,56 +524,66 @@ class MCPServer:
517
524
  # Final fallback: Create a minimal FastMCP-compatible wrapper
518
525
  logger.info(f"Using low-level MCP Server fallback for: {self.name}")
519
526
  self._mcp = self._create_fallback_server()
520
-
527
+
521
528
  def _create_fallback_server(self):
522
529
  """Create a fallback server when FastMCP is not available."""
523
530
  logger.info("Creating fallback server implementation")
524
-
531
+
525
532
  class FallbackMCPServer:
526
533
  """Minimal FastMCP-compatible server for when FastMCP is unavailable."""
527
-
534
+
528
535
  def __init__(self, name: str):
529
536
  self.name = name
530
537
  self._tools = {}
531
538
  self._resources = {}
532
539
  self._prompts = {}
533
540
  logger.info(f"Fallback MCP server '{name}' initialized")
534
-
541
+
535
542
  def tool(self, *args, **kwargs):
536
543
  """Tool decorator that stores tool registration."""
544
+
537
545
  def decorator(func):
538
546
  tool_name = func.__name__
539
547
  self._tools[tool_name] = func
540
548
  logger.debug(f"Registered fallback tool: {tool_name}")
541
549
  return func
550
+
542
551
  return decorator
543
-
552
+
544
553
  def resource(self, uri):
545
554
  """Resource decorator that stores resource registration."""
555
+
546
556
  def decorator(func):
547
557
  self._resources[uri] = func
548
558
  logger.debug(f"Registered fallback resource: {uri}")
549
559
  return func
560
+
550
561
  return decorator
551
-
562
+
552
563
  def prompt(self, name):
553
564
  """Prompt decorator that stores prompt registration."""
565
+
554
566
  def decorator(func):
555
567
  self._prompts[name] = func
556
568
  logger.debug(f"Registered fallback prompt: {name}")
557
569
  return func
570
+
558
571
  return decorator
559
-
572
+
560
573
  def run(self, **kwargs):
561
574
  """Placeholder run method."""
562
- logger.warning(f"Fallback server '{self.name}' run() called - FastMCP features limited")
563
- logger.info(f"Registered: {len(self._tools)} tools, {len(self._resources)} resources, {len(self._prompts)} prompts")
575
+ logger.warning(
576
+ f"Fallback server '{self.name}' run() called - FastMCP features limited"
577
+ )
578
+ logger.info(
579
+ f"Registered: {len(self._tools)} tools, {len(self._resources)} resources, {len(self._prompts)} prompts"
580
+ )
564
581
  # In a real implementation, we would set up low-level MCP protocol here
565
582
  raise NotImplementedError(
566
583
  "Full MCP protocol not implemented in fallback mode. "
567
584
  "Install 'fastmcp>=2.10.0' or wait for official MCP package fix."
568
585
  )
569
-
586
+
570
587
  return FallbackMCPServer(self.name)
571
588
 
572
589
  def tool(
@@ -15,7 +15,14 @@ from datetime import datetime, timezone
15
15
  from typing import Any, Dict, List, Optional, Union
16
16
  from urllib.parse import parse_qs
17
17
 
18
- from fastapi import Depends, FastAPI, HTTPException, Request, WebSocket, WebSocketDisconnect
18
+ from fastapi import (
19
+ Depends,
20
+ FastAPI,
21
+ HTTPException,
22
+ Request,
23
+ WebSocket,
24
+ WebSocketDisconnect,
25
+ )
19
26
  from fastapi.middleware.cors import CORSMiddleware
20
27
  from fastapi.responses import JSONResponse, StreamingResponse
21
28
  from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
@@ -24,7 +24,11 @@ from enum import Enum
24
24
  from typing import Any, Dict, List, Optional, Tuple
25
25
 
26
26
  from kailash.access_control import UserContext
27
- from kailash.nodes.admin.audit_log import AuditEventType, AuditSeverity, EnterpriseAuditLogNode
27
+ from kailash.nodes.admin.audit_log import (
28
+ AuditEventType,
29
+ AuditSeverity,
30
+ EnterpriseAuditLogNode,
31
+ )
28
32
  from kailash.nodes.base import Node, NodeParameter, register_node
29
33
  from kailash.nodes.data import AsyncSQLDatabaseNode
30
34
  from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
@@ -26,6 +26,7 @@ from typing import Any, Dict, List, Optional, Set, Union
26
26
  from uuid import uuid4
27
27
 
28
28
  import bcrypt
29
+
29
30
  from kailash.nodes.base import Node, NodeParameter, register_node
30
31
  from kailash.nodes.data import SQLDatabaseNode
31
32
  from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
kailash/nodes/api/http.py CHANGED
@@ -12,11 +12,16 @@ from typing import Any
12
12
 
13
13
  import aiohttp
14
14
  import requests
15
+ from pydantic import BaseModel
16
+
15
17
  from kailash.nodes.base import Node, NodeParameter, register_node
16
18
  from kailash.nodes.base_async import AsyncNode
17
19
  from kailash.sdk_exceptions import NodeExecutionError, NodeValidationError
18
- from kailash.utils.resource_manager import AsyncResourcePool, ResourcePool, managed_resource
19
- from pydantic import BaseModel
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):
kailash/nodes/auth/mfa.py CHANGED
@@ -17,6 +17,7 @@ from datetime import UTC, datetime, timedelta
17
17
  from typing import Any, Dict, List, Optional, Tuple
18
18
 
19
19
  import qrcode
20
+
20
21
  from kailash.nodes.base import Node, NodeParameter, register_node
21
22
  from kailash.nodes.mixins import LoggingMixin, PerformanceMixin, SecurityMixin
22
23
  from kailash.nodes.security.audit_log import AuditLogNode
kailash/nodes/base.py CHANGED
@@ -1070,7 +1070,9 @@ class Node(ABC):
1070
1070
  # ENTERPRISE PARAMETER INJECTION FIX: Runtime inputs should take precedence over config dict
1071
1071
  # First apply config dict values, then re-apply runtime inputs to ensure they override
1072
1072
  for key, value in nested_config.items():
1073
- if key not in runtime_inputs: # Only use config values if not overridden by runtime
1073
+ if (
1074
+ key not in runtime_inputs
1075
+ ): # Only use config values if not overridden by runtime
1074
1076
  merged_inputs[key] = value
1075
1077
  # Don't remove the config key as some nodes might need it
1076
1078
 
@@ -368,7 +368,7 @@ class CacheInvalidationNode(AsyncNode):
368
368
  return
369
369
 
370
370
  redis_url = kwargs.get("redis_url", "redis://localhost:6379")
371
-
371
+
372
372
  # Only recreate Redis client if the current one is problematic
373
373
  if self._redis_client:
374
374
  try:
@@ -382,18 +382,22 @@ class CacheInvalidationNode(AsyncNode):
382
382
  await self._redis_client.aclose()
383
383
  except:
384
384
  pass # Ignore errors when closing old client
385
-
385
+
386
386
  try:
387
387
  self._redis_client = redis.from_url(redis_url)
388
388
  # Test connection with proper error handling
389
389
  try:
390
390
  await asyncio.wait_for(self._redis_client.ping(), timeout=2.0)
391
- self.logger.debug(f"Fresh Redis connection established to {redis_url}")
391
+ self.logger.debug(
392
+ f"Fresh Redis connection established to {redis_url}"
393
+ )
392
394
  except (asyncio.TimeoutError, RuntimeError) as e:
393
395
  if "Event loop is closed" in str(e):
394
396
  # Event loop issue - create new client without ping test
395
397
  self._redis_client = redis.from_url(redis_url)
396
- self.logger.debug("Created Redis client without ping test due to event loop issue")
398
+ self.logger.debug(
399
+ "Created Redis client without ping test due to event loop issue"
400
+ )
397
401
  else:
398
402
  raise
399
403
  except Exception as e:
@@ -843,10 +847,10 @@ class CacheInvalidationNode(AsyncNode):
843
847
  # Event loop is running, schedule the coroutine
844
848
  import concurrent.futures
845
849
  import threading
846
-
850
+
847
851
  result_holder = {}
848
852
  exception_holder = {}
849
-
853
+
850
854
  def run_in_new_loop():
851
855
  try:
852
856
  # Create a new event loop for this thread
@@ -854,17 +858,17 @@ class CacheInvalidationNode(AsyncNode):
854
858
  asyncio.set_event_loop(new_loop)
855
859
  try:
856
860
  result = new_loop.run_until_complete(self.async_run(**kwargs))
857
- result_holder['result'] = result
861
+ result_holder["result"] = result
858
862
  finally:
859
863
  new_loop.close()
860
864
  except Exception as e:
861
- exception_holder['error'] = e
862
-
865
+ exception_holder["error"] = e
866
+
863
867
  thread = threading.Thread(target=run_in_new_loop)
864
868
  thread.start()
865
869
  thread.join()
866
-
867
- if 'error' in exception_holder:
868
- raise exception_holder['error']
869
-
870
- return result_holder['result']
870
+
871
+ if "error" in exception_holder:
872
+ raise exception_holder["error"]
873
+
874
+ return result_holder["result"]
@@ -57,7 +57,11 @@ from pathlib import Path
57
57
  from typing import Any, get_type_hints
58
58
 
59
59
  from kailash.nodes.base import Node, NodeMetadata, NodeParameter, register_node
60
- from kailash.sdk_exceptions import NodeConfigurationError, NodeExecutionError, SafetyViolationError
60
+ from kailash.sdk_exceptions import (
61
+ NodeConfigurationError,
62
+ NodeExecutionError,
63
+ SafetyViolationError,
64
+ )
61
65
  from kailash.security import (
62
66
  ExecutionTimeoutError,
63
67
  MemoryLimitError,
@@ -402,7 +406,6 @@ class CodeExecutor:
402
406
 
403
407
  # Sanitize inputs
404
408
  sanitized_inputs = validate_node_parameters(inputs, self.security_config)
405
-
406
409
 
407
410
  # Create isolated namespace
408
411
  import builtins