diff --git a/.gitignore b/.gitignore index 96189e7..6f52837 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,4 @@ Release/ .vscode/ venv*/ *.pyc +config.yml diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..66a3cdf --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,4 @@ +**/__pycache__/ +build/ +dist/ +**/*.egg-info diff --git a/backend/MANIFEST.in b/backend/MANIFEST.in new file mode 100644 index 0000000..4a32307 --- /dev/null +++ b/backend/MANIFEST.in @@ -0,0 +1 @@ +include monsun_backend/defaults/config.yml diff --git a/backend/container.py b/backend/container.py deleted file mode 100644 index 7c443ff..0000000 --- a/backend/container.py +++ /dev/null @@ -1,58 +0,0 @@ -from pathlib import Path -from typing import Dict -from typing import Optional - -from dependency_injector import containers -from dependency_injector import providers -from flask_api import FlaskAPI -from serial import Serial - -DEFAULTS_DIR = Path(__file__).parent / "defaults" -CONFIG_FILE = DEFAULTS_DIR / "config.yml" - - -class Container(containers.DeclarativeContainer): - config = providers.Configuration("config") - - serial = providers.Factory( - Serial, - port=config.device_id.required(), - baudrate=config.baudrate.required(), - ) - - -__container: Optional[Container] = None - - -def initialize_container( - config: Optional[Dict] = None, - config_file: Optional[Path] = None, -) -> Container: - global __container - if __container is not None: - raise RuntimeError("Container already initialized") - - __container = Container() - __container.config.from_yaml(CONFIG_FILE) - - if config is not None: - __container.config.from_dict(config) - - if config_file is not None: - __container.config.from_yaml(config_file) - - return __container - - -def init_app(app: FlaskAPI): - initialize_container( - config=app.config.get("DI_CONFIG"), - config_file=app.config.get("DI_CONFIG_FILE"), - ) - - -def get_container() -> Container: - global __container - if __container is None: - raise RuntimeError("Container not initialized") - return __container diff --git a/backend/__init__.py b/backend/monsun_backend/__init__.py similarity index 59% rename from backend/__init__.py rename to backend/monsun_backend/__init__.py index 32e488b..c9e1258 100644 --- a/backend/__init__.py +++ b/backend/monsun_backend/__init__.py @@ -5,7 +5,12 @@ from flask_api import FlaskAPI from . import command_endpoint from . import command_execution -from . import container + +__title__ = "monsun-backend" +__author__ = "Andreas Berthoud, Fabian Klein" +__version__ = "0.0.0" +__email__ = "andreasberthoud@gmail.com" +__copyright__ = f"2021 {__author__}" def create_app() -> FlaskAPI: @@ -15,23 +20,30 @@ def create_app() -> FlaskAPI: dictConfig( { "version": 1, + "disable_existing_loggers": False, "formatters": { "default": { "format": "[%(asctime)s] %(levelname)s in %(module)s: %(message)s", }, }, "handlers": { - "wsgi": { + "console": { "class": "logging.StreamHandler", - "stream": "ext://flask.logging.wsgi_errors_stream", "formatter": "default", + "stream": "ext://sys.stdout", + }, + }, + "root": {"level": "NOTSET", "handlers": ["console"]}, + "logger": { + "monsun_backend": { + "level": "DEBUG", + "propagate": "no", }, }, - "root": {"level": "DEBUG", "handlers": ["wsgi"]}, }, ) - container.init_app(app) + # container.init_app(app) if os.environ.get("WERKZEUG_RUN_MAIN") != "true": # prevent from be called twice in debug mode diff --git a/backend/command_endpoint.py b/backend/monsun_backend/command_endpoint.py similarity index 100% rename from backend/command_endpoint.py rename to backend/monsun_backend/command_endpoint.py diff --git a/backend/command_execution.py b/backend/monsun_backend/command_execution.py similarity index 96% rename from backend/command_execution.py rename to backend/monsun_backend/command_execution.py index ca44356..1a33cf0 100644 --- a/backend/command_execution.py +++ b/backend/monsun_backend/command_execution.py @@ -17,8 +17,7 @@ from .commands import Command from .commands import CommandId from .commands import Request from .commands import Response -from .container import get_container -from .container import initialize_container +from .container import get_initialize_container _logger = logging.getLogger(__file__) @@ -34,7 +33,6 @@ class State(Enum): def worker_process( queue: Queue, - config, ): logging.basicConfig( level=logging.DEBUG, @@ -43,8 +41,7 @@ def worker_process( logger = logging.getLogger("Command Loop") logger.setLevel(logging.INFO) - initialize_container(config) - container = get_container() + container = get_initialize_container() heartbeat_interval = container.config.heartbeat_interval() serial_reconnection_wait_timeout = ( container.config.serial_reconnection_wait_timeout() @@ -231,15 +228,12 @@ def _end_running_process(): def start_backgroup_process(): - _logger.warning("start_backgroup_process called") + _logger.info("start_backgroup_process called") global _process _process = Process( target=worker_process, - args=( - _command_queue, - get_container().config(), - ), + args=(_command_queue,), ) _process.start() atexit.register(_end_running_process) diff --git a/backend/commands.py b/backend/monsun_backend/commands.py similarity index 100% rename from backend/commands.py rename to backend/monsun_backend/commands.py diff --git a/backend/monsun_backend/container.py b/backend/monsun_backend/container.py new file mode 100644 index 0000000..f410006 --- /dev/null +++ b/backend/monsun_backend/container.py @@ -0,0 +1,56 @@ +import logging +from pathlib import Path +from pprint import pformat +from typing import Dict +from typing import Optional + +from dependency_injector import containers +from dependency_injector import providers +from serial import Serial + +from .util import log_function_call + +DEFAULTS_DIR = Path(__file__).parent / "defaults" +CONFIG_FILE = DEFAULTS_DIR / "config.yml" + +_logger = logging.getLogger(__name__) + + +class Container(containers.DeclarativeContainer): + config = providers.Configuration("config") + + serial = providers.Factory( + Serial, + port=config.device_id.required(), + baudrate=config.baudrate.required(), + ) + + +@log_function_call +def get_initialize_container( + config: Optional[Dict] = None, + config_file: Optional[Path] = None, +) -> Container: + logger = _logger.getChild("initialize_container") + + logger.debug("initialize container...") + container = Container() + + logger.debug(f"initialize container from config file: {CONFIG_FILE}") + container.config.from_yaml(CONFIG_FILE, required=True) + + user_config = Path.cwd() / "config.yml" + if user_config.is_file(): + logger.debug(f"initialize container from user config file: {user_config}") + container.config.from_yaml(user_config, required=True) + + if config is not None: + logger.debug(f"initialize container with config: {config}") + container.config.from_dict(config) + + if config_file is not None: + logger.debug(f"initialize container from config file: {config_file}") + container.config.from_yaml(config_file) + + logger.debug(f"container config:\n{pformat(container.config())}") + return container diff --git a/backend/defaults/config.yml b/backend/monsun_backend/defaults/config.yml similarity index 51% rename from backend/defaults/config.yml rename to backend/monsun_backend/defaults/config.yml index 232414b..3294877 100644 --- a/backend/defaults/config.yml +++ b/backend/monsun_backend/defaults/config.yml @@ -1,5 +1,3 @@ -device_id: /dev/tty.usbmodem2067368F32521 -# device_id: /dev/tty.usbmodem207E3283544E1 baudrate: 115200 header_size: 4 heartbeat_interval: 1 diff --git a/backend/monsun_backend/util.py b/backend/monsun_backend/util.py new file mode 100644 index 0000000..5631bf7 --- /dev/null +++ b/backend/monsun_backend/util.py @@ -0,0 +1,27 @@ +import functools +import logging + +function_call_logger = logging.getLogger("call") + + +# decorator +def log_function_call(func): + if logging.getLogger().level != logging.DEBUG: + return func + + @functools.wraps(func) + def wrapper(*args, **kwargs): + function_call_logger.debug( + "calling {func}({arguments})".format( + func=func.__name__, + arguments=", ".join( + [str(value) for value in args] + + [f"{key}={value}" for key, value in kwargs.items()], + ), + ), + ) + return_val = func(*args, **kwargs) + function_call_logger.debug(f"{func.__name__}() returned {return_val}") + return return_val + + return wrapper diff --git a/backend/requirements.txt b/backend/requirements.txt new file mode 100644 index 0000000..bd17f68 --- /dev/null +++ b/backend/requirements.txt @@ -0,0 +1,4 @@ +dependency-injector[yaml]>=4.34.0,<5 +flask-api>=3.0.post1,<4 +pyserial>=3.5,<4 +uwsgi diff --git a/backend/runsever.py b/backend/runsever.py deleted file mode 100644 index a316945..0000000 --- a/backend/runsever.py +++ /dev/null @@ -1,3 +0,0 @@ -from . import create_app - -application = create_app() diff --git a/backend/setup.py b/backend/setup.py new file mode 100644 index 0000000..a1fecc9 --- /dev/null +++ b/backend/setup.py @@ -0,0 +1,19 @@ +import setuptools +from monsun_backend import __author__ +from monsun_backend import __email__ +from monsun_backend import __version__ + +setuptools.setup( + name="monsun-backend", + version=__version__, + author=__author__, + author_email=__email__, + description="Monsun backend", + packages=setuptools.find_packages(), + classifiers=[ + "Programming Language :: Python :: 3", + "Operating System :: OS Independent", + ], + python_requires=">=3.7", + include_package_data=True, +) diff --git a/backend/wsgi.py b/backend/wsgi.py new file mode 100644 index 0000000..0572266 --- /dev/null +++ b/backend/wsgi.py @@ -0,0 +1,3 @@ +from monsun_backend import create_app + +application = create_app() diff --git a/config_example.yml b/config_example.yml new file mode 100644 index 0000000..028ae0b --- /dev/null +++ b/config_example.yml @@ -0,0 +1 @@ +device_id: /dev/tty.usbmodem207E3283544E1 diff --git a/requirements.txt b/requirements.txt index 88ebcb4..416634f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1 @@ -dependency-injector[yaml]>=4.34.0,<5 -flask-api>=3.0.post1,<4 pre-commit -pyserial>=3.5,<4