Compare commits

...

7 Commits

94 changed files with 10700 additions and 8702 deletions

View File

@ -83,6 +83,7 @@ limit-inference-results=100
# usually to register additional checkers.
load-plugins=pylint.extensions.docparams,
definition_order,
import_order,
# Pickle collected data for later comparisons.
persistent=yes

File diff suppressed because it is too large Load Diff

Before

Width:  |  Height:  |  Size: 993 KiB

After

Width:  |  Height:  |  Size: 1.2 MiB

View File

@ -39,9 +39,9 @@ It will check current settings on common errors and compare configuration with k
Base configuration settings.
* ``apply_migrations`` - perform migrations on application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
* ``database`` - path to SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, optional.
* ``apply_migrations`` - perform database migrations on the application start, boolean, optional, default ``yes``. Useful if you are using git version. Note, however, that this option must be changed only if you know what to do and going to handle migrations manually.
* ``database`` - path to the application SQLite database, string, required.
* ``include`` - path to directory with configuration files overrides, string, optional. Files will be read in alphabetical order.
* ``logging`` - path to logging configuration, string, required. Check ``logging.ini`` for reference.
``alpm:*`` groups
@ -51,9 +51,9 @@ libalpm and AUR related configuration. Group name can refer to architecture, e.g
* ``database`` - path to pacman system database cache, string, required.
* ``mirror`` - package database mirror used by pacman for synchronization, string, required. This option supports standard pacman substitutions with ``$arch`` and ``$repo``. Note that the mentioned mirror should contain all repositories which are set by ``alpm.repositories`` option.
* ``repositories`` - list of pacman repositories, space separated list of strings, required.
* ``root`` - root for alpm library, string, required.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands).
* ``repositories`` - list of pacman repositories, used for package search, space separated list of strings, required.
* ``root`` - root for alpm library, string, required. In the most cases it must point to the system root.
* ``use_ahriman_cache`` - use local pacman package cache instead of system one, boolean, required. With this option enabled you might want to refresh database periodically (available as additional flag for some subcommands). If set to ``no``, databases must be synchronized manually.
``auth`` group
--------------
@ -64,7 +64,7 @@ Base authorization settings. ``OAuth`` provider requires ``aioauth-client`` libr
* ``allow_read_only`` - allow requesting status APIs without authorization, boolean, required.
* ``client_id`` - OAuth2 application client ID, string, required in case if ``oauth`` is used.
* ``client_secret`` - OAuth2 application client secret key, string, required in case if ``oauth`` is used.
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 URL-safe base64-encoded bytes and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically invalidated during the service restart.
* ``cookie_secret_key`` - secret key which will be used for cookies encryption, string, optional. It must be 32 bytes URL-safe base64-encoded and can be generated as following ``base64.urlsafe_b64encode(os.urandom(32)).decode("utf8")``. If not set, it will be generated automatically; note, however, that in this case, all sessions will be automatically invalidated during the service restart.
* ``max_age`` - parameter which controls both cookie expiration and token expiration inside the service in seconds, integer, optional, default is 7 days.
* ``oauth_icon`` - OAuth2 login button icon, string, optional, default is ``google``. Must be valid `Bootstrap icon <https://icons.getbootstrap.com/>`__ name.
* ``oauth_provider`` - OAuth2 provider class name as is in ``aioauth-client`` (e.g. ``GoogleClient``, ``GithubClient`` etc), string, required in case if ``oauth`` is used.
@ -148,7 +148,7 @@ Keyring generator plugin
* ``homepage`` - URL to homepage location if any, string, optional.
* ``license`` - list of licenses which are applied to this package, space separated list of strings, optional, default is ``Unlicense``.
* ``package`` - keyring package name, string, optional, default is ``repo-keyring``, where ``repo`` is the repository name.
* ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the ``key_*`` options from ``sign`` group will be used.
* ``packagers`` - list of packagers keys, space separated list of strings, optional, if not set, the user keys from database will be used.
* ``revoked`` - list of revoked packagers keys, space separated list of strings, optional.
* ``trusted`` - list of master keys, space separated list of strings, optional, if not set, the ``key`` option from ``sign`` group will be used.

View File

@ -1,7 +1,7 @@
# Maintainer: Evgeniy Alekseev
pkgname='ahriman'
pkgver=2.12.2
pkgver=2.13.0
pkgrel=1
pkgdesc="ArcH linux ReposItory MANager"
arch=('any')

View File

