Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: separate queries from printing #55

Draft
wants to merge 7 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@ repos:
rev: v0.940
hooks:
- id: mypy
additional_dependencies: [types-PyYAML]
16 changes: 16 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "huemon",
"args": ["discover", "lights"],
"justMyCode": true
}
]
}
3 changes: 2 additions & 1 deletion src/huemon/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from huemon.infrastructure.bootstrapper import bootstrap
from huemon.infrastructure.config_factory import create_config
from huemon.infrastructure.logger_factory import bootstrap_logger
from huemon.sinks.stdout_sink import StdoutSink
from huemon.utils.const import EXIT_OK
from huemon.utils.errors import exit_fail
from huemon.utils.plugins import get_command_plugins_path
Expand All @@ -24,7 +25,7 @@ def main(argv):
bootstrap()

command_handler = create_default_command_handler(
CONFIG, get_command_plugins_path(CONFIG)
CONFIG, StdoutSink(), get_command_plugins_path(CONFIG)
)

if len(argv) <= 1:
Expand Down
3 changes: 2 additions & 1 deletion src/huemon/api_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

from huemon.commands.command_handler import create_default_command_handler
from huemon.infrastructure.logger_factory import create_logger
from huemon.sinks.stdout_sink import StdoutSink
from huemon.utils.plugins import get_command_plugins_path

LOG = create_logger()
Expand Down Expand Up @@ -47,7 +48,7 @@ def create(config: dict) -> FastAPI:
app = FastAPI()

command_handler = create_default_command_handler(
config, get_command_plugins_path(config)
config, StdoutSink(), get_command_plugins_path(config)
)

for command_name in command_handler.available_commands():
Expand Down
25 changes: 17 additions & 8 deletions src/huemon/commands/command_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from huemon.commands.hue_command_interface import HueCommand
from huemon.infrastructure.logger_factory import create_logger
from huemon.infrastructure.plugin_loader import load_plugins
from huemon.sinks.sink_interface import SinkInterface
from huemon.utils.errors import exit_fail
from huemon.utils.monads.either import rights
from huemon.utils.paths import create_local_path
Expand All @@ -20,12 +21,17 @@


def create_name_to_command_mapping(
config: dict, api: ApiInterface, plugins: List[Type[HueCommand]]
config: dict,
api: ApiInterface,
processor: SinkInterface,
plugins: List[Type[HueCommand]],
) -> dict:
return reduce(lambda p, c: {**p, c.name(): c(config, api)}, plugins, {})
return reduce(lambda p, c: {**p, c.name(): c(config, api, processor)}, plugins, {})


def __load_command_plugins(config: dict, command_plugins_path: str = None) -> dict:
def __load_command_plugins(
config: dict, processor: SinkInterface, command_plugins_path: str = None
) -> dict:
LOG.debug("Loading command plugins (path=%s)", command_plugins_path)
if not command_plugins_path:
return {}
Expand All @@ -35,6 +41,7 @@ def __load_command_plugins(config: dict, command_plugins_path: str = None) -> di
command_handler_plugins = create_name_to_command_mapping(
config,
create_api(config),
processor,
command_plugins,
)
LOG.debug("Finished loading command plugins (path=%s)", command_plugins_path)
Expand All @@ -43,19 +50,21 @@ def __load_command_plugins(config: dict, command_plugins_path: str = None) -> di


def __load_plugins_and_hardwired_handlers(
config: dict, command_plugins_path: str = None
config: dict, processor: SinkInterface, command_plugins_path: str = None
) -> dict:
hardwired_commands_path = create_local_path(os.path.join("commands", "internal"))

return {
**__load_command_plugins(config, command_plugins_path),
**__load_command_plugins(config, hardwired_commands_path),
**__load_command_plugins(config, processor, command_plugins_path),
**__load_command_plugins(config, processor, hardwired_commands_path),
}


def create_default_command_handler(config: dict, command_plugins_path: str):
def create_default_command_handler(
config: dict, processor: SinkInterface, command_plugins_path: str = None
):
return CommandHandler(
__load_plugins_and_hardwired_handlers(config, command_plugins_path)
__load_plugins_and_hardwired_handlers(config, processor, command_plugins_path)
)


Expand Down
14 changes: 8 additions & 6 deletions src/huemon/commands/hue_command_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@
from functools import reduce

from huemon.api.api_interface import ApiInterface
from huemon.sinks.sink_interface import SinkInterface


