haystack/test/core/component/test_component.py
Silvano Cerza 0191b1e6e4
feat: Change Component's I/O dunder type (#6916)
* Add Pipeline.get_component_name() method

* Add utility class to ease discoverability of Component I/O

* Move InputOutput in component package

* Rename InputOutput to _InputOutput

* Raise if inputs or outputs field already exist

* Fix tests

* Add release notes

* Move InputSocket and OutputSocket in types package

* Move _InputOutput in socket package

* Rename _InputOutput class to Sockets

* Simplify Sockets class

* Dictch I/O dunder fields in favour of inputs and outputs fields

* Update Sockets docstrings

* Update release notes

* Fix mypy

* Remove unnecessary assignment

* Remove unused logging

* Change SocketsType to SocketsIOType to avoid confusion

* Change sockets type and name

* Change Sockets.__repr__ to return component instance

* Fix linting

* Fix sockets tests

* Revert to dunder fields for Component IO

* Use singular in IO dunder fields

* Delete release notes

* Update haystack/core/component/types.py

Co-authored-by: Massimiliano Pippi <mpippi@gmail.com>

---------

Co-authored-by: Massimiliano Pippi <mpippi@gmail.com>
2024-02-05 17:46:45 +01:00

192 lines
4.9 KiB
Python

from typing import Any
import pytest
from haystack.core.component import Component, InputSocket, OutputSocket, component
from haystack.core.errors import ComponentError
def test_correct_declaration():
@component
class MockComponent:
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
@component.output_types(output_value=int)
def run(self, input_value: int):
return {"output_value": input_value}
# Verifies also instantiation works with no issues
assert MockComponent()
assert component.registry["test_component.MockComponent"] == MockComponent
def test_correct_declaration_with_additional_readonly_property():
@component
class MockComponent:
@property
def store(self):
return "test_store"
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
@component.output_types(output_value=int)
def run(self, input_value: int):
return {"output_value": input_value}
# Verifies that instantiation works with no issues
assert MockComponent()
assert component.registry["test_component.MockComponent"] == MockComponent
assert MockComponent().store == "test_store"
def test_correct_declaration_with_additional_writable_property():
@component
class MockComponent:
@property
def store(self):
return "test_store"
@store.setter
def store(self, value):
self._store = value
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
@component.output_types(output_value=int)
def run(self, input_value: int):
return {"output_value": input_value}
# Verifies that instantiation works with no issues
assert component.registry["test_component.MockComponent"] == MockComponent
comp = MockComponent()
comp.store = "test_store"
assert comp.store == "test_store"
def test_missing_run():
with pytest.raises(ComponentError, match=r"must have a 'run\(\)' method"):
@component
class MockComponent:
def another_method(self, input_value: int):
return {"output_value": input_value}
def test_set_input_types():
@component
class MockComponent:
def __init__(self):
component.set_input_types(self, value=Any)
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
@component.output_types(value=int)
def run(self, **kwargs):
return {"value": 1}
comp = MockComponent()
assert comp.__haystack_input__._sockets_dict == {"value": InputSocket("value", Any)}
assert comp.run() == {"value": 1}
def test_set_output_types():
@component
class MockComponent:
def __init__(self):
component.set_output_types(self, value=int)
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
def run(self, value: int):
return {"value": 1}
comp = MockComponent()
assert comp.__haystack_output__._sockets_dict == {"value": OutputSocket("value", int)}
def test_output_types_decorator_with_compatible_type():
@component
class MockComponent:
@component.output_types(value=int)
def run(self, value: int):
return {"value": 1}
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
comp = MockComponent()
assert comp.__haystack_output__._sockets_dict == {"value": OutputSocket("value", int)}
def test_component_decorator_set_it_as_component():
@component
class MockComponent:
@component.output_types(value=int)
def run(self, value: int):
return {"value": 1}
def to_dict(self):
return {}
@classmethod
def from_dict(cls, data):
return cls()
comp = MockComponent()
assert isinstance(comp, Component)
def test_input_has_default_value():
@component
class MockComponent:
@component.output_types(value=int)
def run(self, value: int = 42):
return {"value": value}
comp = MockComponent()
assert comp.__haystack_input__._sockets_dict["value"].default_value == 42
assert not comp.__haystack_input__._sockets_dict["value"].is_mandatory
def test_keyword_only_args():
@component
class MockComponent:
def __init__(self):
component.set_output_types(self, value=int)
def run(self, *, arg: int):
return {"value": arg}
comp = MockComponent()
component_inputs = {name: {"type": socket.type} for name, socket in comp.__haystack_input__._sockets_dict.items()}
assert component_inputs == {"arg": {"type": int}}