iPOPO Components

A component is an object with a life-cycle, requiring services and providing ones, and associated to properties. The code of a component is reduced to its functional purpose: its life-cycle, dependencies, etc. are handled by iPOPO. In iPOPO, a component is an instance of component factory, i.e. a Python class manipulated with the iPOPO decorators.

Life-cycle

iPOPO component life-cycle graph

The component life cycle is handled by an instance manager created by the iPOPO service. This instance manager will inject control methods, run-time dependencies, and will register the component services. All changes will be notified to the component using the callback methods it decorated.

State

Description

INSTANTIATED

The component has been instantiated. Its constructor has been called and the control methods have been injected

VALIDATED

All required dependencies have been injected. All services provided by the component will be registered right after this method returned

KILLED

The component has been invalidated and won’t be usable again

ERRONEOUS

The component raised an error during its validation. It is not destroyed and a validation can be retried manually

API

iPOPO components are handled through the iPOPO core service, which can itself be accessed through the Pelix API or the utility context manager use_ipopo(). The core service provides the pelix.ipopo.core specification.

pelix.ipopo.constants.use_ipopo(bundle_context: BundleContext) Generator[IPopoService, None, None]

Utility context to use the iPOPO service safely in a “with” block. It looks after the the iPOPO service and releases its reference when exiting the context.

Parameters:

bundle_context – The calling bundle context

Returns:

The iPOPO service

Raises:

BundleException – Service not found

The following snippet shows how to use this method:

from pelix.ipopo.constants import use_ipopo

# ... considering "context" being a BundleContext object
with use_ipopo(context) as ipopo:
    # use the iPOPO core service with the "ipopo" variable
    ipopo.instantiate("my.factory", "my.component",
                      {"some.property": [1, 2, 3], "answer": 42})

# ... out of the "with" context, the iPOPO service has been released
# and shouldn't be used

Here are the most commonly used methods from the iPOPO core service to handle components and factories:

class pelix.ipopo.constants.IPopoService(*args, **kwargs)

Interface of the iPOPO core service

get_factories() List[str]

Retrieves the names of the registered factories

Returns:

A list of factories. Can be empty.

get_factory_details(name: str) Dict[str, Any]

Retrieves a dictionary with details about the given factory

  • name: The factory name

  • bundle: The Bundle object of the bundle providing the factory

  • properties: Copy of the components properties defined by the factory

  • requirements: List of the requirements defined by the factory

    • id: Requirement ID (field where it is injected)

    • specification: Specification of the required service

    • aggregate: If True, multiple services will be injected

    • optional: If True, the requirement is optional

  • services: List of the specifications of the services provided by components of this factory

  • handlers: Dictionary of the non-built-in handlers required by this factory. The dictionary keys are handler IDs, and it contains a tuple with:

    • A copy of the configuration of the handler (0)

    • A flag indicating if the handler is present or not

Parameters:

name – The name of a factory

Returns:

A dictionary describing the factory

Raises:

ValueError – Invalid factory

get_instance_details(name: str) Dict[str, Any]

Retrieves a snapshot of the given component instance. The result dictionary has the following keys:

  • name: The component name

  • factory: The name of the component factory

  • bundle_id: The ID of the bundle providing the component factory

  • state: The current component state

  • services: A {Service ID Service reference} dictionary, with all services provided by the component

  • dependencies: A dictionary associating field names with the following dictionary:

    • handler: The name of the type of the dependency handler

    • filter (optional): The requirement LDAP filter

    • optional: A flag indicating whether the requirement is optional or not

    • aggregate: A flag indicating whether the requirement is a set of services or not

    • binding: A list of the ServiceReference the component is bound to

  • properties: A dictionary key → value, with all properties of the component. The value is converted to its string representation, to avoid unexpected behaviours.

Parameters:

name – The name of a component instance

Returns:

A dictionary of details

Raises:

ValueError – Invalid component name