class HueCommand:
def __init__(self, config: dict, api: ApiInterface):
raise NotImplementedError("Command requires a constructor")
def __init__(self, config: dict, api: ApiInterface, processor: SinkInterface):
self.config = config
self.api = api
self.processor = processor

@staticmethod
def get_by_unique_id(unique_id: str, items: list) -> list:
Expand All @@ -20,10 +23,6 @@ def get_by_unique_id(unique_id: str, items: list) -> list:
)
)[0]

@staticmethod
def _process(value):
print(value)

@staticmethod
def _mapper(path: str, value_type):
return lambda value: value_type(
Expand All @@ -34,5 +33,8 @@ def _mapper(path: str, value_type):
def name():
pass

def _process(self, value):
self.processor.process(value)

def exec(self, arguments):
pass
22 changes: 10 additions & 12 deletions src/huemon/commands/internal/agent_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,10 @@

import uvicorn # type: ignore

from huemon.api.api_interface import ApiInterface
from huemon.api_server import HuemonServerFactory
from huemon.commands.hue_command_interface import HueCommand
from huemon.infrastructure.logger_factory import create_logger
from huemon.utils.assertions import assert_exists, assert_num_args
from huemon.utils.assertions import assert_exists_e, assert_num_args_e

LOG = create_logger()

Expand Down Expand Up @@ -38,24 +37,23 @@ class AgentCommand(HueCommand):
"start": MyServer.start,
}

def __init__(
self, config: dict, _: ApiInterface
): # pylint: disable=unused-argument
self.config = config

@staticmethod
def name():
return "agent"

def exec(self, arguments):
LOG.debug("Running `%s` command (arguments=%s)", AgentCommand.name(), arguments)
assert_num_args(1, arguments, AgentCommand.name())

action, *_ = arguments

assert_exists(list(AgentCommand.__SYSTEM_ACTION_MAP), action)
self._process(
assert_num_args_e(1, arguments, AgentCommand.name())
.bind(
lambda ax: assert_exists_e(
list(AgentCommand.__SYSTEM_ACTION_MAP), ax[0]
)
)
.fmap(lambda action: self.__SYSTEM_ACTION_MAP[action](self.config))
)

HueCommand._process(self.__SYSTEM_ACTION_MAP[action](self.config))
LOG.debug(
"Finished `%s` command (arguments=%s)", AgentCommand.name(), arguments
)
56 changes: 34 additions & 22 deletions src/huemon/commands/internal/discover_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,31 @@

import os
from functools import reduce
from typing import Dict, List, Type
from typing import Dict, List, Type, TypeVar

from huemon.api.api_interface import ApiInterface
from huemon.commands.hue_command_interface import HueCommand
from huemon.discoveries.discovery_interface import Discovery
from huemon.infrastructure.logger_factory import create_logger
from huemon.infrastructure.plugin_loader import load_plugins
from huemon.utils.assertions import assert_exists, assert_num_args
from huemon.sinks.sink_interface import SinkInterface
from huemon.utils.assertions import assert_exists_e, assert_num_args_e
from huemon.utils.common import fst
from huemon.utils.monads.either import Either, rights
from huemon.utils.monads.maybe import Maybe, maybe, of
from huemon.utils.paths import create_local_path
from huemon.utils.plugins import get_discovery_plugins_path

LOG = create_logger()

TA = TypeVar("TA")
TB = TypeVar("TB")


def create_discovery_handlers(
api: ApiInterface, plugins: List[Type[Discovery]]
api: ApiInterface, sink: SinkInterface, plugins: List[Type[Discovery]]
) -> Dict[str, Discovery]:
return reduce(lambda p, c: {**p, c.name(): c(api)}, plugins, {})
return reduce(lambda p, c: {**p, c.name(): c(api, sink)}, plugins, {})


class DiscoveryHandler: # pylint: disable=too-few-public-methods
Expand All @@ -39,9 +44,9 @@ def exec(self, discovery_type):
)
target, maybe_sub_target, *_ = discovery_type.split(":") + [None]

assert_exists(list(self.handlers), target)

self.handlers[target].exec([maybe_sub_target] if maybe_sub_target else [])
assert_exists_e(list(self.handlers), target).fmap(
lambda tx: self.handlers[tx]
).fmap(lambda hlr: hlr.exec([maybe_sub_target] if maybe_sub_target else []))

