Skip to content

Commit fd743a0

Browse files
authored
Merge pull request #8 from JustAGhosT/copilot/sub-pr-7
Migrate config to Pydantic, fix datetime deprecation, add import guards
2 parents 0a1964f + ac2895e commit fd743a0

7 files changed

Lines changed: 154 additions & 58 deletions

File tree

codeflow_engine/__init__.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,16 @@
3434
)
3535
from codeflow_engine.integrations.base import Integration
3636
from codeflow_engine.quality.metrics_collector import MetricsCollector
37-
from codeflow_engine.security.authorization.enterprise_manager import (
38-
EnterpriseAuthorizationManager,
39-
)
37+
38+
# Security - guarded import
39+
EnterpriseAuthorizationManager: type[Any] | None = None
40+
try:
41+
from codeflow_engine.security.authorization.enterprise_manager import (
42+
EnterpriseAuthorizationManager,
43+
)
44+
except (ImportError, OSError):
45+
pass
46+
4047
from codeflow_engine.workflows.base import Workflow
4148
from codeflow_engine.workflows.engine import WorkflowEngine
4249

codeflow_engine/actions/__init__.py

Lines changed: 60 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,66 @@
1717

1818
from codeflow_engine.actions.registry import ActionRegistry
1919

20-
# Import category modules
21-
from codeflow_engine.actions import (
22-
ai_actions,
23-
analysis,
24-
base,
25-
generation,
26-
git,
27-
issues,
28-
maintenance,
29-
platform,
30-
quality,
31-
scripts,
32-
)
20+
# Import category modules with error handling for optional dependencies
21+
ai_actions = None
22+
try:
23+
from codeflow_engine.actions import ai_actions
24+
except (ImportError, OSError):
25+
pass
26+
27+
analysis = None
28+
try:
29+
from codeflow_engine.actions import analysis
30+
except (ImportError, OSError):
31+
pass
32+
33+
base = None
34+
try:
35+
from codeflow_engine.actions import base
36+
except (ImportError, OSError):
37+
pass
38+
39+
generation = None
40+
try:
41+
from codeflow_engine.actions import generation
42+
except (ImportError, OSError):
43+
pass
44+
45+
git = None
46+
try:
47+
from codeflow_engine.actions import git
48+
except (ImportError, OSError):
49+
pass
50+
51+
issues = None
52+
try:
53+
from codeflow_engine.actions import issues
54+
except (ImportError, OSError):
55+
pass
56+
57+
maintenance = None
58+
try:
59+
from codeflow_engine.actions import maintenance
60+
except (ImportError, OSError):
61+
pass
62+
63+
platform = None
64+
try:
65+
from codeflow_engine.actions import platform
66+
except (ImportError, OSError):
67+
pass
68+
69+
quality = None
70+
try:
71+
from codeflow_engine.actions import quality
72+
except (ImportError, OSError):
73+
pass
74+
75+
scripts = None
76+
try:
77+
from codeflow_engine.actions import scripts
78+
except (ImportError, OSError):
79+
pass
3380

3481
# Re-export commonly used actions for backward compatibility
3582
# Analysis

codeflow_engine/actions/ai_actions/__init__.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,18 @@
3737
except ImportError:
3838
pass
3939

40-
# Submodule exports
41-
from codeflow_engine.actions.ai_actions import autogen, llm
40+
# Submodule exports with guarded imports
41+
autogen = None
42+
try:
43+
from codeflow_engine.actions.ai_actions import autogen
44+
except (ImportError, OSError):
45+
pass
46+
47+
llm = None
48+
try:
49+
from codeflow_engine.actions.ai_actions import llm
50+
except (ImportError, OSError):
51+
pass
4252

