From a29ffe92a6a3f26d32fa949d5ff409834396db61 Mon Sep 17 00:00:00 2001 From: Sebastian Lenzlinger Date: Thu, 27 Jun 2024 17:50:21 +0200 Subject: [PATCH] Make installable by pip in editable mode --- iottb/main.py | 66 +++++++++++++++++++++++++--------------------- poetry.lock | 7 +++++ pyproject.toml | 2 ++ tests/test_main.py | 18 +++++++++++++ 4 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 poetry.lock diff --git a/iottb/main.py b/iottb/main.py index 9422bf3..1231bf2 100644 --- a/iottb/main.py +++ b/iottb/main.py @@ -1,9 +1,6 @@ import json -import subprocess - import click -from pathlib import PurePath, Path -import configparser +from pathlib import Path import logging from logging.handlers import RotatingFileHandler import sys @@ -60,48 +57,57 @@ def setup_logging(verbosity, debug): def create_default_config(cfg_file): """Create default iottb config file.""" - assert logger is not None, 'Logger must be initialized' logger.info(f'Creating default config file at {cfg_file}') - # By default, create iottb.db in user home defaults = { 'DefaultDatabase': DB_NAME, 'DatabaseLocations': { DB_NAME: str(Path.home() / DB_NAME) } } - with open(cfg_file, 'w') as config_file: - json.dump(defaults, config_file) + try: + with open(cfg_file, 'w') as config_file: + json.dump(defaults, config_file) + except IOError as e: + logger.error(f"Failed to create default configuration file at {cfg_file}: {e}") + raise RuntimeError(f"Failed to create configuration file: {e}") from e return defaults -def load_config(ctx, param, value): - """ Try to load iottb config file. - If the file does not exist, it will be created with default values.` - Try to load the iottb config file from the given path. - If the file does not exist, it will be created with default values. - The only value set is the path to the iottb.db file. - """ - logger.info(f'Loading config from {value}') - cfg_file = value +def load_config(cfg_file): + """Loads or creates default configuration from given file path.""" if not cfg_file.is_file(): - defaults = create_default_config(cfg_file) - else: - defaults = json.load(cfg_file) - - ctx.obj['path']['cfg'] = cfg_file - ctx.obj['path']['db'] = defaults['DatabaseLocations'][defaults['DefaultDatabase']] + return create_default_config(cfg_file) + with open(cfg_file, 'r') as config_file: + return json.load(config_file) @click.group() -@click.option('-v', '--verbosity', type=click.IntRange(0, MAX_VERBOSITY), default=0, +@click.option('-v', '--verbosity', count=True, type=click.IntRange(0, 3), default=0, help='Set verbosity') -@click.option('-d', '--debug', is_flag=True, default=False, help='Enable debug mode') -@click.option('--cfg-file', type=click.Path(), default=Path(click.get_app_dir(APP_NAME)).joinpath('iottb.cfg'), - envvar='IOTTB_CONF_HOME', is_eager=True, callback=load_config, help='Path to iottb config file') +@click.option('-d', '--debug', is_flag=True, default=False, + help='Enable debug mode') +@click.option('--cfg-file', type=click.Path(exists=True), + default=Path(click.get_app_dir(APP_NAME)).joinpath('iottb.cfg'), + envvar='IOTTB_CONF_HOME', help='Path to iottb config file') @click.pass_context -def main(verbosity, debug): - setup_logging(verbosity, debug) +def cli(ctx, verbosity, debug, cfg_file): + ctx.ensure_object(dict) # Make sure context is ready for use + ctx.obj['CONFIG'] = load_config(cfg_file) # Load configuration directly + setup_logging(verbosity, debug) # Setup logging based on the loaded configuration and other options + + +@click.command() +@click.option('-d', '--dest', default=Path.home(), type=str, help='Location to put (new) iottb database') +@click.option('--name', default=DB_NAME, type=str, help='Name of new database.') +@click.option('--update-default/ --no-update-default', default=True, + help='If new db should be set as the new default') +@click.pass_context +def init_db(ctx, dest, name, update_default): + logger.debug('TOP init_db') + + +cli.add_command(init_db) if __name__ == '__main__': - main(auto_envvar_prefix='IOTTB') + cli(auto_envvar_prefix='IOTTB') diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..1034779 --- /dev/null +++ b/poetry.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +package = [] + +[metadata] +lock-version = "2.0" +python-versions = "^3.12" +content-hash = "34e39677d8527182346093002688d17a5d2fc204b9eb3e094b2e6ac519028228" diff --git a/pyproject.toml b/pyproject.toml index 5192efb..b05d1f3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,6 +8,8 @@ readme = "README.md" [tool.poetry.dependencies] python = "^3.12" +[tool.poetry.scripts] +iottb = "iottb.main:cli" [build-system] requires = ["poetry-core"] diff --git a/tests/test_main.py b/tests/test_main.py index e69de29..61cda1b 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -0,0 +1,18 @@ +# test_main.py +import pytest +from click.testing import CliRunner +from iottb.main import cli # Adjust this import according to your project's structure + + +@pytest.fixture +def runner(): + """Fixture to return a Click CliRunner instance.""" + return CliRunner() + + +def test_debug_option(runner): + """Test if the debug mode sets the appropriate flag in the context.""" + result = runner.invoke(cli, ['--debug']) + assert result.exit_code == 0 + # If debug mode affects logging or other behavior, validate those: + assert 'DEBUG mode on' in result.output