@ -1,85 +1,352 @@
[settings]
; Relative path to directory with configuration files overrides. Overrides will be applied in alphabetic order.
include = ahriman.ini.d
; Relative path to configuration used by logging package.
logging = ahriman.ini.d/logging.ini
apply_migrations = yes
; Perform database migrations on the application start. Do not touch this option unless you know what are you doing.
;apply_migrations = yes
; Path to the application SQLite database.
database = /var/lib/ahriman/ahriman.db
[alpm]
; Path to pacman system database cache.
database = /var/lib/pacman
; Arch linux mirror used by local pacman for synchronization.
mirror = https://geo.mirror.pkgbuild.com/$repo/os/$arch
; Space separated list of pacman repositories to search for packages.
repositories = core extra multilib
; Pacman's root directory. In the most cases it must point to the system root.
root = /
; Use local packages cache. If this option is enabled, the service will be able to synchronize databases (available
; as additional option for some subcommands). If set to no, databases must be synchronized manually.
use_ahriman_cache = yes
[auth]
; Authentication provider, must be one of disabled, configuration, oauth.
target = disabled
max_age = 604800
oauth_provider = GoogleClient
oauth_scopes = https://www.googleapis.com/auth/userinfo.email
; Allow read-only endpoint to be called without authentication.
allow_read_only = yes
; OAuth2 application client ID and secret. Required if oauth is used.
;client_id =
;client_secret =
; Cookie secret key to be used for cookies encryption. Must be valid 32 bytes URL-safe base64-encoded string.
; If not set, it will be generated automatically.
;cookie_secret_key =
; Authentication cookie expiration in seconds.
;max_age = 604800
; OAuth2 provider icon for the web interface.
;oauth_icon = google
; OAuth2 provider class name, one of provided by aioauth-client. Required if oauth is used.
;oauth_provider = GoogleClient
; Scopes list for OAuth2 provider. Required if oauth is used.
;oauth_scopes = https://www.googleapis.com/auth/userinfo.email
; Optional password salt.
;salt =
[build]
archbuild_flags =
ignore_packages =
makechrootpkg_flags =
; List of additional flags passed to archbuild command.
;archbuild_flags =
; List of packages to be ignored during automatic updates.
;ignore_packages =
; List of additional flags passed to makechrootpkg command.
;makechrootpkg_flags =
; List of additional flags passed to makepkg command.
makepkg_flags = --nocolor --ignorearch
; List of enabled triggers in the order of calls.
triggers = ahriman.core.gitremote.RemotePullTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.gitremote.RemotePushTrigger
; List of well-known triggers. Used only for configuration purposes.
triggers_known = ahriman.core.distributed.WorkerLoaderTrigger ahriman.core.distributed.WorkerRegisterTrigger ahriman.core.distributed.WorkerTrigger ahriman.core.distributed.WorkerUnregisterTrigger ahriman.core.gitremote.RemotePullTrigger ahriman.core.gitremote.RemotePushTrigger ahriman.core.report.ReportTrigger ahriman.core.upload.UploadTrigger ahriman.core.support.KeyringTrigger ahriman.core.support.MirrorlistTrigger
vcs_allowed_age = 604800
; Maximal age in seconds of the VCS packages before their version will be updated with its remote source.
;vcs_allowed_age = 604800
; List of worker nodes addresses used for build process, e.g.:
; workers = http://10.0.0.1:8080 http://10.0.0.3:8080
; Empty list means run on the local instance.
;workers =
[repository]
; Application root.
root = /var/lib/ahriman
[sign]
; Enable repository or package signing. Must be one of repository, package.
target =
[keyring]
target =
[mirrorlist]
target =
[remote-pull]
target =
[remote-push]
target =
[report]
target = console
[console]
use_utf = yes
[email]
no_empty_report = yes
template = email-index.jinja2
templates = /usr/share/ahriman/templates
ssl = disabled
[html]
template = repo-index.jinja2
templates = /usr/share/ahriman/templates
; PGP key used for signing as default.
;key =
[status]
; Global switch to enable or disable status reporting.
enabled = yes
; Address of the remote service, e.g.:
; address = http://1.0.0.1:8080
; In case if unix sockets are used, it might point to the valid socket with encoded path, e.g.:
; address = http+unix://%2Fvar%2Flib%2Fahriman%2Fsocket
;address =
; Optional password for authentication (if enabled).
;password =
; Do not log HTTP errors if occurs.
suppress_http_log_errors = yes
[telegram]
template = telegram-index.jinja2
templates = /usr/share/ahriman/templates
[upload]
target =
[rsync]
command = rsync --archive --compress --partial --delete
[s3]
chunk_size = 8388608
; HTTP request timeout in seconds.
;timeout = 30
; Optional username for authentication (if enabled).
;username =
[web]
; External address of the web service. Will be used for some features like OAuth. If none set will be generated as
; address = http://web.host:web.port
;address =
; Enable file upload endpoint used by some triggers.
;enable_archive_upload = no
; Address to bind the server.
host = 127.0.0.1
; Full URL to the repository index page used by templates.
;index_url =
; Max file size in bytes which can be uploaded to the server.
;max_body_size =
; Port to listen. Must be set, if the web service is enabled.
;port =
; Disable status (e.g. package status, logs, etc) endpoints. Useful for build only modes.
;service_only = no
; Path to directory with static files.
static_path = /usr/share/ahriman/templates/static
; List of directories with templates.
templates = /usr/share/ahriman/templates
unix_socket_unsafe = yes
; Path to unix socket. If none set, unix socket will be disabled.
;unix_socket =
; Allow unix socket to be world readable.
;unix_socket_unsafe = yes
; Maximum amount of time in seconds to be waited before lock will be free, used by spawned processes (0 is infinite).
;wait_timeout =
[keyring]
; List of configuration section names for keyring generator plugin, e.g.:
; target = keyring-trigger
target =
; Keyring generator trigger sample.
;[keyring-trigger]
; Generator type name.
;type = keyring-generator
; Optional keyring package description.
;description=
; Optional URL to the repository homepage.
;homepage=
; Keyring package licenses list.
;license = Unlicense
; Optional keyring package name.
;package =
; Optional packager PGP keys list. If none set, it will read from database.
;packagers =
; List of revoked PGP keys.
;revoked =
; List of master PGP keys. If none set, the sign.key value will be used.
;trusted =
[mirrorlist]
; List of configuration section names for mirrorlist generator plugin, e.g.:
; target = mirrorlist-trigger
target =
; Mirror list generator trigger sample.
;[mirrorlist-trigger]
; Generator type name.
;type = mirrorlist-generator
; Optional mirrorlist package description.
;description=
; Optional URL to the repository homepage.
;homepage=
; Mirrorlist package licenses list.
;license = Unlicense
; Optional mirrorlist package name.
;package =
; Absolute path to generated mirrorlist file, usually path inside /etc/pacman.d directory.
;path =
; List of repository mirrors.
;servers =
[remote-pull]
; List of configuration section names for git remote pull plugin, e.g.:
; target = remote-pull-trigger
target =
; git remote pull trigger sample.
;[remote-pull-trigger]
; Valid URL to pull repository, e.g.:
; pull_url = https://github.com/arcan1s/arcanisrepo.git
;pull_url =
; Remote branch to pull.
;pull_branch = master
[remote-push]
; List of configuration section names for git remote push plugin, e.g.:
; target = remote-push-trigger
target =
; git remote push trigger sample.
;[remote-push-trigger]
; Author commit email.
;commit_email = ahriman@localhost
; Author commit user.
;commit_user = ahriman
; Valid URL to push repository, e.g.:
; push_url = https://key:token@github.com/arcan1s/arcanisrepo.git
; Note, that more likely authentication must be enabled.
;push_url =
; Remote branch to push.
;push_branch = master
[report]
; List of configuration section names for reporting plugin.
target = console
; Console reporting trigger configuration sample.
[console]
; Trigger type name
;type = console
; Use utf8 symbols in output.
use_utf = yes
; Email reporting trigger configuration sample.
[email]
; Trigger type name
;type = email
; Optional URL to the repository homepage.
;homepage=
; SMTP server address.
;host =
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Skip report generation if no packages were updated.
;no_empty_report = yes
; SMTP password.
;password =
; SMTP server port.
;port =
; List of emails to receive the reports.
;receivers =
; Sender email.
;sender =
; SMTP server SSL mode, one of ssl, starttls, disabled.
;ssl = disabled
; Template name to be used.
template = email-index.jinja2
; Template name to be used for full packages list generation (same as HTML report).
;template_full =
; List of directories with templates.
templates = /usr/share/ahriman/templates
; SMTP user.
;user =
; HTML reporting trigger configuration sample.
[html]
; Trigger type name
;type = html
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Output path for the HTML report.
;path =
; Template name to be used.
template = repo-index.jinja2
; List of directories with templates.
templates = /usr/share/ahriman/templates
; Remote service callback trigger configuration sample.
[remote-call]
; Trigger type name
;type = remote-call
; Call for AUR packages update.
;aur = no
; Call for local packages update.
;local = no
; Call for manual packages update.
;manual = no
; Wait until remote process will be terminated in seconds.
;wait_timeout = -1
; Telegram reporting trigger configuration sample.
[telegram]
; Trigger type name
;type = telegram
; Telegram bot API key.
;api_key =
; Telegram chat ID.
;chat_id =
; Optional URL to the repository homepage.
;homepage=
; Prefix for packages links. Link to a package will be formed as link_path / filename.
;link_path =
; Template name to be used.
template = telegram-index.jinja2
; Telegram specific template mode, one of MarkdownV2, HTML or Markdown.
;template_type = HTML
; List of directories with templates.
templates = /usr/share/ahriman/templates
; HTTP request timeout in seconds.
;timeout = 30
[upload]
; List of configuration section names for remote upload plugin, e.g.:
; target = rsync s3
target =
; GitHub upload trigger configuration sample.
[github]
; Trigger type name
;type = github
; GitHub repository owner username.
;owner =
; GitHub API key. public_repo (repo) scope is required.
;password =
; GitHub repository name.
;repository =
; HTTP request timeout in seconds.
;timeout = 30
; Include repository name to release name (recommended).
;use_full_release_name = no
; GitHub authentication username.
;username =
; Remote instance upload trigger configuration sample.
[remote-service]
; Trigger type name
;type = remote-service
; HTTP request timeout in seconds.
;timeout = 30
; rsync upload trigger configuration sample.
[rsync]
; Trigger type name
;type = rsync
; rsync command to run.
command = rsync --archive --compress --partial --delete
; Remote address and directory to sync, e.g.:
; remote = ahriman@10.0.0.1:/srv/repo
;remote =
; S3 upload trigger configuration sample.
[s3]
; Trigger type name
;type = s3
; AWS services access key.
;access_key =
; AWS S3 bucket name.
;bucket =
; Chunk size tp calculate ETags. Do not edit this value.
;chunk_size = 8388608
; Optional path prefix for stored objects.
;object_path =
; AWS S3 bucket region.
;region =
; AWS services secret key.
;secret_key =
; Remote worker configuration sample.
;[worker]
; Remotely reachable address of this instance, e.g.:
; address = http://10.0.0.1:8080
;address =
; Unique identifier of this instance if any.
;identifier =
; Maximum amount of time in seconds after which worker will be considered offline in case of no reports.
;time_to_live = 60

