Skip to content

Commit

Permalink
0.12.4 (#115)
Browse files Browse the repository at this point in the history
0.12.4
- MultiModeItem supports a custom disable function
- Reworked folder watcher (fixes #90)
- Added profiler which gives better overview for long running functions (fixes #96)
- Docker fixes
- some cleanup
  • Loading branch information
spacemanspiff2007 authored Mar 11, 2020
1 parent bb2f72b commit 3d1cab4
Show file tree
Hide file tree
Showing 32 changed files with 361 additions and 299 deletions.
3 changes: 1 addition & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ RUN apk add --no-cache \
# Support for Timezones
tzdata \
# ujson won't compile without these libs
musl-dev \
gcc
g++

# Always use latest versions
RUN mkdir -p /usr/src/app
Expand Down
2 changes: 1 addition & 1 deletion HABApp/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__VERSION__ = '0.12.3'
__VERSION__ = '0.12.4'
21 changes: 7 additions & 14 deletions HABApp/config/config_loader.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
import ruamel.yaml

from HABApp.__version__ import __VERSION__
from HABApp.runtime import FileEventTarget
from . import CONFIG
from .default_logfile import get_default_logfile

Expand All @@ -31,7 +30,7 @@ class InvalidConfigException(Exception):
pass


class HABAppConfigLoader(FileEventTarget):
class HABAppConfigLoader:

def __init__(self, config_folder: Path):

Expand All @@ -48,25 +47,19 @@ def __init__(self, config_folder: Path):
self.first_start = True
try:
# try load logging config first. If we use abs path we can log errors when loading config.yml
self.on_file_added(self.file_conf_logging)
self.on_file_added(self.file_conf_habapp)
self.on_file_event(self.file_conf_logging)
self.on_file_event(self.file_conf_habapp)
except AbsolutePathExpected:
self.on_file_added(self.file_conf_habapp)
self.on_file_added(self.file_conf_logging)
self.on_file_event(self.file_conf_habapp)
self.on_file_event(self.file_conf_logging)
self.first_start = False

def on_file_added(self, path: Path):
self.on_file_changed(path)

def on_file_changed(self, path: Path):
def on_file_event(self, path: Path):
if path.name == 'config.yml':
self.load_cfg()
if path.name == 'logging.yml':
self.load_log()

def on_file_removed(self, path: Path):
pass

def __check_create_logging(self):
if self.file_conf_logging.is_file():
return None
Expand All @@ -90,7 +83,7 @@ def load_cfg(self):
return None

def load_log(self):
# File has to exist - check because we also get FileDelete events
# config gets created on startup - if it gets deleted we do nothing here
if not self.file_conf_logging.is_file():
return None

Expand Down
2 changes: 1 addition & 1 deletion HABApp/core/EventBus.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import threading
import typing

from HABApp.util import log_exception
from HABApp.core.wrapper import log_exception
from . import EventBusListener
from .events import ComplexEventValue

Expand Down
1 change: 1 addition & 0 deletions HABApp/core/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from . import const
from . import wrapper

from .wrappedfunction import WrappedFunction

Expand Down
3 changes: 2 additions & 1 deletion HABApp/core/const/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .const import MISSING
from . import json
from . import topics
from .const import MISSING
from .loop import loop
8 changes: 8 additions & 0 deletions HABApp/core/const/json.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
try:
import ujson
load_json = ujson.loads
dump_json = ujson.dumps
except ImportError:
import json
load_json = json.loads
dump_json = json.dumps
6 changes: 3 additions & 3 deletions HABApp/core/items/item_times.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@
import typing

import HABApp
from HABApp.util.wrapper import log_exception
from HABApp.core.wrapper import log_exception
from ..const import loop
from ..events import ItemNoChangeEvent, ItemNoUpdateEvent


class BaseWatch:
EVENT: typing.Union[typing.Type[ItemNoUpdateEvent], typing.Type[ItemNoChangeEvent]]

def __init__(self, name: str, secs: int):
def __init__(self, name: str, secs: typing.Union[int, float]):
self._secs: typing.Union[int, float] = secs
self._name: str = name
self._task: typing.Optional[asyncio.Task] = None
Expand Down Expand Up @@ -75,7 +75,7 @@ def set(self, dt: datetime.datetime, events=True):
asyncio.run_coroutine_threadsafe(self.schedule_events(), loop)
return None

def add_watch(self, secs: int) -> BaseWatch:
def add_watch(self, secs: typing.Union[int, float]) -> BaseWatch:
assert secs > 0, secs

# don't add the watch two times
Expand Down
24 changes: 23 additions & 1 deletion HABApp/core/wrappedfunction.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
import asyncio
import concurrent.futures
import io
import logging
import time
import traceback
from cProfile import Profile
from pstats import Stats
try:
from pstats import SortKey
STAT_SORT_KEY = SortKey.CUMULATIVE
except ImportError:
STAT_SORT_KEY = 'cumulative', 'cumtime'

import HABApp

Expand All @@ -27,7 +35,6 @@ def __init__(self, func, logger=None, warn_too_long=True, name=None):
# Allow custom logger
self.log = default_logger
if logger:
assert isinstance(logger, logging.getLoggerClass())
self.log = logger

self.__warn_too_long = warn_too_long
Expand Down Expand Up @@ -80,13 +87,28 @@ def __run(self, *args, **kwargs):
self.log.warning(f'Starting of {self.name} took too long: {__start - self.__time_submitted:.2f}s. '
f'Maybe there are not enough threads?')

# start profiler
pr = Profile()
pr.enable()

# Execute the function
try:
self._func(*args, **kwargs)
except Exception as e:
self.__format_traceback(e, *args, **kwargs)

# disable profiler
pr.disable()

# log warning if execution takes too long
__dur = time.time() - __start
if self.__warn_too_long and __dur > 0.8:
self.log.warning(f'Execution of {self.name} took too long: {__dur:.2f}s')

s = io.StringIO()
ps = Stats(pr, stream=s).sort_stats(STAT_SORT_KEY)
ps.print_stats(0.1) # limit to output to 10% of the lines

for line in s.getvalue().splitlines()[4:]: # skip the amount of calls and "Ordered by:"
if line:
self.log.warning(line)
Loading

0 comments on commit 3d1cab4

Please sign in to comment.