fix: Update imports to use engine.pipeline instead of engine.pipeline.core

The old engine/pipeline/core.py file was removed as part of the Sideline/Mainline split.
All imports that referenced engine.pipeline.core have been updated to use engine.pipeline
which re-exports from sideline.pipeline.core.

This ensures consistency and avoids duplicate DataType enum instances.
This commit is contained in:
2026-03-23 20:45:40 -07:00
parent 98a5862c74
commit fc7f58685a
19 changed files with 66 additions and 373 deletions

View File

@@ -67,13 +67,13 @@ class PipelineIntrospectionSource(DataSource):
@property @property
def inlet_types(self) -> set: def inlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.NONE} return {DataType.NONE}
@property @property
def outlet_types(self) -> set: def outlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.SOURCE_ITEMS} return {DataType.SOURCE_ITEMS}

View File

@@ -20,13 +20,13 @@ class TintEffect(EffectPlugin):
# Define inlet types for PureData-style typing # Define inlet types for PureData-style typing
@property @property
def inlet_types(self) -> set: def inlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.TEXT_BUFFER} return {DataType.TEXT_BUFFER}
@property @property
def outlet_types(self) -> set: def outlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.TEXT_BUFFER} return {DataType.TEXT_BUFFER}

View File

@@ -28,7 +28,7 @@ from engine.effects.types import (
apply_param_bindings, apply_param_bindings,
create_effect_context, create_effect_context,
) )
from engine.pipeline.core import ( from engine.pipeline import (
DataType, DataType,
Stage, Stage,
StageConfig, StageConfig,

View File

@@ -26,6 +26,11 @@ from sideline.pipeline import (
register_source, register_source,
) )
# Also re-export from sideline.core for compatibility
from sideline.pipeline.core import (
DataType,
)
# Re-export from engine.pipeline.presets (Mainline-specific) # Re-export from engine.pipeline.presets (Mainline-specific)
from engine.pipeline.presets import ( from engine.pipeline.presets import (
DEMO_PRESET, DEMO_PRESET,
@@ -91,4 +96,6 @@ __all__ = [
"register_effect", "register_effect",
"register_display", "register_display",
"register_camera", "register_camera",
# Core types (from sideline)
"DataType",
] ]

View File

@@ -3,7 +3,7 @@
import time import time
from typing import Any from typing import Any
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
class CameraClockStage(Stage): class CameraClockStage(Stage):

View File

@@ -8,7 +8,7 @@ This module provides adapters that wrap existing components
from typing import Any from typing import Any
from engine.data_sources import SourceItem from engine.data_sources import SourceItem
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
class DataSourceStage(Stage): class DataSourceStage(Stage):

View File

@@ -2,7 +2,7 @@
from typing import Any from typing import Any
from engine.pipeline.core import PipelineContext, Stage from engine.pipeline import PipelineContext, Stage
class DisplayStage(Stage): class DisplayStage(Stage):
@@ -59,13 +59,13 @@ class DisplayStage(Stage):
@property @property
def inlet_types(self) -> set: def inlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.TEXT_BUFFER} # Display consumes rendered text return {DataType.TEXT_BUFFER} # Display consumes rendered text
@property @property
def outlet_types(self) -> set: def outlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.NONE} # Display is a terminal stage (no output) return {DataType.NONE} # Display is a terminal stage (no output)

View File

@@ -2,7 +2,7 @@
from typing import Any from typing import Any
from engine.pipeline.core import PipelineContext, Stage from engine.pipeline import PipelineContext, Stage
class EffectPluginStage(Stage): class EffectPluginStage(Stage):
@@ -69,13 +69,13 @@ class EffectPluginStage(Stage):
@property @property
def inlet_types(self) -> set: def inlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.TEXT_BUFFER} return {DataType.TEXT_BUFFER}
@property @property
def outlet_types(self) -> set: def outlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.TEXT_BUFFER} return {DataType.TEXT_BUFFER}

View File

