100% coverage

This commit is contained in:
2021-04-03 21:30:57 +03:00
parent cad85b0f20
commit 207da4caa7
31 changed files with 534 additions and 45 deletions

View File

@ -274,11 +274,18 @@ def _set_web_parser(root: SubParserAction) -> argparse.ArgumentParser:
return parser
if __name__ == "__main__":
args_parser = _parser()
args = args_parser.parse_args()
def run():
"""
run application instance
"""
if __name__ == "__main__":
args_parser = _parser()
args = args_parser.parse_args()
handler: handlers.Handler = args.handler
status = handler.execute(args)
handler: handlers.Handler = args.handler
status = handler.execute(args)
sys.exit(status)
sys.exit(status)
run()

View File

@ -51,6 +51,13 @@ class Application:
self.architecture = architecture
self.repository = Repository(architecture, configuration)
def _finalize(self) -> None:
"""
generate report and sync to remote server
"""
self.report([])
self.sync([])
def _known_packages(self) -> Set[str]:
"""
load packages from repository and pacman repositories
@ -63,13 +70,6 @@ class Application:
known_packages.update(self.repository.pacman.all_packages())
return known_packages
def _finalize(self) -> None:
"""
generate report and sync to remote server
"""
self.report([])
self.sync([])
def get_updates(self, filter_packages: List[str], no_aur: bool, no_manual: bool, no_vcs: bool,
log_fn: Callable[[str], None]) -> List[Package]:
"""
@ -182,6 +182,7 @@ class Application:
continue
for archive in package.packages.values():
if archive.filepath is None:
self.logger.warning(f"filepath is empty for {package.base}")
continue # avoid mypy warning
src = self.repository.paths.repository / archive.filepath
dst = self.repository.paths.packages / archive.filepath

View File

@ -19,7 +19,7 @@
#
import argparse
from typing import Type
from typing import Callable, Type
from ahriman.application.application import Application
from ahriman.application.handlers.handler import Handler
@ -39,13 +39,22 @@ class Update(Handler):
:param architecture: repository architecture
:param configuration: configuration instance
"""
# typing workaround
def log_fn(line: str) -> None:
return print(line) if args.dry_run else application.logger.info(line)
application = Application(architecture, configuration)
packages = application.get_updates(args.package, args.no_aur, args.no_manual, args.no_vcs, log_fn)
packages = application.get_updates(args.package, args.no_aur, args.no_manual, args.no_vcs,
Update.log_fn(application, args.dry_run))
if args.dry_run:
return
application.update(packages)
@staticmethod
def log_fn(application: Application, dry_run: bool) -> Callable[[str], None]:
"""
package updates log function
:param application: application instance
:param dry_run: do not perform update itself
:return: in case if dry_run is set it will return print, logger otherwise
"""
def inner(line: str) -> None:
return print(line) if dry_run else application.logger.info(line)
return inner

View File

@ -45,6 +45,15 @@ class WebClient(Client):
self.host = host
self.port = port
@staticmethod
def _exception_response_text(exception: requests.exceptions.HTTPError) -> str:
"""
safe response exception text generation
:param exception: exception raised
:return: text of the response if it is not None and empty string otherwise
"""
return exception.response.text if exception.response is not None else ''
def _ahriman_url(self) -> str:
"""
url generator
@ -75,7 +84,7 @@ class WebClient(Client):
response = requests.post(self._package_url(package.base), json=payload)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not add {package.base}: {e.response.text}")
self.logger.exception(f"could not add {package.base}: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception(f"could not add {package.base}")
@ -95,7 +104,7 @@ class WebClient(Client):
for package in status_json
]
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not get {base}: {e.response.text}")
self.logger.exception(f"could not get {base}: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception(f"could not get {base}")
return []
@ -112,7 +121,7 @@ class WebClient(Client):
status_json = response.json()
return BuildStatus.from_json(status_json)
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not get service status: {e.response.text}")
self.logger.exception(f"could not get service status: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception("could not get service status")
return BuildStatus()
@ -126,7 +135,7 @@ class WebClient(Client):
response = requests.delete(self._package_url(base))
response.raise_for_status()
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not delete {base}: {e.response.text}")
self.logger.exception(f"could not delete {base}: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception(f"could not delete {base}")
@ -142,7 +151,7 @@ class WebClient(Client):
response = requests.post(self._package_url(base), json=payload)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not update {base}: {e.response.text}")
self.logger.exception(f"could not update {base}: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception(f"could not update {base}")
@ -157,6 +166,6 @@ class WebClient(Client):
response = requests.post(self._ahriman_url(), json=payload)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
self.logger.exception(f"could not update service status: {e.response.text}")
self.logger.exception(f"could not update service status: {WebClient._exception_response_text(e)}")
except Exception:
self.logger.exception("could not update service status")

View File

@ -89,6 +89,6 @@ def pretty_size(size: Optional[float], level: int = 0) -> str:
if size is None:
return ""
if size < 1024 or level == 3:
if size < 1024 or level >= 3:
return f"{size:.1f} {str_level()}"
return pretty_size(size / 1024, level + 1)

View File

@ -154,7 +154,7 @@ class Package:
:return: package properties
"""
packages = {
key: PackageDescription(**value)
key: PackageDescription.from_json(value)
for key, value in dump.get("packages", {}).items()
}
return Package(

View File

@ -19,10 +19,10 @@
#
from __future__ import annotations
from dataclasses import dataclass, field
from dataclasses import dataclass, field, fields
from pathlib import Path
from pyalpm import Package # type: ignore
from typing import List, Optional, Type
from typing import Any, Dict, List, Optional, Type
@dataclass
@ -59,6 +59,18 @@ class PackageDescription:
"""
return Path(self.filename) if self.filename is not None else None
@classmethod
def from_json(cls: Type[PackageDescription], dump: Dict[str, Any]) -> PackageDescription:
"""
construct package properties from json dump
:param dump: json dump body
:return: package properties
"""
# filter to only known fields
known_fields = [pair.name for pair in fields(cls)]
dump = {key: value for key, value in dump.items() if key in known_fields}
return cls(**dump)
@classmethod
def from_package(cls: Type[PackageDescription], package: Package, path: Path) -> PackageDescription:
"""

View File

@ -31,6 +31,7 @@ class ReportSettings(Enum):
:cvar HTML: html report generation
"""
Disabled = auto() # for testing purpose
HTML = auto()
@classmethod

View File

@ -32,6 +32,7 @@ class UploadSettings(Enum):
:cvar S3: sync to Amazon S3
"""
Disabled = auto() # for testing purpose
Rsync = auto()
S3 = auto()