mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-04-24 23:37:18 +00:00
PEP-673 use Self as return type for classmethods
This commit is contained in:
parent
9dc6d56a8d
commit
277850c10f
2
.github/workflows/setup.sh
vendored
2
.github/workflows/setup.sh
vendored
@ -10,7 +10,7 @@ echo -e '[arcanisrepo]\nServer = http://repo.arcanis.me/$arch\nSigLevel = Never'
|
|||||||
# refresh the image
|
# refresh the image
|
||||||
pacman --noconfirm -Syu
|
pacman --noconfirm -Syu
|
||||||
# main dependencies
|
# main dependencies
|
||||||
pacman --noconfirm -Sy base-devel devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-setuptools python-srcinfo sudo
|
pacman --noconfirm -Sy base-devel devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo sudo
|
||||||
# make dependencies
|
# make dependencies
|
||||||
pacman --noconfirm -Sy python-build python-installer python-wheel
|
pacman --noconfirm -Sy python-build python-installer python-wheel
|
||||||
# optional dependencies
|
# optional dependencies
|
||||||
|
2
.github/workflows/tests.sh
vendored
2
.github/workflows/tests.sh
vendored
@ -4,7 +4,7 @@
|
|||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
# install dependencies
|
# install dependencies
|
||||||
pacman --noconfirm -Syu base-devel python-pip python-setuptools python-tox
|
pacman --noconfirm -Syu base-devel python-setuptools python-tox
|
||||||
|
|
||||||
# run test and check targets
|
# run test and check targets
|
||||||
make check tests
|
make check tests
|
||||||
|
@ -90,8 +90,9 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
self.instance_attribute = ""
|
self.instance_attribute = ""
|
||||||
```
|
```
|
||||||
|
|
||||||
* Type annotations are the must, even for local functions.
|
* Type annotations are the must, even for local functions. For the function argument `self` (for instance methods) and `cls` (for class methods) should not be annotated.
|
||||||
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
|
* For collection types built-in classes must be used if possible (e.g. `dict` instead of `typing.Dict`, `tuple` instead of `typing.Tuple`). In case if built-in type is not available, but `collections.abc` provides interface, it must be used (e.g. `collections.abc.Awaitable` instead of `typing.Awaitable`, `collections.abc.Iterable` instead of `typing.Iterable`). For union classes, the bar operator (`|`) must be used (e.g. `float | int` instead of `typing.Union[float, int]`), which also includes `typinng.Optional` (e.g. `str | None` instead of `Optional[str]`).
|
||||||
|
* `classmethod` should always return `Self`. In case of mypy warning (e.g. if there is a branch in which function doesn't return the instance of `cls`) consider using `staticmethod` instead.
|
||||||
* Recommended order of function definitions in class:
|
* Recommended order of function definitions in class:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@ -103,7 +104,7 @@ Again, the most checks can be performed by `make check` command, though some add
|
|||||||
def property(self) -> Any: ...
|
def property(self) -> Any: ...
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def class_method(cls: type[Clazz]) -> Clazz: ...
|
def class_method(cls) -> Self: ...
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def static_method() -> Any: ...
|
def static_method() -> Any: ...
|
||||||
|
@ -28,7 +28,7 @@ RUN useradd -m -d "/home/build" -s "/usr/bin/nologin" build && \
|
|||||||
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
COPY "docker/install-aur-package.sh" "/usr/local/bin/install-aur-package"
|
||||||
## install package dependencies
|
## install package dependencies
|
||||||
## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size
|
## 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-cerberus python-inflection python-passlib python-requests python-setuptools python-srcinfo && \
|
RUN pacman --noconfirm -Sy devtools git pyalpm python-cerberus python-inflection python-passlib python-requests python-srcinfo && \
|
||||||
pacman --noconfirm -Sy python-build python-installer python-wheel && \
|
pacman --noconfirm -Sy python-build python-installer python-wheel && \
|
||||||
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-aiohttp-cors python-boto3 python-cryptography python-jinja python-requests-unixsocket rsync subversion && \
|
pacman --noconfirm -Sy breezy mercurial python-aiohttp python-aiohttp-cors python-boto3 python-cryptography python-jinja python-requests-unixsocket rsync subversion && \
|
||||||
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-apispec-git python-aiohttp-jinja2 \
|
runuser -u build -- install-aur-package python-aioauth-client python-aiohttp-apispec-git python-aiohttp-jinja2 \
|
||||||
|
@ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager"
|
|||||||
arch=('any')
|
arch=('any')
|
||||||
url="https://github.com/arcan1s/ahriman"
|
url="https://github.com/arcan1s/ahriman"
|
||||||
license=('GPL3')
|
license=('GPL3')
|
||||||
depends=('devtools' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-setuptools' 'python-srcinfo')
|
depends=('devtools' 'git' 'pyalpm' 'python-cerberus' 'python-inflection' 'python-passlib' 'python-requests' 'python-srcinfo')
|
||||||
makedepends=('python-build' 'python-installer' 'python-wheel')
|
makedepends=('python-build' 'python-installer' 'python-wheel')
|
||||||
optdepends=('breezy: -bzr packages support'
|
optdepends=('breezy: -bzr packages support'
|
||||||
'darcs: -darcs packages support'
|
'darcs: -darcs packages support'
|
||||||
|
1
setup.py
1
setup.py
@ -34,7 +34,6 @@ setup(
|
|||||||
"inflection",
|
"inflection",
|
||||||
"passlib",
|
"passlib",
|
||||||
"requests",
|
"requests",
|
||||||
"setuptools",
|
|
||||||
"srcinfo",
|
"srcinfo",
|
||||||
],
|
],
|
||||||
setup_requires=[
|
setup_requires=[
|
||||||
|
@ -17,3 +17,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
|
# workaround for python3.10 while it wasn't yet released in arch
|
||||||
|
# also remove mypy --python-version flag later
|
||||||
|
import typing
|
||||||
|
import typing_extensions
|
||||||
|
|
||||||
|
|
||||||
|
for extension in dir(typing_extensions):
|
||||||
|
if extension not in dir(typing):
|
||||||
|
setattr(typing, extension, getattr(typing_extensions, extension))
|
||||||
|
@ -21,6 +21,7 @@ from ahriman.core.configuration import Configuration
|
|||||||
from ahriman.core.database import SQLite
|
from ahriman.core.database import SQLite
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
from ahriman.core.repository import Repository
|
from ahriman.core.repository import Repository
|
||||||
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
|
|
||||||
|
|
||||||
class ApplicationProperties(LazyLogging):
|
class ApplicationProperties(LazyLogging):
|
||||||
@ -34,8 +35,8 @@ class ApplicationProperties(LazyLogging):
|
|||||||
repository(Repository): repository instance
|
repository(Repository): repository instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration, *,
|
def __init__(self, architecture: str, configuration: Configuration, *, report: bool, unsafe: bool,
|
||||||
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None:
|
refresh_pacman_database: PacmanSynchronization = PacmanSynchronization.Disabled) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
@ -44,8 +45,8 @@ class ApplicationProperties(LazyLogging):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
|
refresh_pacman_database(PacmanSynchronization, optional): pacman database synchronization level
|
||||||
(Default value = 0)
|
(Default value = PacmanSynchronization.Disabled)
|
||||||
"""
|
"""
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
|
@ -30,7 +30,7 @@ class Add(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -36,7 +36,7 @@ class Backup(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -30,7 +30,7 @@ class Clean(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -30,7 +30,7 @@ class Daemon(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -32,7 +32,7 @@ class Dump(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
@ -52,7 +50,7 @@ class Handler:
|
|||||||
ALLOW_MULTI_ARCHITECTURE_RUN = True
|
ALLOW_MULTI_ARCHITECTURE_RUN = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def architectures_extract(cls: type[Handler], args: argparse.Namespace) -> list[str]:
|
def architectures_extract(cls, args: argparse.Namespace) -> list[str]:
|
||||||
"""
|
"""
|
||||||
get known architectures
|
get known architectures
|
||||||
|
|
||||||
@ -83,7 +81,7 @@ class Handler:
|
|||||||
return sorted(architectures)
|
return sorted(architectures)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def call(cls: type[Handler], args: argparse.Namespace, architecture: str) -> bool:
|
def call(cls, args: argparse.Namespace, architecture: str) -> bool:
|
||||||
"""
|
"""
|
||||||
additional function to wrap all calls for multiprocessing library
|
additional function to wrap all calls for multiprocessing library
|
||||||
|
|
||||||
@ -108,7 +106,7 @@ class Handler:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def execute(cls: type[Handler], args: argparse.Namespace) -> int:
|
def execute(cls, args: argparse.Namespace) -> int:
|
||||||
"""
|
"""
|
||||||
execute function for all aru
|
execute function for all aru
|
||||||
|
|
||||||
@ -137,7 +135,7 @@ class Handler:
|
|||||||
return 0 if all(result) else 1
|
return 0 if all(result) else 1
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -31,7 +31,7 @@ class Help(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -32,7 +32,7 @@ class KeyImport(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -38,7 +38,7 @@ class Patch(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -32,7 +32,7 @@ class Rebuild(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -30,7 +30,7 @@ class Remove(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -31,7 +31,7 @@ class RemoveUnknown(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -33,7 +33,7 @@ class Restore(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -40,10 +40,14 @@ class Search(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
SORT_FIELDS = {field.name for field in fields(AURPackage) if field.default_factory is not list} # type: ignore
|
SORT_FIELDS = {
|
||||||
|
field.name
|
||||||
|
for field in fields(AURPackage)
|
||||||
|
if field.default_factory is not list # type: ignore[comparison-overlap]
|
||||||
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -35,7 +35,7 @@ class ServiceUpdates(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -45,7 +45,7 @@ class Setup(Handler):
|
|||||||
SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
|
SUDOERS_DIR_PATH = Path("/etc/sudoers.d")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
@ -153,7 +153,7 @@ class Setup(Handler):
|
|||||||
configuration = Configuration(allow_no_value=True)
|
configuration = Configuration(allow_no_value=True)
|
||||||
# preserve case
|
# preserve case
|
||||||
# stupid mypy thinks that it is impossible
|
# stupid mypy thinks that it is impossible
|
||||||
configuration.optionxform = lambda key: key # type: ignore
|
configuration.optionxform = lambda key: key # type: ignore[method-assign]
|
||||||
|
|
||||||
# load default configuration first
|
# load default configuration first
|
||||||
# we cannot use Include here because it will be copied to new chroot, thus no includes there
|
# we cannot use Include here because it will be copied to new chroot, thus no includes there
|
||||||
|
@ -37,7 +37,7 @@ class Shell(Handler):
|
|||||||
ALLOW_MULTI_ARCHITECTURE_RUN = False
|
ALLOW_MULTI_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -30,7 +30,7 @@ class Sign(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -37,7 +37,7 @@ class Status(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -33,7 +33,7 @@ class StatusUpdate(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -34,7 +34,7 @@ class Structure(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -31,7 +31,7 @@ class Triggers(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -33,7 +33,7 @@ class UnsafeCommands(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -32,7 +32,7 @@ class Update(Handler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -39,7 +39,7 @@ class Users(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -39,7 +39,7 @@ class Validate(Handler):
|
|||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
ALLOW_AUTO_ARCHITECTURE_RUN = False
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -18,9 +18,12 @@
|
|||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import argparse
|
import argparse
|
||||||
import pkg_resources
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from collections.abc import Generator
|
||||||
|
from importlib import metadata
|
||||||
|
|
||||||
from ahriman import version
|
from ahriman import version
|
||||||
from ahriman.application.handlers import Handler
|
from ahriman.application.handlers import Handler
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -30,12 +33,16 @@ from ahriman.core.formatters import VersionPrinter
|
|||||||
class Versions(Handler):
|
class Versions(Handler):
|
||||||
"""
|
"""
|
||||||
version handler
|
version handler
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
PEP423_PACKAGE_NAME(str): (class attribute) special regex for valid PEP423 package name
|
||||||
"""
|
"""
|
||||||
|
|
||||||
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
ALLOW_AUTO_ARCHITECTURE_RUN = False # it should be called only as "no-architecture"
|
||||||
|
PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
@ -49,37 +56,43 @@ class Versions(Handler):
|
|||||||
"""
|
"""
|
||||||
VersionPrinter(f"Module version {version.__version__}",
|
VersionPrinter(f"Module version {version.__version__}",
|
||||||
{"Python": sys.version}).print(verbose=False, separator=" ")
|
{"Python": sys.version}).print(verbose=False, separator=" ")
|
||||||
packages = Versions.package_dependencies("ahriman", ("pacman", "s3", "web"))
|
packages = Versions.package_dependencies("ahriman")
|
||||||
VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ")
|
VersionPrinter("Installed packages", dict(packages)).print(verbose=False, separator=" ")
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def package_dependencies(root: str, root_extras: tuple[str, ...] = ()) -> dict[str, str]:
|
def package_dependencies(root: str) -> Generator[tuple[str, str], None, None]:
|
||||||
"""
|
"""
|
||||||
extract list of ahriman package dependencies installed into system with their versions
|
extract list of ahriman package dependencies installed into system with their versions
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
root(str): root package name
|
root(str): root package name
|
||||||
root_extras(tuple[str, ...], optional): extras for the root package (Default value = ())
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
dict[str, str]: map of installed dependency to its version
|
Generator[tuple[str, str], None, None]: map of installed dependency to its version
|
||||||
"""
|
"""
|
||||||
resources: dict[str, pkg_resources.Distribution] = pkg_resources.working_set.by_key # type: ignore
|
def dependencies_by_key(key: str) -> Generator[str, None, None]:
|
||||||
|
# in importlib it returns requires in the following format
|
||||||
def dependencies_by_key(key: str, extras: tuple[str, ...] = ()) -> list[str]:
|
# ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"]
|
||||||
return [entry.key for entry in resources[key].requires(extras)]
|
try:
|
||||||
|
requires = metadata.requires(key)
|
||||||
|
except metadata.PackageNotFoundError:
|
||||||
|
return
|
||||||
|
for entry in requires or []:
|
||||||
|
yield from Versions.PEP423_PACKAGE_NAME.findall(entry)
|
||||||
|
|
||||||
keys: list[str] = []
|
keys: list[str] = []
|
||||||
portion = {key for key in dependencies_by_key(root, root_extras) if key in resources}
|
portion = set(dependencies_by_key(root))
|
||||||
while portion:
|
while portion:
|
||||||
keys.extend(portion)
|
keys.extend(portion)
|
||||||
portion = {
|
portion = {
|
||||||
key
|
key
|
||||||
for key in sum((dependencies_by_key(key) for key in portion), start=[])
|
for key in sum((list(dependencies_by_key(key)) for key in portion), start=[])
|
||||||
if key not in keys and key in resources
|
if key not in keys
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
for key in keys:
|
||||||
resource.project_name: resource.version
|
try:
|
||||||
for resource in map(lambda key: resources[key], keys)
|
distribution = metadata.distribution(key)
|
||||||
}
|
yield distribution.name, distribution.version
|
||||||
|
except metadata.PackageNotFoundError:
|
||||||
|
continue
|
||||||
|
@ -33,7 +33,7 @@ class Web(Handler):
|
|||||||
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
|
ALLOW_MULTI_ARCHITECTURE_RUN = False # required to be able to spawn external processes
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def run(cls: type[Handler], args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
def run(cls, args: argparse.Namespace, architecture: str, configuration: Configuration, *,
|
||||||
report: bool, unsafe: bool) -> None:
|
report: bool, unsafe: bool) -> None:
|
||||||
"""
|
"""
|
||||||
callback for command line
|
callback for command line
|
||||||
|
@ -17,12 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
from types import TracebackType
|
from types import TracebackType
|
||||||
from typing import Literal
|
from typing import Literal, Self
|
||||||
|
|
||||||
from ahriman import version
|
from ahriman import version
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
@ -111,7 +109,7 @@ class Lock(LazyLogging):
|
|||||||
except FileExistsError:
|
except FileExistsError:
|
||||||
raise DuplicateRunError()
|
raise DuplicateRunError()
|
||||||
|
|
||||||
def __enter__(self) -> Lock:
|
def __enter__(self) -> Self:
|
||||||
"""
|
"""
|
||||||
default workflow is the following:
|
default workflow is the following:
|
||||||
|
|
||||||
@ -120,6 +118,9 @@ class Lock(LazyLogging):
|
|||||||
3. Check web status watcher status
|
3. Check web status watcher status
|
||||||
4. Create lock file
|
4. Create lock file
|
||||||
5. Report to status page if enabled
|
5. Report to status page if enabled
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Self: always instance of self
|
||||||
"""
|
"""
|
||||||
self.check_user()
|
self.check_user()
|
||||||
self.check_version()
|
self.check_version()
|
||||||
|
@ -21,12 +21,13 @@ import shutil
|
|||||||
|
|
||||||
from collections.abc import Callable, Generator
|
from collections.abc import Callable, Generator
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore
|
from pyalpm import DB, Handle, Package, SIG_PACKAGE, error as PyalpmError # type: ignore[import]
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
from ahriman.core.util import trim_package
|
from ahriman.core.util import trim_package
|
||||||
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
@ -40,28 +41,28 @@ class Pacman(LazyLogging):
|
|||||||
|
|
||||||
handle: Handle
|
handle: Handle
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> None:
|
def __init__(self, architecture: str, configuration: Configuration, *,
|
||||||
|
refresh_database: PacmanSynchronization) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
architecture(str): repository architecture
|
architecture(str): repository architecture
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be
|
refresh_database(PacmanSynchronization): synchronize local cache to remote
|
||||||
enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization
|
|
||||||
"""
|
"""
|
||||||
self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle(
|
self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle(
|
||||||
architecture, configuration, refresh_database=refresh_database)
|
architecture, configuration, refresh_database=refresh_database)
|
||||||
|
|
||||||
def __create_handle(self, architecture: str, configuration: Configuration, *, refresh_database: int) -> Handle:
|
def __create_handle(self, architecture: str, configuration: Configuration, *,
|
||||||
|
refresh_database: PacmanSynchronization) -> Handle:
|
||||||
"""
|
"""
|
||||||
create lazy handle function
|
create lazy handle function
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
architecture(str): repository architecture
|
architecture(str): repository architecture
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be
|
refresh_database(PacmanSynchronization): synchronize local cache to remote
|
||||||
enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Handle: fully initialized pacman handle
|
Handle: fully initialized pacman handle
|
||||||
@ -79,7 +80,7 @@ class Pacman(LazyLogging):
|
|||||||
self.database_copy(handle, database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
|
self.database_copy(handle, database, pacman_root, paths, use_ahriman_cache=use_ahriman_cache)
|
||||||
|
|
||||||
if use_ahriman_cache and refresh_database:
|
if use_ahriman_cache and refresh_database:
|
||||||
self.database_sync(handle, force=refresh_database > 1)
|
self.database_sync(handle, force=refresh_database == PacmanSynchronization.Force)
|
||||||
|
|
||||||
return handle
|
return handle
|
||||||
|
|
||||||
|
@ -44,6 +44,33 @@ class AUR(Remote):
|
|||||||
DEFAULT_RPC_VERSION = "5"
|
DEFAULT_RPC_VERSION = "5"
|
||||||
DEFAULT_TIMEOUT = 30
|
DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remote_git_url(cls, package_base: str, repository: str) -> str:
|
||||||
|
"""
|
||||||
|
generate remote git url from the package base
|
||||||
|
|
||||||
|
Args
|
||||||
|
package_base(str): package base
|
||||||
|
repository(str): repository name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: git url for the specific base
|
||||||
|
"""
|
||||||
|
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remote_web_url(cls, package_base: str) -> str:
|
||||||
|
"""
|
||||||
|
generate remote web url from the package base
|
||||||
|
|
||||||
|
Args
|
||||||
|
package_base(str): package base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: web url for the specific base
|
||||||
|
"""
|
||||||
|
return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
|
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
@ -64,33 +91,6 @@ class AUR(Remote):
|
|||||||
raise PackageInfoError(error_details)
|
raise PackageInfoError(error_details)
|
||||||
return [AURPackage.from_json(package) for package in response["results"]]
|
return [AURPackage.from_json(package) for package in response["results"]]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
|
|
||||||
"""
|
|
||||||
generate remote git url from the package base
|
|
||||||
|
|
||||||
Args
|
|
||||||
package_base(str): package base
|
|
||||||
repository(str): repository name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: git url for the specific base
|
|
||||||
"""
|
|
||||||
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remote_web_url(cls: type[Remote], package_base: str) -> str:
|
|
||||||
"""
|
|
||||||
generate remote web url from the package base
|
|
||||||
|
|
||||||
Args
|
|
||||||
package_base(str): package base
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: web url for the specific base
|
|
||||||
"""
|
|
||||||
return f"{AUR.DEFAULT_AUR_URL}/packages/{package_base}"
|
|
||||||
|
|
||||||
def make_request(self, request_type: str, *args: str, **kwargs: str) -> list[AURPackage]:
|
def make_request(self, request_type: str, *args: str, **kwargs: str) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
perform request to AUR RPC
|
perform request to AUR RPC
|
||||||
|
@ -44,6 +44,35 @@ class Official(Remote):
|
|||||||
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json"
|
||||||
DEFAULT_TIMEOUT = 30
|
DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remote_git_url(cls, package_base: str, repository: str) -> str:
|
||||||
|
"""
|
||||||
|
generate remote git url from the package base
|
||||||
|
|
||||||
|
Args
|
||||||
|
package_base(str): package base
|
||||||
|
repository(str): repository name
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: git url for the specific base
|
||||||
|
"""
|
||||||
|
if repository.lower() in ("core", "extra", "testing", "kde-unstable"):
|
||||||
|
return "https://github.com/archlinux/svntogit-packages.git" # hardcoded, ok
|
||||||
|
return "https://github.com/archlinux/svntogit-community.git"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def remote_web_url(cls, package_base: str) -> str:
|
||||||
|
"""
|
||||||
|
generate remote web url from the package base
|
||||||
|
|
||||||
|
Args
|
||||||
|
package_base(str): package base
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: web url for the specific base
|
||||||
|
"""
|
||||||
|
return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}"
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
|
def parse_response(response: dict[str, Any]) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
@ -62,35 +91,6 @@ class Official(Remote):
|
|||||||
raise PackageInfoError("API validation error")
|
raise PackageInfoError("API validation error")
|
||||||
return [AURPackage.from_repo(package) for package in response["results"]]
|
return [AURPackage.from_repo(package) for package in response["results"]]
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
|
|
||||||
"""
|
|
||||||
generate remote git url from the package base
|
|
||||||
|
|
||||||
Args
|
|
||||||
package_base(str): package base
|
|
||||||
repository(str): repository name
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: git url for the specific base
|
|
||||||
"""
|
|
||||||
if repository.lower() in ("core", "extra", "testing", "kde-unstable"):
|
|
||||||
return "https://github.com/archlinux/svntogit-packages.git" # hardcoded, ok
|
|
||||||
return "https://github.com/archlinux/svntogit-community.git"
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def remote_web_url(cls: type[Remote], package_base: str) -> str:
|
|
||||||
"""
|
|
||||||
generate remote web url from the package base
|
|
||||||
|
|
||||||
Args
|
|
||||||
package_base(str): package base
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: web url for the specific base
|
|
||||||
"""
|
|
||||||
return f"{Official.DEFAULT_ARCHLINUX_URL}/packages/{package_base}"
|
|
||||||
|
|
||||||
def make_request(self, *args: str, by: str) -> list[AURPackage]:
|
def make_request(self, *args: str, by: str) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
perform request to official repositories RPC
|
perform request to official repositories RPC
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
@ -42,7 +40,7 @@ class Remote(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def info(cls: type[Remote], package_name: str, *, pacman: Pacman) -> AURPackage:
|
def info(cls, package_name: str, *, pacman: Pacman) -> AURPackage:
|
||||||
"""
|
"""
|
||||||
get package info by its name
|
get package info by its name
|
||||||
|
|
||||||
@ -56,7 +54,7 @@ class Remote(LazyLogging):
|
|||||||
return cls().package_info(package_name, pacman=pacman)
|
return cls().package_info(package_name, pacman=pacman)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def multisearch(cls: type[Remote], *keywords: str, pacman: Pacman) -> list[AURPackage]:
|
def multisearch(cls, *keywords: str, pacman: Pacman) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search in remote repository by using API with multiple words. This method is required in order to handle
|
search in remote repository by using API with multiple words. This method is required in order to handle
|
||||||
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
https://bugs.archlinux.org/task/49133. In addition, short words will be dropped
|
||||||
@ -80,7 +78,7 @@ class Remote(LazyLogging):
|
|||||||
return list(packages.values())
|
return list(packages.values())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def remote_git_url(cls: type[Remote], package_base: str, repository: str) -> str:
|
def remote_git_url(cls, package_base: str, repository: str) -> str:
|
||||||
"""
|
"""
|
||||||
generate remote git url from the package base
|
generate remote git url from the package base
|
||||||
|
|
||||||
@ -97,7 +95,7 @@ class Remote(LazyLogging):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def remote_web_url(cls: type[Remote], package_base: str) -> str:
|
def remote_web_url(cls, package_base: str) -> str:
|
||||||
"""
|
"""
|
||||||
generate remote web url from the package base
|
generate remote web url from the package base
|
||||||
|
|
||||||
@ -113,7 +111,7 @@ class Remote(LazyLogging):
|
|||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def search(cls: type[Remote], *keywords: str, pacman: Pacman) -> list[AURPackage]:
|
def search(cls, *keywords: str, pacman: Pacman) -> list[AURPackage]:
|
||||||
"""
|
"""
|
||||||
search package in AUR web
|
search package in AUR web
|
||||||
|
|
||||||
|
@ -62,8 +62,8 @@ class Auth(LazyLogging):
|
|||||||
"""
|
"""
|
||||||
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
|
return """<button type="button" class="btn btn-link" data-bs-toggle="modal" data-bs-target="#login-modal" style="text-decoration: none"><i class="bi bi-box-arrow-in-right"></i> login</button>"""
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def load(cls: type[Auth], configuration: Configuration, database: SQLite) -> Auth:
|
def load(configuration: Configuration, database: SQLite) -> Auth:
|
||||||
"""
|
"""
|
||||||
load authorization module from settings
|
load authorization module from settings
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ class Auth(LazyLogging):
|
|||||||
if provider == AuthSettings.OAuth:
|
if provider == AuthSettings.OAuth:
|
||||||
from ahriman.core.auth.oauth import OAuth
|
from ahriman.core.auth.oauth import OAuth
|
||||||
return OAuth(configuration, database)
|
return OAuth(configuration, database)
|
||||||
return cls(configuration)
|
return Auth(configuration)
|
||||||
|
|
||||||
async def check_credentials(self, username: str | None, password: str | None) -> bool:
|
async def check_credentials(self, username: str | None, password: str | None) -> bool:
|
||||||
"""
|
"""
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import aiohttp_security # type: ignore
|
import aiohttp_security # type: ignore[import]
|
||||||
_has_aiohttp_security = True
|
_has_aiohttp_security = True
|
||||||
except ImportError:
|
except ImportError:
|
||||||
_has_aiohttp_security = False
|
_has_aiohttp_security = False
|
||||||
|
@ -128,7 +128,7 @@ class OAuth(Mapping):
|
|||||||
client.access_token = access_token
|
client.access_token = access_token
|
||||||
|
|
||||||
user, _ = await client.user_info()
|
user, _ = await client.user_info()
|
||||||
username: str = user.email # type: ignore
|
username: str = user.email # type: ignore[attr-defined]
|
||||||
return username
|
return username
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.exception("got exception while performing request")
|
self.logger.exception("got exception while performing request")
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import configparser
|
import configparser
|
||||||
import shlex
|
import shlex
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.exceptions import InitializeError
|
from ahriman.core.exceptions import InitializeError
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
@ -113,7 +111,7 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
return RepositoryPaths(self.getpath("repository", "root"), architecture)
|
return RepositoryPaths(self.getpath("repository", "root"), architecture)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_path(cls: type[Configuration], path: Path, architecture: str) -> Configuration:
|
def from_path(cls, path: Path, architecture: str) -> Self:
|
||||||
"""
|
"""
|
||||||
constructor with full object initialization
|
constructor with full object initialization
|
||||||
|
|
||||||
@ -122,7 +120,7 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
architecture(str): repository architecture
|
architecture(str): repository architecture
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Configuration: configuration instance
|
Self: configuration instance
|
||||||
"""
|
"""
|
||||||
configuration = cls()
|
configuration = cls()
|
||||||
configuration.load(path)
|
configuration.load(path)
|
||||||
@ -186,9 +184,9 @@ class Configuration(configparser.RawConfigParser):
|
|||||||
|
|
||||||
# pylint and mypy are too stupid to find these methods
|
# pylint and mypy are too stupid to find these methods
|
||||||
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
|
# pylint: disable=missing-function-docstring,multiple-statements,unused-argument
|
||||||
def getlist(self, *args: Any, **kwargs: Any) -> list[str]: ... # type: ignore
|
def getlist(self, *args: Any, **kwargs: Any) -> list[str]: ... # type: ignore[empty-body]
|
||||||
|
|
||||||
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore
|
def getpath(self, *args: Any, **kwargs: Any) -> Path: ... # type: ignore[empty-body]
|
||||||
|
|
||||||
def gettype(self, section: str, architecture: str, *, fallback: str | None = None) -> tuple[str, str]:
|
def gettype(self, section: str, architecture: str, *, fallback: str | None = None) -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
#
|
#
|
||||||
import ipaddress
|
import ipaddress
|
||||||
|
|
||||||
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore
|
from cerberus import TypeDefinition, Validator as RootValidator # type: ignore[import]
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
@ -74,7 +74,7 @@ class Validator(RootValidator):
|
|||||||
bool: value converted to boolean according to configuration rules
|
bool: value converted to boolean according to configuration rules
|
||||||
"""
|
"""
|
||||||
# pylint: disable=protected-access
|
# pylint: disable=protected-access
|
||||||
converted: bool = self.configuration._convert_to_boolean(value) # type: ignore
|
converted: bool = self.configuration._convert_to_boolean(value) # type: ignore[attr-defined]
|
||||||
return converted
|
return converted
|
||||||
|
|
||||||
def _normalize_coerce_integer(self, value: str) -> int:
|
def _normalize_coerce_integer(self, value: str) -> int:
|
||||||
|
@ -23,6 +23,7 @@ from ahriman.core.alpm.pacman import Pacman
|
|||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.util import package_like
|
from ahriman.core.util import package_like
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["migrate_data", "steps"]
|
__all__ = ["migrate_data", "steps"]
|
||||||
@ -61,7 +62,7 @@ def migrate_package_depends(connection: Connection, configuration: Configuration
|
|||||||
return
|
return
|
||||||
|
|
||||||
_, architecture = configuration.check_loaded()
|
_, architecture = configuration.check_loaded()
|
||||||
pacman = Pacman(architecture, configuration, refresh_database=False)
|
pacman = Pacman(architecture, configuration, refresh_database=PacmanSynchronization.Disabled)
|
||||||
|
|
||||||
package_list = []
|
package_list = []
|
||||||
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
|
for full_path in filter(package_like, configuration.repository_paths.repository.iterdir()):
|
||||||
|
@ -17,12 +17,11 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.database.migrations import Migrations
|
from ahriman.core.database.migrations import Migrations
|
||||||
@ -46,7 +45,7 @@ class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations,
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: type[SQLite], configuration: Configuration) -> SQLite:
|
def load(cls, configuration: Configuration) -> Self:
|
||||||
"""
|
"""
|
||||||
construct instance from configuration
|
construct instance from configuration
|
||||||
|
|
||||||
@ -54,7 +53,7 @@ class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations,
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
SQLite: fully initialized instance of the database
|
Self: fully initialized instance of the database
|
||||||
"""
|
"""
|
||||||
path = cls.database_path(configuration)
|
path = cls.database_path(configuration)
|
||||||
database = cls(path)
|
database = cls(path)
|
||||||
|
@ -68,7 +68,7 @@ class RemotePullTrigger(Trigger):
|
|||||||
self.targets = self.configuration_sections(configuration)
|
self.targets = self.configuration_sections(configuration)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extract configuration sections from configuration
|
extract configuration sections from configuration
|
||||||
|
|
||||||
|
@ -76,7 +76,7 @@ class RemotePushTrigger(Trigger):
|
|||||||
self.targets = self.configuration_sections(configuration)
|
self.targets = self.configuration_sections(configuration)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extract configuration sections from configuration
|
extract configuration sections from configuration
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
|
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ class HttpLogHandler(logging.Handler):
|
|||||||
self.suppress_errors = suppress_errors
|
self.suppress_errors = suppress_errors
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls, configuration: Configuration, *, report: bool) -> HttpLogHandler:
|
def load(cls, configuration: Configuration, *, report: bool) -> Self:
|
||||||
"""
|
"""
|
||||||
install logger. This function creates handler instance and adds it to the handler list in case if no other
|
install logger. This function creates handler instance and adds it to the handler list in case if no other
|
||||||
http handler found
|
http handler found
|
||||||
@ -60,6 +60,9 @@ class HttpLogHandler(logging.Handler):
|
|||||||
Args:
|
Args:
|
||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Self: logger instance with loaded settings
|
||||||
"""
|
"""
|
||||||
root = logging.getLogger()
|
root = logging.getLogger()
|
||||||
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
if (handler := next((handler for handler in root.handlers if isinstance(handler, cls)), None)) is not None:
|
||||||
|
@ -66,8 +66,8 @@ class Report(LazyLogging):
|
|||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def load(cls: type[Report], architecture: str, configuration: Configuration, target: str) -> Report:
|
def load(architecture: str, configuration: Configuration, target: str) -> Report:
|
||||||
"""
|
"""
|
||||||
load client from settings
|
load client from settings
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ class Report(LazyLogging):
|
|||||||
if provider == ReportSettings.Telegram:
|
if provider == ReportSettings.Telegram:
|
||||||
from ahriman.core.report.telegram import Telegram
|
from ahriman.core.report.telegram import Telegram
|
||||||
return Telegram(architecture, configuration, section)
|
return Telegram(architecture, configuration, section)
|
||||||
return cls(architecture, configuration) # should never happen
|
return Report(architecture, configuration) # should never happen
|
||||||
|
|
||||||
def generate(self, packages: list[Package], result: Result) -> None:
|
def generate(self, packages: list[Package], result: Result) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -205,7 +205,7 @@ class ReportTrigger(Trigger):
|
|||||||
self.targets = self.configuration_sections(configuration)
|
self.targets = self.configuration_sections(configuration)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extract configuration sections from configuration
|
extract configuration sections from configuration
|
||||||
|
|
||||||
|
@ -17,10 +17,9 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.core import _Context, context
|
from ahriman.core import _Context, context
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
@ -32,6 +31,7 @@ from ahriman.core.sign.gpg import GPG
|
|||||||
from ahriman.core.util import package_like
|
from ahriman.core.util import package_like
|
||||||
from ahriman.models.context_key import ContextKey
|
from ahriman.models.context_key import ContextKey
|
||||||
from ahriman.models.package import Package
|
from ahriman.models.package import Package
|
||||||
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
|
|
||||||
|
|
||||||
class Repository(Executor, UpdateHandler):
|
class Repository(Executor, UpdateHandler):
|
||||||
@ -58,8 +58,8 @@ class Repository(Executor, UpdateHandler):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: type[Repository], architecture: str, configuration: Configuration, database: SQLite, *,
|
def load(cls, architecture: str, configuration: Configuration, database: SQLite, *, report: bool, unsafe: bool,
|
||||||
report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> Repository:
|
refresh_pacman_database: PacmanSynchronization = PacmanSynchronization.Disabled) -> Self:
|
||||||
"""
|
"""
|
||||||
load instance from argument list
|
load instance from argument list
|
||||||
|
|
||||||
@ -69,8 +69,11 @@ class Repository(Executor, UpdateHandler):
|
|||||||
database(SQLite): database instance
|
database(SQLite): database instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
|
refresh_pacman_database(PacmanSynchronization, optional): pacman database synchronization level
|
||||||
(Default value = 0)
|
(Default value = PacmanSynchronization.Disabled)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Self: fully loaded repository class instance
|
||||||
"""
|
"""
|
||||||
instance = cls(architecture, configuration, database,
|
instance = cls(architecture, configuration, database,
|
||||||
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
|
report=report, unsafe=unsafe, refresh_pacman_database=refresh_pacman_database)
|
||||||
|
@ -27,6 +27,7 @@ from ahriman.core.sign.gpg import GPG
|
|||||||
from ahriman.core.status.client import Client
|
from ahriman.core.status.client import Client
|
||||||
from ahriman.core.triggers import TriggerLoader
|
from ahriman.core.triggers import TriggerLoader
|
||||||
from ahriman.core.util import check_user
|
from ahriman.core.util import check_user
|
||||||
|
from ahriman.models.pacman_synchronization import PacmanSynchronization
|
||||||
from ahriman.models.repository_paths import RepositoryPaths
|
from ahriman.models.repository_paths import RepositoryPaths
|
||||||
|
|
||||||
|
|
||||||
@ -49,8 +50,8 @@ class RepositoryProperties(LazyLogging):
|
|||||||
vcs_allowed_age(int): maximal age of the VCS packages before they will be checked
|
vcs_allowed_age(int): maximal age of the VCS packages before they will be checked
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *,
|
def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *, report: bool, unsafe: bool,
|
||||||
report: bool, unsafe: bool, refresh_pacman_database: int) -> None:
|
refresh_pacman_database: PacmanSynchronization) -> None:
|
||||||
"""
|
"""
|
||||||
default constructor
|
default constructor
|
||||||
|
|
||||||
@ -60,7 +61,7 @@ class RepositoryProperties(LazyLogging):
|
|||||||
database(SQLite): database instance
|
database(SQLite): database instance
|
||||||
report(bool): force enable or disable reporting
|
report(bool): force enable or disable reporting
|
||||||
unsafe(bool): if set no user check will be performed before path creation
|
unsafe(bool): if set no user check will be performed before path creation
|
||||||
refresh_pacman_database(int, optional): pacman database syncronization level, ``0`` is disabled
|
refresh_pacman_database(PacmanSynchronization): pacman database synchronization level
|
||||||
"""
|
"""
|
||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
|
@ -32,8 +32,8 @@ class Client:
|
|||||||
base build status reporter client
|
base build status reporter client
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def load(cls: type[Client], configuration: Configuration, *, report: bool) -> Client:
|
def load(configuration: Configuration, *, report: bool) -> Client:
|
||||||
"""
|
"""
|
||||||
load client from settings
|
load client from settings
|
||||||
|
|
||||||
@ -45,7 +45,7 @@ class Client:
|
|||||||
Client: client according to current settings
|
Client: client according to current settings
|
||||||
"""
|
"""
|
||||||
if not report:
|
if not report:
|
||||||
return cls()
|
return Client()
|
||||||
|
|
||||||
address = configuration.get("web", "address", fallback=None)
|
address = configuration.get("web", "address", fallback=None)
|
||||||
host = configuration.get("web", "host", fallback=None)
|
host = configuration.get("web", "host", fallback=None)
|
||||||
@ -58,7 +58,7 @@ class Client:
|
|||||||
if address or (host and port) or socket:
|
if address or (host and port) or socket:
|
||||||
from ahriman.core.status.web_client import WebClient
|
from ahriman.core.status.web_client import WebClient
|
||||||
return WebClient(configuration)
|
return WebClient(configuration)
|
||||||
return cls()
|
return Client()
|
||||||
|
|
||||||
def add(self, package: Package, status: BuildStatusEnum) -> None:
|
def add(self, package: Package, status: BuildStatusEnum) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -131,7 +131,7 @@ class WebClient(Client, LazyLogging):
|
|||||||
requests.Session: generated session object
|
requests.Session: generated session object
|
||||||
"""
|
"""
|
||||||
if use_unix_socket:
|
if use_unix_socket:
|
||||||
import requests_unixsocket # type: ignore
|
import requests_unixsocket # type: ignore[import]
|
||||||
session: requests.Session = requests_unixsocket.Session()
|
session: requests.Session = requests_unixsocket.Session()
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.configuration.schema import ConfigurationSchema
|
from ahriman.core.configuration.schema import ConfigurationSchema
|
||||||
from ahriman.core.log import LazyLogging
|
from ahriman.core.log import LazyLogging
|
||||||
@ -70,8 +68,7 @@ class Trigger(LazyLogging):
|
|||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_schema(cls: type[Trigger], architecture: str,
|
def configuration_schema(cls, architecture: str, configuration: Configuration | None) -> ConfigurationSchema:
|
||||||
configuration: Configuration | None) -> ConfigurationSchema:
|
|
||||||
"""
|
"""
|
||||||
configuration schema based on supplied service configuration
|
configuration schema based on supplied service configuration
|
||||||
|
|
||||||
@ -102,7 +99,7 @@ class Trigger(LazyLogging):
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extract configuration sections from configuration
|
extract configuration sections from configuration
|
||||||
|
|
||||||
@ -116,8 +113,8 @@ class Trigger(LazyLogging):
|
|||||||
This method can be used in order to extract specific configuration sections which are set by user, e.g.
|
This method can be used in order to extract specific configuration sections which are set by user, e.g.
|
||||||
from sources::
|
from sources::
|
||||||
|
|
||||||
>>> @staticmethod
|
>>> @classmethod
|
||||||
>>> def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
>>> def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
>>> return configuration.getlist("report", "target", fallback=[])
|
>>> return configuration.getlist("report", "target", fallback=[])
|
||||||
"""
|
"""
|
||||||
del configuration
|
del configuration
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@ -26,6 +24,7 @@ from collections.abc import Generator
|
|||||||
from importlib import import_module, machinery
|
from importlib import import_module, machinery
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from types import ModuleType
|
from types import ModuleType
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.core.configuration import Configuration
|
from ahriman.core.configuration import Configuration
|
||||||
from ahriman.core.exceptions import ExtensionError
|
from ahriman.core.exceptions import ExtensionError
|
||||||
@ -66,7 +65,7 @@ class TriggerLoader(LazyLogging):
|
|||||||
self.triggers: list[Trigger] = []
|
self.triggers: list[Trigger] = []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def load(cls: type[TriggerLoader], architecture: str, configuration: Configuration) -> TriggerLoader:
|
def load(cls, architecture: str, configuration: Configuration) -> Self:
|
||||||
"""
|
"""
|
||||||
create instance from configuration
|
create instance from configuration
|
||||||
|
|
||||||
@ -75,7 +74,7 @@ class TriggerLoader(LazyLogging):
|
|||||||
configuration(Configuration): configuration instance
|
configuration(Configuration): configuration instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TriggerLoader: fully loaded trigger instance
|
Self: fully loaded trigger instance
|
||||||
"""
|
"""
|
||||||
instance = cls()
|
instance = cls()
|
||||||
instance.triggers = [
|
instance.triggers = [
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import boto3 # type: ignore
|
import boto3 # type: ignore[import]
|
||||||
import hashlib
|
import hashlib
|
||||||
import mimetypes
|
import mimetypes
|
||||||
|
|
||||||
|
@ -66,8 +66,8 @@ class Upload(LazyLogging):
|
|||||||
self.architecture = architecture
|
self.architecture = architecture
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def load(cls: type[Upload], architecture: str, configuration: Configuration, target: str) -> Upload:
|
def load(architecture: str, configuration: Configuration, target: str) -> Upload:
|
||||||
"""
|
"""
|
||||||
load client from settings
|
load client from settings
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ class Upload(LazyLogging):
|
|||||||
if provider == UploadSettings.Github:
|
if provider == UploadSettings.Github:
|
||||||
from ahriman.core.upload.github import Github
|
from ahriman.core.upload.github import Github
|
||||||
return Github(architecture, configuration, section)
|
return Github(architecture, configuration, section)
|
||||||
return cls(architecture, configuration) # should never happen
|
return Upload(architecture, configuration) # should never happen
|
||||||
|
|
||||||
def run(self, path: Path, built_packages: list[Package]) -> None:
|
def run(self, path: Path, built_packages: list[Package]) -> None:
|
||||||
"""
|
"""
|
||||||
|
@ -136,7 +136,7 @@ class UploadTrigger(Trigger):
|
|||||||
self.targets = self.configuration_sections(configuration)
|
self.targets = self.configuration_sections(configuration)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]:
|
def configuration_sections(cls, configuration: Configuration) -> list[str]:
|
||||||
"""
|
"""
|
||||||
extract configuration sections from configuration
|
extract configuration sections from configuration
|
||||||
|
|
||||||
|
@ -17,15 +17,13 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import inflection
|
import inflection
|
||||||
|
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, field, fields
|
from dataclasses import dataclass, field, fields
|
||||||
from pyalpm import Package # type: ignore
|
from pyalpm import Package # type: ignore[import]
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json, full_version
|
from ahriman.core.util import filter_json, full_version
|
||||||
|
|
||||||
@ -102,7 +100,7 @@ class AURPackage:
|
|||||||
keywords: list[str] = field(default_factory=list)
|
keywords: list[str] = field(default_factory=list)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[AURPackage], dump: dict[str, Any]) -> AURPackage:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package descriptor from RPC properties
|
construct package descriptor from RPC properties
|
||||||
|
|
||||||
@ -110,7 +108,7 @@ class AURPackage:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AURPackage: AUR package descriptor
|
Self: AUR package descriptor
|
||||||
"""
|
"""
|
||||||
# filter to only known fields
|
# filter to only known fields
|
||||||
known_fields = [pair.name for pair in fields(cls)]
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
@ -118,7 +116,7 @@ class AURPackage:
|
|||||||
return cls(**filter_json(properties, known_fields))
|
return cls(**filter_json(properties, known_fields))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_pacman(cls: type[AURPackage], package: Package) -> AURPackage:
|
def from_pacman(cls, package: Package) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package descriptor from official repository wrapper
|
construct package descriptor from official repository wrapper
|
||||||
|
|
||||||
@ -126,7 +124,7 @@ class AURPackage:
|
|||||||
package(Package): pyalpm package descriptor
|
package(Package): pyalpm package descriptor
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AURPackage: AUR package descriptor
|
Self: AUR package descriptor
|
||||||
"""
|
"""
|
||||||
return cls(
|
return cls(
|
||||||
id=0,
|
id=0,
|
||||||
@ -155,7 +153,7 @@ class AURPackage:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_repo(cls: type[AURPackage], dump: dict[str, Any]) -> AURPackage:
|
def from_repo(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package descriptor from official repository RPC properties
|
construct package descriptor from official repository RPC properties
|
||||||
|
|
||||||
@ -163,7 +161,7 @@ class AURPackage:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
AURPackage: AUR package descriptor
|
Self: AUR package descriptor
|
||||||
"""
|
"""
|
||||||
return cls(
|
return cls(
|
||||||
id=0,
|
id=0,
|
||||||
|
@ -36,23 +36,6 @@ class AuthSettings(str, Enum):
|
|||||||
Configuration = "configuration"
|
Configuration = "configuration"
|
||||||
OAuth = "oauth2"
|
OAuth = "oauth2"
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def from_option(cls: type[AuthSettings], value: str) -> AuthSettings:
|
|
||||||
"""
|
|
||||||
construct value from configuration
|
|
||||||
|
|
||||||
Args:
|
|
||||||
value(str): configuration value
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
AuthSettings: parsed value
|
|
||||||
"""
|
|
||||||
if value.lower() in ("configuration", "mapping"):
|
|
||||||
return cls.Configuration
|
|
||||||
if value.lower() in ("oauth", "oauth2"):
|
|
||||||
return cls.OAuth
|
|
||||||
return cls.Disabled
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_enabled(self) -> bool:
|
def is_enabled(self) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -64,3 +47,20 @@ class AuthSettings(str, Enum):
|
|||||||
if self == AuthSettings.Disabled:
|
if self == AuthSettings.Disabled:
|
||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def from_option(value: str) -> AuthSettings:
|
||||||
|
"""
|
||||||
|
construct value from configuration
|
||||||
|
|
||||||
|
Args:
|
||||||
|
value(str): configuration value
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
AuthSettings: parsed value
|
||||||
|
"""
|
||||||
|
if value.lower() in ("configuration", "mapping"):
|
||||||
|
return AuthSettings.Configuration
|
||||||
|
if value.lower() in ("oauth", "oauth2"):
|
||||||
|
return AuthSettings.OAuth
|
||||||
|
return AuthSettings.Disabled
|
||||||
|
@ -17,11 +17,9 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass, field, fields
|
from dataclasses import dataclass, field, fields
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json, pretty_datetime, utcnow
|
from ahriman.core.util import filter_json, pretty_datetime, utcnow
|
||||||
|
|
||||||
@ -65,7 +63,7 @@ class BuildStatus:
|
|||||||
object.__setattr__(self, "status", BuildStatusEnum(self.status))
|
object.__setattr__(self, "status", BuildStatusEnum(self.status))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[BuildStatus], dump: dict[str, Any]) -> BuildStatus:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct status properties from json dump
|
construct status properties from json dump
|
||||||
|
|
||||||
@ -73,7 +71,7 @@ class BuildStatus:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
BuildStatus: status properties
|
Self: status properties
|
||||||
"""
|
"""
|
||||||
known_fields = [pair.name for pair in fields(cls)]
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
return cls(**filter_json(dump, known_fields))
|
return cls(**filter_json(dump, known_fields))
|
||||||
|
@ -17,10 +17,8 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass, fields
|
from dataclasses import dataclass, fields
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json
|
from ahriman.core.util import filter_json
|
||||||
from ahriman.models.build_status import BuildStatus
|
from ahriman.models.build_status import BuildStatus
|
||||||
@ -49,7 +47,7 @@ class Counters:
|
|||||||
success: int = 0
|
success: int = 0
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[Counters], dump: dict[str, Any]) -> Counters:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct counters from json dump
|
construct counters from json dump
|
||||||
|
|
||||||
@ -57,14 +55,14 @@ class Counters:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Counters: status counters
|
Self: status counters
|
||||||
"""
|
"""
|
||||||
# filter to only known fields
|
# filter to only known fields
|
||||||
known_fields = [pair.name for pair in fields(cls)]
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
return cls(**filter_json(dump, known_fields))
|
return cls(**filter_json(dump, known_fields))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_packages(cls: type[Counters], packages: list[tuple[Package, BuildStatus]]) -> Counters:
|
def from_packages(cls, packages: list[tuple[Package, BuildStatus]]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct counters from packages statuses
|
construct counters from packages statuses
|
||||||
|
|
||||||
@ -72,7 +70,7 @@ class Counters:
|
|||||||
packages(list[tuple[Package, BuildStatus]]): list of package and their status as per watcher property
|
packages(list[tuple[Package, BuildStatus]]): list of package and their status as per watcher property
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Counters: status counters
|
Self: status counters
|
||||||
"""
|
"""
|
||||||
per_status = {"total": len(packages)}
|
per_status = {"total": len(packages)}
|
||||||
for _, status in packages:
|
for _, status in packages:
|
||||||
|
@ -17,10 +17,8 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import asdict, dataclass, field
|
from dataclasses import asdict, dataclass, field
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.models.build_status import BuildStatus
|
from ahriman.models.build_status import BuildStatus
|
||||||
from ahriman.models.counters import Counters
|
from ahriman.models.counters import Counters
|
||||||
@ -46,7 +44,7 @@ class InternalStatus:
|
|||||||
version: str | None = None
|
version: str | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[InternalStatus], dump: dict[str, Any]) -> InternalStatus:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct internal status from json dump
|
construct internal status from json dump
|
||||||
|
|
||||||
@ -54,7 +52,7 @@ class InternalStatus:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
InternalStatus: internal status
|
Self: internal status
|
||||||
"""
|
"""
|
||||||
counters = Counters.from_json(dump["packages"]) if "packages" in dump else Counters(total=0)
|
counters = Counters.from_json(dump["packages"]) if "packages" in dump else Counters(total=0)
|
||||||
build_status = dump.get("status") or {}
|
build_status = dump.get("status") or {}
|
||||||
|
@ -25,9 +25,9 @@ import copy
|
|||||||
from collections.abc import Iterable
|
from collections.abc import Iterable
|
||||||
from dataclasses import asdict, dataclass
|
from dataclasses import asdict, dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pyalpm import vercmp # type: ignore
|
from pyalpm import vercmp # type: ignore[import]
|
||||||
from srcinfo.parse import parse_srcinfo # type: ignore
|
from srcinfo.parse import parse_srcinfo # type: ignore[import]
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.alpm.pacman import Pacman
|
from ahriman.core.alpm.pacman import Pacman
|
||||||
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
|
from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb
|
||||||
@ -179,7 +179,7 @@ class Package(LazyLogging):
|
|||||||
return sorted(packages)
|
return sorted(packages)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_archive(cls: type[Package], path: Path, pacman: Pacman, remote: RemoteSource | None) -> Package:
|
def from_archive(cls, path: Path, pacman: Pacman, remote: RemoteSource | None) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from package archive
|
construct package properties from package archive
|
||||||
|
|
||||||
@ -189,14 +189,14 @@ class Package(LazyLogging):
|
|||||||
remote(RemoteSource): package remote source if applicable
|
remote(RemoteSource): package remote source if applicable
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Package: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
package = pacman.handle.load_pkg(str(path))
|
package = pacman.handle.load_pkg(str(path))
|
||||||
description = PackageDescription.from_package(package, path)
|
description = PackageDescription.from_package(package, path)
|
||||||
return cls(base=package.base, version=package.version, remote=remote, packages={package.name: description})
|
return cls(base=package.base, version=package.version, remote=remote, packages={package.name: description})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_aur(cls: type[Package], name: str, pacman: Pacman) -> Package:
|
def from_aur(cls, name: str, pacman: Pacman) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from AUR page
|
construct package properties from AUR page
|
||||||
|
|
||||||
@ -205,7 +205,7 @@ class Package(LazyLogging):
|
|||||||
pacman(Pacman): alpm wrapper instance
|
pacman(Pacman): alpm wrapper instance
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Package: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
package = AUR.info(name, pacman=pacman)
|
package = AUR.info(name, pacman=pacman)
|
||||||
remote = RemoteSource.from_source(PackageSource.AUR, package.package_base, package.repository)
|
remote = RemoteSource.from_source(PackageSource.AUR, package.package_base, package.repository)
|
||||||
@ -216,7 +216,7 @@ class Package(LazyLogging):
|
|||||||
packages={package.name: PackageDescription.from_aur(package)})
|
packages={package.name: PackageDescription.from_aur(package)})
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_build(cls: type[Package], path: Path, architecture: str) -> Package:
|
def from_build(cls, path: Path, architecture: str) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from sources directory
|
construct package properties from sources directory
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ class Package(LazyLogging):
|
|||||||
architecture(str): load package for specific architecture
|
architecture(str): load package for specific architecture
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Package: package properties
|
Self: package properties
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
InvalidPackageInfo: if there are parsing errors
|
InvalidPackageInfo: if there are parsing errors
|
||||||
@ -254,7 +254,7 @@ class Package(LazyLogging):
|
|||||||
return cls(base=srcinfo["pkgbase"], version=version, remote=None, packages=packages)
|
return cls(base=srcinfo["pkgbase"], version=version, remote=None, packages=packages)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[Package], dump: dict[str, Any]) -> Package:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from json dump
|
construct package properties from json dump
|
||||||
|
|
||||||
@ -262,7 +262,7 @@ class Package(LazyLogging):
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Package: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
packages_json = dump.get("packages") or {}
|
packages_json = dump.get("packages") or {}
|
||||||
packages = {
|
packages = {
|
||||||
@ -273,7 +273,7 @@ class Package(LazyLogging):
|
|||||||
return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages)
|
return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_official(cls: type[Package], name: str, pacman: Pacman, *, use_syncdb: bool = True) -> Package:
|
def from_official(cls, name: str, pacman: Pacman, *, use_syncdb: bool = True) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from official repository page
|
construct package properties from official repository page
|
||||||
|
|
||||||
@ -283,7 +283,7 @@ class Package(LazyLogging):
|
|||||||
use_syncdb(bool, optional): use pacman databases instead of official repositories RPC (Default value = True)
|
use_syncdb(bool, optional): use pacman databases instead of official repositories RPC (Default value = True)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Package: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
|
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
|
||||||
remote = RemoteSource.from_source(PackageSource.Repository, package.package_base, package.repository)
|
remote = RemoteSource.from_source(PackageSource.Repository, package.package_base, package.repository)
|
||||||
|
@ -17,12 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import asdict, dataclass, field, fields
|
from dataclasses import asdict, dataclass, field, fields
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from pyalpm import Package # type: ignore
|
from pyalpm import Package # type: ignore[import]
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json, trim_package
|
from ahriman.core.util import filter_json, trim_package
|
||||||
from ahriman.models.aur_package import AURPackage
|
from ahriman.models.aur_package import AURPackage
|
||||||
@ -99,7 +97,7 @@ class PackageDescription:
|
|||||||
return Path(self.filename) if self.filename is not None else None
|
return Path(self.filename) if self.filename is not None else None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_aur(cls: type[PackageDescription], package: AURPackage) -> PackageDescription:
|
def from_aur(cls, package: AURPackage) -> Self:
|
||||||
"""
|
"""
|
||||||
construct properties from AUR package model
|
construct properties from AUR package model
|
||||||
|
|
||||||
@ -107,7 +105,7 @@ class PackageDescription:
|
|||||||
package(AURPackage): AUR package model
|
package(AURPackage): AUR package model
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
PackageDescription: package properties based on source AUR package
|
Self: package properties based on source AUR package
|
||||||
"""
|
"""
|
||||||
return cls(
|
return cls(
|
||||||
depends=package.depends,
|
depends=package.depends,
|
||||||
@ -120,7 +118,7 @@ class PackageDescription:
|
|||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[PackageDescription], dump: dict[str, Any]) -> PackageDescription:
|
def from_json(cls, dump: dict[str, Any]) -> Self:
|
||||||
"""
|
"""
|
||||||
construct package properties from json dump
|
construct package properties from json dump
|
||||||
|
|
||||||
@ -128,14 +126,14 @@ class PackageDescription:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
PackageDescription: package properties
|
Self: package properties
|
||||||
"""
|
"""
|
||||||
# filter to only known fields
|
# filter to only known fields
|
||||||
known_fields = [pair.name for pair in fields(cls)]
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
return cls(**filter_json(dump, known_fields))
|
return cls(**filter_json(dump, known_fields))
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_package(cls: type[PackageDescription], package: Package, path: Path) -> PackageDescription:
|
def from_package(cls, package: Package, path: Path) -> Self:
|
||||||
"""
|
"""
|
||||||
construct class from alpm package class
|
construct class from alpm package class
|
||||||
|
|
||||||
@ -144,7 +142,7 @@ class PackageDescription:
|
|||||||
path(Path): path to package archive
|
path(Path): path to package archive
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
PackageDescription: package properties based on tarball
|
Self: package properties based on tarball
|
||||||
"""
|
"""
|
||||||
return cls(
|
return cls(
|
||||||
architecture=package.arch,
|
architecture=package.arch,
|
||||||
|
35
src/ahriman/models/pacman_synchronization.py
Normal file
35
src/ahriman/models/pacman_synchronization.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#
|
||||||
|
# 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 enum import Enum
|
||||||
|
|
||||||
|
|
||||||
|
class PacmanSynchronization(int, Enum):
|
||||||
|
"""
|
||||||
|
pacman database synchronization flag
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
Disabled(PacmanSynchronization): (class attribute) do not synchronize local database
|
||||||
|
Enabled(PacmanSynchronization): (class attribute) synchronize local database (same as pacman -Sy)
|
||||||
|
Force(PacmanSynchronization): (class attribute) force synchronize local database (same as pacman -Syy)
|
||||||
|
"""
|
||||||
|
|
||||||
|
Disabled = 0
|
||||||
|
Enabled = 1
|
||||||
|
Force = 2
|
@ -17,11 +17,9 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import asdict, dataclass, fields
|
from dataclasses import asdict, dataclass, fields
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any
|
from typing import Any, Self
|
||||||
|
|
||||||
from ahriman.core.util import filter_json
|
from ahriman.core.util import filter_json
|
||||||
from ahriman.models.package_source import PackageSource
|
from ahriman.models.package_source import PackageSource
|
||||||
@ -63,7 +61,7 @@ class RemoteSource:
|
|||||||
return Path(self.path)
|
return Path(self.path)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls: type[RemoteSource], dump: dict[str, Any]) -> RemoteSource | None:
|
def from_json(cls, dump: dict[str, Any]) -> Self | None:
|
||||||
"""
|
"""
|
||||||
construct remote source from the json dump (or database row)
|
construct remote source from the json dump (or database row)
|
||||||
|
|
||||||
@ -71,7 +69,7 @@ class RemoteSource:
|
|||||||
dump(dict[str, Any]): json dump body
|
dump(dict[str, Any]): json dump body
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RemoteSource | None: remote source
|
Self | None: remote source
|
||||||
"""
|
"""
|
||||||
# filter to only known fields
|
# filter to only known fields
|
||||||
known_fields = [pair.name for pair in fields(cls)]
|
known_fields = [pair.name for pair in fields(cls)]
|
||||||
@ -81,8 +79,7 @@ class RemoteSource:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_source(cls: type[RemoteSource], source: PackageSource, package_base: str,
|
def from_source(cls, source: PackageSource, package_base: str, repository: str) -> Self | None:
|
||||||
repository: str) -> RemoteSource | None:
|
|
||||||
"""
|
"""
|
||||||
generate remote source from the package base
|
generate remote source from the package base
|
||||||
|
|
||||||
@ -92,11 +89,11 @@ class RemoteSource:
|
|||||||
repository(str): repository name
|
repository(str): repository name
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
RemoteSource | None: generated remote source if any, None otherwise
|
Self | None: generated remote source if any, None otherwise
|
||||||
"""
|
"""
|
||||||
if source == PackageSource.AUR:
|
if source == PackageSource.AUR:
|
||||||
from ahriman.core.alpm.remote import AUR
|
from ahriman.core.alpm.remote import AUR
|
||||||
return RemoteSource(
|
return cls(
|
||||||
git_url=AUR.remote_git_url(package_base, repository),
|
git_url=AUR.remote_git_url(package_base, repository),
|
||||||
web_url=AUR.remote_web_url(package_base),
|
web_url=AUR.remote_web_url(package_base),
|
||||||
path=".",
|
path=".",
|
||||||
@ -105,7 +102,7 @@ class RemoteSource:
|
|||||||
)
|
)
|
||||||
if source == PackageSource.Repository:
|
if source == PackageSource.Repository:
|
||||||
from ahriman.core.alpm.remote import Official
|
from ahriman.core.alpm.remote import Official
|
||||||
return RemoteSource(
|
return cls(
|
||||||
git_url=Official.remote_git_url(package_base, repository),
|
git_url=Official.remote_git_url(package_base, repository),
|
||||||
web_url=Official.remote_web_url(package_base),
|
web_url=Official.remote_web_url(package_base),
|
||||||
path="trunk",
|
path="trunk",
|
||||||
|
@ -40,8 +40,8 @@ class ReportSettings(str, Enum):
|
|||||||
Console = "console"
|
Console = "console"
|
||||||
Telegram = "telegram"
|
Telegram = "telegram"
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def from_option(cls: type[ReportSettings], value: str) -> ReportSettings:
|
def from_option(value: str) -> ReportSettings:
|
||||||
"""
|
"""
|
||||||
construct value from configuration
|
construct value from configuration
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ class ReportSettings(str, Enum):
|
|||||||
ReportSettings: parsed value
|
ReportSettings: parsed value
|
||||||
"""
|
"""
|
||||||
if value.lower() in ("html",):
|
if value.lower() in ("html",):
|
||||||
return cls.HTML
|
return ReportSettings.HTML
|
||||||
if value.lower() in ("email",):
|
if value.lower() in ("email",):
|
||||||
return cls.Email
|
return ReportSettings.Email
|
||||||
if value.lower() in ("console",):
|
if value.lower() in ("console",):
|
||||||
return cls.Console
|
return ReportSettings.Console
|
||||||
if value.lower() in ("telegram",):
|
if value.lower() in ("telegram",):
|
||||||
return cls.Telegram
|
return ReportSettings.Telegram
|
||||||
return cls.Disabled
|
return ReportSettings.Disabled
|
||||||
|
@ -17,8 +17,6 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
@ -117,7 +115,7 @@ class RepositoryPaths:
|
|||||||
return self.owner(self.root)
|
return self.owner(self.root)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def known_architectures(cls: type[RepositoryPaths], root: Path) -> set[str]:
|
def known_architectures(cls, root: Path) -> set[str]:
|
||||||
"""
|
"""
|
||||||
get known architectures
|
get known architectures
|
||||||
|
|
||||||
|
@ -36,8 +36,8 @@ class SignSettings(str, Enum):
|
|||||||
Packages = "packages"
|
Packages = "packages"
|
||||||
Repository = "repository"
|
Repository = "repository"
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def from_option(cls: type[SignSettings], value: str) -> SignSettings:
|
def from_option(value: str) -> SignSettings:
|
||||||
"""
|
"""
|
||||||
construct value from configuration
|
construct value from configuration
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class SignSettings(str, Enum):
|
|||||||
SignSettings: parsed value
|
SignSettings: parsed value
|
||||||
"""
|
"""
|
||||||
if value.lower() in ("package", "packages", "sign-package"):
|
if value.lower() in ("package", "packages", "sign-package"):
|
||||||
return cls.Packages
|
return SignSettings.Packages
|
||||||
if value.lower() in ("repository", "sign-repository"):
|
if value.lower() in ("repository", "sign-repository"):
|
||||||
return cls.Repository
|
return SignSettings.Repository
|
||||||
return cls.Disabled
|
return SignSettings.Disabled
|
||||||
|
@ -36,8 +36,8 @@ class SmtpSSLSettings(str, Enum):
|
|||||||
SSL = "ssl"
|
SSL = "ssl"
|
||||||
STARTTLS = "starttls"
|
STARTTLS = "starttls"
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def from_option(cls: type[SmtpSSLSettings], value: str) -> SmtpSSLSettings:
|
def from_option(value: str) -> SmtpSSLSettings:
|
||||||
"""
|
"""
|
||||||
construct value from configuration
|
construct value from configuration
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class SmtpSSLSettings(str, Enum):
|
|||||||
SmtpSSLSettings: parsed value
|
SmtpSSLSettings: parsed value
|
||||||
"""
|
"""
|
||||||
if value.lower() in ("ssl", "ssl/tls"):
|
if value.lower() in ("ssl", "ssl/tls"):
|
||||||
return cls.SSL
|
return SmtpSSLSettings.SSL
|
||||||
if value.lower() in ("starttls",):
|
if value.lower() in ("starttls",):
|
||||||
return cls.STARTTLS
|
return SmtpSSLSettings.STARTTLS
|
||||||
return cls.Disabled
|
return SmtpSSLSettings.Disabled
|
||||||
|
@ -38,8 +38,8 @@ class UploadSettings(str, Enum):
|
|||||||
S3 = "s3"
|
S3 = "s3"
|
||||||
Github = "github"
|
Github = "github"
|
||||||
|
|
||||||
@classmethod
|
@staticmethod
|
||||||
def from_option(cls: type[UploadSettings], value: str) -> UploadSettings:
|
def from_option(value: str) -> UploadSettings:
|
||||||
"""
|
"""
|
||||||
construct value from configuration
|
construct value from configuration
|
||||||
|
|
||||||
@ -50,9 +50,9 @@ class UploadSettings(str, Enum):
|
|||||||
UploadSettings: parsed value
|
UploadSettings: parsed value
|
||||||
"""
|
"""
|
||||||
if value.lower() in ("rsync",):
|
if value.lower() in ("rsync",):
|
||||||
return cls.Rsync
|
return UploadSettings.Rsync
|
||||||
if value.lower() in ("s3",):
|
if value.lower() in ("s3",):
|
||||||
return cls.S3
|
return UploadSettings.S3
|
||||||
if value.lower() in ("github",):
|
if value.lower() in ("github",):
|
||||||
return cls.Github
|
return UploadSettings.Github
|
||||||
return cls.Disabled
|
return UploadSettings.Disabled
|
||||||
|
@ -17,11 +17,10 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
from dataclasses import dataclass, replace
|
from dataclasses import dataclass, replace
|
||||||
from passlib.hash import sha512_crypt
|
from passlib.hash import sha512_crypt
|
||||||
from passlib.pwd import genword as generate_password
|
from passlib.pwd import genword as generate_password
|
||||||
|
from typing import Self
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
|
|
||||||
@ -66,8 +65,8 @@ class User:
|
|||||||
_HASHER = sha512_crypt
|
_HASHER = sha512_crypt
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_option(cls: type[User], username: str | None, password: str | None,
|
def from_option(cls, username: str | None, password: str | None,
|
||||||
access: UserAccess = UserAccess.Read) -> User | None:
|
access: UserAccess = UserAccess.Read) -> Self | None:
|
||||||
"""
|
"""
|
||||||
build user descriptor from configuration options
|
build user descriptor from configuration options
|
||||||
|
|
||||||
@ -77,7 +76,7 @@ class User:
|
|||||||
access(UserAccess, optional): optional user access (Default value = UserAccess.Read)
|
access(UserAccess, optional): optional user access (Default value = UserAccess.Read)
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
User | None: generated user descriptor if all options are supplied and None otherwise
|
Self | None: generated user descriptor if all options are supplied and None otherwise
|
||||||
"""
|
"""
|
||||||
if username is None or password is None:
|
if username is None or password is None:
|
||||||
return None
|
return None
|
||||||
@ -114,7 +113,7 @@ class User:
|
|||||||
verified = False # the absence of evidence is not the evidence of absence (c) Gin Rummy
|
verified = False # the absence of evidence is not the evidence of absence (c) Gin Rummy
|
||||||
return verified
|
return verified
|
||||||
|
|
||||||
def hash_password(self, salt: str) -> User:
|
def hash_password(self, salt: str) -> Self:
|
||||||
"""
|
"""
|
||||||
generate hashed password from plain text
|
generate hashed password from plain text
|
||||||
|
|
||||||
@ -122,7 +121,7 @@ class User:
|
|||||||
salt(str): salt for hashed password
|
salt(str): salt for hashed password
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
User: user with hashed password to store in configuration
|
Self: user with hashed password to store in configuration
|
||||||
"""
|
"""
|
||||||
if not self.password:
|
if not self.password:
|
||||||
# in case of empty password we leave it empty. This feature is used by any external (like OAuth) provider
|
# in case of empty password we leave it empty. This feature is used by any external (like OAuth) provider
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import Application
|
from aiohttp.web import Application
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_cors # type: ignore
|
import aiohttp_cors # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import Application
|
from aiohttp.web import Application
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_security # type: ignore
|
import aiohttp_security # type: ignore[import]
|
||||||
import socket
|
import socket
|
||||||
import types
|
import types
|
||||||
|
|
||||||
|
@ -17,9 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
from __future__ import annotations
|
from aiohttp_cors import CorsViewMixin # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp_cors import CorsViewMixin # type: ignore
|
|
||||||
from aiohttp.web import Request, StreamResponse, View
|
from aiohttp.web import Request, StreamResponse, View
|
||||||
from collections.abc import Awaitable, Callable
|
from collections.abc import Awaitable, Callable
|
||||||
from typing import Any, TypeVar
|
from typing import Any, TypeVar
|
||||||
@ -89,7 +87,7 @@ class BaseView(View, CorsViewMixin):
|
|||||||
return validator
|
return validator
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_permission(cls: type[BaseView], request: Request) -> UserAccess:
|
async def get_permission(cls, request: Request) -> UserAccess:
|
||||||
"""
|
"""
|
||||||
retrieve user permission from the request
|
retrieve user permission from the request
|
||||||
|
|
||||||
@ -168,7 +166,7 @@ class BaseView(View, CorsViewMixin):
|
|||||||
return await self.data_as_json(list_keys or [])
|
return await self.data_as_json(list_keys or [])
|
||||||
|
|
||||||
# pylint: disable=not-callable,protected-access
|
# pylint: disable=not-callable,protected-access
|
||||||
async def head(self) -> StreamResponse: # type: ignore
|
async def head(self) -> StreamResponse: # type: ignore[return]
|
||||||
"""
|
"""
|
||||||
HEAD method implementation based on the result of GET method
|
HEAD method implementation based on the result of GET method
|
||||||
|
|
||||||
@ -181,7 +179,7 @@ class BaseView(View, CorsViewMixin):
|
|||||||
if get_method is not None:
|
if get_method is not None:
|
||||||
# there is a bug in pylint, see https://github.com/pylint-dev/pylint/issues/6005
|
# there is a bug in pylint, see https://github.com/pylint-dev/pylint/issues/6005
|
||||||
response = await get_method()
|
response = await get_method()
|
||||||
response._body = b"" # type: ignore
|
response._body = b"" # type: ignore[assignment]
|
||||||
return response
|
return response
|
||||||
|
|
||||||
self._raise_allowed_methods()
|
self._raise_allowed_methods()
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPNoContent
|
from aiohttp.web import HTTPNoContent
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, HTTPNotFound, Response, json_response
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPNoContent, Response, json_response
|
from aiohttp.web import HTTPNoContent, Response, json_response
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
from aiohttp.web import HTTPBadRequest, HTTPNoContent, Response, json_response
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
|
from aiohttp.web import HTTPFound, HTTPMethodNotAllowed, HTTPUnauthorized
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
#
|
#
|
||||||
import aiohttp_apispec # type: ignore
|
import aiohttp_apispec # type: ignore[import]
|
||||||
|
|
||||||
from aiohttp.web import HTTPFound, HTTPUnauthorized
|
from aiohttp.web import HTTPFound, HTTPUnauthorized
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ def setup_service(architecture: str, configuration: Configuration, spawner: Spaw
|
|||||||
application.logger.info("setup debug panel")
|
application.logger.info("setup debug panel")
|
||||||
debug_enabled = configuration.getboolean("web", "debug", fallback=False)
|
debug_enabled = configuration.getboolean("web", "debug", fallback=False)
|
||||||
if debug_enabled:
|
if debug_enabled:
|
||||||
import aiohttp_debugtoolbar # type: ignore
|
import aiohttp_debugtoolbar # type: ignore[import]
|
||||||
aiohttp_debugtoolbar.setup(application,
|
aiohttp_debugtoolbar.setup(application,
|
||||||
hosts=configuration.getlist("web", "debug_allowed_hosts", fallback=[]),
|
hosts=configuration.getlist("web", "debug_allowed_hosts", fallback=[]),
|
||||||
check_host=configuration.getboolean("web", "debug_check_host", fallback=False))
|
check_host=configuration.getboolean("web", "debug_check_host", fallback=False))
|
||||||
|
@ -15,7 +15,7 @@ def test_run(args: argparse.Namespace, configuration: Configuration, mocker: Moc
|
|||||||
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
print_mock = mocker.patch("ahriman.core.formatters.Printer.print")
|
||||||
|
|
||||||
Versions.run(args, "x86_64", configuration, report=False, unsafe=False)
|
Versions.run(args, "x86_64", configuration, report=False, unsafe=False)
|
||||||
application_mock.assert_called_once_with("ahriman", ("pacman", "s3", "web"))
|
application_mock.assert_called_once_with("ahriman")
|
||||||
print_mock.assert_has_calls([MockCall(verbose=False, separator=" "), MockCall(verbose=False, separator=" ")])
|
print_mock.assert_has_calls([MockCall(verbose=False, separator=" "), MockCall(verbose=False, separator=" ")])
|
||||||
|
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ def test_package_dependencies() -> None:
|
|||||||
"""
|
"""
|
||||||
must extract package dependencies
|
must extract package dependencies
|
||||||
"""
|
"""
|
||||||
packages = Versions.package_dependencies("srcinfo")
|
packages = dict(Versions.package_dependencies("srcinfo"))
|
||||||
assert packages
|
assert packages
|
||||||
assert packages.get("parse") is not None
|
assert packages.get("parse") is not None
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ def test_package_dependencies_missing() -> None:
|
|||||||
"""
|
"""
|
||||||
must extract package dependencies even if some of them are missing
|
must extract package dependencies even if some of them are missing
|
||||||
"""
|
"""
|
||||||
packages = Versions.package_dependencies("ahriman", ("docs", "pacman", "s3", "web"))
|
packages = dict(Versions.package_dependencies("ahriman"))
|
||||||
assert packages
|
assert packages
|
||||||
assert packages.get("pyalpm") is not None
|
assert packages.get("pyalpm") is not None
|
||||||
assert packages.get("Sphinx") is None
|
assert packages.get("Sphinx") is None
|
||||||
|
@ -68,7 +68,7 @@ def schema_request(handler: Callable[..., Awaitable[Any]], *, location: str = "j
|
|||||||
Returns:
|
Returns:
|
||||||
Schema: request schema as set by the decorators
|
Schema: request schema as set by the decorators
|
||||||
"""
|
"""
|
||||||
schemas: list[dict[str, Any]] = handler.__schemas__ # type: ignore
|
schemas: list[dict[str, Any]] = handler.__schemas__ # type: ignore[attr-defined]
|
||||||
return next(schema["schema"] for schema in schemas if schema["put_into"] == location)
|
return next(schema["schema"] for schema in schemas if schema["put_into"] == location)
|
||||||
|
|
||||||
|
|
||||||
@ -84,7 +84,7 @@ def schema_response(handler: Callable[..., Awaitable[Any]], *, code: int = 200)
|
|||||||
Returns:
|
Returns:
|
||||||
Schema: response schema as set by the decorators
|
Schema: response schema as set by the decorators
|
||||||
"""
|
"""
|
||||||
schemas: dict[int, Any] = handler.__apispec__["responses"] # type: ignore
|
schemas: dict[int, Any] = handler.__apispec__["responses"] # type: ignore[attr-defined]
|
||||||
schema = schemas[code]["schema"]
|
schema = schemas[code]["schema"]
|
||||||
if callable(schema):
|
if callable(schema):
|
||||||
schema = schema()
|
schema = schema()
|
||||||
|
@ -1,22 +1,25 @@
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from aiohttp.test_utils import TestClient
|
from aiohttp.test_utils import TestClient
|
||||||
|
from pytest_mock import MockerFixture
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from ahriman.models.user_access import UserAccess
|
from ahriman.models.user_access import UserAccess
|
||||||
from ahriman.web.views.api.swagger import SwaggerView
|
from ahriman.web.views.api.swagger import SwaggerView
|
||||||
|
|
||||||
|
|
||||||
def _client(client: TestClient) -> TestClient:
|
def _client(client: TestClient, mocker: MockerFixture) -> TestClient:
|
||||||
"""
|
"""
|
||||||
generate test client with docs
|
generate test client with docs. Thanks to deprecation, we can't change application state since it was run
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
client(TestClient): test client fixture
|
client(TestClient): test client fixture
|
||||||
|
mocker(MockerFixture): mocker object
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
TestClient: test client fixture with additional properties
|
TestClient: test client fixture with additional properties
|
||||||
"""
|
"""
|
||||||
client.app["swagger_dict"] = {
|
swagger_dict = {
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/v1/logout": {
|
"/api/v1/logout": {
|
||||||
"get": {
|
"get": {
|
||||||
@ -62,6 +65,14 @@ def _client(client: TestClient) -> TestClient:
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
|
source = client.app.__getitem__
|
||||||
|
|
||||||
|
def getitem(name: str) -> Any:
|
||||||
|
if name == "swagger_dict":
|
||||||
|
return swagger_dict
|
||||||
|
return source(name)
|
||||||
|
|
||||||
|
mocker.patch("aiohttp.web.Application.__getitem__", side_effect=getitem)
|
||||||
|
|
||||||
return client
|
return client
|
||||||
|
|
||||||
@ -75,11 +86,11 @@ async def test_get_permission() -> None:
|
|||||||
assert await SwaggerView.get_permission(request) == UserAccess.Unauthorized
|
assert await SwaggerView.get_permission(request) == UserAccess.Unauthorized
|
||||||
|
|
||||||
|
|
||||||
async def test_get(client: TestClient) -> None:
|
async def test_get(client: TestClient, mocker: MockerFixture) -> None:
|
||||||
"""
|
"""
|
||||||
must generate api-docs correctly
|
must generate api-docs correctly
|
||||||
"""
|
"""
|
||||||
client = _client(client)
|
client = _client(client, mocker)
|
||||||
response = await client.get("/api-docs/swagger.json")
|
response = await client.get("/api-docs/swagger.json")
|
||||||
assert response.ok
|
assert response.ok
|
||||||
|
|
||||||
|
3
tox.ini
3
tox.ini
@ -4,7 +4,7 @@ dependencies = -e .[pacman,s3,web]
|
|||||||
project_name = ahriman
|
project_name = ahriman
|
||||||
|
|
||||||
[mypy]
|
[mypy]
|
||||||
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any
|
flags = --implicit-reexport --strict --allow-untyped-decorators --allow-subclassing-any --python-version 3.11
|
||||||
|
|
||||||
[pytest]
|
[pytest]
|
||||||
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
addopts = --cov=ahriman --cov-report=term-missing:skip-covered --no-cov-on-fail --cov-fail-under=100 --spec
|
||||||
@ -57,5 +57,6 @@ commands =
|
|||||||
deps =
|
deps =
|
||||||
{[tox]dependencies}
|
{[tox]dependencies}
|
||||||
-e .[tests]
|
-e .[tests]
|
||||||
|
typing_extensions
|
||||||
commands =
|
commands =
|
||||||
pytest {posargs}
|
pytest {posargs}
|
||||||
|
Loading…
Reference in New Issue
Block a user