@@ -7,7 +7,7 @@ Wraps pipeline stages to capture frames for animation report generation.
from typing import Any from typing import Any
from engine.display.backends.animation_report import AnimationReportDisplay from engine.display.backends.animation_report import AnimationReportDisplay
from engine.pipeline.core import PipelineContext, Stage from engine.pipeline import PipelineContext, Stage
class FrameCaptureStage(Stage): class FrameCaptureStage(Stage):

View File

@@ -12,7 +12,7 @@ from datetime import datetime
from engine import config from engine import config
from engine.effects.legacy import vis_trunc from engine.effects.legacy import vis_trunc
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
from engine.render.blocks import big_wrap from engine.render.blocks import big_wrap
from engine.render.gradient import msg_gradient from engine.render.gradient import msg_gradient

View File

@@ -10,7 +10,7 @@ different ANSI positioning approaches:
from enum import Enum from enum import Enum
from typing import Any from typing import Any
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
class PositioningMode(Enum): class PositioningMode(Enum):

View File

@@ -4,7 +4,7 @@ from typing import Any
import engine.render import engine.render
from engine.data_sources import SourceItem from engine.data_sources import SourceItem
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
def estimate_simple_height(text: str, width: int) -> int: def estimate_simple_height(text: str, width: int) -> int:

View File

@@ -9,7 +9,7 @@ import time
from dataclasses import dataclass, field from dataclasses import dataclass, field
from typing import Any from typing import Any
from engine.pipeline.core import PipelineContext, Stage, StageError, StageResult from engine.pipeline import PipelineContext, Stage, StageError, StageResult
from engine.pipeline.params import PipelineParams from engine.pipeline.params import PipelineParams
from engine.pipeline.registry import StageRegistry from engine.pipeline.registry import StageRegistry
@@ -640,7 +640,7 @@ class Pipeline:
Raises StageError if type mismatch is detected. Raises StageError if type mismatch is detected.
""" """
from engine.pipeline.core import DataType from engine.pipeline import DataType
errors: list[str] = [] errors: list[str] = []

View File