LOG.debug(
"Finished `%s` command (discovery_type=%s)",
Expand All @@ -51,17 +56,16 @@ def exec(self, discovery_type):


class Discover: # pylint: disable=too-few-public-methods
def __init__(self, config: dict, api: ApiInterface):
self.api = api

def __init__(self, config: dict, api: ApiInterface, sink: SinkInterface):
self.discovery_plugins_path = get_discovery_plugins_path(config)

self.handler = self.__create_discovery_handler()
self.handler = self.__create_discovery_handler(api, sink)

def __create_discovery_handler(self):
def __create_discovery_handler(self, api: ApiInterface, sink: SinkInterface):
LOG.debug("Loading discovery plugins (path=%s)", self.discovery_plugins_path)
discovery_handler_plugins = create_discovery_handlers(
self.api,
api,
sink,
rights(
Discover.__load_plugins_and_hardwired_handlers(
of(self.discovery_plugins_path)
Expand Down Expand Up @@ -95,22 +99,30 @@ def discover(self, discovery_type):


class DiscoverCommand(HueCommand):
def __init__(
self, config: dict, api: ApiInterface
): # pylint: disable=super-init-not-called
self.discovery = Discover(config, api)
def __init__(self, config: dict, api: ApiInterface, processor: SinkInterface):
super().__init__(config, api, processor)

self.discovery = Discover(config, api, processor)

@staticmethod
def name():
return "discover"

def exec(self, arguments):
def exec(self, arguments: List[str]):
LOG.debug(
"Running `%s` command (arguments=%s)", DiscoverCommand.name(), arguments
)
assert_num_args(1, arguments, DiscoverCommand.name())

discovery_type, *_ = arguments
error, param = assert_num_args_e(1, arguments, DiscoverCommand.name()).fmap(
fst # type: ignore
)

self.discovery.discover(discovery_type)
LOG.debug("Finished `discover` command (arguments=%s)", arguments)
if error:
self.processor.process(error)
return

self.discovery.discover(param)

LOG.debug(
"Finished `%s` command (arguments=%s)", DiscoverCommand.name(), arguments
)
14 changes: 4 additions & 10 deletions src/huemon/commands/internal/light_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,14 @@
# This source code is licensed under the MPL-2.0 license found in the
# LICENSE file in the root directory of this source tree.

from huemon.api.api_interface import ApiInterface
from huemon.commands.hue_command_interface import HueCommand
from huemon.infrastructure.logger_factory import create_logger
from huemon.utils.assertions import assert_exists, assert_num_args
from huemon.utils.assertions import assert_exists_e, assert_num_args_e

LOG = create_logger()


class LightCommand(HueCommand):
def __init__(
self, config: dict, api: ApiInterface
): # pylint: disable=unused-argument
self.api = api

__LIGHT_ACTION_MAP = {
"is_upgrade_available": lambda light: int(
light["swupdate"]["state"] != "noupdates"
Expand All @@ -38,13 +32,13 @@ def name():

def exec(self, arguments):
LOG.debug("Running `%s` command (arguments=%s)", LightCommand.name(), arguments)
assert_num_args(2, arguments, LightCommand.name())
assert_num_args_e(2, arguments, LightCommand.name())

light_id, action = arguments

assert_exists(list(LightCommand.__LIGHT_ACTION_MAP), action)
assert_exists_e(list(LightCommand.__LIGHT_ACTION_MAP), action)

HueCommand._process(
self._process(
self.__map_light(light_id, LightCommand.__LIGHT_ACTION_MAP[action])
)

Expand Down
8 changes: 1 addition & 7 deletions src/huemon/commands/internal/sensor_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
# This source code is licensed under the MPL-2.0 license found in the
# LICENSE file in the root directory of this source tree.

from huemon.api.api_interface import ApiInterface
from huemon.commands.hue_command_interface import HueCommand
from huemon.infrastructure.logger_factory import create_logger
from huemon.utils.assertions import assert_exists, assert_num_args
Expand All @@ -12,11 +11,6 @@


class SensorCommand(HueCommand):
def __init__(
self, config: dict, api: ApiInterface
): # pylint: disable=unused-argument
self.api = api

def __get_sensor(self, device_id):
return HueCommand.get_by_unique_id(device_id, self.api.get_sensors())

Expand Down Expand Up @@ -45,7 +39,7 @@ def exec(self, arguments):

assert_exists(list(SensorCommand.__SENSOR_ACTION_MAP), action)

HueCommand._process(
self._process(
self.__map_sensor(device_id, SensorCommand.__SENSOR_ACTION_MAP[action])
)
LOG.debug(
Expand Down
Loading