def run(command=None, config_filepath=None, verbose=False, **params): """High-level API entry point. All 'params' are passed to 'Client.safely_run()'. 'config_filepath' specifies the path to a custom config file (optional). If 'verbose' is set, debug level log messages are printed to the terminal. This function can be used for scripting. Provide 'command' and 'params' according to what the command line interface accepts (consult help via `financeager [command] --help`), e.g. {"command": "add", "name": "champagne", "value": "99"}. :return: UNIX return code (zero for success, non-zero otherwise) """ if verbose: make_log_stream_handler_verbose() exit_code = FAILURE if config_filepath is None and os.path.exists(financeager.CONFIG_FILEPATH): config_filepath = financeager.CONFIG_FILEPATH try: configuration = Configuration(filepath=config_filepath) except InvalidConfigError as e: logger.error("Invalid configuration: {}".format(e)) return FAILURE date_format = configuration.get_option("FRONTEND", "date_format") try: _preprocess(params, date_format) except PreprocessingError as e: logger.error(e) return FAILURE service_name = configuration.get_option("SERVICE", "name") if service_name == "flask": init_logger("urllib3") client = Client(configuration=configuration, out=Client.Out(logger.info, logger.error)) success, store_offline = client.safely_run(command, **params) if success: exit_code = SUCCESS # When regular command was successfully executed, attempt to recover # offline backup try: if offline.recover(client): logger.info("Recovered offline backup.") except OfflineRecoveryError: logger.error("Offline backup recovery failed!") exit_code = FAILURE if store_offline and offline.add(command, **params): logger.info("Stored '{}' request in offline backup.".format(command)) if service_name == "none": client.run("stop") return exit_code
def __init__(self, *, configuration, sinks): """Set up proxy and urllib3 logger.""" super().__init__(configuration=configuration, sinks=sinks) self.proxy = httprequests.Proxy( http_config=configuration.get_section("SERVICE:FLASK")) financeager.init_logger("urllib3")
def create_app(data_dir=None, config=None): """Create web app with RESTful API built from resources. The function is named such that the flask cli detects it as app factory method. The log file handler is set up very first. If 'data_dir' or the environment variable 'FINANCEAGER_FLASK_DATA_DIR' is given, a directory is created to store application data. An instance of 'server.Server' is created, passing 'data_dir'. If 'data_dir' is not given, the application data is stored in memory and will be lost when the app terminates. 'config' is a dict of configuration variables that flask understands. """ setup_log_file_handler() # Propagate flask and werkzeug log messages to financeager logs init_logger("flask.app") init_logger("werkzeug") app = Flask(__name__) app.config.update(config or {}) if app.debug: make_log_stream_handler_verbose() data_dir = data_dir or os.environ.get("FINANCEAGER_FLASK_DATA_DIR") if data_dir is None: logger.warning("'data_dir' not given. Application data is stored in " "memory and is lost when the flask app terminates. Set " "the environment variable FINANCEAGER_FLASK_DATA_DIR " "accordingly for persistent data storage.") else: os.makedirs(data_dir, exist_ok=True) logger.debug("Created flask app {} - {} mode".format( app.name, "debug" if app.debug else "production")) srv = server.Server(data_dir=data_dir) logger.debug( "Started financeager server with data dir '{}'".format(data_dir)) api = Api(app) api.add_resource(resources.PocketsResource, POCKETS_TAIL, resource_class_args=(srv, )) api.add_resource(resources.CopyResource, COPY_TAIL, resource_class_args=(srv, )) api.add_resource(resources.PocketResource, "{}/<pocket_name>".format(POCKETS_TAIL), resource_class_args=(srv, )) api.add_resource( resources.EntryResource, "{}/<pocket_name>/<table_name>/<eid>".format(POCKETS_TAIL), resource_class_args=(srv, )) # Assign attribute such that e.g. test_cli can access Server methods app._server = srv return app
"""Command line interface of financeager application.""" from datetime import datetime import argparse import os import sys from financeager import offline, __version__, PERIOD_DATE_FORMAT,\ init_logger, make_log_stream_handler_verbose, setup_log_file_handler import financeager from .communication import Client from .config import Configuration from .entries import CategoryEntry from .exceptions import OfflineRecoveryError, InvalidConfigError,\ PreprocessingError logger = init_logger(__name__) # Exit codes SUCCESS = 0 FAILURE = 1 def main(): """Main command line entry point of the application. The config and the log directory are created. A FileHandler is added to the package logger. All command line arguments and options are parsed and passed to 'run()'. """ os.makedirs(financeager.DATA_DIR, exist_ok=True) # Adding the FileHandler here avoids cluttering the log during tests setup_log_file_handler()
def run(command=None, config_filepath=None, verbose=False, **cl_kwargs): """High-level API entry point, useful for scripts. Run 'command' passing 'cl_kwargs' according to what the command line interface accepts (consult help via `financeager [command] --help`), e.g. {"command": "add", "name": "champagne", "value": "99"}. All kwargs are passed to 'communication.run()'. 'config' specifies the path to a custom config file (optional). If 'verbose' is set, debug level log messages are printed to the terminal. :return: UNIX return code (zero for success, non-zero otherwise) """ if verbose: make_log_stream_handler_verbose() exit_code = FAILURE if config_filepath is None and os.path.exists(financeager.CONFIG_FILEPATH): config_filepath = financeager.CONFIG_FILEPATH try: configuration = Configuration(filepath=config_filepath) except InvalidConfigError as e: logger.error("Invalid configuration: {}".format(e)) return FAILURE backend_name = configuration.get_option("SERVICE", "name") communication_module = communication.module(backend_name) proxy_kwargs = {} if backend_name == "flask": init_logger("urllib3") proxy_kwargs["http_config"] = configuration.get_option("SERVICE:FLASK") else: # 'none' is the only other option proxy_kwargs["data_dir"] = financeager.DATA_DIR # Indicate whether to store request offline, if failed store_offline = False proxy = communication_module.proxy(**proxy_kwargs) try: logger.info( communication.run(proxy, command, default_category=configuration.get_option( "FRONTEND", "default_category"), date_format=configuration.get_option( "FRONTEND", "date_format"), **cl_kwargs)) if offline.recover(proxy): logger.info("Recovered offline backup.") exit_code = SUCCESS except OfflineRecoveryError: logger.error("Offline backup recovery failed!") except (PreprocessingError, InvalidRequest) as e: # Command is erroneous and hence not stored offline logger.error(e) except CommunicationError as e: logger.error(e) store_offline = True except Exception: logger.exception("Unexpected error") store_offline = True if store_offline and offline.add(command, **cl_kwargs): logger.info("Stored '{}' request in offline backup.".format(command)) if backend_name == "none": communication.run(proxy, "stop") return exit_code