Skip to content

Commit

Permalink
Merge pull request #85 from s-kostyuk/refactor_things
Browse files Browse the repository at this point in the history
This pull request contains a lot of changes (including BREAKING changes) for the current structure and naming of Things-related components:

- all abstract implementations of Thing and derivative classes are now moved to `dpl.integrations` package;
- all abstract implementations of Thing and derivative classes now have the `Abs` prefix in their names;
- Thing class definition is remained unchanged (but there was an idea to rename it to the BaseThing);
- Thing base class now handles the saving of `con_instance` and `con_params` constructor params by itself;
- fixed a DTO construction for non-Actuator Things;
- UnsupportedCommandError and UnacceptableCommandArgumentsError exceptions are now defined in `things.capabilities.i_actuator` module;
- a lot of small PEP8 fixes (mostly for "line too long" errors).
  • Loading branch information
s-kostyuk committed Mar 19, 2018
2 parents 0ebbe83 + c4d9583 commit d44ca11
Show file tree
Hide file tree
Showing 14 changed files with 200 additions and 135 deletions.
7 changes: 4 additions & 3 deletions dpl/dtos/actuator_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ class and a corresponding builder to be used to

from .dto_builder import build_dto
from .thing_dto import ThingDto, build_thing_dto
from dpl.things.actuator import Actuator
from dpl.integrations.abs_actuator import AbsActuator


ActuatorDto = ThingDto


