Skip to content

Conversation

NFUChen
Copy link
Contributor

@NFUChen NFUChen commented Jun 8, 2025

Add Support for Qualifiers and Component Registration Validation

Changes Overview

This PR introduces two major features to enhance component management in PySpring:

  1. Qualifier Support

    • Added support for qualifiers in dependency injection
    • Implemented handling of Annotated types for qualifier-based injection
    • Enhanced error messages to include qualifier information
  2. Component Registration Validation

    • Added validation to prevent duplicate component registration
    • Enhanced error handling with clear feedback for developers
    • Improved component registration robustness

Key Changes

Component Class Enhancements

  • Added name configuration option to Component.Config
  • Enhanced get_name() to support custom component names

Application Context Improvements

  • Simplified component registration process
  • Added _determine_target_cls_name() to handle component resolution logic
  • Enhanced dependency injection to support qualifiers
  • Improved error handling for component initialization
  • Added duplicate component registration prevention

Dependency Injection Updates

  • Modified get_component() and get_bean() to accept qualifier parameter
  • Enhanced type hints for entity getters
  • Improved error messages with qualifier information

Other Changes

  • Removed type checking service as it's no longer needed
  • Added better error handling for component initialization

Example Usage

1. Using Qualifiers for Dependency Injection

from typing import Annotated
from py_spring_core import Component

class ServiceConsumer(Component):
    # Will inject the specifically qualified implementation
    service_a: Annotated[AbstractService, "ServiceA"]
    service_b: Annotated[AbstractService, "ServiceB"]

    def post_construct(self) -> None:
        print(self.service_a.process())  # Uses ServiceA
        print(self.service_b.process())  # Uses ServiceB

2. Complete Working Example

from abc import ABC, abstractmethod
from typing import Annotated
from py_spring_core import Component
from py_spring_core.core.entities.component import ComponentScope

# Abstract base class
class AbstractExample(Component, ABC):
    class Config:
        scope = ComponentScope.Singleton

    @abstractmethod
    def say_hello(self) -> str: ...

# First implementation
class ExampleA(AbstractExample):
    class Config:
        name = "ExampleA"
        scope = ComponentScope.Singleton

    def say_hello(self) -> str:
        return "Hello from Example A!"

# Second implementation
class ExampleB(AbstractExample):
    class Config:
        name = "ExampleB"
        scope = ComponentScope.Singleton

    def say_hello(self) -> str:
        return "Hello from Example B!"

# Service using both implementations
class TestService(Component):
    # Will inject the specifically qualified implementations
    example_a: Annotated[AbstractExample, "ExampleA"]
    example_b: Annotated[AbstractExample, "ExampleB"]

    def post_construct(self) -> None:
        print(self.example_a.say_hello())  # Output: "Hello from Example A!"
        print(self.example_b.say_hello())  # Output: "Hello from Example B!"

3. Duplicate Registration Prevention Example

from py_spring_core import Component
from py_spring_core.core.entities.component import ComponentScope

# First registration of MyService
class MyService(Component):
    class Config:
        name = "MyService"
        scope = ComponentScope.Singleton

    def do_something(self) -> str:
        return "Service is working!"

# Attempting to register the same component again will raise ValueError
try:
    class MyService(Component):  # This will raise ValueError
        class Config:
            name = "MyService"
            scope = ComponentScope.Singleton

        def do_something(self) -> str:
            return "This will never be registered!"
except ValueError as e:
    print(f"Error: {e}")  # Output: "Error: [COMPONENT REGISTRATION ERROR] Component: MyService already registered"

Key Points to Remember:

  1. Use Annotated[Type, "QualifierName"] to inject specific implementations
  2. The name in Config can be used to override the default class name
  3. All components must have a valid implementation of the abstract methods
  4. Components can only be registered once with the same name

Testing

Please test the following scenarios:

  1. Dependency injection with qualifiers
  2. Error cases for duplicate component registration
  3. Abstract class initialization handling

Breaking Changes

  • get_component() and get_bean() now require a qualifier parameter (can be None)
  • Type checking service has been removed
  • Primary component validation has been removed

- Added methods to validate and retrieve primary components, ensuring no duplicates are registered.
- Implemented logic to determine the target class name for components, improving error handling for abstract classes.
- Updated the Component class to include an `is_primary` flag in its configuration for better component classification.
…error handling

- Added support for qualifiers in component retrieval methods to allow more precise dependency injection.
- Introduced a new method for initializing singleton components with enhanced error logging for abstract class instantiation failures.
- Updated the Component class to include a configurable name in its configuration for better identification.
- Improved dependency injection logic to handle Annotated types and provide clearer error messages.
- Enhanced the logic for determining the target class name for component classes, including handling of qualifiers and abstract base classes (ABCs).
- Updated method documentation to clarify the behavior for various cases, including direct returns for non-ABCs and error handling for ABCs with no implementations or multiple implementations.
- Added a check to ensure that a component class cannot be registered more than once, raising a ValueError if a duplicate is detected.
- This enhancement improves error handling during component registration, ensuring clearer feedback for developers.
…alidation methods

- Removed methods for validating and retrieving primary components to streamline the component registration process.
- Updated logic in the target class name determination to handle cases without primary components more efficiently.
- Added a new test suite to validate component features, including qualifier-based injection and duplicate registration prevention.
@NFUChen NFUChen self-assigned this Jun 14, 2025
@NFUChen NFUChen added the enhancement New feature or request label Jun 14, 2025
@NFUChen NFUChen merged commit 1cc1aa8 into main Jun 14, 2025
1 check passed
@NFUChen NFUChen deleted the feat/qualifier branch June 14, 2025 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants