From e0ded18c50945f9706bd34e4d021f4ebe030a043 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timoth=C3=A9e=20Mazzucotelli?= Date: Wed, 7 Aug 2019 17:57:12 +0200 Subject: [PATCH] feat: Improve exceptions handling with loguru --- poetry.lock | 48 +++++++++++++++++++++++++++++++++++++---- pyproject.toml | 1 + src/aria2p/__init__.py | 6 ++++++ src/aria2p/api.py | 21 ++++++++++++++---- src/aria2p/cli.py | 14 ++++++++++++ src/aria2p/downloads.py | 28 +++++++++++++++++------- 6 files changed, 102 insertions(+), 16 deletions(-) diff --git a/poetry.lock b/poetry.lock index a506e09..cea7d6e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -6,6 +6,17 @@ optional = false python-versions = "*" version = "0.7.12" +[[package]] +category = "main" +description = "Produce colored terminal text with an xml-like markup" +name = "ansimarkup" +optional = false +python-versions = "*" +version = "1.4.0" + +[package.dependencies] +colorama = "*" + [[package]] category = "dev" description = "apipkg: namespace control and lazy-import mechanism" @@ -80,6 +91,19 @@ PyYAML = ">=3.12" six = ">=1.10.0" stevedore = ">=1.20.0" +[[package]] +category = "main" +description = "Pretty and helpful exceptions, automatically" +name = "better-exceptions-fork" +optional = false +python-versions = "*" +version = "0.2.1.post6" + +[package.dependencies] +ansimarkup = ">=1.3.0" +colorama = "*" +pygments = ">=2.2.0" + [[package]] category = "dev" description = "The uncompromising code formatter." @@ -119,9 +143,8 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "7.0" [[package]] -category = "dev" +category = "main" description = "Cross-platform colored terminal text." -marker = "sys_platform == \"win32\"" name = "colorama" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" @@ -320,6 +343,20 @@ jinja2 = "*" reference = "7e590419a1fcaf438055a54e45a34cb50f31acff" type = "git" url = "https://github.com/mattrobenolt/jinja2-cli.git" + +[[package]] +category = "main" +description = "Python logging made (stupidly) simple" +name = "loguru" +optional = false +python-versions = ">=3.5" +version = "0.2.5" + +[package.dependencies] +ansimarkup = ">=1.4.0" +better-exceptions-fork = ">=0.2.1.post6" +colorama = ">=0.3.4" + [[package]] category = "main" description = "Safely add untrusted strings to HTML/XML markup." @@ -455,7 +492,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" version = "2.1.1" [[package]] -category = "dev" +category = "main" description = "Pygments is a syntax highlighting package written in Python." name = "pygments" optional = false @@ -747,11 +784,12 @@ python-versions = "*" version = "0.1.7" [metadata] -content-hash = "3c2679449e67f0a46720a6ba4db1cff86f9ba1f605cd06da02fd5a47d7e16487" +content-hash = "41de7c6f9b5127a6fe3d3f7dce830b2f14b21d7d0b53c96cf3869f443b5234ea" python-versions = "^3.6" [metadata.hashes] alabaster = ["446438bdcca0e05bd45ea2de1668c1d9b032e1a9154c2c259092d77031ddd359", "a661d72d58e6ea8a57f7a86e37d86716863ee5e92788398526d58b26a4e4dc02"] +ansimarkup = ["06365e3ef89a12734fc408b2449cb4642d5fe2e603e95e7296eff9e98a0fe0b4", "174d920481416cec8d5a707af542d6fba25a1df1c21d8996479c32ba453649a4"] apipkg = ["37228cda29411948b422fae072f57e31d3396d2ee1c9783775980ee9c9990af6", "58587dd4dc3daefad0487f6d9ae32b4542b185e1c36db6993290e7c41ca2b47c"] appdirs = ["9e5896d1372858f8dd3344faf4e5014d21849c756c8d5701f78f8a103b372d92", "d8b24664561d0d34ddfaec54636d502d7cea6e29c3eaf68f3df6180863e2166e"] appnope = ["5b26757dc6f79a3b7dc9fab95359328d5747fcb2409d331ea66d0272b90ab2a0", "8b995ffe925347a2138d7ac0fe77155e4311a0ea6d6da4f5128fe4b3cbe5ed71"] @@ -760,6 +798,7 @@ attrs = ["69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79", "f0 babel = ["6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669", "8cba50f48c529ca3fa18cf81fa9403be176d374ac4d60738b839122dfaaa3d23"] backcall = ["38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", "bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2"] bandit = ["6102b5d6afd9d966df5054e0bdfc2e73a24d0fea400ec25f2e54c134412158d7", "9413facfe9de1e1bd291d525c784e1beb1a55c9916b51dae12979af63a69ba4c"] +better-exceptions-fork = ["5f0983da51e956dbdaf8b9a3d10e2774b382ce6c6ff2e54685c33e2dbe8f1472"] black = ["09a9dcb7c46ed496a9850b76e4e825d6049ecd38b611f1224857a79bd985a8cf", "68950ffd4d9169716bcb8719a56c07a2f4485354fec061cdd5910aa07369731c"] certifi = ["59b7658e26ca9c7339e00f8f4636cdfe59d34fa37b9b04f6f9e9926b3cece1a5", "b26104d6835d1f5e49452a26eb2ff87fe7090b89dfcaee5ea2212697e1e1d7ae"] chardet = ["84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", "fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691"] @@ -783,6 +822,7 @@ isort = ["18c796c2cd35eb1a1d3f012a214a542790a1aed95e29768bdcb9f2197eccbd0b", "96 jedi = ["2bb0603e3506f708e792c7f4ad8fc2a7a9d9c2d292a358fbbd58da531695595b", "2c6bcd9545c7d6440951b12b44d373479bf18123a401a52025cf98563fbd826c"] jinja2 = ["74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd", "f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"] jinja2-cli = [] +loguru = ["68297d9f23064c2f4764bb5d0c5c767f3ed7f9fc1218244841878f5fc7c94add", "ebac59630946721fd6207264679b267a8bdc290b086226067d6aad86830e3123"] markupsafe = ["00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473", "09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", "24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", "62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", "6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", "7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905", "88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735", "8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d", "98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e", "9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d", "9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c", "ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21", "b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2", "b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5", "b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b", "ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", "e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"] mccabe = ["ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42", "dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"] more-itertools = ["0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40", "590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"] diff --git a/pyproject.toml b/pyproject.toml index 8487302..9f7e0fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -21,6 +21,7 @@ include = [ [tool.poetry.dependencies] python = "^3.6" requests = "*" +loguru = "^0.2.5" [tool.poetry.dev-dependencies] bandit = "^1.5" diff --git a/src/aria2p/__init__.py b/src/aria2p/__init__.py index 08929ad..f3a0ce1 100644 --- a/src/aria2p/__init__.py +++ b/src/aria2p/__init__.py @@ -8,6 +8,7 @@ please refer to the README.md included in this package to get the link to the official documentation. """ +from loguru import logger from .api import API from .client import Client, ClientException @@ -15,6 +16,8 @@ from .options import Options from .stats import Stats +logger.disable("aria2p") + __all__ = ["API", "ClientException", "Client", "Download", "BitTorrent", "File", "Options", "Stats"] # TODO: use proper logging messages (esp. in except: pass) @@ -22,6 +25,9 @@ # TODO: handle both str and pathlib.Path for paths consistently # TODO: add command "add" for normal downloads!! # TODO: add "--options" options for "add" commands +# TODO: in API, support download arguments to be both Download or str (GID)? +# TODO: add clean parameter for api.move_files method (to clean .aria2 files) +# TODO: add value verification for options (see man page) # Roadmap: # - feature: Ability to hide metadata downloads (magnet URIs) diff --git a/src/aria2p/api.py b/src/aria2p/api.py index 10cde8a..59f9ba3 100644 --- a/src/aria2p/api.py +++ b/src/aria2p/api.py @@ -6,6 +6,8 @@ from base64 import b64encode from pathlib import Path +from loguru import logger + from .client import Client, ClientException from .downloads import Download from .options import Options @@ -332,18 +334,23 @@ def remove(self, downloads, force=False): try: removed_gid = remove_func(download.gid) except ClientException as e: + logger.debug(f"Failed to remove download {download.gid}") + logger.opt(exception=True).trace(e) result.append(e) else: result.append(True) try: self.client.remove_download_result(download.gid) - except ClientException: - pass + except ClientException as ee: + logger.debug(f"Failed to remove download result {download.gid}") + logger.opt(exception=True).trace(ee) if removed_gid != download.gid: + logger.debug(f"Removed download GID#{removed_gid} is different than download GID#{download.gid}") try: self.client.remove_download_result(removed_gid) - except ClientException: - pass + except ClientException as ee: + logger.debug(f"Failed to remove download result {removed_gid}") + logger.opt(exception=True).trace(ee) return result @@ -382,6 +389,8 @@ def pause(self, downloads, force=False): try: pause_func(download.gid) except ClientException as e: + logger.debug(f"Failed to pause download {download.gid}") + logger.opt(exception=True).trace(e) result.append(e) else: result.append(True) @@ -423,6 +432,8 @@ def resume(self, downloads): try: self.client.unpause(download.gid) except ClientException as e: + logger.debug(f"Failed to resume download {download.gid}") + logger.opt(exception=True).trace(e) result.append(e) else: result.append(True) @@ -461,6 +472,8 @@ def purge(self, downloads): try: self.client.remove_download_result(download.gid) except ClientException as e: + logger.debug(f"Failed to purge download result {download.gid}") + logger.opt(exception=True).trace(e) result.append(e) else: result.append(True) diff --git a/src/aria2p/cli.py b/src/aria2p/cli.py index c6a43db..60c06f8 100644 --- a/src/aria2p/cli.py +++ b/src/aria2p/cli.py @@ -20,6 +20,7 @@ import sys import requests +from loguru import logger from aria2p import Download @@ -36,6 +37,10 @@ def main(args=None): check_args(parser, args) kwargs = args.__dict__ + logger.remove() + logger.configure(handlers=[{"sink": sys.stderr, "level": kwargs.pop("log_level")}]) + logger.enable("aria2p") + api = API(Client(host=kwargs.pop("host"), port=kwargs.pop("port"), secret=kwargs.pop("secret"))) # Warn if no aria2 daemon process seems to be running @@ -104,6 +109,15 @@ def get_parser(): global_options.add_argument( "-s", "--secret", dest="secret", default="", help="Secret token to use to connect to the remote server." ) + global_options.add_argument( + "-L", + "--log-level", + dest="log_level", + default="ERROR", + help="Log level to use", + choices=("TRACE", "DEBUG", "INFO", "SUCCESS", "WARNING", "ERROR", "CRITICAL"), + type=str.upper, + ) # ========= SUBPARSERS ========= # subparsers = parser.add_subparsers(dest="subcommand", title="Commands", metavar="", prog="aria2p") diff --git a/src/aria2p/downloads.py b/src/aria2p/downloads.py index dec7bbd..e03c797 100644 --- a/src/aria2p/downloads.py +++ b/src/aria2p/downloads.py @@ -5,6 +5,8 @@ from datetime import datetime, timedelta from pathlib import Path +from loguru import logger + from .client import ClientException from .utils import bool_or_value, human_readable_bytes, human_readable_timedelta @@ -244,8 +246,9 @@ def root_files_paths(self): continue try: relative_path = file.path.relative_to(self.dir) - except ValueError: - pass # TODO: log + except ValueError as e: + logger.warning(f"Can't determine file path '{file.path}' relative to '{self.dir}'") + logger.opt(exception=True).trace(e) else: path = self.dir / relative_path.parts[0] if path not in paths: @@ -517,8 +520,9 @@ def followed_by(self): for gid in self.followed_by_ids: try: result.append(self.api.get_download(gid)) - except ClientException: - pass + except ClientException as e: + logger.warning(f"Can't find download with GID {gid}, try to update download {self.gid} ({id(self)}") + logger.opt(exception=True).trace(e) self._followed_by = result return self._followed_by @@ -541,8 +545,12 @@ def following(self): if not self._following: try: self._following = self.api.get_download(self.following_id) - except ClientException: - return None + except ClientException as e: + logger.warning( + f"Can't find download with GID {self.following_id}, try to update download {self.gid} ({id(self)}" + ) + logger.opt(exception=True).trace(e) + self._following = None return self._following @property @@ -566,8 +574,12 @@ def belongs_to(self): if not self._belongs_to: try: self._belongs_to = self.api.get_download(self.belongs_to_id) - except ClientException: - return None + except ClientException as e: + logger.warning( + f"Can't find download with GID {self.belongs_to_id}, try to update download {self.gid} ({id(self)}" + ) + logger.opt(exception=True).trace(e) + self._belongs_to = None return self._belongs_to @property