View File

@ -1,4 +1,4 @@
.TH AHRIMAN "1" "2024\-01\-02" "ahriman" "Generated Python Manual"
.TH AHRIMAN "1" "2024\-01\-05" "ahriman" "Generated Python Manual"
.SH NAME
ahriman
.SH SYNOPSIS

View File

@ -25,19 +25,19 @@ from pylint.lint import PyLinter
from typing import Any
class MethodTypeOrder(StrEnum):
class MethodType(StrEnum):
"""
method type enumeration
Attributes:
Class(MethodTypeOrder): (class attribute) class method
Delete(MethodTypeOrder): (class attribute) destructor-like methods
Init(MethodTypeOrder): (class attribute) initialization method
Magic(MethodTypeOrder): (class attribute) other magical methods
New(MethodTypeOrder): (class attribute) constructor method
Normal(MethodTypeOrder): (class attribute) usual method
Property(MethodTypeOrder): (class attribute) property method
Static(MethodTypeOrder): (class attribute) static method
Class(MethodType): (class attribute) class method
Delete(MethodType): (class attribute) destructor-like methods
Init(MethodType): (class attribute) initialization method
Magic(MethodType): (class attribute) other magical methods
New(MethodType): (class attribute) constructor method
Normal(MethodType): (class attribute) usual method
Property(MethodType): (class attribute) property method
Static(MethodType): (class attribute) static method
"""
Class = "classmethod"
@ -59,20 +59,20 @@ class DefinitionOrder(BaseRawFileChecker):
"""
DECORATED_METHODS_ORDER = {
"cached_property": MethodTypeOrder.Property,
"classmethod": MethodTypeOrder.Class,
"property": MethodTypeOrder.Property,
"staticmethod": MethodTypeOrder.Static,
"cached_property": MethodType.Property,
"classmethod": MethodType.Class,
"property": MethodType.Property,
"staticmethod": MethodType.Static,
}
name = "method-ordering"
msgs = {
"W6001": (
"Invalid method order %s, expected %s",
"methods-out-of-order",
"Methods are defined out of recommended order.",
)
),
}
name = "method-ordering"
options = (
(
"method-type-order",
@ -114,7 +114,7 @@ class DefinitionOrder(BaseRawFileChecker):
return list(filter(is_defined_function, source))
@staticmethod
def resolve_type(function: nodes.FunctionDef) -> MethodTypeOrder:
def resolve_type(function: nodes.FunctionDef) -> MethodType:
"""
resolve type of the function
@ -122,15 +122,15 @@ class DefinitionOrder(BaseRawFileChecker):
function(nodes.FunctionDef): function definition
Returns:
MethodTypeOrder: resolved function type
MethodType: resolved function type
"""
# init methods
if function.name in ("__init__", "__post_init__"):
return MethodTypeOrder.Init
return MethodType.Init
if function.name in ("__new__",):
return MethodTypeOrder.New
return MethodType.New
if function.name in ("__del__",):
return MethodTypeOrder.Delete
return MethodType.Delete
# decorated methods
decorators = []
@ -142,10 +142,10 @@ class DefinitionOrder(BaseRawFileChecker):
# magic methods
if function.name.startswith("__") and function.name.endswith("__"):
return MethodTypeOrder.Magic
return MethodType.Magic
# normal method
return MethodTypeOrder.Normal
return MethodType.Normal
def check_class(self, clazz: nodes.ClassDef) -> None:
"""
@ -184,7 +184,7 @@ class DefinitionOrder(BaseRawFileChecker):
try:
function_type_index = self.linter.config.method_type_order.index(function_type)
except ValueError:
function_type_index = 10 # not in the list
function_type_index = len(self.linter.config.method_type_order) # not in the list
return function_type_index, function.name

View File