@build_dto.register(Actuator)
def _(thing: Actuator) -> ActuatorDto:
@build_dto.register(AbsActuator)
def _(thing: AbsActuator) -> ActuatorDto:
result = {
'state': thing.state.name,
'commands': thing.commands,
'is_active': thing.is_active
}
Expand Down
1 change: 0 additions & 1 deletion dpl/dtos/thing_dto.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class and a corresponding builder to be used to
def build_thing_dto(thing: Thing) -> ThingDto:
result = {
'id': thing.domain_id,
'state': thing.state.name,
'is_enabled': thing.is_enabled,
'is_available': thing.is_available,
'last_updated': thing.last_updated
Expand Down
64 changes: 28 additions & 36 deletions dpl/things/actuator.py → dpl/integrations/abs_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,30 +7,15 @@

# Include DPL modules
from dpl.things.capabilities.i_state import IState
from dpl.things.capabilities.i_actuator import IActuator
from dpl.things.capabilities.i_actuator import (
IActuator,
UnsupportedCommandError,
UnacceptableCommandArgumentsError
)
from dpl.things.thing import Thing, TDomainId, Connection


class UnsupportedCommandError(ValueError):
"""
An exceptions to be raised if the specified command
is not supported by this instance of Thing
"""
pass


class UnacceptableCommandArgumentsError(Exception):
"""
An exception to be raised if at least one of the specified
command arguments has an unacceptable type or if there is
an incorrect set of arguments passed (i.e. if one of the
mandatory arguments is missing or if one of the specified
arguments is extra and isn't related to the specified command)
"""
pass


class Actuator(Thing, IActuator, IState):
class AbsActuator(Thing, IActuator, IState):
"""
Actuator is an abstraction of devices that can 'act', perform some commands
and change their states after that.
Expand All @@ -39,22 +24,26 @@ class Actuator(Thing, IActuator, IState):
- it can be in one of two states: 'activated' or 'deactivated';
- 'activation' is a switching of a device to some specific active state
(like 'on' for LightBulb, 'opened' for Door and 'capturing' for Camera and
'playing' for Player);
- 'deactivation' is a switching of a device to some specific non-active state
(like 'off' for LightBulb, 'closed' for Door and 'idle' for Camera and
'stopped'/'paused' for Player);
(like 'on' for LightBulb, 'opened' for Door and 'capturing' for Camera
and 'playing' for Player);
- 'deactivation' is a switching of a device to some specific non-active
state (like 'off' for LightBulb, 'closed' for Door and 'idle' for Camera
and 'stopped'/'paused' for Player);
- it can be toggled between those to states;
- it provides a list of available commands;
- each available command can be executed;
- if command can't be executed for any reason, corresponding method raises an
exception (FIXME: CC9: or returns an error code???)
- if command can't be executed for any reason, corresponding method raises
an exception (FIXME: CC9: or returns an error code???)
"""
def __init__(self, domain_id: TDomainId, con_instance: Connection, con_params: dict, metadata: dict = None):
def __init__(
self, domain_id: TDomainId,
con_instance: Connection, con_params: dict,
metadata: dict = None
):
"""
Constructor of a Thing. Receives an instance of Connection and some specific
parameters to use it properly. Also can receive some metadata to be stored like
object placement, description or user-friendly name.
Constructor of a Thing. Receives an instance of Connection and some
specific parameters to use it properly. Also can receive some metadata
to be stored like object placement, description or user-friendly name.
:param domain_id: a unique identifier of this Thing
:param con_instance: an instance of connection to be used
Expand All @@ -66,7 +55,7 @@ def __init__(self, domain_id: TDomainId, con_instance: Connection, con_params: d
self._really_internal_state_value = self.States.unknown

@property
def _state(self) -> 'Actuator.States':
def _state(self) -> 'AbsActuator.States':
"""
Return a really_internal_state_value
Expand All @@ -75,7 +64,7 @@ def _state(self) -> 'Actuator.States':
return self._really_internal_state_value

@_state.setter
def _state(self, new_value: 'Actuator.States') -> None:
def _state(self, new_value: 'AbsActuator.States') -> None:
"""
Internal setter for a really_internal_state_value that can be used to
set a new state value and update last_updated time
Expand All @@ -89,7 +78,8 @@ def _state(self, new_value: 'Actuator.States') -> None:
@property
def commands(self) -> Iterable[str]:
"""
Returns a list of available commands. Must be overridden in derivative classes.
Returns a list of available commands. Must be overridden in derivative
classes.
:return: a tuple of command names (strings)
"""
Expand All @@ -111,7 +101,9 @@ def execute(self, command: str, args: Mapping = EMPTY_MAPPING) -> None:
can't be executed
"""
if command not in self.commands:
raise UnsupportedCommandError("Unsupported command passed: {0}".format(command))
raise UnsupportedCommandError(
"Unsupported command passed: {0}".format(command)
)

command_method = getattr(self, command)

Expand Down
15 changes: 8 additions & 7 deletions dpl/things/player.py → dpl/integrations/abs_player.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@

# Include 3rd-party modules
# Include DPL modules
from dpl.things import Actuator
from .abs_actuator import AbsActuator


class Player(Actuator):
class AbsPlayer(AbsActuator):
"""
Player is an abstraction of basic player device or application. It can be in
one of three states: 'stopped', 'playing' and 'paused'.
Player is an abstraction of basic player device or application. It can be
in one of three states: 'stopped', 'playing' and 'paused'.
"""
class States(Enum):
stopped = 0
Expand All @@ -21,7 +21,8 @@ class States(Enum):
@property
def commands(self) -> Tuple[str, ...]:
"""
Returns a list of available commands. Must be overridden in derivative classes.
Returns a list of available commands. Must be overridden in derivative
classes.
:return: a tuple of command names (strings)
"""
Expand Down Expand Up @@ -54,8 +55,8 @@ def deactivate(self) -> None:

def play(self, *args, **kwargs) -> None:
"""
Starts playing and switches the object to the 'playing' state. Additional parameters
like track name or URL can be provided.
Starts playing and switches the object to the 'playing' state.
Additional parameters like track name or URL can be provided.
:param args: positional parameters
:param kwargs: keyword parameters
Expand Down
17 changes: 10 additions & 7 deletions dpl/things/slider.py → dpl/integrations/abs_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@

# Include 3rd-party modules
# Include DPL modules
from dpl.things import Actuator
from .abs_actuator import AbsActuator


class Slider(Actuator):
class AbsSlider(AbsActuator):
"""
Slider is an abstraction of real-life object, that can be in one of two
stable states: 'opened' and 'closed' and also can be two transition states:
Expand All @@ -23,7 +23,8 @@ class States(Enum):
@property
def commands(self) -> Tuple[str, ...]:
"""
Returns a list of available commands. Must be overridden in derivative classes.
Returns a list of available commands. Must be overridden in derivative
classes.
:return: a tuple of command names (strings)
"""
Expand Down Expand Up @@ -56,17 +57,19 @@ def deactivate(self) -> None:

def open(self) -> None:
"""
Switches an object to the 'opening' and then 'opened' state if its current state
is 'undefined', 'closed' or 'closing'. Command must be ignored otherwise.
Switches an object to the 'opening' and then 'opened' state if its
current state is 'undefined', 'closed' or 'closing'. Command must be
ignored otherwise.
:return: None
"""
raise NotImplementedError()

def close(self) -> None:
"""
Switches an object to the 'closing' and then 'closed' state if its current state
is 'undefined', 'opened' or 'opening'. Command must be ignored otherwise.
Switches an object to the 'closing' and then 'closed' state if its
current state is 'undefined', 'opened' or 'opening'. Command must be
ignored otherwise.
:return: None
"""
Expand Down
12 changes: 7 additions & 5 deletions dpl/things/switch.py → dpl/integrations/abs_switch.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

# Include 3rd-party modules
# Include DPL modules
from dpl.things import Actuator
from .abs_actuator import AbsActuator


class Switch(Actuator):
class AbsSwitch(AbsActuator):
"""
Switch is an abstraction of objects with two only available states: 'on' and 'off'.
Like simple light bulb, power socket, relay or fan with uncontrollable speed.
Switch is an abstraction of objects with two only available states: 'on'
and 'off'. Like simple light bulb, power socket, relay or fan with
uncontrollable speed.
"""
class States(Enum):
on = True
Expand All @@ -20,7 +21,8 @@ class States(Enum):
@property
def commands(self) -> Tuple[str, ...]:
"""
Returns a list of available commands. Must be overridden in derivative classes.
Returns a list of available commands. Must be overridden in derivative
classes.
:return: a tuple of command names (strings)
"""
Expand Down
23 changes: 17 additions & 6 deletions dpl/service_impls/thing_service.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
from typing import Optional, Mapping, Any

from dpl.model.domain_id import TDomainId
from dpl.things.actuator import Actuator, UnsupportedCommandError, UnacceptableCommandArgumentsError
from dpl.integrations.abs_actuator import (
AbsActuator, UnsupportedCommandError, UnacceptableCommandArgumentsError
)
from dpl.dtos.thing_dto import ThingDto
# noinspection PyUnresolvedReferences
from dpl.dtos.actuator_dto import ActuatorDto
from dpl.dtos.dto_builder import build_dto
from dpl.services.abs_thing_service import AbsThingService, \
ServiceEntityResolutionError, ServiceTypeError, ServiceInvalidArgumentsError, \
from dpl.services.abs_thing_service import (
AbsThingService,
ServiceEntityResolutionError,
ServiceTypeError,
ServiceInvalidArgumentsError,
ServiceUnsupportedCommandError
)

from dpl.repos.abs_thing_repository import AbsThingRepository

Expand Down Expand Up @@ -75,7 +81,9 @@ def remove(self, domain_id: TDomainId) -> None:
# FIXME: Handle exceptions caused by link breakages
self._things.delete(domain_id)

def select_by_placement(self, placement_id: Optional[TDomainId]): # -> Collection[ThingDto]:
def select_by_placement(
self, placement_id: Optional[TDomainId]
): # -> Collection[ThingDto]:
"""
Selects all Things that are physically present in the
specified Placement
Expand All @@ -90,7 +98,10 @@ def select_by_placement(self, placement_id: Optional[TDomainId]): # -> Collecti
build_dto(i) for i in self._things.select_by_placement(placement_id)
]

def send_command(self, to_actuator_id: TDomainId, command: str, command_args: Mapping[str, Any]) -> None:
def send_command(
self, to_actuator_id: TDomainId,
command: str, command_args: Mapping[str, Any]
) -> None:
"""
Allows to send a command to Actuator or any other Thing
which has an 'execute' method implemented.
Expand Down Expand Up @@ -120,7 +131,7 @@ def send_command(self, to_actuator_id: TDomainId, command: str, command_args: Ma
command is not supported by this instance of Thing
:raises ServiceUnsupportedCommandError:
"""
thing = self._things.load(to_actuator_id) # type: Actuator
thing = self._things.load(to_actuator_id) # type: AbsActuator

if thing is None:
raise ServiceEntityResolutionError(
Expand Down
7 changes: 2 additions & 5 deletions dpl/things/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
from .thing import Thing
from .actuator import Actuator
from .switch import Switch
from .slider import Slider
from .player import Player
from . import capabilities

__all__ = ["Thing", "Actuator", "Switch", "Slider", "Player"]
__all__ = ["Thing", "capabilities"]
19 changes: 19 additions & 0 deletions dpl/things/capabilities/i_actuator.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@
from .i_state import IState


class UnsupportedCommandError(ValueError):
"""
An exceptions to be raised if the specified command
is not supported by this instance of Thing
"""
pass


class UnacceptableCommandArgumentsError(Exception):
"""
An exception to be raised if at least one of the specified
command arguments has an unacceptable type or if there is
an incorrect set of arguments passed (i.e. if one of the
mandatory arguments is missing or if one of the specified
arguments is extra and isn't related to the specified command)
"""
pass


class IActuator(IState):
"""
IActuator capability is usually mapped to Actuators.
Expand Down

0 comments on commit d44ca11

Please sign in to comment.