diff --git a/.github/workflows/setup.sh b/.github/workflows/setup.sh
index fccb79f9..d7df4e73 100755
--- a/.github/workflows/setup.sh
+++ b/.github/workflows/setup.sh
@@ -8,7 +8,7 @@ echo -e '[arcanisrepo]\nServer = http://repo.arcanis.me/$arch\nSigLevel = Never'
# refresh the image
pacman --noconfirm -Syu
# main dependencies
-pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-srcinfo sudo
+pacman --noconfirm -Sy base-devel devtools git pyalpm python-aur python-passlib python-setuptools python-srcinfo sudo
# make dependencies
pacman --noconfirm -Sy python-build python-installer python-wheel
# optional dependencies
diff --git a/.github/workflows/tests.sh b/.github/workflows/tests.sh
index 864a8e0c..736a189b 100755
--- a/.github/workflows/tests.sh
+++ b/.github/workflows/tests.sh
@@ -4,7 +4,7 @@
set -ex
# install dependencies
-pacman --noconfirm -Syu base-devel python-pip python-tox
+pacman --noconfirm -Syu base-devel python-pip python-setuptools python-tox
# run test and check targets
make check tests
diff --git a/Dockerfile b/Dockerfile
index 8449fb3f..a3508518 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -21,7 +21,7 @@ RUN useradd -m -d /home/build -s /usr/bin/nologin build && \
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
## install package dependencies
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
-RUN pacman --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-srcinfo && \
+RUN pacman --noconfirm -Sy devtools git pyalpm python-inflection python-passlib python-requests python-setuptools python-srcinfo && \
pacman --noconfirm -Sy python-build python-installer python-wheel && \
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-boto3 python-cryptography python-jinja rsync subversion && \
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-jinja2 python-aiohttp-debugtoolbar \
diff --git a/docs/configuration.rst b/docs/configuration.rst
index 6aa5cf26..1a36920b 100644
--- a/docs/configuration.rst
+++ b/docs/configuration.rst
@@ -138,6 +138,7 @@ Section name must be either ``telegram`` (plus optional architecture name, e.g.
* ``link_path`` - prefix for HTML links, string, required.
* ``template_path`` - path to Jinja2 template, string, required.
* ``template_type`` - ``parse_mode`` to be passed to telegram API, one of ``MarkdownV2``, ``HTML``, ``Markdown``, string, optional, default ``HTML``.
+* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
``upload`` group
----------------
@@ -167,6 +168,7 @@ This feature requires Github key creation (see below). Section name must be eith
#. Generate new token. Required scope is ``public_repo`` (or ``repo`` for private repository support).
* ``repository`` - Github repository name, string, required. Repository must be created before any action and must have active branch (e.g. with readme).
+* ``timeout`` - HTTP request timeout in seconds, int, optional, default is ``30``.
* ``username`` - Github authorization user, string, required. Basically the same as ``owner``.
``rsync`` type
diff --git a/package/archlinux/PKGBUILD b/package/archlinux/PKGBUILD
index de3081ae..dec9819d 100644
--- a/package/archlinux/PKGBUILD
+++ b/package/archlinux/PKGBUILD
@@ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager"
arch=('any')
url="https://github.com/arcan1s/ahriman"
license=('GPL3')
-depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
+depends=('devtools' 'git' 'pyalpm' 'python-inflection' 'python-passlib' 'python-requests' 'python-setuptools' 'python-srcinfo')
makedepends=('python-build' 'python-installer' 'python-wheel')
optdepends=('breezy: -bzr packages support'
'darcs: -darcs packages support'
diff --git a/setup.py b/setup.py
index eaf3b083..986af05e 100644
--- a/setup.py
+++ b/setup.py
@@ -32,6 +32,7 @@ setup(
"inflection",
"passlib",
"requests",
+ "setuptools",
"srcinfo",
],
setup_requires=[
diff --git a/src/ahriman/application/application/application_packages.py b/src/ahriman/application/application/application_packages.py
index 559612d6..a6766113 100644
--- a/src/ahriman/application/application/application_packages.py
+++ b/src/ahriman/application/application/application_packages.py
@@ -129,7 +129,7 @@ class ApplicationPackages(ApplicationProperties):
source(str): remote URL of the package archive
"""
dst = self.repository.paths.packages / Path(source).name # URL is path, is not it?
- response = requests.get(source, stream=True)
+ response = requests.get(source, stream=True, timeout=None) # timeout=None to suppress pylint warns
response.raise_for_status()
with dst.open("wb") as local_file:
diff --git a/src/ahriman/core/alpm/remote/aur.py b/src/ahriman/core/alpm/remote/aur.py
index cb521914..416bd7ff 100644
--- a/src/ahriman/core/alpm/remote/aur.py
+++ b/src/ahriman/core/alpm/remote/aur.py
@@ -36,11 +36,13 @@ class AUR(Remote):
DEFAULT_AUR_URL(str): (class attribute) default AUR url
DEFAULT_RPC_URL(str): (class attribute) default AUR RPC url
DEFAULT_RPC_VERSION(str): (class attribute) default AUR RPC version
+ DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
"""
DEFAULT_AUR_URL = "https://aur.archlinux.org"
DEFAULT_RPC_URL = f"{DEFAULT_AUR_URL}/rpc"
DEFAULT_RPC_VERSION = "5"
+ DEFAULT_TIMEOUT = 30
@staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
@@ -113,7 +115,7 @@ class AUR(Remote):
query[key] = value
try:
- response = requests.get(self.DEFAULT_RPC_URL, params=query)
+ response = requests.get(self.DEFAULT_RPC_URL, params=query, timeout=self.DEFAULT_TIMEOUT)
response.raise_for_status()
return self.parse_response(response.json())
except requests.HTTPError as e:
diff --git a/src/ahriman/core/alpm/remote/official.py b/src/ahriman/core/alpm/remote/official.py
index b39e78c1..58edee39 100644
--- a/src/ahriman/core/alpm/remote/official.py
+++ b/src/ahriman/core/alpm/remote/official.py
@@ -36,11 +36,13 @@ class Official(Remote):
DEFAULT_ARCHLINUX_URL(str): (class attribute) default archlinux url
DEFAULT_SEARCH_REPOSITORIES(List[str]): (class attribute) default list of repositories to search
DEFAULT_RPC_URL(str): (class attribute) default archlinux repositories RPC url
+ DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
"""
DEFAULT_ARCHLINUX_URL = "https://archlinux.org"
DEFAULT_SEARCH_REPOSITORIES = ["Core", "Extra", "Multilib", "Community"]
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
+ DEFAULT_TIMEOUT = 30
@staticmethod
def parse_response(response: Dict[str, Any]) -> List[AURPackage]:
@@ -101,7 +103,10 @@ class Official(Remote):
List[AURPackage]: response parsed to package list
"""
try:
- response = requests.get(self.DEFAULT_RPC_URL, params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES})
+ response = requests.get(
+ self.DEFAULT_RPC_URL,
+ params={by: args, "repo": self.DEFAULT_SEARCH_REPOSITORIES},
+ timeout=self.DEFAULT_TIMEOUT)
response.raise_for_status()
return self.parse_response(response.json())
except requests.HTTPError as e:
diff --git a/src/ahriman/core/report/__init__.py b/src/ahriman/core/report/__init__.py
index cca1d51f..ad4c2ae0 100644
--- a/src/ahriman/core/report/__init__.py
+++ b/src/ahriman/core/report/__init__.py
@@ -18,11 +18,4 @@
# along with this program. If not, see .
#
from ahriman.core.report.report import Report
-from ahriman.core.report.jinja_template import JinjaTemplate
-
-from ahriman.core.report.console import Console
-from ahriman.core.report.email import Email
-from ahriman.core.report.html import HTML
-from ahriman.core.report.telegram import Telegram
-
from ahriman.core.report.report_trigger import ReportTrigger
diff --git a/src/ahriman/core/report/email.py b/src/ahriman/core/report/email.py
index af5ce2c1..cfd413e7 100644
--- a/src/ahriman/core/report/email.py
+++ b/src/ahriman/core/report/email.py
@@ -25,7 +25,8 @@ from email.mime.text import MIMEText
from typing import Dict, Iterable
from ahriman.core.configuration import Configuration
-from ahriman.core.report import JinjaTemplate, Report
+from ahriman.core.report import Report
+from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.core.util import pretty_datetime
from ahriman.models.package import Package
from ahriman.models.result import Result
diff --git a/src/ahriman/core/report/html.py b/src/ahriman/core/report/html.py
index 63fb3756..34bd67a7 100644
--- a/src/ahriman/core/report/html.py
+++ b/src/ahriman/core/report/html.py
@@ -20,7 +20,8 @@
from typing import Iterable
from ahriman.core.configuration import Configuration
-from ahriman.core.report import JinjaTemplate, Report
+from ahriman.core.report import Report
+from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.models.package import Package
from ahriman.models.result import Result
diff --git a/src/ahriman/core/report/report.py b/src/ahriman/core/report/report.py
index 5bc0ebf3..50aeb9e6 100644
--- a/src/ahriman/core/report/report.py
+++ b/src/ahriman/core/report/report.py
@@ -84,16 +84,16 @@ class Report(LazyLogging):
section, provider_name = configuration.gettype(target, architecture)
provider = ReportSettings.from_option(provider_name)
if provider == ReportSettings.HTML:
- from ahriman.core.report import HTML
+ from ahriman.core.report.html import HTML
return HTML(architecture, configuration, section)
if provider == ReportSettings.Email:
- from ahriman.core.report import Email
+ from ahriman.core.report.email import Email
return Email(architecture, configuration, section)
if provider == ReportSettings.Console:
- from ahriman.core.report import Console
+ from ahriman.core.report.console import Console
return Console(architecture, configuration, section)
if provider == ReportSettings.Telegram:
- from ahriman.core.report import Telegram
+ from ahriman.core.report.telegram import Telegram
return Telegram(architecture, configuration, section)
return cls(architecture, configuration) # should never happen
diff --git a/src/ahriman/core/report/telegram.py b/src/ahriman/core/report/telegram.py
index ce5a22ee..b4b724aa 100644
--- a/src/ahriman/core/report/telegram.py
+++ b/src/ahriman/core/report/telegram.py
@@ -23,7 +23,8 @@ import requests
from typing import Iterable
from ahriman.core.configuration import Configuration
-from ahriman.core.report import JinjaTemplate, Report
+from ahriman.core.report import Report
+from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.core.util import exception_response_text
from ahriman.models.package import Package
from ahriman.models.result import Result
@@ -40,6 +41,7 @@ class Telegram(Report, JinjaTemplate):
chat_id(str): chat id to post message, either string with @ or integer
template_path(Path): path to template for built packages
template_type(str): template message type to be used in parse mode, one of MarkdownV2, HTML, Markdown
+ timeout(int): HTTP request timeout in seconds
"""
TELEGRAM_API_URL = "https://api.telegram.org"
@@ -61,6 +63,7 @@ class Telegram(Report, JinjaTemplate):
self.chat_id = configuration.get(section, "chat_id")
self.template_path = configuration.getpath(section, "template_path")
self.template_type = configuration.get(section, "template_type", fallback="HTML")
+ self.timeout = configuration.getint(section, "timeout", fallback=30)
def _send(self, text: str) -> None:
"""
@@ -72,7 +75,8 @@ class Telegram(Report, JinjaTemplate):
try:
response = requests.post(
f"{self.TELEGRAM_API_URL}/bot{self.api_key}/sendMessage",
- data={"chat_id": self.chat_id, "text": text, "parse_mode": self.template_type})
+ data={"chat_id": self.chat_id, "text": text, "parse_mode": self.template_type},
+ timeout=self.timeout)
response.raise_for_status()
except requests.HTTPError as e:
self.logger.exception("could not perform request: %s", exception_response_text(e))
diff --git a/src/ahriman/core/sign/gpg.py b/src/ahriman/core/sign/gpg.py
index 6e9c72fd..f8c20467 100644
--- a/src/ahriman/core/sign/gpg.py
+++ b/src/ahriman/core/sign/gpg.py
@@ -34,6 +34,7 @@ class GPG(LazyLogging):
gnupg wrapper
Attributes:
+ DEFAULT_TIMEOUT(int): (class attribute) HTTP request timeout in seconds
architecture(str): repository architecture
configuration(Configuration): configuration instance
default_key(Optional[str]): default PGP key ID to use
@@ -41,6 +42,7 @@ class GPG(LazyLogging):
"""
_check_output = check_output
+ DEFAULT_TIMEOUT = 30
def __init__(self, architecture: str, configuration: Configuration) -> None:
"""
@@ -120,7 +122,7 @@ class GPG(LazyLogging):
"op": "get",
"options": "mr",
"search": key
- })
+ }, timeout=self.DEFAULT_TIMEOUT)
response.raise_for_status()
except requests.exceptions.HTTPError as e:
self.logger.exception("could not download key %s from %s: %s", key, server, exception_response_text(e))
diff --git a/src/ahriman/core/upload/__init__.py b/src/ahriman/core/upload/__init__.py
index 753a291a..24bdcb2e 100644
--- a/src/ahriman/core/upload/__init__.py
+++ b/src/ahriman/core/upload/__init__.py
@@ -18,10 +18,4 @@
# along with this program. If not, see .
#
from ahriman.core.upload.upload import Upload
-from ahriman.core.upload.http_upload import HttpUpload
-
-from ahriman.core.upload.github import Github
-from ahriman.core.upload.rsync import Rsync
-from ahriman.core.upload.s3 import S3
-
from ahriman.core.upload.upload_trigger import UploadTrigger
diff --git a/src/ahriman/core/upload/github.py b/src/ahriman/core/upload/github.py
index af87d99f..afeb6e0f 100644
--- a/src/ahriman/core/upload/github.py
+++ b/src/ahriman/core/upload/github.py
@@ -24,7 +24,7 @@ from pathlib import Path
from typing import Any, Dict, Iterable, Optional
from ahriman.core.configuration import Configuration
-from ahriman.core.upload import HttpUpload
+from ahriman.core.upload.http_upload import HttpUpload
from ahriman.core.util import walk
from ahriman.models.package import Package
diff --git a/src/ahriman/core/upload/http_upload.py b/src/ahriman/core/upload/http_upload.py
index 54b34077..5c1db486 100644
--- a/src/ahriman/core/upload/http_upload.py
+++ b/src/ahriman/core/upload/http_upload.py
@@ -34,6 +34,7 @@ class HttpUpload(Upload):
Attributes:
auth(Tuple[str, str]): HTTP auth object
+ timeout(int): HTTP request timeout in seconds
"""
def __init__(self, architecture: str, configuration: Configuration, section: str) -> None:
@@ -49,6 +50,7 @@ class HttpUpload(Upload):
password = configuration.get(section, "password")
username = configuration.get(section, "username")
self.auth = (password, username)
+ self.timeout = configuration.getint(section, "timeout", fallback=30)
@staticmethod
def calculate_hash(path: Path) -> str:
@@ -108,7 +110,7 @@ class HttpUpload(Upload):
requests.Response: request response object
"""
try:
- response = requests.request(method, url, auth=self.auth, **kwargs)
+ response = requests.request(method, url, auth=self.auth, timeout=self.timeout, **kwargs)
response.raise_for_status()
except requests.HTTPError as e:
self.logger.exception("could not perform %s request to %s: %s", method, url, exception_response_text(e))
diff --git a/src/ahriman/core/upload/upload.py b/src/ahriman/core/upload/upload.py
index 2f0da5ad..ede890fb 100644
--- a/src/ahriman/core/upload/upload.py
+++ b/src/ahriman/core/upload/upload.py
@@ -83,13 +83,13 @@ class Upload(LazyLogging):
section, provider_name = configuration.gettype(target, architecture)
provider = UploadSettings.from_option(provider_name)
if provider == UploadSettings.Rsync:
- from ahriman.core.upload import Rsync
+ from ahriman.core.upload.rsync import Rsync
return Rsync(architecture, configuration, section)
if provider == UploadSettings.S3:
- from ahriman.core.upload import S3
+ from ahriman.core.upload.s3 import S3
return S3(architecture, configuration, section)
if provider == UploadSettings.Github:
- from ahriman.core.upload import Github
+ from ahriman.core.upload.github import Github
return Github(architecture, configuration, section)
return cls(architecture, configuration) # should never happen
diff --git a/tests/ahriman/application/application/test_application_packages.py b/tests/ahriman/application/application/test_application_packages.py
index 35053d99..52a29694 100644
--- a/tests/ahriman/application/application/test_application_packages.py
+++ b/tests/ahriman/application/application/test_application_packages.py
@@ -111,7 +111,7 @@ def test_add_remote(application_packages: ApplicationPackages, package_descripti
application_packages._add_remote(url)
open_mock.assert_called_once_with("wb")
- request_mock.assert_called_once_with(url, stream=True)
+ request_mock.assert_called_once_with(url, stream=True, timeout=None)
response_mock.raise_for_status.assert_called_once_with()
diff --git a/tests/ahriman/core/alpm/remote/test_aur.py b/tests/ahriman/core/alpm/remote/test_aur.py
index 4698c5f0..2cca99f0 100644
--- a/tests/ahriman/core/alpm/remote/test_aur.py
+++ b/tests/ahriman/core/alpm/remote/test_aur.py
@@ -78,7 +78,9 @@ def test_make_request(aur: AUR, aur_package_ahriman: AURPackage,
assert aur.make_request("info", "ahriman") == [aur_package_ahriman]
request_mock.assert_called_once_with(
- "https://aur.archlinux.org/rpc", params={"v": "5", "type": "info", "arg": ["ahriman"]})
+ "https://aur.archlinux.org/rpc",
+ params={"v": "5", "type": "info", "arg": ["ahriman"]},
+ timeout=aur.DEFAULT_TIMEOUT)
def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
@@ -92,7 +94,9 @@ def test_make_request_multi_arg(aur: AUR, aur_package_ahriman: AURPackage,
assert aur.make_request("search", "ahriman", "is", "cool") == [aur_package_ahriman]
request_mock.assert_called_once_with(
- "https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg[]": ["ahriman", "is", "cool"]})
+ "https://aur.archlinux.org/rpc",
+ params={"v": "5", "type": "search", "arg[]": ["ahriman", "is", "cool"]},
+ timeout=aur.DEFAULT_TIMEOUT)
def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
@@ -106,7 +110,9 @@ def test_make_request_with_kwargs(aur: AUR, aur_package_ahriman: AURPackage,
assert aur.make_request("search", "ahriman", by="name") == [aur_package_ahriman]
request_mock.assert_called_once_with(
- "https://aur.archlinux.org/rpc", params={"v": "5", "type": "search", "arg": ["ahriman"], "by": "name"})
+ "https://aur.archlinux.org/rpc",
+ params={"v": "5", "type": "search", "arg": ["ahriman"], "by": "name"},
+ timeout=aur.DEFAULT_TIMEOUT)
def test_make_request_failed(aur: AUR, mocker: MockerFixture) -> None:
diff --git a/tests/ahriman/core/alpm/remote/test_official.py b/tests/ahriman/core/alpm/remote/test_official.py
index 7d9716fd..28342d2e 100644
--- a/tests/ahriman/core/alpm/remote/test_official.py
+++ b/tests/ahriman/core/alpm/remote/test_official.py
@@ -84,8 +84,10 @@ def test_make_request(official: Official, aur_package_akonadi: AURPackage,
request_mock = mocker.patch("requests.get", return_value=response_mock)
assert official.make_request("akonadi", by="q") == [aur_package_akonadi]
- request_mock.assert_called_once_with("https://archlinux.org/packages/search/json",
- params={"q": ("akonadi",), "repo": Official.DEFAULT_SEARCH_REPOSITORIES})
+ request_mock.assert_called_once_with(
+ "https://archlinux.org/packages/search/json",
+ params={"q": ("akonadi",), "repo": Official.DEFAULT_SEARCH_REPOSITORIES},
+ timeout=official.DEFAULT_TIMEOUT)
def test_make_request_failed(official: Official, mocker: MockerFixture) -> None:
diff --git a/tests/ahriman/core/report/test_console.py b/tests/ahriman/core/report/test_console.py
index 7f2b64c0..83c8dab0 100644
--- a/tests/ahriman/core/report/test_console.py
+++ b/tests/ahriman/core/report/test_console.py
@@ -2,7 +2,7 @@ from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.configuration import Configuration
-from ahriman.core.report import Console
+from ahriman.core.report.console import Console
from ahriman.models.package import Package
from ahriman.models.result import Result
diff --git a/tests/ahriman/core/report/test_email.py b/tests/ahriman/core/report/test_email.py
index fc2a046d..f215702d 100644
--- a/tests/ahriman/core/report/test_email.py
+++ b/tests/ahriman/core/report/test_email.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
-from ahriman.core.report import Email
+from ahriman.core.report.email import Email
from ahriman.models.package import Package
from ahriman.models.result import Result
@@ -90,7 +90,7 @@ def test_generate(configuration: Configuration, package_ahriman: Package, mocker
"""
must generate report
"""
- send_mock = mocker.patch("ahriman.core.report.Email._send")
+ send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], Result())
@@ -102,7 +102,7 @@ def test_generate_with_built(configuration: Configuration, package_ahriman: Pack
"""
must generate report with built packages
"""
- send_mock = mocker.patch("ahriman.core.report.Email._send")
+ send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], result)
@@ -117,7 +117,7 @@ def test_generate_with_built_and_full_path(
"""
must generate report with built packages and full packages lists
"""
- send_mock = mocker.patch("ahriman.core.report.Email._send")
+ send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.full_template_path = report.template_path
@@ -130,7 +130,7 @@ def test_generate_no_empty(configuration: Configuration, package_ahriman: Packag
must not generate report with built packages if no_empty_report is set
"""
configuration.set_option("email", "no_empty_report", "yes")
- send_mock = mocker.patch("ahriman.core.report.Email._send")
+ send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], Result())
@@ -143,7 +143,7 @@ def test_generate_no_empty_with_built(configuration: Configuration, package_ahri
must generate report with built packages if no_empty_report is set
"""
configuration.set_option("email", "no_empty_report", "yes")
- send_mock = mocker.patch("ahriman.core.report.Email._send")
+ send_mock = mocker.patch("ahriman.core.report.email.Email._send")
report = Email("x86_64", configuration, "email")
report.generate([package_ahriman], result)
diff --git a/tests/ahriman/core/report/test_html.py b/tests/ahriman/core/report/test_html.py
index bb8c540e..b57e3b60 100644
--- a/tests/ahriman/core/report/test_html.py
+++ b/tests/ahriman/core/report/test_html.py
@@ -3,7 +3,7 @@ import pytest
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
-from ahriman.core.report import HTML
+from ahriman.core.report.html import HTML
from ahriman.models.package import Package
diff --git a/tests/ahriman/core/report/test_jinja_template.py b/tests/ahriman/core/report/test_jinja_template.py
index 7a9783c0..93deccbf 100644
--- a/tests/ahriman/core/report/test_jinja_template.py
+++ b/tests/ahriman/core/report/test_jinja_template.py
@@ -1,5 +1,5 @@
from ahriman.core.configuration import Configuration
-from ahriman.core.report import JinjaTemplate
+from ahriman.core.report.jinja_template import JinjaTemplate
from ahriman.models.package import Package
from ahriman.models.result import Result
diff --git a/tests/ahriman/core/report/test_report.py b/tests/ahriman/core/report/test_report.py
index 5864c960..6cc269db 100644
--- a/tests/ahriman/core/report/test_report.py
+++ b/tests/ahriman/core/report/test_report.py
@@ -13,7 +13,7 @@ def test_report_failure(configuration: Configuration, mocker: MockerFixture) ->
"""
must raise ReportFailed on errors
"""
- mocker.patch("ahriman.core.report.HTML.generate", side_effect=Exception())
+ mocker.patch("ahriman.core.report.html.HTML.generate", side_effect=Exception())
with pytest.raises(ReportFailed):
Report.load("x86_64", configuration, "html").run([], Result())
@@ -32,7 +32,7 @@ def test_report_console(configuration: Configuration, result: Result, mocker: Mo
"""
must generate console report
"""
- report_mock = mocker.patch("ahriman.core.report.Console.generate")
+ report_mock = mocker.patch("ahriman.core.report.console.Console.generate")
Report.load("x86_64", configuration, "console").run(result, [])
report_mock.assert_called_once_with([], result)
@@ -41,7 +41,7 @@ def test_report_email(configuration: Configuration, result: Result, mocker: Mock
"""
must generate email report
"""
- report_mock = mocker.patch("ahriman.core.report.Email.generate")
+ report_mock = mocker.patch("ahriman.core.report.email.Email.generate")
Report.load("x86_64", configuration, "email").run(result, [])
report_mock.assert_called_once_with([], result)
@@ -50,7 +50,7 @@ def test_report_html(configuration: Configuration, result: Result, mocker: Mocke
"""
must generate html report
"""
- report_mock = mocker.patch("ahriman.core.report.HTML.generate")
+ report_mock = mocker.patch("ahriman.core.report.html.HTML.generate")
Report.load("x86_64", configuration, "html").run(result, [])
report_mock.assert_called_once_with([], result)
@@ -59,6 +59,6 @@ def test_report_telegram(configuration: Configuration, result: Result, mocker: M
"""
must generate telegram report
"""
- report_mock = mocker.patch("ahriman.core.report.Telegram.generate")
+ report_mock = mocker.patch("ahriman.core.report.telegram.Telegram.generate")
Report.load("x86_64", configuration, "telegram").run(result, [])
report_mock.assert_called_once_with([], result)
diff --git a/tests/ahriman/core/report/test_telegram.py b/tests/ahriman/core/report/test_telegram.py
index 920e263c..ddd44afa 100644
--- a/tests/ahriman/core/report/test_telegram.py
+++ b/tests/ahriman/core/report/test_telegram.py
@@ -5,7 +5,7 @@ from pytest_mock import MockerFixture
from unittest import mock
from ahriman.core.configuration import Configuration
-from ahriman.core.report import Telegram
+from ahriman.core.report.telegram import Telegram
from ahriman.models.package import Package
from ahriman.models.result import Result
@@ -20,7 +20,8 @@ def test_send(configuration: Configuration, mocker: MockerFixture) -> None:
report._send("a text")
request_mock.assert_called_once_with(
pytest.helpers.anyvar(str, strict=True),
- data={"chat_id": pytest.helpers.anyvar(str, strict=True), "text": "a text", "parse_mode": "HTML"})
+ data={"chat_id": pytest.helpers.anyvar(str, strict=True), "text": "a text", "parse_mode": "HTML"},
+ timeout=report.timeout)
def test_send_failed(configuration: Configuration, mocker: MockerFixture) -> None:
@@ -50,7 +51,7 @@ def test_generate(configuration: Configuration, package_ahriman: Package, result
"""
must generate report
"""
- send_mock = mocker.patch("ahriman.core.report.Telegram._send")
+ send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
@@ -62,7 +63,7 @@ def test_generate_big_text_without_spaces(configuration: Configuration, package_
"""
must raise ValueError in case if there are no new lines in text
"""
- mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="ab" * 4096)
+ mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab" * 4096)
report = Telegram("x86_64", configuration, "telegram")
with pytest.raises(ValueError):
@@ -74,8 +75,8 @@ def test_generate_big_text(configuration: Configuration, package_ahriman: Packag
"""
must generate report with big text
"""
- mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="a\n" * 4096)
- send_mock = mocker.patch("ahriman.core.report.Telegram._send")
+ mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="a\n" * 4096)
+ send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
@@ -89,8 +90,8 @@ def test_generate_very_big_text(configuration: Configuration, package_ahriman: P
"""
must generate report with very big text
"""
- mocker.patch("ahriman.core.report.JinjaTemplate.make_html", return_value="ab\n" * 4096)
- send_mock = mocker.patch("ahriman.core.report.Telegram._send")
+ mocker.patch("ahriman.core.report.jinja_template.JinjaTemplate.make_html", return_value="ab\n" * 4096)
+ send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], result)
@@ -105,7 +106,7 @@ def test_generate_no_empty(configuration: Configuration, package_ahriman: Packag
"""
must generate report
"""
- send_mock = mocker.patch("ahriman.core.report.Telegram._send")
+ send_mock = mocker.patch("ahriman.core.report.telegram.Telegram._send")
report = Telegram("x86_64", configuration, "telegram")
report.generate([package_ahriman], Result())
diff --git a/tests/ahriman/core/sign/test_gpg.py b/tests/ahriman/core/sign/test_gpg.py
index 33e9399f..34f64c24 100644
--- a/tests/ahriman/core/sign/test_gpg.py
+++ b/tests/ahriman/core/sign/test_gpg.py
@@ -83,7 +83,9 @@ def test_key_download(gpg: GPG, mocker: MockerFixture) -> None:
requests_mock = mocker.patch("requests.get")
gpg.key_download("pgp.mit.edu", "0xE989490C")
requests_mock.assert_called_once_with(
- "http://pgp.mit.edu/pks/lookup", params={"op": "get", "options": "mr", "search": "0xE989490C"})
+ "http://pgp.mit.edu/pks/lookup",
+ params={"op": "get", "options": "mr", "search": "0xE989490C"},
+ timeout=gpg.DEFAULT_TIMEOUT)
def test_key_download_failure(gpg: GPG, mocker: MockerFixture) -> None:
diff --git a/tests/ahriman/core/upload/conftest.py b/tests/ahriman/core/upload/conftest.py
index 082749e7..24de8b5d 100644
--- a/tests/ahriman/core/upload/conftest.py
+++ b/tests/ahriman/core/upload/conftest.py
@@ -5,7 +5,9 @@ from typing import Any, Dict, List
from unittest.mock import MagicMock
from ahriman.core.configuration import Configuration
-from ahriman.core.upload import Github, Rsync, S3
+from ahriman.core.upload.github import Github
+from ahriman.core.upload.rsync import Rsync
+from ahriman.core.upload.s3 import S3
_s3_object = namedtuple("s3_object", ["key", "e_tag", "delete"])
diff --git a/tests/ahriman/core/upload/test_github.py b/tests/ahriman/core/upload/test_github.py
index 483efca9..8ff44fd0 100644
--- a/tests/ahriman/core/upload/test_github.py
+++ b/tests/ahriman/core/upload/test_github.py
@@ -6,14 +6,14 @@ from pytest_mock import MockerFixture
from typing import Any, Dict
from unittest import mock
-from ahriman.core.upload import Github
+from ahriman.core.upload.github import Github
def test_asset_remove(github: Github, github_release: Dict[str, Any], mocker: MockerFixture) -> None:
"""
must remove asset from the release
"""
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.asset_remove(github_release, "asset_name")
request_mock.assert_called_once_with("DELETE", "asset_url")
@@ -22,7 +22,7 @@ def test_asset_remove_unknown(github: Github, github_release: Dict[str, Any], mo
"""
must not fail if no asset found
"""
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.asset_remove(github_release, "unknown_asset_name")
request_mock.assert_not_called()
@@ -32,8 +32,8 @@ def test_asset_upload(github: Github, github_release: Dict[str, Any], mocker: Mo
must upload asset to the repository
"""
mocker.patch("pathlib.Path.open", return_value=b"")
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
- remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
+ remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
github.asset_upload(github_release, Path("/root/new.tar.xz"))
request_mock.assert_called_once_with("POST", "upload_url", params={"name": "new.tar.xz"},
@@ -46,8 +46,8 @@ def test_asset_upload_with_removal(github: Github, github_release: Dict[str, Any
must remove existing file before upload
"""
mocker.patch("pathlib.Path.open", return_value=b"")
- mocker.patch("ahriman.core.upload.Github._request")
- remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
+ mocker.patch("ahriman.core.upload.github.Github._request")
+ remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
github.asset_upload(github_release, Path("asset_name"))
github.asset_upload(github_release, Path("/root/asset_name"))
@@ -62,9 +62,9 @@ def test_asset_upload_empty_mimetype(github: Github, github_release: Dict[str, A
must upload asset to the repository with empty mime type if cannot guess it
"""
mocker.patch("pathlib.Path.open", return_value=b"")
- mocker.patch("ahriman.core.upload.Github.asset_remove")
+ mocker.patch("ahriman.core.upload.github.Github.asset_remove")
mocker.patch("mimetypes.guess_type", return_value=(None, None))
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.asset_upload(github_release, Path("/root/new.tar.xz"))
request_mock.assert_called_once_with("POST", "upload_url", params={"name": "new.tar.xz"},
@@ -84,7 +84,7 @@ def test_files_remove(github: Github, github_release: Dict[str, Any], mocker: Mo
"""
must remove files from the remote
"""
- remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
+ remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a", "b": "b"})
remove_mock.assert_called_once_with(github_release, "b")
@@ -93,7 +93,7 @@ def test_files_remove_empty(github: Github, github_release: Dict[str, Any], mock
"""
must remove nothing if nothing changed
"""
- remove_mock = mocker.patch("ahriman.core.upload.Github.asset_remove")
+ remove_mock = mocker.patch("ahriman.core.upload.github.Github.asset_remove")
github.files_remove(github_release, {Path("a"): "a"}, {"a": "a"})
remove_mock.assert_not_called()
@@ -102,7 +102,7 @@ def test_files_upload(github: Github, github_release: Dict[str, Any], mocker: Mo
"""
must upload files to the remote
"""
- upload_mock = mocker.patch("ahriman.core.upload.Github.asset_upload")
+ upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
github.files_upload(github_release, {Path("a"): "a", Path("b"): "c", Path("c"): "c"}, {"a": "a", "b": "b"})
upload_mock.assert_has_calls([
mock.call(github_release, Path("b")),
@@ -114,7 +114,7 @@ def test_files_upload_empty(github: Github, github_release: Dict[str, Any], mock
"""
must upload nothing if nothing changed
"""
- upload_mock = mocker.patch("ahriman.core.upload.Github.asset_upload")
+ upload_mock = mocker.patch("ahriman.core.upload.github.Github.asset_upload")
github.files_upload(github_release, {Path("a"): "a"}, {"a": "a"})
upload_mock.assert_not_called()
@@ -123,7 +123,7 @@ def test_release_create(github: Github, mocker: MockerFixture) -> None:
"""
must create release
"""
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.release_create()
request_mock.assert_called_once_with("POST", pytest.helpers.anyvar(str, True),
json={"tag_name": github.architecture, "name": github.architecture})
@@ -133,7 +133,7 @@ def test_release_get(github: Github, mocker: MockerFixture) -> None:
"""
must get release
"""
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.release_get()
request_mock.assert_called_once_with("GET", pytest.helpers.anyvar(str, True))
@@ -144,7 +144,7 @@ def test_release_get_empty(github: Github, mocker: MockerFixture) -> None:
"""
response = requests.Response()
response.status_code = 404
- mocker.patch("ahriman.core.upload.Github._request", side_effect=requests.HTTPError(response=response))
+ mocker.patch("ahriman.core.upload.github.Github._request", side_effect=requests.HTTPError(response=response))
assert github.release_get() is None
@@ -152,7 +152,7 @@ def test_release_get_exception(github: Github, mocker: MockerFixture) -> None:
"""
must re-raise non HTTPError exception
"""
- mocker.patch("ahriman.core.upload.Github._request", side_effect=Exception())
+ mocker.patch("ahriman.core.upload.github.Github._request", side_effect=Exception())
with pytest.raises(Exception):
github.release_get()
@@ -162,7 +162,7 @@ def test_release_get_exception_http_error(github: Github, mocker: MockerFixture)
must re-raise HTTPError exception with code differs from 404
"""
exception = requests.HTTPError(response=requests.Response())
- mocker.patch("ahriman.core.upload.Github._request", side_effect=exception)
+ mocker.patch("ahriman.core.upload.github.Github._request", side_effect=exception)
with pytest.raises(requests.HTTPError):
github.release_get()
@@ -171,7 +171,7 @@ def test_release_update(github: Github, github_release: Dict[str, Any], mocker:
"""
must update release
"""
- request_mock = mocker.patch("ahriman.core.upload.Github._request")
+ request_mock = mocker.patch("ahriman.core.upload.github.Github._request")
github.release_update(github_release, "body")
request_mock.assert_called_once_with("POST", "release_url", json={"body": "body"})
@@ -180,12 +180,12 @@ def test_release_sync(github: Github, mocker: MockerFixture) -> None:
"""
must run sync command
"""
- release_get_mock = mocker.patch("ahriman.core.upload.Github.release_get", return_value={})
- get_hashes_mock = mocker.patch("ahriman.core.upload.Github.get_hashes", return_value={})
- get_local_files_mock = mocker.patch("ahriman.core.upload.Github.get_local_files", return_value={})
- files_upload_mock = mocker.patch("ahriman.core.upload.Github.files_upload")
- files_remove_mock = mocker.patch("ahriman.core.upload.Github.files_remove")
- release_update_mock = mocker.patch("ahriman.core.upload.Github.release_update")
+ release_get_mock = mocker.patch("ahriman.core.upload.github.Github.release_get", return_value={})
+ get_hashes_mock = mocker.patch("ahriman.core.upload.github.Github.get_hashes", return_value={})
+ get_local_files_mock = mocker.patch("ahriman.core.upload.github.Github.get_local_files", return_value={})
+ files_upload_mock = mocker.patch("ahriman.core.upload.github.Github.files_upload")
+ files_remove_mock = mocker.patch("ahriman.core.upload.github.Github.files_remove")
+ release_update_mock = mocker.patch("ahriman.core.upload.github.Github.release_update")
github.sync(Path("local"), [])
release_get_mock.assert_called_once_with()
@@ -200,13 +200,13 @@ def test_release_sync_create_release(github: Github, mocker: MockerFixture) -> N
"""
must create release in case if it does not exist
"""
- mocker.patch("ahriman.core.upload.Github.release_get", return_value=None)
- mocker.patch("ahriman.core.upload.Github.get_hashes")
- mocker.patch("ahriman.core.upload.Github.get_local_files")
- mocker.patch("ahriman.core.upload.Github.files_upload")
- mocker.patch("ahriman.core.upload.Github.files_remove")
- mocker.patch("ahriman.core.upload.Github.release_update")
- release_create_mock = mocker.patch("ahriman.core.upload.Github.release_create")
+ mocker.patch("ahriman.core.upload.github.Github.release_get", return_value=None)
+ mocker.patch("ahriman.core.upload.github.Github.get_hashes")
+ mocker.patch("ahriman.core.upload.github.Github.get_local_files")
+ mocker.patch("ahriman.core.upload.github.Github.files_upload")
+ mocker.patch("ahriman.core.upload.github.Github.files_remove")
+ mocker.patch("ahriman.core.upload.github.Github.release_update")
+ release_create_mock = mocker.patch("ahriman.core.upload.github.Github.release_create")
github.sync(Path("local"), [])
release_create_mock.assert_called_once_with()
diff --git a/tests/ahriman/core/upload/test_http_upload.py b/tests/ahriman/core/upload/test_http_upload.py
index c6bbb9b6..95a19765 100644
--- a/tests/ahriman/core/upload/test_http_upload.py
+++ b/tests/ahriman/core/upload/test_http_upload.py
@@ -5,7 +5,8 @@ from pathlib import Path
from pytest_mock import MockerFixture
from unittest.mock import MagicMock
-from ahriman.core.upload import Github, HttpUpload
+from ahriman.core.upload.github import Github
+from ahriman.core.upload.http_upload import HttpUpload
def test_calculate_hash_empty(resource_path_root: Path) -> None:
@@ -49,7 +50,7 @@ def test_request(github: Github, mocker: MockerFixture) -> None:
request_mock = mocker.patch("requests.request", return_value=response_mock)
github._request("GET", "url", arg="arg")
- request_mock.assert_called_once_with("GET", "url", auth=github.auth, arg="arg")
+ request_mock.assert_called_once_with("GET", "url", auth=github.auth, timeout=github.timeout, arg="arg")
response_mock.raise_for_status.assert_called_once_with()
diff --git a/tests/ahriman/core/upload/test_rsync.py b/tests/ahriman/core/upload/test_rsync.py
index cdd7f6d7..52bf595a 100644
--- a/tests/ahriman/core/upload/test_rsync.py
+++ b/tests/ahriman/core/upload/test_rsync.py
@@ -1,13 +1,13 @@
from pathlib import Path
from pytest_mock import MockerFixture
-from ahriman.core.upload import Rsync
+from ahriman.core.upload.rsync import Rsync
def test_sync(rsync: Rsync, mocker: MockerFixture) -> None:
"""
must run sync command
"""
- check_output_mock = mocker.patch("ahriman.core.upload.Rsync._check_output")
+ check_output_mock = mocker.patch("ahriman.core.upload.rsync.Rsync._check_output")
rsync.sync(Path("path"), [])
check_output_mock.assert_called_once_with(*rsync.command, "path", rsync.remote, exception=None, logger=rsync.logger)
diff --git a/tests/ahriman/core/upload/test_s3.py b/tests/ahriman/core/upload/test_s3.py
index 0480909c..a4fa86d8 100644
--- a/tests/ahriman/core/upload/test_s3.py
+++ b/tests/ahriman/core/upload/test_s3.py
@@ -4,7 +4,7 @@ from typing import Any, List, Optional, Tuple
from unittest import mock
from unittest.mock import MagicMock
-from ahriman.core.upload import S3
+from ahriman.core.upload.s3 import S3
_chunk_size = 8 * 1024 * 1024
@@ -104,10 +104,10 @@ def test_sync(s3: S3, mocker: MockerFixture) -> None:
"""
must run sync command
"""
- local_files_mock = mocker.patch("ahriman.core.upload.S3.get_local_files", return_value=["a"])
- remote_objects_mock = mocker.patch("ahriman.core.upload.S3.get_remote_objects", return_value=["b"])
- remove_files_mock = mocker.patch("ahriman.core.upload.S3.files_remove")
- upload_files_mock = mocker.patch("ahriman.core.upload.S3.files_upload")
+ local_files_mock = mocker.patch("ahriman.core.upload.s3.S3.get_local_files", return_value=["a"])
+ remote_objects_mock = mocker.patch("ahriman.core.upload.s3.S3.get_remote_objects", return_value=["b"])
+ remove_files_mock = mocker.patch("ahriman.core.upload.s3.S3.files_remove")
+ upload_files_mock = mocker.patch("ahriman.core.upload.s3.S3.files_upload")
s3.sync(Path("root"), [])
remote_objects_mock.assert_called_once_with()
diff --git a/tests/ahriman/core/upload/test_upload.py b/tests/ahriman/core/upload/test_upload.py
index 396fe335..74f3d94d 100644
--- a/tests/ahriman/core/upload/test_upload.py
+++ b/tests/ahriman/core/upload/test_upload.py
@@ -13,7 +13,7 @@ def test_upload_failure(configuration: Configuration, mocker: MockerFixture) ->
"""
must raise SyncFailed on errors
"""
- mocker.patch("ahriman.core.upload.Rsync.sync", side_effect=Exception())
+ mocker.patch("ahriman.core.upload.rsync.Rsync.sync", side_effect=Exception())
with pytest.raises(SyncFailed):
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
@@ -32,7 +32,7 @@ def test_upload_rsync(configuration: Configuration, mocker: MockerFixture) -> No
"""
must upload via rsync
"""
- upload_mock = mocker.patch("ahriman.core.upload.Rsync.sync")
+ upload_mock = mocker.patch("ahriman.core.upload.rsync.Rsync.sync")
Upload.load("x86_64", configuration, "rsync").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@@ -41,7 +41,7 @@ def test_upload_s3(configuration: Configuration, mocker: MockerFixture) -> None:
"""
must upload via s3
"""
- upload_mock = mocker.patch("ahriman.core.upload.S3.sync")
+ upload_mock = mocker.patch("ahriman.core.upload.s3.S3.sync")
Upload.load("x86_64", configuration, "customs3").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])
@@ -50,6 +50,6 @@ def test_upload_github(configuration: Configuration, mocker: MockerFixture) -> N
"""
must upload via github
"""
- upload_mock = mocker.patch("ahriman.core.upload.Github.sync")
+ upload_mock = mocker.patch("ahriman.core.upload.github.Github.sync")
Upload.load("x86_64", configuration, "github").run(Path("path"), [])
upload_mock.assert_called_once_with(Path("path"), [])