@ -0,0 +1,196 @@
#
# Copyright (c) 2021-2023 ahriman team.
#
# This file is part of ahriman
# (see https://github.com/arcan1s/ahriman).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from astroid import nodes
from collections.abc import Iterable
from enum import StrEnum
from pylint.checkers import BaseRawFileChecker
from pylint.lint import PyLinter
from typing import Any
class ImportType(StrEnum):
"""
import type enumeration
Attributes:
Package(MethodTypeOrder): (class attribute) package import
PackageFrom(MethodTypeOrder): (class attribute) package import, from clause
System(ImportType): (class attribute) system installed packages
SystemFrom(MethodTypeOrder): (class attribute) system installed packages, from clause
"""
Package = "package"
PackageFrom = "package-from"
System = "system"
SystemFrom = "system-from"
class ImportOrder(BaseRawFileChecker):
"""
check if imports are defined in recommended order
"""
msgs = {
"W6002": (
"Invalid import order %s, expected before %s",
"imports-out-of-order",
"Imports are defined out of recommended order.",
),
"W6003": (
"Import contains more than one package: %s",
"multiple-package-imports",
"Multiple package imports are not allowed.",
),
"W6004": (
"Invalid from import order %s, expected %s",
"from-imports-out-of-order",
"From imports are defined out of recommended order.",
),
}
name = "import-ordering"
options = (
(
"import-type-order",
{
"default": [
"system",
"system-from",
"package",
"package-from",
],
"type": "csv",
"metavar": "<comma-separated types>",
"help": "Import types order to check.",
},
),
(
"root-module",
{
"default": "ahriman",
"type": "string",
"help": "Root module name",
}
)
)
@staticmethod
def imports(source: Iterable[Any], start_lineno: int = 0) -> list[nodes.Import | nodes.ImportFrom]:
"""
extract import nodes from list of raw nodes
Args:
source(Iterable[Any]): all available nodes
start_lineno(int, optional): minimal allowed line number (Default value = 0)
Returns:
list[nodes.Import | nodes.ImportFrom]: list of import nodes
"""
def is_defined_import(imports: Any) -> bool:
return isinstance(imports, (nodes.Import, nodes.ImportFrom)) \
and imports.lineno is not None \
and imports.lineno >= start_lineno
return list(filter(is_defined_import, source))
def check_from_imports(self, imports: nodes.ImportFrom) -> None:
"""
check import from statement
Args:
imports(nodes.ImportFrom): import from node
"""
imported = [names for names, _ in imports.names]
for real, expected in zip(imported, sorted(imported)):
if real == expected:
continue
self.add_message("from-imports-out-of-order", line=imports.lineno, args=(real, expected))
break
def check_imports(self, imports: list[nodes.Import | nodes.ImportFrom], root_package: str) -> None:
"""
check imports
Args:
imports(list[nodes.Import | nodes.ImportFrom]): list of imports in their defined order
root_package(str): root package name
"""
last_statement: tuple[int, str] | None = None
for statement in imports:
# define types and perform specific checks
if isinstance(statement, nodes.ImportFrom):
import_name = statement.modname
root, *_ = import_name.split(".", maxsplit=1)
import_type = ImportType.PackageFrom if root_package == root else ImportType.SystemFrom
# check from import itself
self.check_from_imports(statement)
else:
import_name = next(name for name, _ in statement.names)
root, *_ = import_name.split(".", maxsplit=1)[0]
import_type = ImportType.Package if root_package == root else ImportType.System
# check import itself
self.check_package_imports(statement)
# extract index
try:
import_type_index = self.linter.config.import_type_order.index(import_type)
except ValueError:
import_type_index = len(self.linter.config.import_type_order)
# check ordering if possible
if last_statement is not None:
_, last_statement_name = last_statement
if last_statement > (import_type_index, import_name):
self.add_message("imports-out-of-order", line=statement.lineno,
args=(import_name, last_statement_name))
# update the last value
last_statement = import_type_index, import_name
def check_package_imports(self, imports: nodes.Import) -> None:
"""
check package import
Args:
imports(nodes.Import): package import node
"""
if len(imports.names) != 1:
self.add_message("multiple-package-imports", line=imports.lineno, args=(imports.names,))
def process_module(self, node: nodes.Module) -> None:
"""
process module
Args:
node(nodes.Module): module node to check
"""
root_module, *_ = node.qname().split(".")
self.check_imports(self.imports(node.values()), root_module)
def register(linter: PyLinter) -> None:
"""
register custom checker
Args:
linter(PyLinter): linter in which checker should be registered
"""
linter.register_checker(ImportOrder(linter))

View File