@@ -1,321 +0,0 @@
"""
Pipeline core - Unified Stage abstraction and PipelineContext.
This module provides the foundation for a clean, dependency-managed pipeline:
- Stage: Base class for all pipeline components (sources, effects, displays, cameras)
- PipelineContext: Dependency injection context for runtime data exchange
- Capability system: Explicit capability declarations with duck-typing support
- DataType: PureData-style inlet/outlet typing for validation
"""
from abc import ABC, abstractmethod
from collections.abc import Callable
from dataclasses import dataclass, field
from enum import Enum, auto
from typing import TYPE_CHECKING, Any
if TYPE_CHECKING:
from engine.pipeline.params import PipelineParams
class DataType(Enum):
"""PureData-style data types for inlet/outlet validation.
Each type represents a specific data format that flows through the pipeline.
This enables compile-time-like validation of connections.
Examples:
SOURCE_ITEMS: List[SourceItem] - raw items from sources
ITEM_TUPLES: List[tuple] - (title, source, timestamp) tuples
TEXT_BUFFER: List[str] - rendered ANSI buffer for display
RAW_TEXT: str - raw text strings
PIL_IMAGE: PIL Image object
"""
SOURCE_ITEMS = auto() # List[SourceItem] - from DataSource
ITEM_TUPLES = auto() # List[tuple] - (title, source, ts)
TEXT_BUFFER = auto() # List[str] - ANSI buffer
RAW_TEXT = auto() # str - raw text
PIL_IMAGE = auto() # PIL Image object
ANY = auto() # Accepts any type
NONE = auto() # No data (terminator)
@dataclass
class StageConfig:
"""Configuration for a single stage."""
name: str
category: str
enabled: bool = True
optional: bool = False
params: dict[str, Any] = field(default_factory=dict)
class Stage(ABC):
"""Abstract base class for all pipeline stages.
A Stage is a single component in the rendering pipeline. Stages can be:
- Sources: Data providers (headlines, poetry, pipeline viz)
- Effects: Post-processors (noise, fade, glitch, hud)
- Displays: Output backends (terminal, pygame, websocket)
- Cameras: Viewport controllers (vertical, horizontal, omni)
- Overlays: UI elements that compose on top (HUD)
Stages declare:
- capabilities: What they provide to other stages
- dependencies: What they need from other stages
- stage_type: Category of stage (source, effect, overlay, display)
- render_order: Execution order within category
- is_overlay: If True, output is composited on top, not passed downstream
Duck-typing is supported: any class with the required methods can act as a Stage.
"""
name: str
category: str # "source", "effect", "overlay", "display", "camera"
optional: bool = False # If True, pipeline continues even if stage fails
@property
def stage_type(self) -> str:
"""Category of stage for ordering.
Valid values: "source", "effect", "overlay", "display", "camera"
Defaults to category for backwards compatibility.
"""
return self.category
@property
def render_order(self) -> int:
"""Execution order within stage_type group.
Higher values execute later. Useful for ordering overlays
or effects that need specific execution order.
"""
return 0
@property
def is_overlay(self) -> bool:
"""If True, this stage's output is composited on top of the buffer.
Overlay stages don't pass their output to the next stage.
Instead, their output is layered on top of the final buffer.
Use this for HUD, status displays, and similar UI elements.
"""
return False
@property
def inlet_types(self) -> set[DataType]:
"""Return set of data types this stage accepts.
PureData-style inlet typing. If the connected upstream stage's
outlet_type is not in this set, the pipeline will raise an error.
Examples:
- Source stages: {DataType.NONE} (no input needed)
- Transform stages: {DataType.ITEM_TUPLES, DataType.TEXT_BUFFER}
- Display stages: {DataType.TEXT_BUFFER}
"""
return {DataType.ANY}
@property
def outlet_types(self) -> set[DataType]:
"""Return set of data types this stage produces.
PureData-style outlet typing. Downstream stages must accept
this type in their inlet_types.
Examples:
- Source stages: {DataType.SOURCE_ITEMS}
- Transform stages: {DataType.TEXT_BUFFER}
- Display stages: {DataType.NONE} (consumes data)
"""
return {DataType.ANY}
@property
def capabilities(self) -> set[str]:
"""Return set of capabilities this stage provides.
Examples:
- "source.headlines"
- "effect.noise"
- "display.output"
- "camera"
"""
return {f"{self.category}.{self.name}"}
@property
def dependencies(self) -> set[str]:
"""Return set of capability names this stage needs.
Examples:
- {"display.output"}
- {"source.headlines"}
- {"camera"}
"""
return set()
@property
def stage_dependencies(self) -> set[str]:
"""Return set of stage names this stage must connect to directly.
This allows explicit stage-to-stage dependencies, useful for enforcing
pipeline structure when capability matching alone is insufficient.
Examples:
- {"viewport_filter"} # Must connect to viewport_filter stage
- {"camera_update"} # Must connect to camera_update stage
NOTE: These are stage names (as added to pipeline), not capabilities.
"""
return set()
def init(self, ctx: "PipelineContext") -> bool:
"""Initialize stage with pipeline context.
Args:
ctx: PipelineContext for accessing services
Returns:
True if initialization succeeded, False otherwise
"""
return True
@abstractmethod
def process(self, data: Any, ctx: "PipelineContext") -> Any:
"""Process input data and return output.
Args:
data: Input data from previous stage (or initial data for first stage)
ctx: PipelineContext for accessing services and state
Returns:
Processed data for next stage
"""
...
def cleanup(self) -> None: # noqa: B027
"""Clean up resources when pipeline shuts down."""
pass
def get_config(self) -> StageConfig:
"""Return current configuration of this stage."""
return StageConfig(
name=self.name,
category=self.category,
optional=self.optional,
)
def set_enabled(self, enabled: bool) -> None:
"""Enable or disable this stage."""
self._enabled = enabled # type: ignore[attr-defined]
def is_enabled(self) -> bool:
"""Check if stage is enabled."""
return getattr(self, "_enabled", True)
@dataclass
class StageResult:
"""Result of stage processing, including success/failure info."""
success: bool
data: Any
error: str | None = None
stage_name: str = ""
class PipelineContext:
"""Dependency injection context passed through the pipeline.
Provides:
- services: Named services (display, config, event_bus, etc.)
- state: Runtime state shared between stages
- params: PipelineParams for animation-driven config
Services can be injected at construction time or lazily resolved.
"""
def __init__(
self,
services: dict[str, Any] | None = None,
initial_state: dict[str, Any] | None = None,
):
self.services: dict[str, Any] = services or {}
self.state: dict[str, Any] = initial_state or {}
self._params: PipelineParams | None = None
# Lazy resolvers for common services
self._lazy_resolvers: dict[str, Callable[[], Any]] = {
"config": self._resolve_config,
"event_bus": self._resolve_event_bus,
}
def _resolve_config(self) -> Any:
from engine.config import get_config
return get_config()
def _resolve_event_bus(self) -> Any:
from engine.eventbus import get_event_bus
return get_event_bus()
def get(self, key: str, default: Any = None) -> Any:
"""Get a service or state value by key.
First checks services, then state, then lazy resolution.
"""
if key in self.services:
return self.services[key]
if key in self.state:
return self.state[key]
if key in self._lazy_resolvers:
try:
return self._lazy_resolvers[key]()
except Exception:
return default
return default
def set(self, key: str, value: Any) -> None:
"""Set a service or state value."""
self.services[key] = value
def set_state(self, key: str, value: Any) -> None:
"""Set a runtime state value."""
self.state[key] = value
def get_state(self, key: str, default: Any = None) -> Any:
"""Get a runtime state value."""
return self.state.get(key, default)
@property
def params(self) -> "PipelineParams | None":
"""Get current pipeline params (for animation)."""
return self._params
@params.setter
def params(self, value: "PipelineParams") -> None:
"""Set pipeline params (from animation controller)."""
self._params = value
def has_capability(self, capability: str) -> bool:
"""Check if a capability is available."""
return capability in self.services or capability in self._lazy_resolvers
class StageError(Exception):
"""Raised when a stage fails to process."""
def __init__(self, stage_name: str, message: str, is_optional: bool = False):
self.stage_name = stage_name
self.message = message
self.is_optional = is_optional
super().__init__(f"Stage '{stage_name}' failed: {message}")
def create_stage_error(
stage_name: str, error: Exception, is_optional: bool = False
) -> StageError:
"""Helper to create a StageError from an exception."""
return StageError(stage_name, str(error), is_optional)

