Skip to main content
Components like providers, validators, and the grammar engine are wired together through a central ServiceContainer rather than hard-coded imports. This makes it straightforward to swap implementations for testing or customize the validation pipeline.

Overview

The DI system consists of:
  • ServiceContainer: Central registry for services
  • Factory Functions: Create components with dependencies
  • ComponentFactory: High-level factory for spell checker components

ServiceContainer

The ServiceContainer manages component lifecycle through factory functions:
from myspellchecker.core.di.container import ServiceContainer
from myspellchecker.core.config import SpellCheckerConfig

config = SpellCheckerConfig()
container = ServiceContainer(config)

Registering Services

Register factory functions that create services:
def create_provider(container):
    """Factory function for provider."""
    config = container.get_config()
    return SQLiteProvider(database_path=config.provider_config.database_path)

# Register as singleton (default)
container.register_factory("provider", create_provider, singleton=True)

# Register as transient (new instance each time)
container.register_factory("temp_service", create_temp, singleton=False)

Retrieving Services

Services are created lazily on first access:
# First call creates the instance
provider = container.get("provider")

# Subsequent calls return cached instance (for singletons)
provider2 = container.get("provider")
assert provider is provider2  # Same instance

Available Services

The default container provides these services:
Service NameTypeDescription
providerDictionaryProviderDictionary storage backend
segmenterSegmenterText segmentation
phonetic_hasherPhoneticHasherPhonetic matching (optional)
symspellSymSpellSymSpell algorithm
context_checkerNgramContextCheckerN-gram checking (optional)
suggestion_strategyCompositeSuggestionStrategyUnified suggestion pipeline aggregating SymSpell, morphology, and compound sources
syllable_validatorSyllableValidatorSyllable validation
word_validatorWordValidatorWord validation
context_validatorContextValidatorContext validation (full: 12 strategies)
context_validator_minimalContextValidatorContext validation (fast: tone, syntactic, question only)
tone_disambiguatorToneDisambiguatorContext-aware tone mark correction (optional)
syntactic_rule_checkerSyntacticRuleCheckerGrammar rule checking (optional)
viterbi_taggerViterbiTaggerHMM-based POS tagging (optional)
homophone_checkerHomophoneCheckerHomophone detection (optional)
semantic_checkerSemanticCheckerAI-powered semantic analysis (optional)
name_heuristicNameHeuristicNamed entity recognition heuristic (optional)

Thread Safety

Singleton creation is thread-safe using double-checked locking:
# Safe to call from multiple threads
from concurrent.futures import ThreadPoolExecutor

with ThreadPoolExecutor(max_workers=4) as executor:
    futures = [executor.submit(container.get, "provider") for _ in range(10)]
    providers = [f.result() for f in futures]

# All return the same instance
assert all(p is providers[0] for p in providers)

Container Operations

# Check if service is registered
if container.has_service("provider"):
    provider = container.get("provider")

# List all registered services
services = container.list_services()
print(services)  # ['context_checker', 'phonetic_hasher', 'provider', ...]

# Clear cached instances (forces recreation)
container.clear_cache()

Component Factories

Individual component factories create specific services:

ProviderFactory

ProviderFactory is a type alias for Callable[[ServiceContainer], DictionaryProvider]. Use create_provider_factory() to get the factory function:
from myspellchecker.core.factories.provider_factory import create_provider_factory

factory_fn = create_provider_factory()
container.register_factory("provider", factory_fn)
provider = container.get("provider")

SymSpellFactory

SymSpellFactory is a type alias for Callable[[ServiceContainer], SymSpell]. Use create_symspell_factory() to get the factory function:
from myspellchecker.core.factories.symspell_factory import create_symspell_factory

factory_fn = create_symspell_factory()
container.register_factory("symspell", factory_fn)
symspell = container.get("symspell")

ValidatorsFactory

ValidatorFactory is a type alias for Callable[[ServiceContainer], Validator]. create_validators_factory() returns a dict of named factory functions:
from myspellchecker.core.factories.validators_factory import create_validators_factory

