mirror of
				https://github.com/arcan1s/ahriman.git
				synced 2025-10-30 21:33:43 +00:00 
			
		
		
		
	PEP-673 use Self as return type for classmethods (#94)
* PEP-673 use Self as return type for classmethods * add dummy test file * remove python3.10 compat
This commit is contained in:
		
							
								
								
									
										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 | ||||
| pacman --noconfirm -Syu | ||||
| # 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 | ||||
| pacman --noconfirm -Sy python-build python-installer python-wheel | ||||
| # optional dependencies | ||||
|  | ||||
							
								
								
									
										2
									
								
								.github/workflows/tests.sh
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/tests.sh
									
									
									
									
										vendored
									
									
								
							| @ -4,7 +4,7 @@ | ||||
| set -ex | ||||
|  | ||||
| # 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 | ||||
| make check tests | ||||
|  | ||||
| @ -90,8 +90,9 @@ Again, the most checks can be performed by `make check` command, though some add | ||||
|             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]`). | ||||
| * `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: | ||||
|  | ||||
|     ```python | ||||
| @ -103,7 +104,7 @@ Again, the most checks can be performed by `make check` command, though some add | ||||
|         def property(self) -> Any: ... | ||||
|  | ||||
|         @classmethod | ||||
|         def class_method(cls: type[Clazz]) -> Clazz: ... | ||||
|         def class_method(cls) -> Self: ... | ||||
|  | ||||
|         @staticmethod | ||||
|         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" | ||||
| ## install package dependencies | ||||
| ## darcs is not installed by reasons, because it requires a lot haskell packages which dramatically increase image size | ||||
| RUN pacman --noconfirm -Sy devtools git pyalpm python-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 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  \ | ||||
|  | ||||
| @ -7,7 +7,7 @@ pkgdesc="ArcH linux ReposItory MANager" | ||||
| arch=('any') | ||||
| url="https://github.com/arcan1s/ahriman" | ||||
| 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') | ||||
| optdepends=('breezy: -bzr packages support' | ||||
|             'darcs: -darcs packages support' | ||||
|  | ||||
							
								
								
									
										1
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								setup.py
									
									
									
									
									
								
							| @ -34,7 +34,6 @@ setup( | ||||
|         "inflection", | ||||
|         "passlib", | ||||
|         "requests", | ||||
|         "setuptools", | ||||
|         "srcinfo", | ||||
|     ], | ||||
|     setup_requires=[ | ||||
|  | ||||
| @ -21,6 +21,7 @@ from ahriman.core.configuration import Configuration | ||||
| from ahriman.core.database import SQLite | ||||
| from ahriman.core.log import LazyLogging | ||||
| from ahriman.core.repository import Repository | ||||
| from ahriman.models.pacman_synchronization import PacmanSynchronization | ||||
|  | ||||
|  | ||||
| class ApplicationProperties(LazyLogging): | ||||
| @ -34,8 +35,8 @@ class ApplicationProperties(LazyLogging): | ||||
|         repository(Repository): repository instance | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, architecture: str, configuration: Configuration, *, | ||||
|                  report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> None: | ||||
|     def __init__(self, architecture: str, configuration: Configuration, *, report: bool, unsafe: bool, | ||||
|                  refresh_pacman_database: PacmanSynchronization = PacmanSynchronization.Disabled) -> None: | ||||
|         """ | ||||
|         default constructor | ||||
|  | ||||
| @ -44,8 +45,8 @@ class ApplicationProperties(LazyLogging): | ||||
|             configuration(Configuration): configuration instance | ||||
|             report(bool): force enable or disable reporting | ||||
|             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 | ||||
|                 (Default value = 0) | ||||
|             refresh_pacman_database(PacmanSynchronization, optional): pacman database synchronization level | ||||
|                 (Default value = PacmanSynchronization.Disabled) | ||||
|         """ | ||||
|         self.configuration = configuration | ||||
|         self.architecture = architecture | ||||
|  | ||||
| @ -30,7 +30,7 @@ class Add(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -36,7 +36,7 @@ class Backup(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -30,7 +30,7 @@ class Clean(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -30,7 +30,7 @@ class Daemon(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -32,7 +32,7 @@ class Dump(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import argparse | ||||
| import logging | ||||
|  | ||||
| @ -52,7 +50,7 @@ class Handler: | ||||
|     ALLOW_MULTI_ARCHITECTURE_RUN = True | ||||
|  | ||||
|     @classmethod | ||||
|     def architectures_extract(cls: type[Handler], args: argparse.Namespace) -> list[str]: | ||||
|     def architectures_extract(cls, args: argparse.Namespace) -> list[str]: | ||||
|         """ | ||||
|         get known architectures | ||||
|  | ||||
| @ -83,7 +81,7 @@ class Handler: | ||||
|         return sorted(architectures) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -108,7 +106,7 @@ class Handler: | ||||
|             return False | ||||
|  | ||||
|     @classmethod | ||||
|     def execute(cls: type[Handler], args: argparse.Namespace) -> int: | ||||
|     def execute(cls, args: argparse.Namespace) -> int: | ||||
|         """ | ||||
|         execute function for all aru | ||||
|  | ||||
| @ -137,7 +135,7 @@ class Handler: | ||||
|         return 0 if all(result) else 1 | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -31,7 +31,7 @@ class Help(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -32,7 +32,7 @@ class KeyImport(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -38,7 +38,7 @@ class Patch(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -32,7 +32,7 @@ class Rebuild(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -30,7 +30,7 @@ class Remove(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -31,7 +31,7 @@ class RemoveUnknown(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -33,7 +33,7 @@ class Restore(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -40,10 +40,14 @@ class Search(Handler): | ||||
|     """ | ||||
|  | ||||
|     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 | ||||
|     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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -35,7 +35,7 @@ class ServiceUpdates(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -45,7 +45,7 @@ class Setup(Handler): | ||||
|     SUDOERS_DIR_PATH = Path("/etc/sudoers.d") | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
| @ -153,7 +153,7 @@ class Setup(Handler): | ||||
|         configuration = Configuration(allow_no_value=True) | ||||
|         # preserve case | ||||
|         # 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 | ||||
|         # 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 | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -30,7 +30,7 @@ class Sign(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -37,7 +37,7 @@ class Status(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -33,7 +33,7 @@ class StatusUpdate(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -34,7 +34,7 @@ class Structure(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -31,7 +31,7 @@ class Triggers(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -33,7 +33,7 @@ class UnsafeCommands(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -32,7 +32,7 @@ class Update(Handler): | ||||
|     """ | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -39,7 +39,7 @@ class Users(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False  # it should be called only as "no-architecture" | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -39,7 +39,7 @@ class Validate(Handler): | ||||
|     ALLOW_AUTO_ARCHITECTURE_RUN = False | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -18,9 +18,12 @@ | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import argparse | ||||
| import pkg_resources | ||||
| import re | ||||
| import sys | ||||
|  | ||||
| from collections.abc import Generator | ||||
| from importlib import metadata | ||||
|  | ||||
| from ahriman import version | ||||
| from ahriman.application.handlers import Handler | ||||
| from ahriman.core.configuration import Configuration | ||||
| @ -30,12 +33,16 @@ from ahriman.core.formatters import VersionPrinter | ||||
| class Versions(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" | ||||
|     PEP423_PACKAGE_NAME = re.compile(r"^[A-Za-z0-9._-]+") | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
| @ -49,37 +56,43 @@ class Versions(Handler): | ||||
|         """ | ||||
|         VersionPrinter(f"Module version {version.__version__}", | ||||
|                        {"Python": sys.version}).print(verbose=False, separator=" ") | ||||
|         packages = Versions.package_dependencies("ahriman", ("pacman", "s3", "web")) | ||||
|         VersionPrinter("Installed packages", packages).print(verbose=False, separator=" ") | ||||
|         packages = Versions.package_dependencies("ahriman") | ||||
|         VersionPrinter("Installed packages", dict(packages)).print(verbose=False, separator=" ") | ||||
|  | ||||
|     @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 | ||||
|  | ||||
|         Args: | ||||
|             root(str): root package name | ||||
|             root_extras(tuple[str, ...], optional): extras for the root package (Default value = ()) | ||||
|  | ||||
|         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, extras: tuple[str, ...] = ()) -> list[str]: | ||||
|             return [entry.key for entry in resources[key].requires(extras)] | ||||
|         def dependencies_by_key(key: str) -> Generator[str, None, None]: | ||||
|             # in importlib it returns requires in the following format | ||||
|             # ["pytest (>=3.0.0) ; extra == 'test'", "pytest-cov ; extra == 'test'"] | ||||
|             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] = [] | ||||
|         portion = {key for key in dependencies_by_key(root, root_extras) if key in resources} | ||||
|         portion = set(dependencies_by_key(root)) | ||||
|         while portion: | ||||
|             keys.extend(portion) | ||||
|             portion = { | ||||
|                 key | ||||
|                 for key in sum((dependencies_by_key(key) for key in portion), start=[]) | ||||
|                 if key not in keys and key in resources | ||||
|                 for key in sum((list(dependencies_by_key(key)) for key in portion), start=[]) | ||||
|                 if key not in keys | ||||
|             } | ||||
|  | ||||
|         return { | ||||
|             resource.project_name: resource.version | ||||
|             for resource in map(lambda key: resources[key], keys) | ||||
|         } | ||||
|         for key in keys: | ||||
|             try: | ||||
|                 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 | ||||
|  | ||||
|     @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: | ||||
|         """ | ||||
|         callback for command line | ||||
|  | ||||
| @ -17,12 +17,10 @@ | ||||
| # 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 __future__ import annotations | ||||
|  | ||||
| import argparse | ||||
|  | ||||
| from types import TracebackType | ||||
| from typing import Literal | ||||
| from typing import Literal, Self | ||||
|  | ||||
| from ahriman import version | ||||
| from ahriman.core.configuration import Configuration | ||||
| @ -111,7 +109,7 @@ class Lock(LazyLogging): | ||||
|         except FileExistsError: | ||||
|             raise DuplicateRunError() | ||||
|  | ||||
|     def __enter__(self) -> Lock: | ||||
|     def __enter__(self) -> Self: | ||||
|         """ | ||||
|         default workflow is the following: | ||||
|  | ||||
| @ -120,6 +118,9 @@ class Lock(LazyLogging): | ||||
|             3. Check web status watcher status | ||||
|             4. Create lock file | ||||
|             5. Report to status page if enabled | ||||
|  | ||||
|         Returns: | ||||
|             Self: always instance of self | ||||
|         """ | ||||
|         self.check_user() | ||||
|         self.check_version() | ||||
|  | ||||
| @ -21,12 +21,13 @@ import shutil | ||||
|  | ||||
| from collections.abc import Callable, Generator | ||||
| 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 ahriman.core.configuration import Configuration | ||||
| from ahriman.core.log import LazyLogging | ||||
| from ahriman.core.util import trim_package | ||||
| from ahriman.models.pacman_synchronization import PacmanSynchronization | ||||
| from ahriman.models.repository_paths import RepositoryPaths | ||||
|  | ||||
|  | ||||
| @ -40,28 +41,28 @@ class Pacman(LazyLogging): | ||||
|  | ||||
|     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 | ||||
|  | ||||
|         Args: | ||||
|             architecture(str): repository architecture | ||||
|             configuration(Configuration): configuration instance | ||||
|             refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be | ||||
|                 enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization | ||||
|             refresh_database(PacmanSynchronization): synchronize local cache to remote | ||||
|         """ | ||||
|         self.__create_handle_fn: Callable[[], Handle] = lambda: self.__create_handle( | ||||
|             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 | ||||
|  | ||||
|         Args: | ||||
|             architecture(str): repository architecture | ||||
|             configuration(Configuration): configuration instance | ||||
|             refresh_database(int): synchronize local cache to remote. If set to ``0``, no synchronization will be | ||||
|                 enabled, if set to ``1`` - normal synchronization, if set to ``2`` - force synchronization | ||||
|             refresh_database(PacmanSynchronization): synchronize local cache to remote | ||||
|  | ||||
|         Returns: | ||||
|             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) | ||||
|  | ||||
|         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 | ||||
|  | ||||
|  | ||||
| @ -44,6 +44,33 @@ class AUR(Remote): | ||||
|     DEFAULT_RPC_VERSION = "5" | ||||
|     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 | ||||
|     def parse_response(response: dict[str, Any]) -> list[AURPackage]: | ||||
|         """ | ||||
| @ -64,33 +91,6 @@ class AUR(Remote): | ||||
|             raise PackageInfoError(error_details) | ||||
|         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]: | ||||
|         """ | ||||
|         perform request to AUR RPC | ||||
|  | ||||
| @ -44,6 +44,35 @@ class Official(Remote): | ||||
|     DEFAULT_RPC_URL = "https://archlinux.org/packages/search/json" | ||||
|     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 | ||||
|     def parse_response(response: dict[str, Any]) -> list[AURPackage]: | ||||
|         """ | ||||
| @ -62,35 +91,6 @@ class Official(Remote): | ||||
|             raise PackageInfoError("API validation error") | ||||
|         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]: | ||||
|         """ | ||||
|         perform request to official repositories RPC | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from ahriman.core.alpm.pacman import Pacman | ||||
| from ahriman.core.log import LazyLogging | ||||
| from ahriman.models.aur_package import AURPackage | ||||
| @ -42,7 +40,7 @@ class Remote(LazyLogging): | ||||
|     """ | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -56,7 +54,7 @@ class Remote(LazyLogging): | ||||
|         return cls().package_info(package_name, pacman=pacman) | ||||
|  | ||||
|     @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 | ||||
|         https://bugs.archlinux.org/task/49133. In addition, short words will be dropped | ||||
| @ -80,7 +78,7 @@ class Remote(LazyLogging): | ||||
|         return list(packages.values()) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -97,7 +95,7 @@ class Remote(LazyLogging): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -113,7 +111,7 @@ class Remote(LazyLogging): | ||||
|         raise NotImplementedError | ||||
|  | ||||
|     @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 | ||||
|  | ||||
|  | ||||
| @ -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>""" | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[Auth], configuration: Configuration, database: SQLite) -> Auth: | ||||
|     @staticmethod | ||||
|     def load(configuration: Configuration, database: SQLite) -> Auth: | ||||
|         """ | ||||
|         load authorization module from settings | ||||
|  | ||||
| @ -81,7 +81,7 @@ class Auth(LazyLogging): | ||||
|         if provider == AuthSettings.OAuth: | ||||
|             from ahriman.core.auth.oauth import OAuth | ||||
|             return OAuth(configuration, database) | ||||
|         return cls(configuration) | ||||
|         return Auth(configuration) | ||||
|  | ||||
|     async def check_credentials(self, username: str | None, password: str | None) -> bool: | ||||
|         """ | ||||
|  | ||||
| @ -20,7 +20,7 @@ | ||||
| from typing import Any | ||||
|  | ||||
| try: | ||||
|     import aiohttp_security  # type: ignore | ||||
|     import aiohttp_security  # type: ignore[import] | ||||
|     _has_aiohttp_security = True | ||||
| except ImportError: | ||||
|     _has_aiohttp_security = False | ||||
|  | ||||
| @ -128,7 +128,7 @@ class OAuth(Mapping): | ||||
|             client.access_token = access_token | ||||
|  | ||||
|             user, _ = await client.user_info() | ||||
|             username: str = user.email  # type: ignore | ||||
|             username: str = user.email  # type: ignore[attr-defined] | ||||
|             return username | ||||
|         except Exception: | ||||
|             self.logger.exception("got exception while performing request") | ||||
|  | ||||
| @ -17,15 +17,13 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import configparser | ||||
| import shlex | ||||
| import sys | ||||
|  | ||||
| from collections.abc import Callable | ||||
| from pathlib import Path | ||||
| from typing import Any | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.exceptions import InitializeError | ||||
| from ahriman.models.repository_paths import RepositoryPaths | ||||
| @ -113,7 +111,7 @@ class Configuration(configparser.RawConfigParser): | ||||
|         return RepositoryPaths(self.getpath("repository", "root"), architecture) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -122,7 +120,7 @@ class Configuration(configparser.RawConfigParser): | ||||
|             architecture(str): repository architecture | ||||
|  | ||||
|         Returns: | ||||
|             Configuration: configuration instance | ||||
|             Self: configuration instance | ||||
|         """ | ||||
|         configuration = cls() | ||||
|         configuration.load(path) | ||||
| @ -186,9 +184,9 @@ class Configuration(configparser.RawConfigParser): | ||||
|  | ||||
|     # pylint and mypy are too stupid to find these methods | ||||
|     # 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]: | ||||
|         """ | ||||
|  | ||||
| @ -19,7 +19,7 @@ | ||||
| # | ||||
| 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 typing import Any | ||||
| from urllib.parse import urlparse | ||||
| @ -74,7 +74,7 @@ class Validator(RootValidator): | ||||
|             bool: value converted to boolean according to configuration rules | ||||
|         """ | ||||
|         # 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 | ||||
|  | ||||
|     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.util import package_like | ||||
| from ahriman.models.package import Package | ||||
| from ahriman.models.pacman_synchronization import PacmanSynchronization | ||||
|  | ||||
|  | ||||
| __all__ = ["migrate_data", "steps"] | ||||
| @ -61,7 +62,7 @@ def migrate_package_depends(connection: Connection, configuration: Configuration | ||||
|         return | ||||
|  | ||||
|     _, architecture = configuration.check_loaded() | ||||
|     pacman = Pacman(architecture, configuration, refresh_database=False) | ||||
|     pacman = Pacman(architecture, configuration, refresh_database=PacmanSynchronization.Disabled) | ||||
|  | ||||
|     package_list = [] | ||||
|     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 | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import json | ||||
| import sqlite3 | ||||
|  | ||||
| from pathlib import Path | ||||
| from typing import Self | ||||
|  | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.core.database.migrations import Migrations | ||||
| @ -46,7 +45,7 @@ class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations, | ||||
|     """ | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[SQLite], configuration: Configuration) -> SQLite: | ||||
|     def load(cls, configuration: Configuration) -> Self: | ||||
|         """ | ||||
|         construct instance from configuration | ||||
|  | ||||
| @ -54,7 +53,7 @@ class SQLite(AuthOperations, BuildOperations, LogsOperations, PackageOperations, | ||||
|             configuration(Configuration): configuration instance | ||||
|  | ||||
|         Returns: | ||||
|             SQLite: fully initialized instance of the database | ||||
|             Self: fully initialized instance of the database | ||||
|         """ | ||||
|         path = cls.database_path(configuration) | ||||
|         database = cls(path) | ||||
|  | ||||
| @ -68,7 +68,7 @@ class RemotePullTrigger(Trigger): | ||||
|         self.targets = self.configuration_sections(configuration) | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|     def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|         """ | ||||
|         extract configuration sections from configuration | ||||
|  | ||||
|  | ||||
| @ -76,7 +76,7 @@ class RemotePushTrigger(Trigger): | ||||
|         self.targets = self.configuration_sections(configuration) | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|     def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|         """ | ||||
|         extract configuration sections from configuration | ||||
|  | ||||
|  | ||||
| @ -17,10 +17,10 @@ | ||||
| # 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 __future__ import annotations | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from typing import Self | ||||
|  | ||||
| from ahriman.core.configuration import Configuration | ||||
|  | ||||
|  | ||||
| @ -52,7 +52,7 @@ class HttpLogHandler(logging.Handler): | ||||
|         self.suppress_errors = suppress_errors | ||||
|  | ||||
|     @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 | ||||
|         http handler found | ||||
| @ -60,6 +60,9 @@ class HttpLogHandler(logging.Handler): | ||||
|         Args: | ||||
|             configuration(Configuration): configuration instance | ||||
|             report(bool): force enable or disable reporting | ||||
|  | ||||
|         Returns: | ||||
|             Self: logger instance with loaded settings | ||||
|         """ | ||||
|         root = logging.getLogger() | ||||
|         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.configuration = configuration | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[Report], architecture: str, configuration: Configuration, target: str) -> Report: | ||||
|     @staticmethod | ||||
|     def load(architecture: str, configuration: Configuration, target: str) -> Report: | ||||
|         """ | ||||
|         load client from settings | ||||
|  | ||||
| @ -93,7 +93,7 @@ class Report(LazyLogging): | ||||
|         if provider == ReportSettings.Telegram: | ||||
|             from ahriman.core.report.telegram import Telegram | ||||
|             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: | ||||
|         """ | ||||
|  | ||||
| @ -205,7 +205,7 @@ class ReportTrigger(Trigger): | ||||
|         self.targets = self.configuration_sections(configuration) | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|     def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|         """ | ||||
|         extract configuration sections from configuration | ||||
|  | ||||
|  | ||||
| @ -17,10 +17,9 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from collections.abc import Iterable | ||||
| from pathlib import Path | ||||
| from typing import Self | ||||
|  | ||||
| from ahriman.core import _Context, context | ||||
| 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.models.context_key import ContextKey | ||||
| from ahriman.models.package import Package | ||||
| from ahriman.models.pacman_synchronization import PacmanSynchronization | ||||
|  | ||||
|  | ||||
| class Repository(Executor, UpdateHandler): | ||||
| @ -58,8 +58,8 @@ class Repository(Executor, UpdateHandler): | ||||
|     """ | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[Repository], architecture: str, configuration: Configuration, database: SQLite, *, | ||||
|              report: bool, unsafe: bool, refresh_pacman_database: int = 0) -> Repository: | ||||
|     def load(cls, architecture: str, configuration: Configuration, database: SQLite, *, report: bool, unsafe: bool, | ||||
|              refresh_pacman_database: PacmanSynchronization = PacmanSynchronization.Disabled) -> Self: | ||||
|         """ | ||||
|         load instance from argument list | ||||
|  | ||||
| @ -69,8 +69,11 @@ class Repository(Executor, UpdateHandler): | ||||
|             database(SQLite): database instance | ||||
|             report(bool): force enable or disable reporting | ||||
|             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 | ||||
|                 (Default value = 0) | ||||
|             refresh_pacman_database(PacmanSynchronization, optional): pacman database synchronization level | ||||
|                 (Default value = PacmanSynchronization.Disabled) | ||||
|  | ||||
|         Returns: | ||||
|             Self: fully loaded repository class instance | ||||
|         """ | ||||
|         instance = cls(architecture, configuration, 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.triggers import TriggerLoader | ||||
| from ahriman.core.util import check_user | ||||
| from ahriman.models.pacman_synchronization import PacmanSynchronization | ||||
| 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 | ||||
|     """ | ||||
|  | ||||
|     def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *, | ||||
|                  report: bool, unsafe: bool, refresh_pacman_database: int) -> None: | ||||
|     def __init__(self, architecture: str, configuration: Configuration, database: SQLite, *, report: bool, unsafe: bool, | ||||
|                  refresh_pacman_database: PacmanSynchronization) -> None: | ||||
|         """ | ||||
|         default constructor | ||||
|  | ||||
| @ -60,7 +61,7 @@ class RepositoryProperties(LazyLogging): | ||||
|             database(SQLite): database instance | ||||
|             report(bool): force enable or disable reporting | ||||
|             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.configuration = configuration | ||||
|  | ||||
| @ -32,8 +32,8 @@ class Client: | ||||
|     base build status reporter client | ||||
|     """ | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[Client], configuration: Configuration, *, report: bool) -> Client: | ||||
|     @staticmethod | ||||
|     def load(configuration: Configuration, *, report: bool) -> Client: | ||||
|         """ | ||||
|         load client from settings | ||||
|  | ||||
| @ -45,7 +45,7 @@ class Client: | ||||
|             Client: client according to current settings | ||||
|         """ | ||||
|         if not report: | ||||
|             return cls() | ||||
|             return Client() | ||||
|  | ||||
|         address = configuration.get("web", "address", fallback=None) | ||||
|         host = configuration.get("web", "host", fallback=None) | ||||
| @ -58,7 +58,7 @@ class Client: | ||||
|         if address or (host and port) or socket: | ||||
|             from ahriman.core.status.web_client import WebClient | ||||
|             return WebClient(configuration) | ||||
|         return cls() | ||||
|         return Client() | ||||
|  | ||||
|     def add(self, package: Package, status: BuildStatusEnum) -> None: | ||||
|         """ | ||||
|  | ||||
| @ -131,7 +131,7 @@ class WebClient(Client, LazyLogging): | ||||
|             requests.Session: generated session object | ||||
|         """ | ||||
|         if use_unix_socket: | ||||
|             import requests_unixsocket  # type: ignore | ||||
|             import requests_unixsocket  # type: ignore[import] | ||||
|             session: requests.Session = requests_unixsocket.Session() | ||||
|             return session | ||||
|  | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.core.configuration.schema import ConfigurationSchema | ||||
| from ahriman.core.log import LazyLogging | ||||
| @ -70,8 +68,7 @@ class Trigger(LazyLogging): | ||||
|         self.configuration = configuration | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_schema(cls: type[Trigger], architecture: str, | ||||
|                              configuration: Configuration | None) -> ConfigurationSchema: | ||||
|     def configuration_schema(cls, architecture: str, configuration: Configuration | None) -> ConfigurationSchema: | ||||
|         """ | ||||
|         configuration schema based on supplied service configuration | ||||
|  | ||||
| @ -102,7 +99,7 @@ class Trigger(LazyLogging): | ||||
|         return result | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|     def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|         """ | ||||
|         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. | ||||
|             from sources:: | ||||
|  | ||||
|                 >>> @staticmethod | ||||
|                 >>> def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|                 >>> @classmethod | ||||
|                 >>> def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|                 >>>     return configuration.getlist("report", "target", fallback=[]) | ||||
|         """ | ||||
|         del configuration | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import contextlib | ||||
| import os | ||||
|  | ||||
| @ -26,6 +24,7 @@ from collections.abc import Generator | ||||
| from importlib import import_module, machinery | ||||
| from pathlib import Path | ||||
| from types import ModuleType | ||||
| from typing import Self | ||||
|  | ||||
| from ahriman.core.configuration import Configuration | ||||
| from ahriman.core.exceptions import ExtensionError | ||||
| @ -66,7 +65,7 @@ class TriggerLoader(LazyLogging): | ||||
|         self.triggers: list[Trigger] = [] | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[TriggerLoader], architecture: str, configuration: Configuration) -> TriggerLoader: | ||||
|     def load(cls, architecture: str, configuration: Configuration) -> Self: | ||||
|         """ | ||||
|         create instance from configuration | ||||
|  | ||||
| @ -75,7 +74,7 @@ class TriggerLoader(LazyLogging): | ||||
|             configuration(Configuration): configuration instance | ||||
|  | ||||
|         Returns: | ||||
|             TriggerLoader: fully loaded trigger instance | ||||
|             Self: fully loaded trigger instance | ||||
|         """ | ||||
|         instance = cls() | ||||
|         instance.triggers = [ | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import boto3  # type: ignore | ||||
| import boto3  # type: ignore[import] | ||||
| import hashlib | ||||
| import mimetypes | ||||
|  | ||||
|  | ||||
| @ -66,8 +66,8 @@ class Upload(LazyLogging): | ||||
|         self.architecture = architecture | ||||
|         self.configuration = configuration | ||||
|  | ||||
|     @classmethod | ||||
|     def load(cls: type[Upload], architecture: str, configuration: Configuration, target: str) -> Upload: | ||||
|     @staticmethod | ||||
|     def load(architecture: str, configuration: Configuration, target: str) -> Upload: | ||||
|         """ | ||||
|         load client from settings | ||||
|  | ||||
| @ -90,7 +90,7 @@ class Upload(LazyLogging): | ||||
|         if provider == UploadSettings.Github: | ||||
|             from ahriman.core.upload.github import Github | ||||
|             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: | ||||
|         """ | ||||
|  | ||||
| @ -136,7 +136,7 @@ class UploadTrigger(Trigger): | ||||
|         self.targets = self.configuration_sections(configuration) | ||||
|  | ||||
|     @classmethod | ||||
|     def configuration_sections(cls: type[Trigger], configuration: Configuration) -> list[str]: | ||||
|     def configuration_sections(cls, configuration: Configuration) -> list[str]: | ||||
|         """ | ||||
|         extract configuration sections from configuration | ||||
|  | ||||
|  | ||||
| @ -17,15 +17,13 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import datetime | ||||
| import inflection | ||||
|  | ||||
| from collections.abc import Callable | ||||
| from dataclasses import dataclass, field, fields | ||||
| from pyalpm import Package  # type: ignore | ||||
| from typing import Any | ||||
| from pyalpm import Package  # type: ignore[import] | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.util import filter_json, full_version | ||||
|  | ||||
| @ -102,7 +100,7 @@ class AURPackage: | ||||
|     keywords: list[str] = field(default_factory=list) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -110,7 +108,7 @@ class AURPackage: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             AURPackage: AUR package descriptor | ||||
|             Self: AUR package descriptor | ||||
|         """ | ||||
|         # filter to only known fields | ||||
|         known_fields = [pair.name for pair in fields(cls)] | ||||
| @ -118,7 +116,7 @@ class AURPackage: | ||||
|         return cls(**filter_json(properties, known_fields)) | ||||
|  | ||||
|     @classmethod | ||||
|     def from_pacman(cls: type[AURPackage], package: Package) -> AURPackage: | ||||
|     def from_pacman(cls, package: Package) -> Self: | ||||
|         """ | ||||
|         construct package descriptor from official repository wrapper | ||||
|  | ||||
| @ -126,7 +124,7 @@ class AURPackage: | ||||
|             package(Package): pyalpm package descriptor | ||||
|  | ||||
|         Returns: | ||||
|             AURPackage: AUR package descriptor | ||||
|             Self: AUR package descriptor | ||||
|         """ | ||||
|         return cls( | ||||
|             id=0, | ||||
| @ -155,7 +153,7 @@ class AURPackage: | ||||
|         ) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -163,7 +161,7 @@ class AURPackage: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             AURPackage: AUR package descriptor | ||||
|             Self: AUR package descriptor | ||||
|         """ | ||||
|         return cls( | ||||
|             id=0, | ||||
|  | ||||
| @ -36,23 +36,6 @@ class AuthSettings(str, Enum): | ||||
|     Configuration = "configuration" | ||||
|     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 | ||||
|     def is_enabled(self) -> bool: | ||||
|         """ | ||||
| @ -64,3 +47,20 @@ class AuthSettings(str, Enum): | ||||
|         if self == AuthSettings.Disabled: | ||||
|             return False | ||||
|         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 | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from dataclasses import dataclass, field, fields | ||||
| from enum import Enum | ||||
| from typing import Any | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.util import filter_json, pretty_datetime, utcnow | ||||
|  | ||||
| @ -65,7 +63,7 @@ class BuildStatus: | ||||
|         object.__setattr__(self, "status", BuildStatusEnum(self.status)) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -73,7 +71,7 @@ class BuildStatus: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             BuildStatus: status properties | ||||
|             Self: status properties | ||||
|         """ | ||||
|         known_fields = [pair.name for pair in fields(cls)] | ||||
|         return cls(**filter_json(dump, known_fields)) | ||||
|  | ||||
| @ -17,10 +17,8 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from dataclasses import dataclass, fields | ||||
| from typing import Any | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.util import filter_json | ||||
| from ahriman.models.build_status import BuildStatus | ||||
| @ -49,7 +47,7 @@ class Counters: | ||||
|     success: int = 0 | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -57,14 +55,14 @@ class Counters: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             Counters: status counters | ||||
|             Self: status counters | ||||
|         """ | ||||
|         # filter to only known fields | ||||
|         known_fields = [pair.name for pair in fields(cls)] | ||||
|         return cls(**filter_json(dump, known_fields)) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -72,7 +70,7 @@ class Counters: | ||||
|             packages(list[tuple[Package, BuildStatus]]): list of package and their status as per watcher property | ||||
|  | ||||
|         Returns: | ||||
|             Counters: status counters | ||||
|             Self: status counters | ||||
|         """ | ||||
|         per_status = {"total": len(packages)} | ||||
|         for _, status in packages: | ||||
|  | ||||
| @ -17,10 +17,8 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| 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.counters import Counters | ||||
| @ -46,7 +44,7 @@ class InternalStatus: | ||||
|     version: str | None = None | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -54,7 +52,7 @@ class InternalStatus: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             InternalStatus: internal status | ||||
|             Self: internal status | ||||
|         """ | ||||
|         counters = Counters.from_json(dump["packages"]) if "packages" in dump else Counters(total=0) | ||||
|         build_status = dump.get("status") or {} | ||||
|  | ||||
| @ -25,9 +25,9 @@ import copy | ||||
| from collections.abc import Iterable | ||||
| from dataclasses import asdict, dataclass | ||||
| from pathlib import Path | ||||
| from pyalpm import vercmp  # type: ignore | ||||
| from srcinfo.parse import parse_srcinfo  # type: ignore | ||||
| from typing import Any | ||||
| from pyalpm import vercmp  # type: ignore[import] | ||||
| from srcinfo.parse import parse_srcinfo  # type: ignore[import] | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.alpm.pacman import Pacman | ||||
| from ahriman.core.alpm.remote import AUR, Official, OfficialSyncdb | ||||
| @ -179,7 +179,7 @@ class Package(LazyLogging): | ||||
|         return sorted(packages) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -189,14 +189,14 @@ class Package(LazyLogging): | ||||
|             remote(RemoteSource): package remote source if applicable | ||||
|  | ||||
|         Returns: | ||||
|             Package: package properties | ||||
|             Self: package properties | ||||
|         """ | ||||
|         package = pacman.handle.load_pkg(str(path)) | ||||
|         description = PackageDescription.from_package(package, path) | ||||
|         return cls(base=package.base, version=package.version, remote=remote, packages={package.name: description}) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -205,7 +205,7 @@ class Package(LazyLogging): | ||||
|             pacman(Pacman): alpm wrapper instance | ||||
|  | ||||
|         Returns: | ||||
|             Package: package properties | ||||
|             Self: package properties | ||||
|         """ | ||||
|         package = AUR.info(name, pacman=pacman) | ||||
|         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)}) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -225,7 +225,7 @@ class Package(LazyLogging): | ||||
|             architecture(str): load package for specific architecture | ||||
|  | ||||
|         Returns: | ||||
|             Package: package properties | ||||
|             Self: package properties | ||||
|  | ||||
|         Raises: | ||||
|             InvalidPackageInfo: if there are parsing errors | ||||
| @ -254,7 +254,7 @@ class Package(LazyLogging): | ||||
|         return cls(base=srcinfo["pkgbase"], version=version, remote=None, packages=packages) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -262,7 +262,7 @@ class Package(LazyLogging): | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             Package: package properties | ||||
|             Self: package properties | ||||
|         """ | ||||
|         packages_json = dump.get("packages") or {} | ||||
|         packages = { | ||||
| @ -273,7 +273,7 @@ class Package(LazyLogging): | ||||
|         return cls(base=dump["base"], version=dump["version"], remote=RemoteSource.from_json(remote), packages=packages) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -283,7 +283,7 @@ class Package(LazyLogging): | ||||
|             use_syncdb(bool, optional): use pacman databases instead of official repositories RPC (Default value = True) | ||||
|  | ||||
|         Returns: | ||||
|             Package: package properties | ||||
|             Self: package properties | ||||
|         """ | ||||
|         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) | ||||
|  | ||||
| @ -17,12 +17,10 @@ | ||||
| # 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 __future__ import annotations | ||||
|  | ||||
| from dataclasses import asdict, dataclass, field, fields | ||||
| from pathlib import Path | ||||
| from pyalpm import Package  # type: ignore | ||||
| from typing import Any | ||||
| from pyalpm import Package  # type: ignore[import] | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.util import filter_json, trim_package | ||||
| 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 | ||||
|  | ||||
|     @classmethod | ||||
|     def from_aur(cls: type[PackageDescription], package: AURPackage) -> PackageDescription: | ||||
|     def from_aur(cls, package: AURPackage) -> Self: | ||||
|         """ | ||||
|         construct properties from AUR package model | ||||
|  | ||||
| @ -107,7 +105,7 @@ class PackageDescription: | ||||
|             package(AURPackage): AUR package model | ||||
|  | ||||
|         Returns: | ||||
|             PackageDescription: package properties based on source AUR package | ||||
|             Self: package properties based on source AUR package | ||||
|         """ | ||||
|         return cls( | ||||
|             depends=package.depends, | ||||
| @ -120,7 +118,7 @@ class PackageDescription: | ||||
|         ) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -128,14 +126,14 @@ class PackageDescription: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             PackageDescription: package properties | ||||
|             Self: package properties | ||||
|         """ | ||||
|         # filter to only known fields | ||||
|         known_fields = [pair.name for pair in fields(cls)] | ||||
|         return cls(**filter_json(dump, known_fields)) | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -144,7 +142,7 @@ class PackageDescription: | ||||
|             path(Path): path to package archive | ||||
|  | ||||
|         Returns: | ||||
|             PackageDescription: package properties based on tarball | ||||
|             Self: package properties based on tarball | ||||
|         """ | ||||
|         return cls( | ||||
|             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 | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from dataclasses import asdict, dataclass, fields | ||||
| from pathlib import Path | ||||
| from typing import Any | ||||
| from typing import Any, Self | ||||
|  | ||||
| from ahriman.core.util import filter_json | ||||
| from ahriman.models.package_source import PackageSource | ||||
| @ -63,7 +61,7 @@ class RemoteSource: | ||||
|         return Path(self.path) | ||||
|  | ||||
|     @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) | ||||
|  | ||||
| @ -71,7 +69,7 @@ class RemoteSource: | ||||
|             dump(dict[str, Any]): json dump body | ||||
|  | ||||
|         Returns: | ||||
|             RemoteSource | None: remote source | ||||
|             Self | None: remote source | ||||
|         """ | ||||
|         # filter to only known fields | ||||
|         known_fields = [pair.name for pair in fields(cls)] | ||||
| @ -81,8 +79,7 @@ class RemoteSource: | ||||
|         return None | ||||
|  | ||||
|     @classmethod | ||||
|     def from_source(cls: type[RemoteSource], source: PackageSource, package_base: str, | ||||
|                     repository: str) -> RemoteSource | None: | ||||
|     def from_source(cls, source: PackageSource, package_base: str, repository: str) -> Self | None: | ||||
|         """ | ||||
|         generate remote source from the package base | ||||
|  | ||||
| @ -92,11 +89,11 @@ class RemoteSource: | ||||
|             repository(str): repository name | ||||
|  | ||||
|         Returns: | ||||
|             RemoteSource | None: generated remote source if any, None otherwise | ||||
|             Self | None: generated remote source if any, None otherwise | ||||
|         """ | ||||
|         if source == PackageSource.AUR: | ||||
|             from ahriman.core.alpm.remote import AUR | ||||
|             return RemoteSource( | ||||
|             return cls( | ||||
|                 git_url=AUR.remote_git_url(package_base, repository), | ||||
|                 web_url=AUR.remote_web_url(package_base), | ||||
|                 path=".", | ||||
| @ -105,7 +102,7 @@ class RemoteSource: | ||||
|             ) | ||||
|         if source == PackageSource.Repository: | ||||
|             from ahriman.core.alpm.remote import Official | ||||
|             return RemoteSource( | ||||
|             return cls( | ||||
|                 git_url=Official.remote_git_url(package_base, repository), | ||||
|                 web_url=Official.remote_web_url(package_base), | ||||
|                 path="trunk", | ||||
|  | ||||
| @ -40,8 +40,8 @@ class ReportSettings(str, Enum): | ||||
|     Console = "console" | ||||
|     Telegram = "telegram" | ||||
|  | ||||
|     @classmethod | ||||
|     def from_option(cls: type[ReportSettings], value: str) -> ReportSettings: | ||||
|     @staticmethod | ||||
|     def from_option(value: str) -> ReportSettings: | ||||
|         """ | ||||
|         construct value from configuration | ||||
|  | ||||
| @ -52,11 +52,11 @@ class ReportSettings(str, Enum): | ||||
|             ReportSettings: parsed value | ||||
|         """ | ||||
|         if value.lower() in ("html",): | ||||
|             return cls.HTML | ||||
|             return ReportSettings.HTML | ||||
|         if value.lower() in ("email",): | ||||
|             return cls.Email | ||||
|             return ReportSettings.Email | ||||
|         if value.lower() in ("console",): | ||||
|             return cls.Console | ||||
|             return ReportSettings.Console | ||||
|         if value.lower() in ("telegram",): | ||||
|             return cls.Telegram | ||||
|         return cls.Disabled | ||||
|             return ReportSettings.Telegram | ||||
|         return ReportSettings.Disabled | ||||
|  | ||||
| @ -17,8 +17,6 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| import os | ||||
| import shutil | ||||
|  | ||||
| @ -117,7 +115,7 @@ class RepositoryPaths: | ||||
|         return self.owner(self.root) | ||||
|  | ||||
|     @classmethod | ||||
|     def known_architectures(cls: type[RepositoryPaths], root: Path) -> set[str]: | ||||
|     def known_architectures(cls, root: Path) -> set[str]: | ||||
|         """ | ||||
|         get known architectures | ||||
|  | ||||
|  | ||||
| @ -36,8 +36,8 @@ class SignSettings(str, Enum): | ||||
|     Packages = "packages" | ||||
|     Repository = "repository" | ||||
|  | ||||
|     @classmethod | ||||
|     def from_option(cls: type[SignSettings], value: str) -> SignSettings: | ||||
|     @staticmethod | ||||
|     def from_option(value: str) -> SignSettings: | ||||
|         """ | ||||
|         construct value from configuration | ||||
|  | ||||
| @ -48,7 +48,7 @@ class SignSettings(str, Enum): | ||||
|             SignSettings: parsed value | ||||
|         """ | ||||
|         if value.lower() in ("package", "packages", "sign-package"): | ||||
|             return cls.Packages | ||||
|             return SignSettings.Packages | ||||
|         if value.lower() in ("repository", "sign-repository"): | ||||
|             return cls.Repository | ||||
|         return cls.Disabled | ||||
|             return SignSettings.Repository | ||||
|         return SignSettings.Disabled | ||||
|  | ||||
| @ -36,8 +36,8 @@ class SmtpSSLSettings(str, Enum): | ||||
|     SSL = "ssl" | ||||
|     STARTTLS = "starttls" | ||||
|  | ||||
|     @classmethod | ||||
|     def from_option(cls: type[SmtpSSLSettings], value: str) -> SmtpSSLSettings: | ||||
|     @staticmethod | ||||
|     def from_option(value: str) -> SmtpSSLSettings: | ||||
|         """ | ||||
|         construct value from configuration | ||||
|  | ||||
| @ -48,7 +48,7 @@ class SmtpSSLSettings(str, Enum): | ||||
|             SmtpSSLSettings: parsed value | ||||
|         """ | ||||
|         if value.lower() in ("ssl", "ssl/tls"): | ||||
|             return cls.SSL | ||||
|             return SmtpSSLSettings.SSL | ||||
|         if value.lower() in ("starttls",): | ||||
|             return cls.STARTTLS | ||||
|         return cls.Disabled | ||||
|             return SmtpSSLSettings.STARTTLS | ||||
|         return SmtpSSLSettings.Disabled | ||||
|  | ||||
| @ -38,8 +38,8 @@ class UploadSettings(str, Enum): | ||||
|     S3 = "s3" | ||||
|     Github = "github" | ||||
|  | ||||
|     @classmethod | ||||
|     def from_option(cls: type[UploadSettings], value: str) -> UploadSettings: | ||||
|     @staticmethod | ||||
|     def from_option(value: str) -> UploadSettings: | ||||
|         """ | ||||
|         construct value from configuration | ||||
|  | ||||
| @ -50,9 +50,9 @@ class UploadSettings(str, Enum): | ||||
|             UploadSettings: parsed value | ||||
|         """ | ||||
|         if value.lower() in ("rsync",): | ||||
|             return cls.Rsync | ||||
|             return UploadSettings.Rsync | ||||
|         if value.lower() in ("s3",): | ||||
|             return cls.S3 | ||||
|             return UploadSettings.S3 | ||||
|         if value.lower() in ("github",): | ||||
|             return cls.Github | ||||
|         return cls.Disabled | ||||
|             return UploadSettings.Github | ||||
|         return UploadSettings.Disabled | ||||
|  | ||||
| @ -17,11 +17,10 @@ | ||||
| # 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 __future__ import annotations | ||||
|  | ||||
| from dataclasses import dataclass, replace | ||||
| from passlib.hash import sha512_crypt | ||||
| from passlib.pwd import genword as generate_password | ||||
| from typing import Self | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
|  | ||||
| @ -66,8 +65,8 @@ class User: | ||||
|     _HASHER = sha512_crypt | ||||
|  | ||||
|     @classmethod | ||||
|     def from_option(cls: type[User], username: str | None, password: str | None, | ||||
|                     access: UserAccess = UserAccess.Read) -> User | None: | ||||
|     def from_option(cls, username: str | None, password: str | None, | ||||
|                     access: UserAccess = UserAccess.Read) -> Self | None: | ||||
|         """ | ||||
|         build user descriptor from configuration options | ||||
|  | ||||
| @ -77,7 +76,7 @@ class User: | ||||
|             access(UserAccess, optional): optional user access (Default value = UserAccess.Read) | ||||
|  | ||||
|         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: | ||||
|             return None | ||||
| @ -114,7 +113,7 @@ class User: | ||||
|             verified = False  # the absence of evidence is not the evidence of absence (c) Gin Rummy | ||||
|         return verified | ||||
|  | ||||
|     def hash_password(self, salt: str) -> User: | ||||
|     def hash_password(self, salt: str) -> Self: | ||||
|         """ | ||||
|         generate hashed password from plain text | ||||
|  | ||||
| @ -122,7 +121,7 @@ class User: | ||||
|             salt(str): salt for hashed password | ||||
|  | ||||
|         Returns: | ||||
|             User: user with hashed password to store in configuration | ||||
|             Self: user with hashed password to store in configuration | ||||
|         """ | ||||
|         if not self.password: | ||||
|             # 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 | ||||
| # 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 typing import Any | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_cors  # type: ignore | ||||
| import aiohttp_cors  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import Application | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_security  # type: ignore | ||||
| import aiohttp_security  # type: ignore[import] | ||||
| import socket | ||||
| import types | ||||
|  | ||||
|  | ||||
| @ -17,9 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| from __future__ import annotations | ||||
|  | ||||
| from aiohttp_cors import CorsViewMixin  # type: ignore | ||||
| from aiohttp_cors import CorsViewMixin  # type: ignore[import] | ||||
| from aiohttp.web import Request, StreamResponse, View | ||||
| from collections.abc import Awaitable, Callable | ||||
| from typing import Any, TypeVar | ||||
| @ -89,7 +87,7 @@ class BaseView(View, CorsViewMixin): | ||||
|         return validator | ||||
|  | ||||
|     @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 | ||||
|  | ||||
| @ -168,7 +166,7 @@ class BaseView(View, CorsViewMixin): | ||||
|             return await self.data_as_json(list_keys or []) | ||||
|  | ||||
|     # 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 | ||||
|  | ||||
| @ -181,7 +179,7 @@ class BaseView(View, CorsViewMixin): | ||||
|         if get_method is not None: | ||||
|             # there is a bug in pylint, see https://github.com/pylint-dev/pylint/issues/6005 | ||||
|             response = await get_method() | ||||
|             response._body = b""  # type: ignore | ||||
|             response._body = b""  # type: ignore[assignment] | ||||
|             return response | ||||
|  | ||||
|         self._raise_allowed_methods() | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPBadRequest, HTTPNoContent | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| 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 | ||||
| # 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 | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPBadRequest, HTTPNoContent | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPBadRequest, HTTPNoContent | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPBadRequest, HTTPNotFound, Response, json_response | ||||
| from collections.abc import Callable | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPNoContent | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| 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 | ||||
| # 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 | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| from aiohttp.web import HTTPNoContent, Response, json_response | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| 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 | ||||
| # 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 | ||||
|  | ||||
|  | ||||
| @ -17,7 +17,7 @@ | ||||
| # You should have received a copy of the GNU General Public License | ||||
| # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
| import aiohttp_apispec  # type: ignore | ||||
| import aiohttp_apispec  # type: ignore[import] | ||||
|  | ||||
| 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") | ||||
|     debug_enabled = configuration.getboolean("web", "debug", fallback=False) | ||||
|     if debug_enabled: | ||||
|         import aiohttp_debugtoolbar  # type: ignore | ||||
|         import aiohttp_debugtoolbar  # type: ignore[import] | ||||
|         aiohttp_debugtoolbar.setup(application, | ||||
|                                    hosts=configuration.getlist("web", "debug_allowed_hosts", fallback=[]), | ||||
|                                    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") | ||||
|  | ||||
|     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=" ")]) | ||||
|  | ||||
|  | ||||
| @ -23,7 +23,7 @@ def test_package_dependencies() -> None: | ||||
|     """ | ||||
|     must extract package dependencies | ||||
|     """ | ||||
|     packages = Versions.package_dependencies("srcinfo") | ||||
|     packages = dict(Versions.package_dependencies("srcinfo")) | ||||
|     assert packages | ||||
|     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 | ||||
|     """ | ||||
|     packages = Versions.package_dependencies("ahriman", ("docs", "pacman", "s3", "web")) | ||||
|     packages = dict(Versions.package_dependencies("ahriman")) | ||||
|     assert packages | ||||
|     assert packages.get("pyalpm") is not None | ||||
|     assert packages.get("Sphinx") is None | ||||
|  | ||||
							
								
								
									
										0
									
								
								tests/ahriman/models/test_pacman_synchronization.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								tests/ahriman/models/test_pacman_synchronization.py
									
									
									
									
									
										Normal file
									
								
							| @ -68,7 +68,7 @@ def schema_request(handler: Callable[..., Awaitable[Any]], *, location: str = "j | ||||
|     Returns: | ||||
|         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) | ||||
|  | ||||
|  | ||||
| @ -84,7 +84,7 @@ def schema_response(handler: Callable[..., Awaitable[Any]], *, code: int = 200) | ||||
|     Returns: | ||||
|         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"] | ||||
|     if callable(schema): | ||||
|         schema = schema() | ||||
|  | ||||
| @ -1,22 +1,25 @@ | ||||
| import pytest | ||||
|  | ||||
| from aiohttp.test_utils import TestClient | ||||
| from pytest_mock import MockerFixture | ||||
| from typing import Any | ||||
|  | ||||
| from ahriman.models.user_access import UserAccess | ||||
| 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: | ||||
|         client(TestClient): test client fixture | ||||
|         mocker(MockerFixture): mocker object | ||||
|  | ||||
|     Returns: | ||||
|         TestClient: test client fixture with additional properties | ||||
|     """ | ||||
|     client.app["swagger_dict"] = { | ||||
|     swagger_dict = { | ||||
|         "paths": { | ||||
|             "/api/v1/logout": { | ||||
|                 "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 | ||||
|  | ||||
| @ -75,11 +86,11 @@ async def test_get_permission() -> None: | ||||
|         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 | ||||
|     """ | ||||
|     client = _client(client) | ||||
|     client = _client(client, mocker) | ||||
|     response = await client.get("/api-docs/swagger.json") | ||||
|     assert response.ok | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user