Skip to main content
Every major class in the library — validators, algorithms, checkers — follows the same constructor signature, configuration, and logging conventions. This page documents the Component base class, ComponentConfig dataclass, and the patterns you should follow when extending the codebase.

Overview

from myspellchecker.core.component import Component, ComponentConfig
from dataclasses import dataclass

@dataclass
class MyConfig(ComponentConfig):
    max_size: int = 100
    threshold: float = 0.5

class MyComponent(Component[MyConfig]):
    def __init__(
        self,
        config: MyConfig,
        provider: SomeProvider,
        *,
        cache: Optional[Cache] = None,
    ):
        super().__init__(config)
        self.provider = provider
        self.cache = cache

Design Principles

Constructor Signature Standards

All components follow this parameter ordering:
class StandardComponent(Component[ConfigType]):
    def __init__(
        self,
        config: ConfigType,           # 1. Config FIRST
        provider: DictionaryProvider, # 2. Required dependencies
        segmenter: Segmenter,         # 2. More required deps
        *,                            # 3. Keyword-only separator
        cache: Optional[Cache] = None,# 4. Optional with defaults
        debug: bool = False,          # 4. More optionals
    ):
        super().__init__(config)
        self.provider = provider
        self.segmenter = segmenter
        self.cache = cache
        self.debug = debug
Why this order:
  1. Config first: Central configuration is always available
  2. Dependencies second: Required services are explicit
  3. Optionals last: Defaults reduce boilerplate

ComponentConfig

Base class for all configuration objects:
from dataclasses import dataclass
from typing import Any, Dict

@dataclass
class ComponentConfig:
    """Base configuration class for components."""

    name: str = ""          # Optional component name
    enabled: bool = True    # Enable/disable flag

    def validate(self) -> bool:
        """Validate the configuration.

        Override to add custom validation logic.

        Returns:
            True if configuration is valid.
        """
        return True

    def to_dict(self) -> Dict[str, Any]:
        """Convert configuration to dictionary."""
        from dataclasses import asdict
        return asdict(self)

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "ComponentConfig":
        """Create configuration from dictionary."""
        valid_fields = {f.name for f in cls.__dataclass_fields__.values()}
        filtered = {k: v for k, v in data.items() if k in valid_fields}
        return cls(**filtered)

Creating Custom Configs

from dataclasses import dataclass
from myspellchecker.core.component import ComponentConfig

@dataclass
class SymSpellConfig(ComponentConfig):
    """Configuration for SymSpell algorithm."""

    prefix_length: int = 10
    max_edit_distance: int = 2
    beam_width: int = 50

    def validate(self) -> bool:
        """Validate SymSpell parameters."""
        if self.prefix_length < 1:
            return False
        if self.max_edit_distance < 0:
            return False
        if self.beam_width < 1:
            return False
        return True


@dataclass
class ValidatorConfig(ComponentConfig):
    """Configuration for text validators."""

    strict_mode: bool = False
    max_word_length: int = 50
    min_frequency: int = 1

    def validate(self) -> bool:
        return self.max_word_length > 0

Component Base Class

Generic base class for configurable components:
from abc import ABC
from typing import Generic, TypeVar, Any, Dict
import logging

C = TypeVar("C", bound=ComponentConfig)

class Component(ABC, Generic[C]):
    """Base class for configurable components.

    Provides:
    - Configuration object as first parameter
    - Automatic logger setup
    - Configuration validation
    - Common metadata handling
    """

    def __init__(self, config: C) -> None:
        self._config = config
        self._logger = get_logger(self.__class__.__name__)
        self._metadata: Dict[str, Any] = {}

        # Validate configuration
        if hasattr(config, "validate") and not config.validate():
            self._logger.warning(
                f"Configuration validation failed for {self.__class__.__name__}"
            )

    @property
    def config(self) -> C:
        """Return the component configuration."""
        return self._config

    @property
    def logger(self) -> logging.Logger:
        """Return the component logger."""
        return self._logger

    @property
    def metadata(self) -> Dict[str, Any]:
        """Return component metadata."""
        return self._metadata

    def reconfigure(self, config: C) -> None:
        """Update the component configuration."""
        self._config = config
        self._logger.debug(f"Reconfigured {self.__class__.__name__}")

Implementing Components

from myspellchecker.core.component import Component, ComponentConfig
from dataclasses import dataclass

@dataclass
class NgramCheckerConfig(ComponentConfig):
    use_bigrams: bool = True
    use_trigrams: bool = True
    smoothing: str = "kneser_ney"
    threshold: float = 0.001

class NgramChecker(Component[NgramCheckerConfig]):
    """N-gram based context checker."""

    def __init__(
        self,
        config: NgramCheckerConfig,
        provider: DictionaryProvider,
        *,
        cache: Optional[LRUCache] = None,
    ):
        super().__init__(config)
        self.provider = provider
        self.cache = cache

        # Initialize based on config
        self._init_ngram_tables()

    def _init_ngram_tables(self) -> None:
        """Initialize n-gram data based on config."""
        if self.config.use_bigrams:
            self.logger.debug("Loading bigram data")
            self._bigrams = self.provider.get_pos_bigram_probabilities()

        if self.config.use_trigrams:
            self.logger.debug("Loading trigram data")
            self._trigrams = self.provider.get_pos_trigram_probabilities()

    def check_context(self, words: List[str]) -> float:
        """Check context probability."""
        # Implementation using config values
        ...