validator_factories = create_validators_factory()
# Keys are "syllable", "word", "context"
for name, factory_fn in validator_factories.items():
    container.register_factory(f"{name}_validator", factory_fn)

syllable_validator = container.get("syllable_validator")
word_validator = container.get("word_validator")
context_validator = container.get("context_validator")

SegmenterFactory

SegmenterFactory is a type alias for Callable[[ServiceContainer], Segmenter]. Use create_segmenter_factory() to get the factory function:
from myspellchecker.core.factories.segmenter_factory import create_segmenter_factory

factory_fn = create_segmenter_factory()
container.register_factory("segmenter", factory_fn)
segmenter = container.get("segmenter")

RankerFactory

The ranker factory uses create_base_ranker() which takes a RankerConfig directly (not the container pattern):
from myspellchecker.core.factories.ranker_factory import create_base_ranker
from myspellchecker.core.config import RankerConfig

ranker_config = RankerConfig()
ranker = create_base_ranker(ranker_config)

PhoneticFactory

PhoneticHasherFactory is a type alias for Callable[[ServiceContainer], Optional[PhoneticHasher]]. Use create_phonetic_hasher_factory() to get the factory function:
from myspellchecker.core.factories.phonetic_factory import create_phonetic_hasher_factory

factory_fn = create_phonetic_hasher_factory()
container.register_factory("phonetic_hasher", factory_fn)
hasher = container.get("phonetic_hasher")  # Returns None if disabled

ContextCheckerFactory

ContextCheckerFactory is a type alias for Callable[[ServiceContainer], Optional[NgramContextChecker]]. Use create_context_checker_factory() to get the factory function:
from myspellchecker.core.factories.context_checker_factory import create_context_checker_factory

factory_fn = create_context_checker_factory()
container.register_factory("context_checker", factory_fn)
checker = container.get("context_checker")  # Returns None if disabled

ContextValidatorFactory

ContextValidatorFactory is a type alias for Callable[[ServiceContainer], ContextValidator]. Use create_context_validator_factory() to create a ContextValidator wired with all validation strategies (tone, syntactic, POS sequence, question structure, homophone, n-gram, semantic):
from myspellchecker.core.factories.context_validator_factory import create_context_validator_factory

factory_fn = create_context_validator_factory()
container.register_factory("context_validator", factory_fn)
validator = container.get("context_validator")
A minimal variant is also available via create_context_validator_minimal_factory() for fast mode (only tone, syntactic, and question structure strategies).

OptionalServicesFactory

The optional_services_factory module provides factory functions for services that enable advanced validation but are not required for basic spell checking. Each returns None if dependencies are unavailable (graceful degradation):
from myspellchecker.core.factories.optional_services_factory import (
    create_tone_disambiguator_factory,
    create_syntactic_rule_checker_factory,
    create_viterbi_tagger_factory,
    create_homophone_checker_factory,
    create_semantic_checker_factory,
    create_name_heuristic_factory,
)

container.register_factory("tone_disambiguator", create_tone_disambiguator_factory())
container.register_factory("syntactic_rule_checker", create_syntactic_rule_checker_factory())
container.register_factory("viterbi_tagger", create_viterbi_tagger_factory())
container.register_factory("homophone_checker", create_homophone_checker_factory())
container.register_factory("semantic_checker", create_semantic_checker_factory())
container.register_factory("name_heuristic", create_name_heuristic_factory())

SuggestionStrategyFactory

SuggestionStrategyFactory is a type alias for Callable[[ServiceContainer], Optional[SuggestionStrategy]]. Use create_suggestion_strategy_factory() to create a CompositeSuggestionStrategy that aggregates suggestions from multiple sources (SymSpell, morphology, compound) with unified ranking:
from myspellchecker.core.factories.suggestion_strategy_factory import create_suggestion_strategy_factory

factory_fn = create_suggestion_strategy_factory()
container.register_factory("suggestion_strategy", factory_fn)
strategy = container.get("suggestion_strategy")  # Returns None if required deps unavailable
Dependencies are resolved in order: provider -> symspell -> context_checker (optional). If context checking is enabled, the composite is wrapped in ContextSuggestionStrategy for context-aware re-ranking.

ComponentFactory

