Skip to content

Commit 1439cfd

Browse files
alokemajumderclaude
andcommitted
Hotfix v1.0.1: Add Pydantic V1/V2 compatibility for Fedora systems
- Add comprehensive Pydantic compatibility layer for Fedora distributions - Implement automatic Pydantic version detection and V1/V2 bridging - Add platform-specific detection for Fedora, Ubuntu, and macOS - Update installation script with Fedora-specific Pydantic handling - Maintain backward compatibility with existing Ubuntu/macOS setups This hotfix addresses Pydantic V2 compatibility issues specifically encountered on Fedora systems while preserving functionality across all supported platforms. Target: v1.0.1 hotfix release 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 9f60857 commit 1439cfd

File tree

4 files changed

+248
-1
lines changed

4 files changed

+248
-1
lines changed

install.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,54 @@ def print_step(step: str):
6868
print(f"\n{Colors.MAGENTA}{'▶' * 3} {step}{Colors.END}")
6969

7070

71+
def detect_fedora() -> bool:
72+
"""Detect if running on Fedora."""
73+
try:
74+
with open('/etc/os-release', 'r') as f:
75+
content = f.read().lower()
76+
return 'fedora' in content or 'red hat' in content
77+
except:
78+
return 'fedora' in platform.platform().lower()
79+
80+
81+
def check_and_fix_pydantic() -> bool:
82+
"""Check Pydantic version and handle Fedora-specific issues."""
83+
is_fedora = detect_fedora()
84+
85+
try:
86+
import pydantic
87+
version = getattr(pydantic, '__version__', getattr(pydantic, 'VERSION', 'unknown'))
88+
89+
if is_fedora:
90+
print_info(f"🐧 Fedora detected with Pydantic {version}")
91+
if version.startswith('2'):
92+
print_success("✅ Pydantic V2 on Fedora - using compatibility mode")
93+
print_info("💡 For optimal performance, you can install V1: pip install 'pydantic>=1.10.0,<2.0.0'")
94+
else:
95+
print_success("✅ Pydantic V1 on Fedora - native mode")
96+
else:
97+
print_success(f"✅ Pydantic {version} detected on {platform.system()}")
98+
99+
except ImportError:
100+
print_warning("⚠️ Pydantic not found. Installing...")
101+
102+
if is_fedora:
103+
# Try system package first on Fedora
104+
try:
105+
print_info("🐧 Trying Fedora system package...")
106+
subprocess.check_call(['sudo', 'dnf', 'install', '-y', 'python3-pydantic'])
107+
print_success("✅ Installed via dnf")
108+
return True
109+
except (subprocess.CalledProcessError, FileNotFoundError):
110+
print_info("📦 System package failed, using pip...")
111+
112+
# Fallback to pip
113+
subprocess.check_call([sys.executable, '-m', 'pip', 'install', 'pydantic>=1.10.0'])
114+
print_success("✅ Installed via pip")
115+
116+
return True
117+
118+
71119
def detect_system_info() -> Dict[str, str]:
72120
"""Detect comprehensive system information."""
73121
system_info = {
@@ -79,6 +127,12 @@ def detect_system_info() -> Dict[str, str]:
79127
'processor': platform.processor() or 'Unknown',
80128
}
81129