Configurable Protocol

Protocol for components that support reconfiguration:
from typing import Protocol, TypeVar, runtime_checkable

C = TypeVar("C", bound=ComponentConfig)

@runtime_checkable
class Configurable(Protocol[C]):
    """Protocol for configurable components."""

    @property
    def config(self) -> C:
        """Return the component configuration."""
        ...

    def reconfigure(self, config: C) -> None:
        """Update the component configuration."""
        ...


# Usage
def update_config(component: Configurable[SomeConfig], new_config: SomeConfig):
    component.reconfigure(new_config)

ComponentMeta

Metadata class for component introspection:
from dataclasses import dataclass, field
from typing import Optional, List

@dataclass
class ComponentMeta:
    """Metadata about a component.

    Used for introspection, documentation, and factory registration.
    """

    name: str
    description: str = ""
    version: str = "1.0.0"
    author: str = ""
    config_class: Optional[type] = None
    dependencies: List[str] = field(default_factory=list)
    optional_dependencies: List[str] = field(default_factory=list)
    tags: List[str] = field(default_factory=list)


# Example usage
SYMSPELL_META = ComponentMeta(
    name="SymSpell",
    description="O(1) spelling suggestion algorithm",
    version="1.0.0",
    config_class=SymSpellConfig,
    dependencies=["DictionaryProvider"],
    optional_dependencies=["LRUCache"],
    tags=["algorithm", "suggestions"],
)

with_config Decorator

Associates configuration class with component:
def with_config(config_class: type) -> type:
    """Class decorator to associate a configuration class.

    Adds metadata about the configuration class
    to the component for introspection and factory use.
    """
    def decorator(cls: type) -> type:
        cls._config_class = config_class
        return cls
    return decorator


# Usage
@with_config(SymSpellConfig)
class SymSpell(Component[SymSpellConfig]):
    pass


# Access config class
config_cls = SymSpell._config_class  # SymSpellConfig

Best Practices

1. Always Use Dataclass Configs

# Good: Dataclass config
@dataclass
class MyConfig(ComponentConfig):
    option1: int = 10
    option2: str = "default"

# Bad: Plain dict
config = {"option1": 10, "option2": "default"}

2. Validate Configuration

@dataclass
class ValidatedConfig(ComponentConfig):
    port: int = 8080
    host: str = "localhost"

    def validate(self) -> bool:
        if not 1 <= self.port <= 65535:
            return False
        if not self.host:
            return False
        return True

3. Use Type Hints

# Good: Explicit types
class MyComponent(Component[MyConfig]):
    def __init__(
        self,
        config: MyConfig,
        provider: DictionaryProvider,
    ) -> None:
        super().__init__(config)

# Bad: No type hints
class MyComponent:
    def __init__(self, config, provider):
        ...

4. Document Dependencies

class DocumentedComponent(Component[MyConfig]):
    """Component with clear dependency documentation.

    Args:
        config: Component configuration
        provider: Dictionary provider for data access
        cache: Optional LRU cache for performance

    Requires:
        - DictionaryProvider with word lookup support
        - Optional: LRUCache for suggestion caching
    """

5. Use Logger Mixin

class MyComponent(Component[MyConfig]):
    def some_method(self):
        self.logger.debug("Starting operation")
        try:
            result = self._do_work()
            self.logger.info(f"Completed: {result}")
        except Exception as e:
            self.logger.error(f"Failed: {e}")
            raise

Integration Example

Complete example showing all patterns:
from dataclasses import dataclass
from typing import Optional, List
from myspellchecker.core.component import Component, ComponentConfig, with_config

@dataclass
class SpellCheckerConfig(ComponentConfig):
    """Main spell checker configuration."""

    validation_level: str = "word"
    use_context_checker: bool = True
    cache_size: int = 10000

    def validate(self) -> bool:
        valid_levels = {"syllable", "word"}
        return self.validation_level in valid_levels


# Note: This is a conceptual example showing the Component pattern.
# The actual SpellChecker does NOT inherit from Component; it uses
# its own constructor: __init__(config, segmenter, provider,
#     syllable_validator, word_validator, context_validator, factory)
class ExampleComponent(Component[SpellCheckerConfig]):
    """Example showing Component pattern (conceptual, not actual SpellChecker)."""

    def __init__(
        self,
        config: SpellCheckerConfig,
        provider: DictionaryProvider,
        *,
        segmenter: Optional[Segmenter] = None,
    ):
        super().__init__(config)
        self.provider = provider
        self.segmenter = segmenter or DefaultSegmenter()

        self.logger.info(f"Initialized with level: {config.validation_level}")

    def check(self, text: str) -> Response:
        """Check text for spelling errors."""
        self.logger.debug(f"Checking text: {text[:50]}...")
        # Implementation
        ...

See Also