4353
__all__ = [
4454
"AutoGenAgentSystem",

codeflow_engine/actions/issues/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@
4343
except ImportError:
4444
pass
4545

46+
FindStaleIssuesOrPRs: type[Any] | None = None
47+
try:
48+
from codeflow_engine.actions.issues.find_stale_issues_or_prs import FindStaleIssuesOrPRs
49+
except ImportError:
50+
pass
51+
4652
__all__ = [
4753
"CreateOrUpdateIssue",
54+
"FindStaleIssuesOrPRs",
4855
"IssueCreator",
4956
"LabelPR",
5057
"LabelPRBySize",

codeflow_engine/models/base.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from abc import ABC
88
from dataclasses import dataclass, field
9-
from datetime import datetime
9+
from datetime import datetime, timezone
1010
from typing import Any
1111

1212

@@ -15,7 +15,7 @@ class BaseModel(ABC):
1515
"""Base class for all CodeFlow models with common functionality."""
1616

1717
id: str | None = None
18-
created_at: datetime = field(default_factory=datetime.utcnow)
18+
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
1919
updated_at: datetime | None = None
2020

2121
def to_dict(self) -> dict[str, Any]:
@@ -33,14 +33,18 @@ def to_dict(self) -> dict[str, Any]:
3333

3434
@dataclass
3535
class TimestampMixin:
36-
"""Mixin for models that need timestamp tracking."""
36+
"""Mixin for models that need timestamp tracking.
37+
38+
Note: Defines created_at and updated_at fields (same as BaseModel).
39+
Be cautious when using with BaseModel in multiple inheritance to avoid conflicts.
40+
"""
3741

38-
created_at: datetime = field(default_factory=datetime.utcnow)
42+
created_at: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
3943
updated_at: datetime | None = None
4044

4145
def touch(self) -> None:
4246
"""Update the updated_at timestamp."""
43-
self.updated_at = datetime.utcnow()
47+
self.updated_at = datetime.now(timezone.utc)
4448

4549

4650
@dataclass

codeflow_engine/models/config.py

Lines changed: 54 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@
44
This module contains data models for configuration objects used in the CodeFlow system.
55
"""
66

7-
from dataclasses import dataclass, field
87
from enum import StrEnum
98
from typing import Any
109

10+
from pydantic import BaseModel, Field, SecretStr, field_validator
11+
1112

1213
class LogLevel(StrEnum):
1314
"""Logging levels for the application."""
@@ -28,59 +29,79 @@ class Environment(StrEnum):
2829
TESTING = "testing"
2930

3031

31-
@dataclass
32-
class DatabaseConfig:
33-
"""Database configuration model."""
32+
class DatabaseConfig(BaseModel):
33+
"""Database configuration model with validation."""
3434

3535
url: str
36-
pool_size: int = 5
37-
max_overflow: int = 10
36+
pool_size: int = Field(default=5, ge=0)
37+
max_overflow: int = Field(default=10, ge=0)
3838
echo: bool = False
3939
ssl_required: bool = True
4040

41+
@field_validator("url")
42+
@classmethod
43+
def validate_url(cls, v: str) -> str:
44+
"""Validate database URL format."""
45+
if not v:
46+
raise ValueError("Database URL cannot be empty")
47+
# Basic URL validation - must contain ://
48+
if "://" not in v:
49+
raise ValueError(
50+
"Invalid database URL format. Expected format: dialect://user:password@host:port/database"
51+
)
52+
return v
53+
4154

42-
@dataclass
43-
class RedisConfig:
44-
"""Redis configuration model."""
55+
class RedisConfig(BaseModel):
56+
"""Redis configuration model with validation."""
4557

4658
url: str
47-
max_connections: int = 10
59+
max_connections: int = Field(default=10, ge=0)
4860
ssl: bool = True
4961

62+
@field_validator("url")
63+
@classmethod
64+
def validate_url(cls, v: str) -> str:
65+
"""Validate Redis URL format."""
66+
if not v:
67+
raise ValueError("Redis URL cannot be empty")
68+
# Basic URL validation - must contain ://
69+
if "://" not in v:
70+
raise ValueError(
71+
"Invalid Redis URL format. Expected format: redis://host:port or rediss://host:port"
72+
)
73+
return v
74+
5075

51-
@dataclass
52-
class LLMConfig:
53-
"""LLM provider configuration model."""
76+
class LLMConfig(BaseModel):
77+
"""LLM provider configuration model with validation."""
5478

5579
provider: str
56-
api_key: str | None = None
80+
api_key: SecretStr | None = None
5781
model: str # Required; provider-specific (e.g., gpt-4, claude-3-opus, mistral-large)
58-
temperature: float = 0.7
59-
max_tokens: int = 4096
82+
temperature: float = Field(default=0.7, ge=0.0, le=2.0)
83+
max_tokens: int = Field(default=4096, gt=0)
6084

6185

62-
@dataclass
63-
class GitHubConfig:
64-
"""GitHub integration configuration model."""
86+
class GitHubConfig(BaseModel):
87+
"""GitHub integration configuration model with secrets protection."""
6588

66-
token: str | None = None
89+
token: SecretStr | None = None
6790
app_id: str | None = None
68-
private_key: str | None = None
69-
webhook_secret: str | None = None
91+
private_key: SecretStr | None = None
92+
webhook_secret: SecretStr | None = None
7093

7194

72-
@dataclass
73-
class WorkflowConfig:
74-
"""Workflow execution configuration model."""
95+
class WorkflowConfig(BaseModel):
96+
"""Workflow execution configuration model with validation."""
7597

76-
max_concurrent: int = 10
77-
timeout_seconds: int = 300
78-
retry_attempts: int = 3
79-
retry_delay_seconds: int = 5
98+
max_concurrent: int = Field(default=10, gt=0)
99+
timeout_seconds: int = Field(default=300, gt=0)
100+
retry_attempts: int = Field(default=3, ge=0)
101+
retry_delay_seconds: int = Field(default=5, gt=0)
80102

81103

82-
@dataclass
83-
class AppConfig:
104+
class AppConfig(BaseModel):
84105
"""Main application configuration model."""
85106

86107
environment: Environment = Environment.DEVELOPMENT
@@ -90,8 +111,8 @@ class AppConfig:
90111
redis: RedisConfig | None = None
91112
llm: LLMConfig | None = None
92113
github: GitHubConfig | None = None
93-
workflow: WorkflowConfig = field(default_factory=WorkflowConfig)
94-
custom_settings: dict[str, Any] = field(default_factory=dict)
114+
workflow: WorkflowConfig = Field(default_factory=WorkflowConfig)
115+
custom_settings: dict[str, Any] = Field(default_factory=dict)
95116

96117

97118
__all__ = [

codeflow_engine/models/events.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"""
66

77
from dataclasses import dataclass, field
8-
from datetime import datetime
8+
from datetime import datetime, timezone
99
from enum import StrEnum
1010
from typing import Any
1111

@@ -133,7 +133,7 @@ class WebhookEvent:
133133
sender: User
134134
installation_id: int | None = None
135135
payload: dict[str, Any] = field(default_factory=dict)
136-
timestamp: datetime = field(default_factory=datetime.utcnow)
136+
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
137137

138138
# Optional specific event data
139139
pull_request: PullRequest | None = None

0 commit comments

Comments
 (0)