@ -8,7 +8,8 @@ name = "ahriman"
description = "ArcH linux ReposItory MANager"
readme = "README.md"
requires-python = ">=3.11"
# Actually we are using features from the latest python, however, ubuntu, which is used for CI doesn't have it
requires-python = ">=3"
license = {file = "COPYING"}
authors = [

View File

@ -17,4 +17,4 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
__version__ = "2.12.2"
__version__ = "2.13.0"

View File

@ -71,7 +71,7 @@ def _parser() -> argparse.ArgumentParser:
fromfile_prefix_chars="@", formatter_class=_formatter)
parser.add_argument("-a", "--architecture", help="filter by target architecture")
parser.add_argument("-c", "--configuration", help="configuration path", type=Path,
default=Path("/etc") / "ahriman.ini")
default=Path("/") / "etc" / "ahriman.ini")
parser.add_argument("--force", help="force run, remove file lock", action="store_true")
parser.add_argument("-l", "--lock", help="lock file", type=Path,
default=Path(tempfile.gettempdir()) / "ahriman.lock")
@ -999,7 +999,7 @@ def _set_service_setup_parser(root: SubParserAction) -> argparse.ArgumentParser:
formatter_class=_formatter)
parser.add_argument("--build-as-user", help="force makepkg user to the specific one")
parser.add_argument("--from-configuration", help="path to default devtools pacman configuration",
type=Path, default=Path("/usr") / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
type=Path, default=Path("/") / "usr" / "share" / "devtools" / "pacman.conf.d" / "extra.conf")
parser.add_argument("--generate-salt", help="generate salt for user passwords",
action=argparse.BooleanOptionalAction, default=False)
parser.add_argument("--makeflags-jobs", help="append MAKEFLAGS variable with parallelism set to number of cores",

View File

@ -17,14 +17,13 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.application.handlers.handler import Handler
from ahriman.application.handlers.add import Add
from ahriman.application.handlers.backup import Backup
from ahriman.application.handlers.change import Change
from ahriman.application.handlers.clean import Clean
from ahriman.application.handlers.daemon import Daemon
from ahriman.application.handlers.dump import Dump
from ahriman.application.handlers.handler import Handler
from ahriman.application.handlers.help import Help
from ahriman.application.handlers.key_import import KeyImport
from ahriman.application.handlers.patch import Patch

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers
from ahriman.models.pkgbuild_patch import PkgbuildPatch

View File

@ -23,7 +23,7 @@ import pwd
from pathlib import Path
from tarfile import TarFile
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ChangesPrinter
from ahriman.models.action import Action

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from ahriman.application.application import Application
from ahriman.application.application.updates_iterator import FixedUpdatesIterator, UpdatesIterator
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.application.handlers.update import Update
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
#
import argparse
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import ConfigurationPathsPrinter, ConfigurationPrinter, StringPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
#
import argparse
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -23,7 +23,7 @@ import sys
from pathlib import Path
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.build_tools.sources import Sources
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PatchPrinter

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.package import Package

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
#
import argparse
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import RepositoryPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from tarfile import TarFile
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
import shlex
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -19,10 +19,10 @@
#
import argparse
from dataclasses import fields
from collections.abc import Callable, Iterable
from dataclasses import fields
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.alpm.remote import AUR, Official
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import OptionError

View File

@ -20,7 +20,7 @@
import argparse
from ahriman import __version__
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import UpdatePrinter
from ahriman.models.package import Package

View File

@ -24,7 +24,7 @@ from pwd import getpwuid
from urllib.parse import quote_plus as urlencode
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.exceptions import MissingArchitectureError
from ahriman.models.repository_id import RepositoryId
@ -44,9 +44,9 @@ class Setup(Handler):
ALLOW_MULTI_ARCHITECTURE_RUN = False # conflicting io
ARCHBUILD_COMMAND_PATH = Path("/usr") / "bin" / "archbuild"
MIRRORLIST_PATH = Path("/etc") / "pacman.d" / "mirrorlist"
SUDOERS_DIR_PATH = Path("/etc") / "sudoers.d"
ARCHBUILD_COMMAND_PATH = Path("/") / "usr" / "bin" / "archbuild"
MIRRORLIST_PATH = Path("/") / "etc" / "pacman.d" / "mirrorlist"
SUDOERS_DIR_PATH = Path("/") / "etc" / "sudoers.d"
@classmethod
def run(cls, args: argparse.Namespace, repository_id: RepositoryId, configuration: Configuration, *,

View File

@ -23,7 +23,7 @@ import sys
from pathlib import Path
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId

View File

@ -22,7 +22,7 @@ import argparse
from collections.abc import Callable
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import PackagePrinter, StatusPrinter
from ahriman.models.build_status import BuildStatus

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.action import Action
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter, TreePrinter
from ahriman.core.tree import Tree

View File

@ -19,7 +19,7 @@
#
import argparse
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.repository_paths import RepositoryPaths

View File

@ -20,7 +20,7 @@
import argparse
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.repository_id import RepositoryId
from ahriman.models.result import Result

View File

@ -19,7 +19,7 @@
#
import argparse
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import StringPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -22,7 +22,7 @@ import argparse
from collections.abc import Callable
from ahriman.application.application import Application
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.models.packagers import Packagers
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
import argparse
import getpass
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.core.exceptions import PasswordError

View File

@ -22,7 +22,7 @@ import copy
from typing import Any
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.configuration.schema import CONFIGURATION_SCHEMA, ConfigurationSchema
from ahriman.core.configuration.validator import Validator

View File

@ -25,7 +25,7 @@ from collections.abc import Generator
from importlib import metadata
from ahriman import __version__
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.formatters import VersionPrinter
from ahriman.models.repository_id import RepositoryId

View File

@ -21,7 +21,7 @@ import argparse
from collections.abc import Generator
from ahriman.application.handlers import Handler
from ahriman.application.handlers.handler import Handler
from ahriman.core.configuration import Configuration
from ahriman.core.spawn import Spawn
from ahriman.core.triggers import TriggerLoader

View File

@ -17,8 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.alpm.remote.remote import Remote
from ahriman.core.alpm.remote.aur import AUR
from ahriman.core.alpm.remote.official import Official
from ahriman.core.alpm.remote.official_syncdb import OfficialSyncdb
from ahriman.core.alpm.remote.remote import Remote

View File

@ -20,7 +20,7 @@
from typing import Any
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
from ahriman.core.alpm.remote.remote import Remote
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage

View File

@ -20,7 +20,7 @@
from typing import Any
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Remote
from ahriman.core.alpm.remote.remote import Remote
from ahriman.core.exceptions import PackageInfoError, UnknownPackageError
from ahriman.models.aur_package import AURPackage

View File

@ -18,7 +18,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.alpm.pacman import Pacman
from ahriman.core.alpm.remote import Official
from ahriman.core.alpm.remote.official import Official
from ahriman.core.exceptions import UnknownPackageError
from ahriman.models.aur_package import AURPackage

View File

@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.database.operations.operations import Operations
from ahriman.core.database.operations.auth_operations import AuthOperations
from ahriman.core.database.operations.build_operations import BuildOperations
from ahriman.core.database.operations.changes_operations import ChangesOperations

View File

@ -19,7 +19,7 @@
#
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.user import User
from ahriman.models.user_access import UserAccess

View File

@ -19,7 +19,7 @@
#
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
#
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.changes import Changes
from ahriman.models.repository_id import RepositoryId

View File

@ -19,7 +19,7 @@
#
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.log_record_id import LogRecordId
from ahriman.models.repository_id import RepositoryId

View File

@ -20,7 +20,7 @@
from collections.abc import Generator, Iterable
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package
from ahriman.models.package_description import PackageDescription

View File

@ -20,7 +20,7 @@
from collections import defaultdict
from sqlite3 import Connection
from ahriman.core.database.operations import Operations
from ahriman.core.database.operations.operations import Operations
from ahriman.models.pkgbuild_patch import PkgbuildPatch

View File

@ -17,7 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from collections import deque
from threading import Lock, Timer
from ahriman.core.configuration import Configuration
@ -47,15 +46,14 @@ class WorkerTrigger(DistributedSystem):
self.ping_interval = configuration.getint(section, "time_to_live", fallback=60) / 4.0
self._lock = Lock()
self._timers: deque[Timer] = deque() # because python doesn't have atomics
self._timer: Timer | None = None
def create_timer(self) -> None:
"""
create timer object and put it to queue
"""
timer = Timer(self.ping_interval, self.ping)
timer.start()
self._timers.append(timer)
self._timer = Timer(self.ping_interval, self.ping)
self._timer.start()
def on_start(self) -> None:
"""
@ -71,21 +69,19 @@ class WorkerTrigger(DistributedSystem):
"""
self.logger.info("removing instance %s in %s", self.worker, self.address)
with self._lock:
current_timers = self._timers.copy() # will be used later
self._timers.clear() # clear timer list
if self._timer is None:
return
for timer in current_timers:
timer.cancel() # cancel remaining timers
self._timer.cancel() # cancel remaining timers
self._timer = None # reset state
def ping(self) -> None:
"""
register itself as alive worker and update the timer
"""
with self._lock:
if not self._timers: # make sure that there is related specific timer
if self._timer is None: # no active timer set, exit loop
return
self._timers.popleft() # pop first timer
self.register()
self.create_timer()

View File

@ -17,8 +17,6 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.formatters.printer import Printer
from ahriman.core.formatters.aur_printer import AurPrinter
from ahriman.core.formatters.build_printer import BuildPrinter
from ahriman.core.formatters.changes_printer import ChangesPrinter
@ -26,6 +24,7 @@ from ahriman.core.formatters.configuration_paths_printer import ConfigurationPat
from ahriman.core.formatters.configuration_printer import ConfigurationPrinter
from ahriman.core.formatters.package_printer import PackagePrinter
from ahriman.core.formatters.patch_printer import PatchPrinter
from ahriman.core.formatters.printer import Printer
from ahriman.core.formatters.repository_printer import RepositoryPrinter
from ahriman.core.formatters.status_printer import StatusPrinter
from ahriman.core.formatters.string_printer import StringPrinter

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.formatters import Printer
from ahriman.core.formatters.printer import Printer
from ahriman.models.changes import Changes
from ahriman.models.property import Property

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.formatters import Printer
from ahriman.core.formatters.printer import Printer
class StringPrinter(Printer):

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from logging import NullHandler
from logging import NullHandler # pylint: disable=imports-out-of-order
from typing import Any

View File

@ -40,7 +40,7 @@ class LogLoader:
DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s"
DEFAULT_LOG_LEVEL = logging.DEBUG
DEFAULT_SYSLOG_DEVICE = Path("/dev") / "log"
DEFAULT_SYSLOG_DEVICE = Path("/") / "dev" / "log"
@staticmethod
def handler(selected: LogHandler | None) -> LogHandler:

View File

@ -18,8 +18,8 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from ahriman.core.configuration import Configuration
from ahriman.core.triggers import Trigger
from ahriman.core.report.report import Report
from ahriman.core.triggers import Trigger
from ahriman.models.package import Package
from ahriman.models.repository_id import RepositoryId
from ahriman.models.result import Result

View File

@ -51,7 +51,7 @@ class MirrorlistGenerator(PkgbuildGenerator):
# configuration fields
self.servers = configuration.getlist(section, "servers")
self.path = configuration.getpath(
section, "path", fallback=Path("/etc") / "pacman.d" / f"{repository_id.name}-mirrorlist")
section, "path", fallback=Path("/") / "etc" / "pacman.d" / f"{repository_id.name}-mirrorlist")
self.path = self.path.relative_to("/") # in pkgbuild we are always operating with relative to / path
# pkgbuild description fields
self.pkgbuild_pkgname = configuration.get(section, "package", fallback=f"{repository_id.name}-mirrorlist")

View File

@ -19,7 +19,7 @@
#
from __future__ import annotations
from collections.abc import Iterable, Callable
from collections.abc import Callable, Iterable
from typing import Any, Self
from ahriman.models.package import Package

View File

@ -17,9 +17,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from secrets import token_urlsafe as generate_password
from dataclasses import dataclass, replace
from passlib.hash import sha512_crypt
from secrets import token_urlsafe as generate_password
from typing import Self
from ahriman.models.user_access import UserAccess

View File

@ -35,7 +35,7 @@ from ahriman.web.schemas.package_names_schema import PackageNamesSchema
from ahriman.web.schemas.package_patch_schema import PackagePatchSchema
from ahriman.web.schemas.package_properties_schema import PackagePropertiesSchema
from ahriman.web.schemas.package_schema import PackageSchema
from ahriman.web.schemas.package_status_schema import PackageStatusSimplifiedSchema, PackageStatusSchema
from ahriman.web.schemas.package_status_schema import PackageStatusSchema, PackageStatusSimplifiedSchema
from ahriman.web.schemas.pagination_schema import PaginationSchema
from ahriman.web.schemas.patch_name_schema import PatchNameSchema
from ahriman.web.schemas.patch_schema import PatchSchema

View File

@ -25,22 +25,6 @@ from ahriman.web.schemas.repository_id_schema import RepositoryIdSchema
from ahriman.web.schemas.status_schema import StatusSchema
class PackageStatusSimplifiedSchema(Schema):
"""
special request package status schema
"""
package = fields.Nested(PackageSchema(), metadata={
"description": "Package description",
})
status = fields.Enum(BuildStatusEnum, by_value=True, required=True, metadata={
"description": "Current status",
})
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier",
})
class PackageStatusSchema(Schema):
"""
response package status schema
@ -55,3 +39,19 @@ class PackageStatusSchema(Schema):
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier",
})
class PackageStatusSimplifiedSchema(Schema):
"""
special request package status schema
"""
package = fields.Nested(PackageSchema(), metadata={
"description": "Package description",
})
status = fields.Enum(BuildStatusEnum, by_value=True, required=True, metadata={
"description": "Current status",
})
repository = fields.Nested(RepositoryIdSchema(), required=True, metadata={
"description": "Repository identifier",
})

View File

@ -17,8 +17,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Request, StreamResponse, View
from aiohttp_cors import CorsViewMixin # type: ignore[import-untyped]
from collections.abc import Awaitable, Callable
from typing import TypeVar

View File

@ -19,8 +19,8 @@
#
import aiohttp_apispec # type: ignore[import-untyped]
from collections.abc import Callable
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
from collections.abc import Callable
from ahriman.models.user_access import UserAccess
from ahriman.models.worker import Worker

View File

@ -22,7 +22,7 @@ import aiohttp_apispec # type: ignore[import-untyped]
from aiohttp.web import HTTPBadRequest, Response, json_response
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, UpdateFlagsSchema, RepositoryIdSchema
from ahriman.web.schemas import AuthSchema, ErrorSchema, ProcessIdSchema, RepositoryIdSchema, UpdateFlagsSchema
from ahriman.web.views.base import BaseView

View File

@ -20,8 +20,8 @@
import aiohttp_apispec # type: ignore[import-untyped]
import itertools
from collections.abc import Callable
from aiohttp.web import HTTPNoContent, Response, json_response
from collections.abc import Callable
from ahriman.models.build_status import BuildStatus
from ahriman.models.package import Package

View File

@ -26,7 +26,7 @@ from ahriman.models.build_status import BuildStatusEnum
from ahriman.models.counters import Counters
from ahriman.models.internal_status import InternalStatus
from ahriman.models.user_access import UserAccess
from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, StatusSchema, RepositoryIdSchema
from ahriman.web.schemas import AuthSchema, ErrorSchema, InternalStatusSchema, RepositoryIdSchema, StatusSchema
from ahriman.web.views.base import BaseView
from ahriman.web.views.status_view_guard import StatusViewGuard

View File

@ -100,7 +100,7 @@ def test_add_local_cache(application_packages: ApplicationPackages, package_ahri
"""
mocker.patch("ahriman.models.package.Package.from_build", return_value=package_ahriman)
mocker.patch("pathlib.Path.is_dir", autospec=True,
side_effect=lambda p: True if p.is_relative_to(application_packages.repository.paths.cache) else False)
side_effect=lambda p: p.is_relative_to(application_packages.repository.paths.cache))
init_mock = mocker.patch("ahriman.core.build_tools.sources.Sources.init")
copytree_mock = mocker.patch("shutil.copytree")
build_queue_mock = mocker.patch("ahriman.core.database.SQLite.build_queue_insert")