View File

@@ -8,10 +8,10 @@ from __future__ import annotations
from typing import TYPE_CHECKING, Any, TypeVar from typing import TYPE_CHECKING, Any, TypeVar
from engine.pipeline.core import Stage from engine.pipeline import Stage
if TYPE_CHECKING: if TYPE_CHECKING:
from engine.pipeline.core import Stage from engine.pipeline import Stage
T = TypeVar("T") T = TypeVar("T")

View File

@@ -14,7 +14,7 @@ from dataclasses import dataclass
from typing import Any from typing import Any
from engine.display import _strip_ansi from engine.display import _strip_ansi
from engine.pipeline.core import DataType, PipelineContext, Stage from engine.pipeline import DataType, PipelineContext, Stage
@dataclass @dataclass

View File

@@ -25,7 +25,7 @@ from dataclasses import dataclass
from typing import TYPE_CHECKING, Any from typing import TYPE_CHECKING, Any
if TYPE_CHECKING: if TYPE_CHECKING:
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
@dataclass @dataclass
@@ -166,13 +166,13 @@ class SensorStage:
@property @property
def inlet_types(self) -> set: def inlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.ANY} return {DataType.ANY}
@property @property
def outlet_types(self) -> set: def outlet_types(self) -> set:
from engine.pipeline.core import DataType from engine.pipeline import DataType
return {DataType.ANY} return {DataType.ANY}

View File

