refactor(docs+comments): add Google-style docstrings and inline comments across backend
Task D — Google-style docstrings (Args/Returns) on every public function, method, and class across all 158 Python files in the backend. Zero ruff D violations (pydocstyle Google convention). Task E — Explanatory one-line comment before every code line (~11600 new comments). ruff check passes clean after isort re-sort.
This commit is contained in:
@@ -1,4 +1,9 @@
|
||||
"""Immutable domain value objects."""
|
||||
# Import MitreId from app.domain.value_objects.mitre_id
|
||||
from app.domain.value_objects.mitre_id import MitreId
|
||||
|
||||
# Import ScoringWeights from app.domain.value_objects.scoring_weights
|
||||
from app.domain.value_objects.scoring_weights import ScoringWeights
|
||||
|
||||
# Assign __all__ = ["MitreId", "ScoringWeights"]
|
||||
__all__ = ["MitreId", "ScoringWeights"]
|
||||
|
||||
@@ -5,47 +5,111 @@ format: ``T`` followed by 4 digits, optionally a dot and 3 more digits
|
||||
for sub-techniques (e.g. ``T1059``, ``T1059.001``).
|
||||
"""
|
||||
|
||||
# Enable future language features for compatibility
|
||||
from __future__ import annotations
|
||||
|
||||
# Import re
|
||||
import re
|
||||
|
||||
# Import dataclass from dataclasses
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Assign _MITRE_ID_RE = re.compile(r"^T\d{4}(\.\d{3})?$")
|
||||
_MITRE_ID_RE = re.compile(r"^T\d{4}(\.\d{3})?$")
|
||||
|
||||
|
||||
# Apply the @dataclass decorator
|
||||
@dataclass(frozen=True, slots=True)
|
||||
# Define class MitreId
|
||||
class MitreId:
|
||||
"""Validated MITRE ATT&CK technique identifier."""
|
||||
|
||||
# value: str
|
||||
value: str
|
||||
|
||||
# Define function __post_init__
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate that *value* matches the expected MITRE ATT&CK ID format.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# Check: not _MITRE_ID_RE.match(self.value)
|
||||
if not _MITRE_ID_RE.match(self.value):
|
||||
# Raise ValueError
|
||||
raise ValueError(
|
||||
f"Invalid MITRE ATT&CK ID '{self.value}'. "
|
||||
# Literal argument value
|
||||
"Expected format: T1234 or T1234.001"
|
||||
)
|
||||
|
||||
# Apply the @property decorator
|
||||
@property
|
||||
# Define function is_subtechnique
|
||||
def is_subtechnique(self) -> bool:
|
||||
"""Return True if this identifier represents a sub-technique.
|
||||
|
||||
Returns:
|
||||
bool: True when the ID contains a dot (e.g. ``T1059.001``).
|
||||
"""
|
||||
# Return "." in self.value
|
||||
return "." in self.value
|
||||
|
||||
# Apply the @property decorator
|
||||
@property
|
||||
# Define function parent_id
|
||||
def parent_id(self) -> str | None:
|
||||
"""Return the parent technique ID (e.g. T1059 for T1059.001)."""
|
||||
"""Return the parent technique ID (e.g. ``T1059`` for ``T1059.001``).
|
||||
|
||||
Returns:
|
||||
str | None: The parent ID string, or None if this is not a sub-technique.
|
||||
"""
|
||||
# Check: not self.is_subtechnique
|
||||
if not self.is_subtechnique:
|
||||
# Return None
|
||||
return None
|
||||
# Return self.value.split(".")[0]
|
||||
return self.value.split(".")[0]
|
||||
|
||||
# Define function __str__
|
||||
def __str__(self) -> str:
|
||||
"""Return the string representation of the MITRE ID.
|
||||
|
||||
Returns:
|
||||
str: The raw identifier string (e.g. ``"T1059.001"``).
|
||||
"""
|
||||
# Return self.value
|
||||
return self.value
|
||||
|
||||
# Define function __eq__
|
||||
def __eq__(self, other: object) -> bool:
|
||||
"""Compare this MitreId to another MitreId or a plain string.
|
||||
|
||||
Args:
|
||||
other (object): The value to compare against; may be a
|
||||
:class:`MitreId` instance or a plain ``str``.
|
||||
|
||||
Returns:
|
||||
bool: True if the identifiers are equal, NotImplemented for
|
||||
unsupported types.
|
||||
"""
|
||||
# Check: isinstance(other, MitreId)
|
||||
if isinstance(other, MitreId):
|
||||
# Return self.value == other.value
|
||||
return self.value == other.value
|
||||
# Check: isinstance(other, str)
|
||||
if isinstance(other, str):
|
||||
# Return self.value == other
|
||||
return self.value == other
|
||||
# Return NotImplemented
|
||||
return NotImplemented
|
||||
|
||||
# Define function __hash__
|
||||
def __hash__(self) -> int:
|
||||
"""Return the hash of the identifier string.
|
||||
|
||||
Returns:
|
||||
int: Hash value derived from the raw identifier string.
|
||||
"""
|
||||
# Return hash(self.value)
|
||||
return hash(self.value)
|
||||
|
||||
@@ -3,22 +3,38 @@
|
||||
Enforces that all five weights are non-negative and sum to exactly 100.
|
||||
"""
|
||||
|
||||
# Enable future language features for compatibility
|
||||
from __future__ import annotations
|
||||
|
||||
# Import dataclass from dataclasses
|
||||
from dataclasses import dataclass
|
||||
|
||||
|
||||
# Apply the @dataclass decorator
|
||||
@dataclass(frozen=True, slots=True)
|
||||
# Define class ScoringWeights
|
||||
class ScoringWeights:
|
||||
"""Five scoring dimension weights that must sum to 100."""
|
||||
|
||||
# tests: float
|
||||
tests: float
|
||||
# detection_rules: float
|
||||
detection_rules: float
|
||||
# d3fend: float
|
||||
d3fend: float
|
||||
# recency: float
|
||||
recency: float
|
||||
# severity: float
|
||||
severity: float
|
||||
|
||||
# Define function __post_init__
|
||||
def __post_init__(self) -> None:
|
||||
"""Validate that all weights are non-negative and sum to exactly 100.
|
||||
|
||||
Returns:
|
||||
None
|
||||
"""
|
||||
# Assign fields = [
|
||||
fields = [
|
||||
self.tests,
|
||||
self.detection_rules,
|
||||
@@ -26,32 +42,66 @@ class ScoringWeights:
|
||||
self.recency,
|
||||
self.severity,
|
||||
]
|
||||
# Iterate over fields
|
||||
for f in fields:
|
||||
# Check: f < 0
|
||||
if f < 0:
|
||||
# Raise ValueError
|
||||
raise ValueError("Scoring weights must be non-negative")
|
||||
|
||||
# Assign total = sum(fields)
|
||||
total = sum(fields)
|
||||
# Check: abs(total - 100) > 0.01
|
||||
if abs(total - 100) > 0.01:
|
||||
# Raise ValueError
|
||||
raise ValueError(
|
||||
f"Scoring weights must sum to 100, got {total}"
|
||||
)
|
||||
|
||||
# Apply the @classmethod decorator
|
||||
@classmethod
|
||||
# Define function default
|
||||
def default(cls) -> ScoringWeights:
|
||||
"""Return the default weight distribution."""
|
||||
"""Return the default weight distribution.
|
||||
|
||||
Returns:
|
||||
ScoringWeights: A weight set with tests=40, detection_rules=25,
|
||||
d3fend=15, recency=10, severity=10.
|
||||
"""
|
||||
# Return cls(
|
||||
return cls(
|
||||
# Keyword argument: tests
|
||||
tests=40.0,
|
||||
# Keyword argument: detection_rules
|
||||
detection_rules=25.0,
|
||||
# Keyword argument: d3fend
|
||||
d3fend=15.0,
|
||||
# Keyword argument: recency
|
||||
recency=10.0,
|
||||
# Keyword argument: severity
|
||||
severity=10.0,
|
||||
)
|
||||
|
||||
# Backward-compatible aliases for older API payloads
|
||||
@property
|
||||
# Define function freshness
|
||||
def freshness(self) -> float:
|
||||
"""Return the recency weight (backward-compatible alias).
|
||||
|
||||
Returns:
|
||||
float: The value of the ``recency`` weight.
|
||||
"""
|
||||
# Return self.recency
|
||||
return self.recency
|
||||
|
||||
# Apply the @property decorator
|
||||
@property
|
||||
# Define function platform_diversity
|
||||
def platform_diversity(self) -> float:
|
||||
"""Return the severity weight (backward-compatible alias).
|
||||
|
||||
Returns:
|
||||
float: The value of the ``severity`` weight.
|
||||
"""
|
||||
# Return self.severity
|
||||
return self.severity
|
||||
|
||||
Reference in New Issue
Block a user