View File

@ -47,7 +47,7 @@ def test_iter(updates_iterator: UpdatesIterator) -> None:
"""
must return self as iterator
"""
assert updates_iterator.__iter__() == updates_iterator
assert iter(updates_iterator) == updates_iterator
def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocker: MockerFixture) -> None:
@ -58,7 +58,7 @@ def test_next(updates_iterator: UpdatesIterator, package_ahriman: Package, mocke
side_effect=[([package_ahriman.base], 2), (None, 2), StopIteration])
sleep_mock = mocker.patch("time.sleep")
updates = [packages for packages in updates_iterator]
updates = list(updates_iterator)
assert updates == [[package_ahriman.base], None]
sleep_mock.assert_has_calls([MockCall(0.5), MockCall(0.5)])

View File

@ -156,7 +156,7 @@ def test_configuration_create_ahriman(args: argparse.Namespace, configuration: C
def test_configuration_create_ahriman_no_multilib(args: argparse.Namespace, configuration: Configuration,
repository_paths: RepositoryPaths, mocker: MockerFixture) -> None:
mocker: MockerFixture) -> None:
"""
must create configuration for the service without multilib repository
"""

View File

@ -558,14 +558,13 @@ def user() -> User:
@pytest.fixture
def watcher(repository_id: RepositoryId, database: SQLite, repository: Repository) -> Watcher:
def watcher(repository_id: RepositoryId, database: SQLite) -> Watcher:
"""
package status watcher fixture
Args:
repository_id(RepositoryId): repository identifier fixture
database(SQLite): database fixture
repository(Repository): repository fixture
Returns:
Watcher: package status watcher test instance

View File

@ -48,7 +48,7 @@ def test_multisearch_single(aur_package_ahriman: AURPackage, pacman: Pacman, moc
search_mock.assert_called_once_with("ahriman", pacman=pacman)
def test_remote_git_url(remote: Remote, pacman: Pacman) -> None:
def test_remote_git_url(remote: Remote) -> None:
"""
must raise NotImplemented for missing remote git url
"""
@ -56,7 +56,7 @@ def test_remote_git_url(remote: Remote, pacman: Pacman) -> None:
remote.remote_git_url("package", "repositorys")
def test_remote_web_url(remote: Remote, pacman: Pacman) -> None:
def test_remote_web_url(remote: Remote) -> None:
"""
must raise NotImplemented for missing remote web url
"""

View File

@ -1,10 +1,10 @@
import importlib
import sys
import ahriman.core.auth.helpers as helpers
from pytest_mock import MockerFixture
from ahriman.core.auth import helpers
def test_import_aiohttp_security() -> None:
"""