The high-level ComponentFactory orchestrates component creation:
from myspellchecker.core.component_factory import ComponentFactory
from myspellchecker.core.config import SpellCheckerConfig

config = SpellCheckerConfig()
factory = ComponentFactory(config)

# Create all components (requires provider and segmenter)
components = factory.create_all(provider, segmenter)

# Access individual components
viterbi_tagger = components.get("viterbi_tagger")
joint_tagger = components.get("joint_segment_tagger")
semantic_checker = components.get("semantic_checker")

Creating Custom Factories

Implement the ComponentFactoryProtocol to provide custom component construction:
from myspellchecker.core.component_factory import ComponentFactoryProtocol
from myspellchecker import SpellChecker

class MyCustomFactory:
    """Custom factory following ComponentFactoryProtocol."""

    def __init__(self, config):
        self.config = config

    def create_all(self, provider, segmenter):
        """Create all components with custom logic."""
        return {
            "symspell": MyCustomSymSpell(provider),
            # ... other components
        }

# Pass custom factory to SpellChecker
checker = SpellChecker(factory=MyCustomFactory(config))
Register with the container using a factory function:
def create_my_component(container):
    config = container.get_config()
    provider = container.get("provider")
    return MyCustomComponent(provider=provider, option=config.my_option)

container.register_factory("my_component", create_my_component)

Dependency Resolution

Dependencies are resolved through the container:
def create_word_validator(container: ServiceContainer):
    """Factory with dependencies."""
    config = container.get_config()

    # Resolve dependencies
    provider = container.get("provider")
    syllable_validator = container.get("syllable_validator")
    symspell = container.get("symspell")

    return WordValidator(
        config=config,
        segmenter=container.get("segmenter"),
        word_repository=provider,       # Provider implements WordRepository protocol
        syllable_repository=provider,   # Provider implements SyllableRepository protocol
        symspell=symspell,
        context_checker=container.get("context_checker"),
        suggestion_strategy=container.get("suggestion_strategy"),
    )

Testing with DI

DI makes testing easier by allowing mock injection:
from unittest.mock import Mock

# Create container with config
config = SpellCheckerConfig()
container = ServiceContainer(config)

# Register mock provider
mock_provider = Mock()
mock_provider.is_valid_word.return_value = True
container.register_factory("provider", lambda c: mock_provider)

# Component uses mock
validator = container.get("word_validator")
# validator uses mock_provider internally

Service Registration

The ServiceContainer provides service registration and discovery:
from myspellchecker.core.di import ServiceContainer

config = SpellCheckerConfig()
container = ServiceContainer(config)

# Register a factory function for a service
def create_provider(container):
    return SQLiteProvider(database_path="mydict.db")

container.register_factory("provider", create_provider)

# Get service instance (lazy initialization)
provider = container.get("provider")

Best Practices

  1. Singleton for expensive resources: Use singleton=True for database connections, caches
  2. Transient for stateful services: Use singleton=False for services with request-specific state
  3. Factory functions: Keep factory functions simple and focused
  4. Dependency declaration: Explicitly resolve dependencies in factory functions
  5. Configuration access: Use container.get_config() for configuration values

Architecture

  +------------------------------------------------------+
  | SpellChecker                                         |
  |                                                      |
  |  +------------------------------------------------+  |
  |  | ServiceContainer                               |  |
  |  |                                                |  |
  |  |  +------------------+  +------------------+    |  |
  |  |  | Provider Factory |  | SymSpell Factory |    |  |
  |  |  +------------------+  +------------------+    |  |
  |  |                                                |  |
  |  |  +--------------------+  +------------------+  |  |
  |  |  | Segmenter Factory  |  | Validators       |  |  |
  |  |  |                    |  | Factory          |  |  |
  |  |  +--------------------+  +------------------+  |  |
  |  |                                                |  |
  |  |  +------------------+  +--------------------+  |  |
  |  |  | Context Factory  |  | Optional Services  |  |  |
  |  |  +------------------+  +--------------------+  |  |
  |  |                                                |  |
  |  +------------------------------------------------+  |
  |                                                      |
  +------------------------------------------------------+

See Also