Files
markitect-main/config/compat.py
tegwick a7a7960ef6 feat: Implement unified configuration management system
Consolidates scattered configuration patterns across TDDAI, Gitea, and
MarkiTect into a unified, maintainable system addressing issue #22.

Key improvements:
- Created centralized config/ module with base classes and utilities
- Eliminated duplicate load_dotenv_file() functions
- Standardized environment variables with MARKITECT_ prefix
- Implemented comprehensive validation with helpful error messages
- Maintained full backward compatibility with existing TDDAI config

Architecture:
- BaseConfig: Abstract base with common functionality
- MarkitectConfig: Main configuration class with legacy support
- Compatibility layer: TddaiConfigCompat and GiteaConfigCompat wrappers
- Unified error handling: ConfigurationError hierarchy

All existing tests pass without modification, ensuring seamless transition.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-26 17:45:56 +02:00

169 lines
5.3 KiB
Python

"""
Backward compatibility layer for existing configuration systems.
Provides compatibility shims that allow existing TDDAI and Gitea
configuration code to work with the unified configuration system.
"""
from dataclasses import dataclass
from pathlib import Path
from typing import Dict, Any, Optional
from .manager import get_unified_config
from .exceptions import ConfigurationError
@dataclass
class TddaiConfigCompat:
"""TDDAI configuration compatibility layer.
Provides the same interface as the original TddaiConfig but
backed by the unified configuration system.
"""
workspace_dir: Path
current_issue_file: str = "current_issue.json"
gitea_url: str = ""
repo_owner: str = ""
repo_name: str = ""
tests_dir: Path = Path("tests")
test_file_pattern: str = "test_issue_{issue_num}_{scenario}.py"
claude_code_command: str = "claude"
def __init__(self, **kwargs):
"""Initialize from unified configuration."""
unified = get_unified_config()
# Map unified config to TDDAI format
self.workspace_dir = unified.workspace_dir
self.gitea_url = unified.gitea_url
self.repo_owner = unified.repo_owner
self.repo_name = unified.repo_name
self.tests_dir = unified.tests_dir
self.test_file_pattern = unified.test_file_pattern
self.claude_code_command = unified.claude_code_command
# Apply any explicit overrides
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
def validate(self) -> None:
"""Validate configuration (for compatibility)."""
if not self.gitea_url or not self.gitea_url.strip():
raise ConfigurationError("gitea_url cannot be empty")
if not self.repo_owner or not self.repo_owner.strip():
raise ConfigurationError("repo_owner cannot be empty")
if not self.repo_name or not self.repo_name.strip():
raise ConfigurationError("repo_name cannot be empty")
@property
def issues_api_url(self) -> str:
"""Get issues API URL (for compatibility)."""
return f"{self.gitea_url}/api/v1/repos/{self.repo_owner}/{self.repo_name}/issues"
@property
def issues_base_url(self) -> str:
"""Get issues base URL (for compatibility)."""
return f"{self.gitea_url}/{self.repo_owner}/{self.repo_name}/issues"
@dataclass
class GiteaConfigCompat:
"""Gitea configuration compatibility layer.
Provides the same interface as the original GiteaConfig but
backed by the unified configuration system.
"""
base_url: str
repo_owner: str
repo_name: str
auth_token: str = ""
api_version: str = "v1"
def __init__(self, **kwargs):
"""Initialize from unified configuration."""
unified = get_unified_config()
# Map unified config to Gitea format
self.base_url = unified.gitea_url
self.repo_owner = unified.repo_owner
self.repo_name = unified.repo_name
self.auth_token = unified.api_token
# Apply any explicit overrides
for key, value in kwargs.items():
if hasattr(self, key):
setattr(self, key, value)
@classmethod
def from_tddai_config(cls, tddai_config) -> 'GiteaConfigCompat':
"""Create from TDDAI config (for compatibility)."""
return cls(
base_url=tddai_config.gitea_url,
repo_owner=tddai_config.repo_owner,
repo_name=tddai_config.repo_name
)
def validate(self) -> None:
"""Validate configuration (for compatibility)."""
if not self.base_url or not self.base_url.strip():
raise ConfigurationError("base_url cannot be empty")
if not self.repo_owner or not self.repo_owner.strip():
raise ConfigurationError("repo_owner cannot be empty")
if not self.repo_name or not self.repo_name.strip():
raise ConfigurationError("repo_name cannot be empty")
# URL validation
if not (self.base_url.startswith('http://') or self.base_url.startswith('https://')):
raise ConfigurationError("base_url must start with http:// or https://")
@property
def api_base_url(self) -> str:
"""Get API base URL."""
return f"{self.base_url}/api/{self.api_version}"
@property
def repo_api_url(self) -> str:
"""Get repository API URL."""
return f"{self.api_base_url}/repos/{self.repo_owner}/{self.repo_name}"
@property
def issues_api_url(self) -> str:
"""Get issues API URL."""
return f"{self.repo_api_url}/issues"
def get_tddai_config(**kwargs) -> TddaiConfigCompat:
"""Get TDDAI-compatible configuration.
This function can be used as a drop-in replacement for the original
get_config() function in tddai/config.py.
Args:
**kwargs: Override values for configuration fields
Returns:
TDDAI-compatible configuration object
"""
return TddaiConfigCompat(**kwargs)
def get_gitea_config(**kwargs) -> GiteaConfigCompat:
"""Get Gitea-compatible configuration.
This function can be used as a drop-in replacement for the original
GiteaConfig creation.
Args:
**kwargs: Override values for configuration fields
Returns:
Gitea-compatible configuration object
"""
return GiteaConfigCompat(**kwargs)