View File

@ -27,7 +27,7 @@ def test_changes(mocker: MockerFixture) -> None:
diff_mock.assert_called_once_with(local, last_commit_sha)
def test_changes_skip(package_ahriman: Package, mocker: MockerFixture) -> None:
def test_changes_skip(mocker: MockerFixture) -> None:
"""
must return none in case if commit sha is not available
"""

View File

@ -1,7 +1,7 @@
import pytest
from sqlite3 import Connection
from pytest_mock import MockerFixture
from sqlite3 import Connection
from unittest.mock import call as MockCall
from ahriman.core.configuration import Configuration

View File

@ -19,7 +19,7 @@ def test_migrate(connection: Connection, configuration: Configuration, mocker: M
run_mock.assert_called_once_with()
def test_migration(migrations: Migrations, connection: Connection) -> None:
def test_migration(migrations: Migrations) -> None:
"""
must perform single migration
"""

View File

@ -1,8 +1,6 @@
from pytest_mock import MockerFixture
from threading import Timer
from pytest_mock import MockerFixture
from ahriman.core.configuration import Configuration
from ahriman.core.distributed import WorkerTrigger
@ -11,10 +9,8 @@ def test_create_timer(worker_trigger: WorkerTrigger) -> None:
must create a timer and put it to queue
"""
worker_trigger.create_timer()
timer = worker_trigger._timers.popleft()
assert timer.function == worker_trigger.ping
timer.cancel()
assert worker_trigger._timer.function == worker_trigger.ping
worker_trigger._timer.cancel()
def test_on_start(worker_trigger: WorkerTrigger, mocker: MockerFixture) -> None:
@ -31,7 +27,7 @@ def test_on_stop(worker_trigger: WorkerTrigger, mocker: MockerFixture) -> None:
must unregister itself as worker
"""
run_mock = mocker.patch("threading.Timer.cancel")
worker_trigger._timers.append(Timer(1, print)) # doesn't matter
worker_trigger._timer = Timer(1, print) # doesn't matter
worker_trigger.on_stop()
run_mock.assert_called_once_with()
@ -50,7 +46,7 @@ def test_ping(worker_trigger: WorkerTrigger, mocker: MockerFixture) -> None:
"""
run_mock = mocker.patch("ahriman.core.distributed.WorkerTrigger.register")
timer_mock = mocker.patch("threading.Timer.start")
worker_trigger._timers.append(Timer(1, print)) # doesn't matter
worker_trigger._timer = Timer(1, print) # doesn't matter
worker_trigger.ping()
run_mock.assert_called_once_with()

View File

@ -2,7 +2,7 @@ import pytest
from pathlib import Path
from ahriman.core.formatters import AurPrinter, ChangesPrinter, ConfigurationPrinter, ConfigurationPathsPrinter, \
from ahriman.core.formatters import AurPrinter, ChangesPrinter, ConfigurationPathsPrinter, ConfigurationPrinter, \
PackagePrinter, PatchPrinter, RepositoryPrinter, StatusPrinter, StringPrinter, TreePrinter, UpdatePrinter, \
UserPrinter, ValidationPrinter, VersionPrinter
from ahriman.models.aur_package import AURPackage

View File

@ -22,10 +22,7 @@ def test_package_update(database: SQLite, configuration: Configuration, package_
patch2 = PkgbuildPatch("key", "value")
local = Path("local")
mocker.patch(
"pathlib.Path.is_file",
autospec=True,
side_effect=lambda p: True if p == Path(".gitignore") else False)
mocker.patch("pathlib.Path.is_file", autospec=True, side_effect=lambda p: p == Path(".gitignore"))
glob_mock = mocker.patch("pathlib.Path.glob", return_value=[Path(".git"), Path(".gitignore")])
rmtree_mock = mocker.patch("shutil.rmtree")
unlink_mock = mocker.patch("pathlib.Path.unlink")

View File

@ -29,7 +29,7 @@ def test_load_archives(package_ahriman: Package, package_python_schedule: Packag
assert len(packages) == 2
assert {package.base for package in packages} == {package_ahriman.base, package_python_schedule.base}
archives = sum([list(package.packages.keys()) for package in packages], start=[])
archives = sum((list(package.packages.keys()) for package in packages), start=[])
assert len(archives) == 3
expected = set(package_ahriman.packages.keys())
expected.update(package_python_schedule.packages.keys())

View File

@ -56,7 +56,7 @@ def test_configuration_schema_empty(configuration: Configuration) -> None:
assert ReportTrigger.configuration_schema(repository_id, None) == ReportTrigger.CONFIGURATION_SCHEMA
def test_configuration_schema_variables(configuration: Configuration) -> None:
def test_configuration_schema_variables() -> None:
"""
must return empty schema
"""

View File

@ -147,7 +147,7 @@ def test_on_result_exception(trigger_loader: TriggerLoader, package_ahriman: Pac
log_mock.assert_called_once()
def test_on_start(trigger_loader: TriggerLoader, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_on_start(trigger_loader: TriggerLoader, mocker: MockerFixture) -> None:
"""
must run triggers on start
"""
@ -187,7 +187,7 @@ def test_on_stop_without_on_start(configuration: Configuration, mocker: MockerFi
on_stop_mock.assert_not_called()
def test_on_stop(trigger_loader: TriggerLoader, package_ahriman: Package, mocker: MockerFixture) -> None:
def test_on_stop(trigger_loader: TriggerLoader, mocker: MockerFixture) -> None:
"""
must run triggers on stop
"""

View File

@ -78,7 +78,7 @@ def test_from_repo(aur_package_akonadi: AURPackage, resource_path_root: Path) ->
assert AURPackage.from_repo(model) == aur_package_akonadi
def test_convert(aur_package_ahriman: AURPackage, resource_path_root: Path) -> None:
def test_convert(resource_path_root: Path) -> None:
"""
must convert fields to snakecase and also apply converters
"""

View File

@ -249,7 +249,7 @@ def test_from_build_architecture(mocker: MockerFixture, resource_path_root: Path
}
def test_from_build_failed(package_ahriman: Package, mocker: MockerFixture) -> None:
def test_from_build_failed(mocker: MockerFixture) -> None:
"""
must raise exception if there are errors during srcinfo load
"""
@ -317,7 +317,7 @@ def test_local_files_empty(mocker: MockerFixture, resource_path_root: Path) -> N
mocker.patch("ahriman.models.package.check_output", return_value=srcinfo)
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
assert list(Package.local_files(Path("path"))) == []
assert not list(Package.local_files(Path("path")))
def test_local_files_error(mocker: MockerFixture) -> None:
@ -342,7 +342,7 @@ def test_local_files_schema(mocker: MockerFixture, resource_path_root: Path) ->
mocker.patch("ahriman.models.package.check_output", return_value="")
mocker.patch("ahriman.models.package.Package.supported_architectures", return_value=["any"])
assert list(Package.local_files(Path("path"))) == []
assert not list(Package.local_files(Path("path")))
def test_local_files_with_install(mocker: MockerFixture, resource_path_root: Path) -> None:

View File

@ -1,6 +1,6 @@
from collections.abc import Callable
from pytest_mock import MockerFixture
from pathlib import Path
from pytest_mock import MockerFixture
from ahriman.models.package_description import PackageDescription
from ahriman.models.package_source import PackageSource

View File

@ -1,16 +1,15 @@
import pytest
from asyncio import BaseEventLoop
from aiohttp.web import Application, Resource, UrlMappingMatchInfo
from aiohttp.test_utils import TestClient
from aiohttp.web import Application, Resource, UrlMappingMatchInfo
from asyncio import BaseEventLoop
from collections.abc import Awaitable, Callable
from marshmallow import Schema
from pytest_mock import MockerFixture
from typing import Any
from unittest.mock import MagicMock, Mock
import ahriman.core.auth.helpers
from ahriman.core.auth import helpers
from ahriman.core.auth.oauth import OAuth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
@ -73,7 +72,7 @@ def request(application: Application, path: str, method: str, params: Any = None
request_mock.match_info = UrlMappingMatchInfo({}, route_mock)
extra = extra or {}
request_mock.get_extra_info.side_effect = lambda key: extra.get(key)
request_mock.get_extra_info.side_effect = extra.get
return request_mock
@ -130,7 +129,7 @@ def application(configuration: Configuration, spawner: Spawn, database: SQLite,
configuration.set_option("web", "port", "8080")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("aiohttp_apispec.setup_aiohttp_apispec")
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False)
mocker.patch.object(helpers, "_has_aiohttp_security", False)
_, repository_id = configuration.check_loaded()
return setup_server(configuration, spawner, [repository_id])
@ -156,7 +155,7 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa
configuration.set_option("web", "port", "8080")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("aiohttp_apispec.setup_aiohttp_apispec")
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", True)
mocker.patch.object(helpers, "_has_aiohttp_security", True)
_, repository_id = configuration.check_loaded()
application = setup_server(configuration, spawner, [repository_id])
@ -167,14 +166,13 @@ def application_with_auth(configuration: Configuration, user: User, spawner: Spa
@pytest.fixture
def application_with_debug(configuration: Configuration, user: User, spawner: Spawn, database: SQLite,
def application_with_debug(configuration: Configuration, spawner: Spawn, database: SQLite,
mocker: MockerFixture) -> Application:
"""
application fixture with debug enabled
Args:
configuration(Configuration): configuration fixture
user(User): user descriptor fixture
spawner(Spawn): spawner fixture
database(SQLite): database fixture
mocker(MockerFixture): mocker object
@ -186,7 +184,7 @@ def application_with_debug(configuration: Configuration, user: User, spawner: Sp
configuration.set_option("web", "port", "8080")
mocker.patch("ahriman.core.database.SQLite.load", return_value=database)
mocker.patch("aiohttp_apispec.setup_aiohttp_apispec")
mocker.patch.object(ahriman.core.auth.helpers, "_has_aiohttp_security", False)
mocker.patch.object(helpers, "_has_aiohttp_security", False)
_, repository_id = configuration.check_loaded()
return setup_server(configuration, spawner, [repository_id])

View File

@ -3,19 +3,17 @@ import pytest
from ahriman.core.auth import Auth
from ahriman.core.configuration import Configuration
from ahriman.core.database import SQLite
from ahriman.models.user import User
from ahriman.web.middlewares.auth_handler import _AuthorizationPolicy
@pytest.fixture
def authorization_policy(configuration: Configuration, database: SQLite, user: User) -> _AuthorizationPolicy:
def authorization_policy(configuration: Configuration, database: SQLite) -> _AuthorizationPolicy:
"""
fixture for authorization policy
Args:
configuration(Configuration): configuration fixture
database(SQLite): database fixture
user(User): user fixture
Returns:
AuthorizationPolicy: authorization policy fixture

View File

@ -24,7 +24,7 @@ async def test_authorized_userid(authorization_policy: _AuthorizationPolicy, use
assert await authorization_policy.authorized_userid(user.username) == user.username
async def test_authorized_userid_unknown(authorization_policy: _AuthorizationPolicy, user: User) -> None:
async def test_authorized_userid_unknown(authorization_policy: _AuthorizationPolicy) -> None:
"""
must not allow unknown user id for authorization
"""
@ -51,7 +51,7 @@ async def test_permits(authorization_policy: _AuthorizationPolicy, user: User) -
])
async def test_auth_handler_unix_socket(client_with_auth: TestClient, mocker: MockerFixture) -> None:
async def test_auth_handler_unix_socket(mocker: MockerFixture) -> None:
"""
must allow calls via unix sockets
"""

View File

@ -1,8 +1,8 @@
import pytest
from multidict import MultiDict
from aiohttp.test_utils import TestClient
from aiohttp.web import HTTPBadRequest, HTTPNotFound
from multidict import MultiDict
from pytest_mock import MockerFixture
from unittest.mock import AsyncMock

View File

@ -1,9 +1,10 @@
[tox]
envlist = check, tests
dependencies = -e .[journald,pacman,s3,web]
project_name = ahriman
isolated_build = True
labels =
release = version, docs, publish
dependencies = -e .[journald,pacman,s3,web]
project_name = ahriman
[mypy]
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any