get_instances() List[Tuple[str, str, int]]

Retrieves the list of the currently registered component instances

Returns:

A list of (name, factory name, state) tuples.

instantiate(factory_name: str, name: str, properties: Dict[str, Any] | None = None) Any

Instantiates a component from the given factory, with the given name

Parameters:
  • factory_name – Name of the component factory

  • name – Name of the instance to be started

  • properties – Initial properties of the component instance

Returns:

The component instance

Raises:
  • TypeError – The given factory is unknown

  • ValueError – The given name or factory name is invalid, or an instance with the given name already exists

  • Exception – Something wrong occurred in the factory

kill(name: str) None

Kills the given component

Parameters:

name – Name of the component to kill

Raises:

ValueError – Invalid component name

retry_erroneous(name: str, properties_update: Dict[str, Any] | None = None) int

Removes the ERRONEOUS state of the given component, and retries a validation

Parameters:
  • name – Name of the component to retry

  • properties_update – A dictionary to update the initial properties of the component

Returns:

The new state of the component

Raises:

ValueError – Invalid component name

Listening to components events

The iPOPO service can be used to register to component events, using the following methods:

class pelix.ipopo.constants.IPopoService(*args, **kwargs)

Interface of the iPOPO core service

add_listener(listener: IPopoEventListener) bool

Register an iPOPO event listener.

The event listener must have a method with the following prototype:

def handle_ipopo_event(self, event):
    '''
    event: A IPopoEvent object
    '''
    # ...
Parameters:

listener – The listener to register

Returns:

True if the listener has been added to the registry

remove_listener(listener: IPopoEventListener) bool

Unregister an iPOPO event listener.

Parameters:

listener – The listener to register

Returns:

True if the listener has been removed from the registry

A component listener must implement the following interface:

class pelix.ipopo.constants.IPopoEventListener(*args, **kwargs)

Interface of iPOPO events listeners

handle_ipopo_event(event: IPopoEvent) None

Handles an iPOPO event

Parameters:

event – Event to handle

A word on Data classes

These indications have to be taken into account when using iPOPO decorators on data classes.

Important notes

  • All fields of the Data Class must have a default value. This will let the @dataclass decorator generate an __init__ method without explicit arguments, which is a requirement for iPOPO.

  • If the init=False argument is given to @dataclass, it is necessary to implement your own __init__, defining all fields, otherwise generated methods like __repr__ won’t work.

Good to know

  • Injected fields (@Property, @Requires, …) will lose the default value given in the class definition, in favor to the ones given to the iPOPO decorators. This is due to the redefinition of the fields by those decorators. Other fields are not touched at all.

  • The @dataclass decorator can be used before or after the iPOPO decorators

iPOPO Waiting List

iPOPO provides a utility service to register components to a waiting list, which will try to instantiate them when a new iPOPO component factory or a new iPOPO handler is available. This is useful for softwares using a composition described in a configuration file: add an instant to the list and let iPOPO instantiate it when possible.

This feature is provided by the pelix.ipopo.waiting bundle, which must be installed and active. Note that the pelix.ipopo.core bundle can be installed and started later: the waiting list will try to instantiate components as soon as the iPOPO service is found.

To use the iPOPO waiting list, get the pelix.ipopo.constants.IPopoWaitingList service (or by its name: pelix.ipopo.waiting_list) which provides the following methods:

class pelix.ipopo.constants.IPopoWaitingList(*args, **kwargs)

iPOPO instantiation waiting list

add(factory: str, component: str, properties: Dict[str, Any] | None = None) None

Enqueues the instantiation of the given component

Parameters:
  • factory – Factory name

  • component – Component name

  • properties – Component properties

Raises:
  • ValueError – Component name already reserved in the queue

  • Exception – Error instantiating the component

remove(component: str) None

Kills/Removes the component with the given name

Parameters:

component – A component name

Raises:

KeyError – Unknown component