Browse Source

backend: Create Python package 'monsun_backend'

Andreas Berthoud 4 years ago
parent
commit
15ccad6106
  1. 1
      .gitignore
  2. 4
      backend/.gitignore
  3. 58
      backend/container.py
  4. 22
      backend/monsun_backend/__init__.py
  5. 0
      backend/monsun_backend/command_endpoint.py
  6. 14
      backend/monsun_backend/command_execution.py
  7. 0
      backend/monsun_backend/commands.py
  8. 56
      backend/monsun_backend/container.py
  9. 2
      backend/monsun_backend/defaults/config.yml
  10. 27
      backend/monsun_backend/util.py
  11. 4
      backend/requirements.txt
  12. 3
      backend/runsever.py
  13. 19
      backend/setup.py
  14. 3
      backend/wsgi.py
  15. 1
      config_example.yml
  16. 3
      requirements.txt

1
.gitignore

@ -65,3 +65,4 @@ Release/
.vscode/
venv*/
*.pyc
config.yml

4
backend/.gitignore

@ -0,0 +1,4 @@
**/__pycache__/
build/
dist/
**/*.egg-info

58
backend/container.py

@ -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

22
backend/__init__.py → 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

0
backend/command_endpoint.py → backend/monsun_backend/command_endpoint.py

14
backend/command_execution.py → 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)

0
backend/commands.py → backend/monsun_backend/commands.py

56
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

2
backend/defaults/config.yml → 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

27
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

4
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

3
backend/runsever.py

@ -1,3 +0,0 @@
from . import create_app
application = create_app()

19
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,
)

3
backend/wsgi.py

@ -0,0 +1,3 @@
from monsun_backend import create_app
application = create_app()

1
config_example.yml

@ -0,0 +1 @@
device_id: /dev/tty.usbmodem207E3283544E1

3
requirements.txt

@ -1,4 +1 @@
dependency-injector[yaml]>=4.34.0,<5
flask-api>=3.0.post1,<4
pre-commit
pyserial>=3.5,<4

Loading…
Cancel
Save