@@ -423,7 +423,7 @@ class Pipeline:
List of stages that were injected List of stages that were injected
""" """
from sideline.camera import Camera from sideline.camera import Camera
from sideline.data_sources.sources import EmptyDataSource from engine.data_sources.sources import EmptyDataSource
from sideline.display import DisplayRegistry from sideline.display import DisplayRegistry
from sideline.pipeline.adapters import ( from sideline.pipeline.adapters import (
CameraClockStage, CameraClockStage,
@@ -1033,7 +1033,7 @@ def create_pipeline_from_params(params: PipelineParams) -> Pipeline:
def create_default_pipeline() -> Pipeline: def create_default_pipeline() -> Pipeline:
"""Create a default pipeline with all standard components.""" """Create a default pipeline with all standard components."""
from sideline.data_sources.sources import HeadlinesDataSource from engine.data_sources.sources import HeadlinesDataSource
from sideline.pipeline.adapters import ( from sideline.pipeline.adapters import (
DataSourceStage, DataSourceStage,
SourceItemsToBufferStage, SourceItemsToBufferStage,

View File

@@ -15,7 +15,7 @@ from engine.pipeline import (
create_default_pipeline, create_default_pipeline,
discover_stages, discover_stages,
) )
from engine.pipeline.core import DataType, StageError from engine.pipeline import DataType, StageError
class TestStageRegistry: class TestStageRegistry:
@@ -118,7 +118,7 @@ class TestPipeline:
def test_build_resolves_dependencies(self): def test_build_resolves_dependencies(self):
"""Pipeline.build resolves execution order.""" """Pipeline.build resolves execution order."""
from engine.pipeline.core import DataType from engine.pipeline import DataType
pipeline = Pipeline() pipeline = Pipeline()
mock_source = MagicMock(spec=Stage) mock_source = MagicMock(spec=Stage)
@@ -153,7 +153,7 @@ class TestPipeline:
def test_execute_runs_stages(self): def test_execute_runs_stages(self):
"""Pipeline.execute runs all stages in order.""" """Pipeline.execute runs all stages in order."""
from engine.pipeline.core import DataType from engine.pipeline import DataType
pipeline = Pipeline() pipeline = Pipeline()
@@ -283,7 +283,7 @@ class TestCapabilityBasedDependencies:
def test_capability_wildcard_resolution(self): def test_capability_wildcard_resolution(self):
"""Pipeline resolves dependencies using wildcard capabilities.""" """Pipeline resolves dependencies using wildcard capabilities."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class SourceStage(Stage): class SourceStage(Stage):
name = "headlines" name = "headlines"
@@ -329,7 +329,7 @@ class TestCapabilityBasedDependencies:
def test_missing_capability_raises_error(self): def test_missing_capability_raises_error(self):
"""Pipeline raises error when capability is missing.""" """Pipeline raises error when capability is missing."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage, StageError from engine.pipeline import Stage, StageError
class RenderStage(Stage): class RenderStage(Stage):
name = "render" name = "render"
@@ -359,7 +359,7 @@ class TestCapabilityBasedDependencies:
def test_multiple_stages_same_capability(self): def test_multiple_stages_same_capability(self):
"""Pipeline uses first registered stage for capability.""" """Pipeline uses first registered stage for capability."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class SourceA(Stage): class SourceA(Stage):
name = "headlines" name = "headlines"
@@ -458,8 +458,15 @@ class TestPipelineContext:
"""PipelineContext resolves lazy services.""" """PipelineContext resolves lazy services."""
ctx = PipelineContext() ctx = PipelineContext()
# Register a lazy service resolver
from unittest.mock import MagicMock
mock_config = MagicMock()
ctx.register_service("config", lambda: mock_config)
config = ctx.get("config") config = ctx.get("config")
assert config is not None assert config is not None
assert config == mock_config
def test_has_capability(self): def test_has_capability(self):
"""PipelineContext.has_capability checks for services.""" """PipelineContext.has_capability checks for services."""
@@ -608,7 +615,7 @@ class TestStageAdapters:
"""DisplayStage.init initializes display.""" """DisplayStage.init initializes display."""
from engine.display.backends.null import NullDisplay from engine.display.backends.null import NullDisplay
from engine.pipeline.adapters import DisplayStage from engine.pipeline.adapters import DisplayStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
from engine.pipeline.params import PipelineParams from engine.pipeline.params import PipelineParams
display = NullDisplay() display = NullDisplay()
@@ -623,7 +630,7 @@ class TestStageAdapters:
"""DisplayStage.process forwards to display.""" """DisplayStage.process forwards to display."""
from engine.display.backends.null import NullDisplay from engine.display.backends.null import NullDisplay
from engine.pipeline.adapters import DisplayStage from engine.pipeline.adapters import DisplayStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
from engine.pipeline.params import PipelineParams from engine.pipeline.params import PipelineParams
display = NullDisplay() display = NullDisplay()
@@ -640,7 +647,7 @@ class TestStageAdapters:
"""CameraStage applies camera transform.""" """CameraStage applies camera transform."""
from engine.camera import Camera, CameraMode from engine.camera import Camera, CameraMode
from engine.pipeline.adapters import CameraStage from engine.pipeline.adapters import CameraStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
camera = Camera(mode=CameraMode.FEED) camera = Camera(mode=CameraMode.FEED)
stage = CameraStage(camera, name="vertical") stage = CameraStage(camera, name="vertical")
@@ -658,7 +665,7 @@ class TestStageAdapters:
""" """
from engine.camera import Camera, CameraMode from engine.camera import Camera, CameraMode
from engine.pipeline.adapters import CameraStage from engine.pipeline.adapters import CameraStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
from engine.pipeline.params import PipelineParams from engine.pipeline.params import PipelineParams
camera = Camera(mode=CameraMode.FEED) camera = Camera(mode=CameraMode.FEED)
@@ -696,7 +703,7 @@ class TestDataSourceStage:
from engine.data_sources.sources import HeadlinesDataSource from engine.data_sources.sources import HeadlinesDataSource
from engine.pipeline.adapters import DataSourceStage from engine.pipeline.adapters import DataSourceStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
mock_items = [ mock_items = [
("Test Headline 1", "TestSource", "12:00"), ("Test Headline 1", "TestSource", "12:00"),
@@ -739,7 +746,7 @@ class TestEffectPluginStage:
"""EffectPluginStage applies sensor param bindings.""" """EffectPluginStage applies sensor param bindings."""
from engine.effects.types import EffectConfig, EffectPlugin from engine.effects.types import EffectConfig, EffectPlugin
from engine.pipeline.adapters import EffectPluginStage from engine.pipeline.adapters import EffectPluginStage
from engine.pipeline.core import PipelineContext from engine.pipeline import PipelineContext
from engine.pipeline.params import PipelineParams from engine.pipeline.params import PipelineParams
class SensorDrivenEffect(EffectPlugin): class SensorDrivenEffect(EffectPlugin):
@@ -772,7 +779,7 @@ class TestFullPipeline:
def test_pipeline_circular_dependency_detection(self): def test_pipeline_circular_dependency_detection(self):
"""Pipeline detects circular dependencies.""" """Pipeline detects circular dependencies."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class StageA(Stage): class StageA(Stage):
name = "a" name = "a"
@@ -819,7 +826,7 @@ class TestPipelineMetrics:
def test_metrics_collected(self): def test_metrics_collected(self):
"""Pipeline collects metrics when enabled.""" """Pipeline collects metrics when enabled."""
from engine.pipeline.controller import Pipeline, PipelineConfig from engine.pipeline.controller import Pipeline, PipelineConfig
from engine.pipeline.core import Stage from engine.pipeline import Stage
class DummyStage(Stage): class DummyStage(Stage):
name = "dummy" name = "dummy"
@@ -842,7 +849,7 @@ class TestPipelineMetrics:
def test_metrics_disabled(self): def test_metrics_disabled(self):
"""Pipeline skips metrics when disabled.""" """Pipeline skips metrics when disabled."""
from engine.pipeline.controller import Pipeline, PipelineConfig from engine.pipeline.controller import Pipeline, PipelineConfig
from engine.pipeline.core import Stage from engine.pipeline import Stage
class DummyStage(Stage): class DummyStage(Stage):
name = "dummy" name = "dummy"
@@ -864,7 +871,7 @@ class TestPipelineMetrics:
def test_reset_metrics(self): def test_reset_metrics(self):
"""Pipeline.reset_metrics clears collected metrics.""" """Pipeline.reset_metrics clears collected metrics."""
from engine.pipeline.controller import Pipeline, PipelineConfig from engine.pipeline.controller import Pipeline, PipelineConfig
from engine.pipeline.core import Stage from engine.pipeline import Stage
class DummyStage(Stage): class DummyStage(Stage):
name = "dummy" name = "dummy"
@@ -894,7 +901,7 @@ class TestOverlayStages:
def test_stage_is_overlay_property(self): def test_stage_is_overlay_property(self):
"""Stage has is_overlay property defaulting to False.""" """Stage has is_overlay property defaulting to False."""
from engine.pipeline.core import Stage from engine.pipeline import Stage
class TestStage(Stage): class TestStage(Stage):
name = "test" name = "test"
@@ -908,7 +915,7 @@ class TestOverlayStages:
def test_stage_render_order_property(self): def test_stage_render_order_property(self):
"""Stage has render_order property defaulting to 0.""" """Stage has render_order property defaulting to 0."""
from engine.pipeline.core import Stage from engine.pipeline import Stage
class TestStage(Stage): class TestStage(Stage):
name = "test" name = "test"
@@ -922,7 +929,7 @@ class TestOverlayStages:
def test_stage_stage_type_property(self): def test_stage_stage_type_property(self):
"""Stage has stage_type property defaulting to category.""" """Stage has stage_type property defaulting to category."""
from engine.pipeline.core import Stage from engine.pipeline import Stage
class TestStage(Stage): class TestStage(Stage):
name = "test" name = "test"
@@ -937,7 +944,7 @@ class TestOverlayStages:
def test_pipeline_get_overlay_stages(self): def test_pipeline_get_overlay_stages(self):
"""Pipeline.get_overlay_stages returns overlay stages sorted by render_order.""" """Pipeline.get_overlay_stages returns overlay stages sorted by render_order."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class OverlayStageA(Stage): class OverlayStageA(Stage):
name = "overlay_a" name = "overlay_a"
@@ -991,7 +998,7 @@ class TestOverlayStages:
def test_pipeline_executes_overlays_after_regular(self): def test_pipeline_executes_overlays_after_regular(self):
"""Pipeline executes overlays after regular stages.""" """Pipeline executes overlays after regular stages."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
call_order = [] call_order = []
@@ -1071,7 +1078,7 @@ class TestOverlayStages:
def test_pipeline_get_stage_type(self): def test_pipeline_get_stage_type(self):
"""Pipeline.get_stage_type returns stage_type for a stage.""" """Pipeline.get_stage_type returns stage_type for a stage."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class TestStage(Stage): class TestStage(Stage):
name = "test" name = "test"
@@ -1093,7 +1100,7 @@ class TestOverlayStages:
def test_pipeline_get_render_order(self): def test_pipeline_get_render_order(self):
"""Pipeline.get_render_order returns render_order for a stage.""" """Pipeline.get_render_order returns render_order for a stage."""
from engine.pipeline.controller import Pipeline from engine.pipeline.controller import Pipeline
from engine.pipeline.core import Stage from engine.pipeline import Stage
class TestStage(Stage): class TestStage(Stage):
name = "test" name = "test"
@@ -1341,7 +1348,7 @@ class TestPipelineMutation:
dependencies: set | None = None, dependencies: set | None = None,
): ):
"""Helper to create a mock stage.""" """Helper to create a mock stage."""
from engine.pipeline.core import DataType from engine.pipeline import DataType
mock = MagicMock(spec=Stage) mock = MagicMock(spec=Stage)
mock.name = name mock.name = name
@@ -1693,7 +1700,7 @@ class TestPipelineMutation:
def test_mutation_preserves_execution_for_remaining_stages(self): def test_mutation_preserves_execution_for_remaining_stages(self):
"""Removing a stage doesn't break execution of remaining stages.""" """Removing a stage doesn't break execution of remaining stages."""
from engine.pipeline.core import DataType from engine.pipeline import DataType
call_log = [] call_log = []