Models API Reference

Data models for reqtrace requirements and traces.

FileStats dataclass

Statistics for a single source file.

Source code in reqtrace/models.py
@dataclass
class FileStats:
    """Statistics for a single source file."""

    file_path: str
    total_lines: int
    mapped_lines: int
    unmapped_ranges: List[Tuple[int, int]]
    is_disabled: bool = False

Requirement dataclass

A defined project requirement.

Source code in reqtrace/models.py
@dataclass
# pylint: disable=too-many-instance-attributes
class Requirement:
    """A defined project requirement."""

    id: str
    title: str
    description: str = ""
    derived_from: List[str] = field(default_factory=list)

    # Source location
    file_path: Optional[str] = None
    line_number: Optional[int] = None

    # Git Metadata (optional, filled during analysis)
    created: Optional[GitMetadata] = None
    last_changed: Optional[GitMetadata] = None

RequirementIndex

An index of all loaded requirements.

Source code in reqtrace/models.py
class RequirementIndex:
    """An index of all loaded requirements."""

    def __init__(self):
        self.requirements: Dict[str, Requirement] = {}

    def add(self, req: Requirement):
        """Adds a requirement to the index."""
        if req.id in self.requirements:
            raise ValueError(f"Requirement with id '{req.id}' already exists.")
        self.requirements[req.id] = req

    def get(self, req_id: str) -> Optional[Requirement]:
        """Gets a requirement by its ID."""
        return self.requirements.get(req_id)

    def validate_graph(self):
        """Validates that all parent IDs exist and there are no cyclic dependencies."""
        # 1. Check for missing parents
        for req in self.requirements.values():
            for parent_id in req.derived_from:
                if parent_id not in self.requirements:
                    raise ValueError(f"Requirement '{req.id}' derives from unknown requirement '{parent_id}'")

        # 2. Check for cycles using DFS
        visited = set()
        recursion_stack = set()

        def dfs(req_id: str):
            visited.add(req_id)
            recursion_stack.add(req_id)

            req = self.requirements[req_id]
            for parent_id in req.derived_from:
                if parent_id not in visited:
                    dfs(parent_id)
                elif parent_id in recursion_stack:
                    raise ValueError(f"Cyclic dependency detected involving requirement '{parent_id}'")

            recursion_stack.remove(req_id)

        for req_id in self.requirements:
            if req_id not in visited:
                dfs(req_id)

add(req)

Adds a requirement to the index.

Source code in reqtrace/models.py
def add(self, req: Requirement):
    """Adds a requirement to the index."""
    if req.id in self.requirements:
        raise ValueError(f"Requirement with id '{req.id}' already exists.")
    self.requirements[req.id] = req

get(req_id)

Gets a requirement by its ID.

Source code in reqtrace/models.py
def get(self, req_id: str) -> Optional[Requirement]:
    """Gets a requirement by its ID."""
    return self.requirements.get(req_id)

validate_graph()

Validates that all parent IDs exist and there are no cyclic dependencies.

Source code in reqtrace/models.py
def validate_graph(self):
    """Validates that all parent IDs exist and there are no cyclic dependencies."""
    # 1. Check for missing parents
    for req in self.requirements.values():
        for parent_id in req.derived_from:
            if parent_id not in self.requirements:
                raise ValueError(f"Requirement '{req.id}' derives from unknown requirement '{parent_id}'")

    # 2. Check for cycles using DFS
    visited = set()
    recursion_stack = set()

    def dfs(req_id: str):
        visited.add(req_id)
        recursion_stack.add(req_id)

        req = self.requirements[req_id]
        for parent_id in req.derived_from:
            if parent_id not in visited:
                dfs(parent_id)
            elif parent_id in recursion_stack:
                raise ValueError(f"Cyclic dependency detected involving requirement '{parent_id}'")

        recursion_stack.remove(req_id)

    for req_id in self.requirements:
        if req_id not in visited:
            dfs(req_id)

SourceStats dataclass

Statistics about the scanned source code.

Source code in reqtrace/models.py
@dataclass
class SourceStats:
    """Statistics about the scanned source code."""

    total_files: int
    total_lines: int
    mapped_lines: int
    unmapped_lines: int
    disabled_files: int = 0
    file_stats: List[FileStats] = field(default_factory=list)

TraceMatch dataclass

A trace tag block found within the source code.

Source code in reqtrace/models.py
@dataclass
class TraceMatch:
    """A trace tag block found within the source code."""

    file_path: str
    line_start: int
    line_end: int
    req_id: str
    percentage: Optional[int] = None
    hash: Optional[str] = None

    # Git Metadata (optional, filled during analysis)
    history: List[GitMetadata] = field(default_factory=list)
    first_implemented: Optional[GitMetadata] = None
    last_changed: Optional[GitMetadata] = None

    def __hash__(self):
        return hash((self.file_path, self.line_start, self.line_end, self.req_id, self.percentage, self.hash))

    def __eq__(self, other):
        if not isinstance(other, TraceMatch):
            return False
        return (
            self.file_path == other.file_path
            and self.line_start == other.line_start
            and self.line_end == other.line_end
            and self.req_id == other.req_id
            and self.percentage == other.percentage
            and self.hash == other.hash
        )