130+
# Detect if we're on Fedora specifically
131+
if system_info['os'] == 'Linux':
132+
system_info['is_fedora'] = detect_fedora()
133+
else:
134+
system_info['is_fedora'] = False
135+
82136
# Detect package manager
83137
if system_info['os'] == 'Linux':
84138
if shutil.which('apt-get'):
@@ -601,6 +655,7 @@ def main() -> int:
601655
setup_steps = [
602656
("Checking Python compatibility", lambda: check_python_version()),
603657
("Checking system dependencies", lambda: check_system_dependencies(system_info)),
658+
("Checking Pydantic compatibility", lambda: check_and_fix_pydantic()),
604659
("Setting up virtual environment", lambda: create_virtual_environment(system_info)),
605660
]
606661

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
"""
2+
Platform-specific compatibility handling for Pydantic V1/V2.
3+
Addresses Fedora-specific issues while maintaining macOS/Ubuntu compatibility.
4+
"""
5+
6+
import os
7+
import platform
8+
import sys
9+
import warnings
10+
from typing import Any, Callable, Optional
11+
12+
def detect_platform() -> dict:
13+
"""Detect current platform and Pydantic version."""
14+
system = platform.system().lower()
15+
16+
# Detect if we're on Fedora
17+
is_fedora = False
18+
if system == 'linux':
19+
try:
20+
with open('/etc/os-release', 'r') as f:
21+
content = f.read().lower()
22+
is_fedora = 'fedora' in content or 'red hat' in content
23+
except (FileNotFoundError, PermissionError):
24+
# Fallback check for Fedora-specific paths
25+
is_fedora = any([
26+
os.path.exists('/etc/fedora-release'),
27+
os.path.exists('/etc/redhat-release'),
28+
'fedora' in platform.platform().lower()
29+
])
30+
31+
# Check Pydantic version
32+
pydantic_version = None
33+
pydantic_v2 = False
34+
try:
35+
import pydantic
36+
pydantic_version = getattr(pydantic, '__version__', getattr(pydantic, 'VERSION', 'unknown'))
37+
pydantic_v2 = pydantic_version.startswith('2')
38+
except ImportError:
39+
pass
40+
41+
return {
42+
'system': system,
43+
'is_fedora': is_fedora,
44+
'is_macos': system == 'darwin',
45+
'is_ubuntu': 'ubuntu' in platform.platform().lower(),
46+
'pydantic_version': pydantic_version,
47+
'pydantic_v2': pydantic_v2,
48+
'platform_string': platform.platform()
49+
}
50+
51+
# Global platform info
52+
PLATFORM_INFO = detect_platform()
53+
54+
def log_platform_info():
55+
"""Log platform detection results for debugging."""
56+
import logging
57+
logging.info(f"Platform detected: {PLATFORM_INFO}")
58+
if PLATFORM_INFO['is_fedora'] and PLATFORM_INFO['pydantic_v2']:
59+
logging.warning("Fedora with Pydantic V2 detected - using compatibility mode")
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
"""
2+
Comprehensive Pydantic V1/V2 compatibility for v1.0.0.
3+
Specifically designed for Fedora compatibility while maintaining macOS/Ubuntu functionality.
4+
"""
5+
6+
import sys
7+
import warnings
8+
from typing import Any, Callable, Dict, Optional, Type, Union
9+
10+
from .platform_compat import PLATFORM_INFO, log_platform_info
11+
12+
# Log platform info for debugging
13+
log_platform_info()
14+
15+
try:
16+
import pydantic
17+
pydantic_available = True
18+
19+
# Check version and create compatibility layer
20+
if hasattr(pydantic, '__version__'):
21+
version = pydantic.__version__
22+
elif hasattr(pydantic, 'VERSION'):
23+
version = pydantic.VERSION
24+
else:
25+
version = "unknown"
26+
27+
PYDANTIC_V2 = version.startswith('2')
28+
29+
if PYDANTIC_V2:
30+
# Pydantic V2 - Create V1 compatibility
31+
from pydantic import BaseModel as V2BaseModel, Field
32+
try:
33+
from pydantic import field_validator, ValidationInfo
34+
except ImportError:
35+
# Fallback for older V2 versions
36+
from pydantic import validator as field_validator
37+
ValidationInfo = None
38+
39+
# V1-compatible validator decorator for V2
40+
def validator(
41+
field_name: str,
42+
*fields: str,
43+
pre: bool = False,
44+
each_item: bool = False,
45+
always: bool = False,
46+
check_fields: bool = True
47+
):
48+
"""V1-compatible validator decorator that works with V2."""
49+
def decorator(func: Callable) -> Callable:
50+
if ValidationInfo is not None:
51+
# Full V2 with ValidationInfo
52+
@field_validator(field_name, *fields)
53+
@classmethod
54+
def wrapper(cls, v: Any, info: ValidationInfo) -> Any:
55+
return func(cls, v)
56+
else:
57+
# Older V2 or fallback
58+
@field_validator(field_name, *fields)
59+
@classmethod
60+
def wrapper(cls, v: Any) -> Any:
61+
return func(cls, v)
62+
63+
return wrapper
64+
return decorator
65+
66+
# V1-compatible BaseModel
67+
class BaseModel(V2BaseModel):
68+
"""V1-compatible BaseModel for V2."""
69+
pass
70+
71+
# Fedora-specific warning
72+
if PLATFORM_INFO['is_fedora']:
73+
warnings.warn(
74+
"Fedora with Pydantic V2 detected. Using compatibility mode. "
75+
"For better performance, consider using Pydantic V1: "
76+
"pip install 'pydantic>=1.10.0,<2.0.0'",
77+
UserWarning
78+
)
79+
80+
else:
81+
# Pydantic V1 - Use directly
82+
from pydantic import BaseModel, Field, validator
83+
ValidationInfo = None
84+
85+
# Log success for non-Fedora systems
86+
if not PLATFORM_INFO['is_fedora']:
87+
import logging
88+
logging.info(f"Pydantic V1 detected on {PLATFORM_INFO['system']} - using native mode")
89+
90+
except ImportError as e:
91+
# Pydantic not available
92+
pydantic_available = False
93+
PYDANTIC_V2 = False
94+
95+
# Create minimal fallback classes
96+
class BaseModel:
97+
def __init__(self, **kwargs):
98+
for key, value in kwargs.items():
99+
setattr(self, key, value)
100+
101+
def Field(*args, **kwargs):
102+
return None
103+
104+
def validator(*args, **kwargs):
105+
def decorator(func):
106+
return func
107+
return decorator
108+
109+
ValidationInfo = None
110+
111+
# Error message with platform-specific guidance
112+
error_msg = f"Pydantic is required but not installed on {PLATFORM_INFO['system']}"
113+
if PLATFORM_INFO['is_fedora']:
114+
error_msg += "\nFor Fedora, install with: sudo dnf install python3-pydantic or pip install pydantic"
115+
elif PLATFORM_INFO['is_ubuntu']:
116+
error_msg += "\nFor Ubuntu, install with: sudo apt install python3-pydantic or pip install pydantic"
117+
elif PLATFORM_INFO['is_macos']:
118+
error_msg += "\nFor macOS, install with: pip install pydantic"
119+
else:
120+
error_msg += "\nInstall with: pip install pydantic"
121+
122+
raise ImportError(error_msg)
123+
124+
# Export unified interface
125+
__all__ = [
126+
'BaseModel',
127+
'Field',
128+
'validator',
129+
'ValidationInfo',
130+
'PYDANTIC_V2',
131+
'pydantic_available'
132+
]

src/wazuh_mcp_server/utils/validation.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@
44
import ipaddress
55
import hashlib
66
from typing import Any, Dict, List, Optional, Union
7-
from pydantic import BaseModel, Field, validator
7+
# Use Fedora-compatible layer instead of direct pydantic import
8+
from .pydantic_compat import BaseModel, Field, validator
89
# Security manager functionality moved to error recovery system
910

1011

0 commit comments

Comments
 (0)