mirror of
https://github.com/arcan1s/ahriman.git
synced 2025-06-28 06:41:43 +00:00
Docs update (#61)
* Improve sphinx documentation * update faq formatting * fix setup doc * fix docs according to the generated htmls
This commit is contained in:
@ -327,7 +327,7 @@ def _set_patch_add_parser(root: SubParserAction) -> argparse.ArgumentParser:
|
||||
parser = root.add_parser("patch-add", help="add patch set", description="create or update source patches",
|
||||
epilog="In order to add a patch set for the package you will need to clone "
|
||||
"the AUR package manually, add required changes (e.g. external patches, "
|
||||
"edit PKGBUILD) and run command, e.g. `ahriman patch path/to/directory`. "
|
||||
"edit PKGBUILD) and run command, e.g. ``ahriman patch path/to/directory``. "
|
||||
"By default it tracks *.patch and *.diff files, but this behavior can be changed "
|
||||
"by using --track option",
|
||||
formatter_class=_formatter)
|
||||
|
@ -27,6 +27,26 @@ from ahriman.models.result import Result
|
||||
class Application(ApplicationPackages, ApplicationRepository):
|
||||
"""
|
||||
base application class
|
||||
|
||||
Examples:
|
||||
This class groups ``Repository`` methods into specific method which process all supposed actions caused by
|
||||
underlying action. E.g.::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>> from ahriman.models.package_source import PackageSource
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> application = Application("x86_64", configuration, no_report=False, unsafe=False)
|
||||
>>> # add packages to build queue
|
||||
>>> application.add(["ahriman"], PackageSource.AUR, without_dependencies=False)
|
||||
>>>
|
||||
>>> # check for updates
|
||||
>>> updates = application.updates([], no_aur=False, no_local=False, no_manual=False, no_vcs=False, log_fn=print)
|
||||
>>> # updates for specified packages
|
||||
>>> application.update(updates)
|
||||
|
||||
In case if specific actions or their order are required, the direct access to ``Repository`` must
|
||||
be used instead.
|
||||
"""
|
||||
|
||||
def _finalize(self, result: Result) -> None:
|
||||
|
@ -38,6 +38,13 @@ class Handler:
|
||||
Attributes:
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN(bool): (class attribute) allow defining architecture from existing repositories
|
||||
ALLOW_MULTI_ARCHITECTURE_RUN(bool): (class attribute) allow running with multiple architectures
|
||||
|
||||
Examples:
|
||||
Wrapper for all command line actions, though each derived class implements ``run`` method, it usually must not
|
||||
be called directly. The recommended way is to call ``execute`` class method, e.g.::
|
||||
|
||||
>>> from ahriman.application.handlers import Add
|
||||
>>> Add.execute(args)
|
||||
"""
|
||||
|
||||
ALLOW_AUTO_ARCHITECTURE_RUN = True
|
||||
|
@ -123,7 +123,7 @@ class Setup(Handler):
|
||||
def configuration_create_devtools(prefix: str, architecture: str, source: Path,
|
||||
no_multilib: bool, repository: str, paths: RepositoryPaths) -> None:
|
||||
"""
|
||||
create configuration for devtools based on `source` configuration
|
||||
create configuration for devtools based on ``source`` configuration
|
||||
|
||||
Args:
|
||||
prefix(str): command prefix in {prefix}-{architecture}-build
|
||||
|
@ -44,6 +44,19 @@ class Lock:
|
||||
reporter(Client): build status reporter instance
|
||||
paths(RepositoryPaths): repository paths instance
|
||||
unsafe(bool): skip user check
|
||||
|
||||
Examples:
|
||||
Instance of this class except for controlling file-based lock is also required for basic applications checks.
|
||||
The common flow is to create instance in ``with`` block and handle exceptions after all::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> try:
|
||||
>>> with Lock(args, "x86_64", configuration):
|
||||
>>> perform_actions()
|
||||
>>> except Exception as exception:
|
||||
>>> handle_exceptions(exception)
|
||||
"""
|
||||
|
||||
def __init__(self, args: argparse.Namespace, architecture: str, configuration: Configuration) -> None:
|
||||
|
@ -28,7 +28,7 @@ class Pacman:
|
||||
alpm wrapper
|
||||
|
||||
Attributes:
|
||||
handle(Handle): pyalpm root `Handle`
|
||||
handle(Handle): pyalpm root ``Handle``
|
||||
"""
|
||||
|
||||
def __init__(self, configuration: Configuration) -> None:
|
||||
|
@ -19,7 +19,7 @@
|
||||
#
|
||||
import requests
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
@ -62,8 +62,8 @@ class AUR(Remote):
|
||||
raise InvalidPackageInfo(error_details)
|
||||
return [AURPackage.from_json(package) for package in response["results"]]
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
@classmethod
|
||||
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
@ -76,8 +76,8 @@ class AUR(Remote):
|
||||
"""
|
||||
return f"{AUR.DEFAULT_AUR_URL}/{package_base}.git"
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
@classmethod
|
||||
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
#
|
||||
import requests
|
||||
|
||||
from typing import Any, Dict, List
|
||||
from typing import Any, Dict, List, Type
|
||||
|
||||
from ahriman.core.alpm.pacman import Pacman
|
||||
from ahriman.core.alpm.remote.remote import Remote
|
||||
@ -60,8 +60,8 @@ class Official(Remote):
|
||||
raise InvalidPackageInfo("API validation error")
|
||||
return [AURPackage.from_repo(package) for package in response["results"]]
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
@classmethod
|
||||
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
@ -76,8 +76,8 @@ class Official(Remote):
|
||||
return "https://github.com/archlinux/svntogit-packages.git" # hardcoded, ok
|
||||
return "https://github.com/archlinux/svntogit-community.git"
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
@classmethod
|
||||
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
|
@ -33,6 +33,19 @@ class Remote:
|
||||
|
||||
Attributes:
|
||||
logger(logging.Logger): class logger
|
||||
|
||||
Examples:
|
||||
These classes are designed to be used without instancing. In order to achieve it several class methods are
|
||||
provided: ``info``, ``multisearch`` and ``search``. Thus, the basic flow is the following::
|
||||
|
||||
>>> from ahriman.core.alpm.remote.aur import AUR
|
||||
>>> from ahriman.core.alpm.remote.official import Official
|
||||
>>>
|
||||
>>> package = AUR.info("ahriman", pacman=pacman)
|
||||
>>> search_result = Official.multisearch("pacman", "manager", pacman=pacman)
|
||||
|
||||
Differnece between ``search`` and ``multisearch`` is that ``search`` passes all arguments to underlying wrapper
|
||||
directly, whereas ``multisearch`` splits search one by one and finds intersection between search results.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
@ -79,8 +92,8 @@ class Remote:
|
||||
}
|
||||
return list(packages.values())
|
||||
|
||||
@staticmethod
|
||||
def remote_git_url(package_base: str, repository: str) -> str:
|
||||
@classmethod
|
||||
def remote_git_url(cls: Type[Remote], package_base: str, repository: str) -> str:
|
||||
"""
|
||||
generate remote git url from the package base
|
||||
|
||||
@ -96,8 +109,8 @@ class Remote:
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@staticmethod
|
||||
def remote_web_url(package_base: str) -> str:
|
||||
@classmethod
|
||||
def remote_web_url(cls: Type[Remote], package_base: str) -> str:
|
||||
"""
|
||||
generate remote web url from the package base
|
||||
|
||||
|
@ -79,7 +79,7 @@ class Sources:
|
||||
@staticmethod
|
||||
def fetch(sources_dir: Path, remote: Optional[RemoteSource]) -> None:
|
||||
"""
|
||||
either clone repository or update it to origin/`branch`
|
||||
either clone repository or update it to origin/``remote.branch``
|
||||
|
||||
Args:
|
||||
sources_dir(Path): local path to fetch
|
||||
|
@ -43,6 +43,25 @@ class Configuration(configparser.RawConfigParser):
|
||||
SYSTEM_CONFIGURATION_PATH(Path): (class attribute) default system configuration path distributed by package
|
||||
architecture(Optional[str]): repository architecture
|
||||
path(Optional[Path]): path to root configuration file
|
||||
|
||||
Examples:
|
||||
Configuration class provides additional method in order to handle application configuration. Since this class is
|
||||
derived from built-in ``configparser.RawConfigParser`` class, the same flow is applicable here. Nevertheless,
|
||||
it is recommended to use ``from_path`` class method which also calls initialization methods::
|
||||
|
||||
>>> from pathlib import Path
|
||||
>>>
|
||||
>>> configuration = Configuration.from_path(Path("/etc/ahriman.ini"), "x86_64", quiet=False)
|
||||
>>> repository_name = configuration.get("repository", "name")
|
||||
>>> makepkg_flags = configuration.getlist("build", "makepkg_flags")
|
||||
|
||||
The configuration instance loaded in this way will contain only sections which are defined for the specified
|
||||
architecture according to the merge rules. Moreover, the architecture names will be removed from section names.
|
||||
|
||||
In order to get current settings, the ``check_loaded`` method can be used. This method will raise an
|
||||
``InitializeException`` in case if configuration was not yet loaded::
|
||||
|
||||
>>> path, architecture = configuration.check_loaded()
|
||||
"""
|
||||
|
||||
DEFAULT_LOG_FORMAT = "[%(levelname)s %(asctime)s] [%(filename)s:%(lineno)d %(funcName)s]: %(message)s"
|
||||
@ -310,7 +329,7 @@ class Configuration(configparser.RawConfigParser):
|
||||
|
||||
def set_option(self, section: str, option: str, value: Optional[str]) -> None:
|
||||
"""
|
||||
set option. Unlike default `configparser.RawConfigParser.set` it also creates section if it does not exist
|
||||
set option. Unlike default ``configparser.RawConfigParser.set`` it also creates section if it does not exist
|
||||
|
||||
Args:
|
||||
section(str): section name
|
||||
|
@ -58,7 +58,7 @@ def migrate_package_remotes(connection: Connection, paths: RepositoryPaths) -> N
|
||||
local_cache = paths.cache_for(package_base)
|
||||
if local_cache.exists() and not package.is_vcs:
|
||||
continue # skip packages which are not VCS and with local cache
|
||||
remote_source = RemoteSource.from_remote(PackageSource.AUR, package_base, "aur")
|
||||
remote_source = RemoteSource.from_source(PackageSource.AUR, package_base, "aur")
|
||||
if remote_source is None:
|
||||
continue # should never happen
|
||||
insert_remote(package_base, remote_source)
|
||||
|
@ -73,7 +73,7 @@ class Operations:
|
||||
commit(bool, optional): if True commit() will be called on success (Default value = False)
|
||||
|
||||
Returns:
|
||||
T: result of the `query` call
|
||||
T: result of the ``query`` call
|
||||
"""
|
||||
with sqlite3.connect(self.path, detect_types=sqlite3.PARSE_DECLTYPES) as connection:
|
||||
connection.row_factory = self.factory
|
||||
|
@ -36,6 +36,16 @@ from ahriman.core.database.operations.patch_operations import PatchOperations
|
||||
class SQLite(AuthOperations, BuildOperations, PackageOperations, PatchOperations):
|
||||
"""
|
||||
wrapper for sqlite3 database
|
||||
|
||||
Examples:
|
||||
Database wrapper must be used together with migration and SQLite3 setup. In order to perform it there is
|
||||
``load`` class method::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> database = SQLite.load(configuration)
|
||||
>>> packages = database.packages_get()
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
|
@ -38,6 +38,25 @@ class Report:
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
logger(logging.Logger): class logger
|
||||
|
||||
Examples:
|
||||
``Report`` classes provide several method in order to operate with the report generation and additional class
|
||||
method ``load`` which can be used in order to determine right report instance::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> report = Report.load("x86_64", configuration, "email")
|
||||
|
||||
The ``generate`` method can be used in order to perform the report itself, whereas ``run`` method handles
|
||||
exception and raises ``ReportFailed`` instead::
|
||||
|
||||
>>> try:
|
||||
>>> report.generate([], Result())
|
||||
>>> except Exception as exception:
|
||||
>>> handle_exceptions(exception)
|
||||
>>>
|
||||
>>> report.run([], Result())
|
||||
"""
|
||||
|
||||
def __init__(self, architecture: str, configuration: Configuration) -> None:
|
||||
|
@ -29,6 +29,25 @@ from ahriman.models.package import Package
|
||||
class Repository(Executor, UpdateHandler):
|
||||
"""
|
||||
base repository control class
|
||||
|
||||
Examples:
|
||||
This class along with traits provides access to local repository actions, e.g. remove packages, update packages,
|
||||
sync local repository to remote, generate report, etc::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>> from ahriman.core.database.sqlite import SQLite
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> database = SQLite.load(configuration)
|
||||
>>> repository = Repository("x86_64", configuration, database, no_report=False, unsafe=False)
|
||||
>>> known_packages = repository.packages()
|
||||
>>>
|
||||
>>> build_result = repository.process_build(known_packages)
|
||||
>>> built_packages = repository.packages_built()
|
||||
>>> update_result = repository.process_update(built_packages)
|
||||
>>>
|
||||
>>> repository.process_report(["email"], update_result)
|
||||
>>> repository.process_sync(["s3"], update_result.success)
|
||||
"""
|
||||
|
||||
def load_archives(self, packages: Iterable[Path]) -> List[Package]:
|
||||
|
@ -179,7 +179,9 @@ class GPG:
|
||||
def process_sign_repository(self, path: Path) -> List[Path]:
|
||||
"""
|
||||
sign repository if required by configuration
|
||||
:note: more likely you just want to pass `repository_sign_args` to repo wrapper
|
||||
|
||||
Note:
|
||||
more likely you just want to pass ``repository_sign_args`` to repo wrapper
|
||||
|
||||
Args:
|
||||
path(Path): path to repository database
|
||||
|
@ -101,7 +101,7 @@ class Client:
|
||||
|
||||
def update(self, base: str, status: BuildStatusEnum) -> None:
|
||||
"""
|
||||
update package build status. Unlike `add` it does not update package properties
|
||||
update package build status. Unlike ``add`` it does not update package properties
|
||||
|
||||
Args:
|
||||
base(str): package base to update
|
||||
|
@ -36,7 +36,8 @@ class Watcher:
|
||||
Attributes:
|
||||
architecture(str): repository architecture
|
||||
database(SQLite): database instance
|
||||
known(Dict[str, Tuple[Package, BuildStatus]]): list of known packages. For the most cases `packages` should be used instead
|
||||
known(Dict[str, Tuple[Package, BuildStatus]]): list of known packages. For the most cases ``packages`` should
|
||||
be used instead
|
||||
logger(logging.Logger): class logger
|
||||
repository(Repository): repository object
|
||||
status(BuildStatus): daemon status
|
||||
|
@ -239,7 +239,7 @@ class WebClient(Client):
|
||||
|
||||
def update(self, base: str, status: BuildStatusEnum) -> None:
|
||||
"""
|
||||
update package build status. Unlike `add` it does not update package properties
|
||||
update package build status. Unlike ``add`` it does not update package properties
|
||||
|
||||
Args:
|
||||
base(str): package base to update
|
||||
|
@ -96,6 +96,36 @@ class Tree:
|
||||
|
||||
Attributes:
|
||||
leaves[List[Leaf]): list of tree leaves
|
||||
|
||||
Examples:
|
||||
The most important feature here is to generate tree levels one by one which can be achieved by using class
|
||||
method::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>> from ahriman.core.database.sqlite import SQLite
|
||||
>>> from ahriman.core.repository import Repository
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> database = SQLite.load(configuration)
|
||||
>>> repository = Repository("x86_64", configuration, database, no_report=False, unsafe=False)
|
||||
>>> packages = repository.packages()
|
||||
>>>
|
||||
>>> tree = Tree.load(packages, database)
|
||||
>>> for tree_level in tree.levels():
|
||||
>>> for package in tree_level:
|
||||
>>> print(package.base)
|
||||
>>> print()
|
||||
|
||||
The direct constructor call is also possible but requires tree leaves to be instantioned in advance, e.g.::
|
||||
|
||||
>>> leaves = [Leaf.load(package, database) for package in packages]
|
||||
>>> tree = Tree(leaves)
|
||||
|
||||
Using the default ``Leaf()`` method is possible, but not really recommended because it requires from the user to
|
||||
build the dependency list by himself::
|
||||
|
||||
>>> leaf = Leaf(package, dependecies)
|
||||
>>> tree = Tree([leaf])
|
||||
"""
|
||||
|
||||
def __init__(self, leaves: List[Leaf]) -> None:
|
||||
|
@ -38,6 +38,24 @@ class Upload:
|
||||
architecture(str): repository architecture
|
||||
configuration(Configuration): configuration instance
|
||||
logger(logging.Logger): application logger
|
||||
|
||||
Examples:
|
||||
These classes provide the way to upload packages to remote sources as it is described in their implementations.
|
||||
Basic flow includes class instantiating by using the ``load`` method and then calling the ``run`` method which
|
||||
wraps any internal exceptions into the ``SyncFailed`` exception::
|
||||
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> upload = Upload.load("x86_64", configuration, "s3")
|
||||
>>> upload.run(configuration.repository_paths.repository, [])
|
||||
|
||||
Or in case if direct access to exception is required, the ``sync`` method can be used::
|
||||
|
||||
>>> try:
|
||||
>>> upload.sync(configuration.repository_paths.repository, [])
|
||||
>>> except Exception as exception:
|
||||
>>> handle_exceptions(exception)
|
||||
"""
|
||||
|
||||
def __init__(self, architecture: str, configuration: Configuration) -> None:
|
||||
|
@ -55,6 +55,23 @@ def check_output(*args: str, exception: Optional[Exception], cwd: Optional[Path]
|
||||
|
||||
Raises:
|
||||
subprocess.CalledProcessError: if subprocess ended with status code different from 0 and no exception supplied
|
||||
|
||||
Examples:
|
||||
Simply call the function::
|
||||
|
||||
>>> check_output("echo", "hello world", exception=None)
|
||||
|
||||
The more complicated calls which include result logging and input data are also possible::
|
||||
|
||||
>>> import logging
|
||||
>>>
|
||||
>>> logger = logging.getLogger()
|
||||
>>> check_output("python", "-c", "greeting = input('say hello: '); print(); print(greeting)",
|
||||
>>> exception=None, input_data="hello world", logger=logger)
|
||||
|
||||
An additional argument ``exception`` can be supplied in order to override the default exception::
|
||||
|
||||
>>> check_output("false", exception=RuntimeError("An exception occurred"))
|
||||
"""
|
||||
def log(single: str) -> None:
|
||||
if logger is not None:
|
||||
@ -101,6 +118,11 @@ def check_user(paths: RepositoryPaths, unsafe: bool) -> None:
|
||||
|
||||
Raises:
|
||||
UnsafeRun: if root uid differs from current uid and check is enabled
|
||||
|
||||
Examples:
|
||||
Simply run function with arguments::
|
||||
|
||||
>>> check_user(paths, unsafe=False)
|
||||
"""
|
||||
if not paths.root.exists():
|
||||
return # no directory found, skip check
|
||||
@ -136,6 +158,16 @@ def filter_json(source: Dict[str, Any], known_fields: Iterable[str]) -> Dict[str
|
||||
|
||||
Returns:
|
||||
Dict[str, Any]: json object without unknown and empty fields
|
||||
|
||||
Examples:
|
||||
This wrapper is mainly used for the dataclasses, thus the flow must be something like this::
|
||||
|
||||
>>> from dataclasses import fields
|
||||
>>> from ahriman.models.package import Package
|
||||
>>>
|
||||
>>> known_fields = [pair.name for pair in fields(Package)]
|
||||
>>> properties = filter_json(dump, known_fields)
|
||||
>>> package = Package(**properties)
|
||||
"""
|
||||
return {key: value for key, value in source.items() if key in known_fields and value is not None}
|
||||
|
||||
@ -164,7 +196,7 @@ def package_like(filename: Path) -> bool:
|
||||
filename(Path): name of file to check
|
||||
|
||||
Returns:
|
||||
bool: True in case if name contains `.pkg.` and not signature, False otherwise
|
||||
bool: True in case if name contains ``.pkg.`` and not signature, False otherwise
|
||||
"""
|
||||
name = filename.name
|
||||
return ".pkg." in name and not name.endswith(".sig")
|
||||
@ -193,7 +225,7 @@ def pretty_size(size: Optional[float], level: int = 0) -> str:
|
||||
|
||||
Args:
|
||||
size(Optional[float]): size to convert
|
||||
level(int, optional): represents current units, 0 is B, 1 is KiB etc (Default value = 0)
|
||||
level(int, optional): represents current units, 0 is B, 1 is KiB, etc (Default value = 0)
|
||||
|
||||
Returns:
|
||||
str: pretty printable size as string
|
||||
@ -226,6 +258,13 @@ def tmpdir() -> Generator[Path, None, None]:
|
||||
|
||||
Yields:
|
||||
Path: path to the created directory
|
||||
|
||||
Examples:
|
||||
This function must be used only inside context manager as decorator states::
|
||||
|
||||
>>> with tmpdir() as path:
|
||||
>>> do_something(path)
|
||||
>>> raise Exception("Clear me after exception please")
|
||||
"""
|
||||
path = Path(tempfile.mkdtemp())
|
||||
try:
|
||||
@ -244,6 +283,16 @@ def walk(directory_path: Path) -> Generator[Path, None, None]:
|
||||
|
||||
Yields:
|
||||
Path: all found files in given directory with full path
|
||||
|
||||
Examples:
|
||||
Since the ``pathlib`` module does not provide an alternative to ``os.walk``, this wrapper can be used instead::
|
||||
|
||||
>>> from pathlib import Path
|
||||
>>>
|
||||
>>> for file_path in walk(Path.cwd()):
|
||||
>>> print(file_path)
|
||||
|
||||
Note, however, that unlike the original method, it does not yield directories.
|
||||
"""
|
||||
for element in directory_path.iterdir():
|
||||
if element.is_dir():
|
||||
|
@ -56,6 +56,21 @@ class AURPackage:
|
||||
provides(List[str]): list of packages which this package provides
|
||||
license(List[str]): list of package licenses
|
||||
keywords(List[str]): list of package keywords
|
||||
|
||||
Examples:
|
||||
Mainly this class must be used from class methods instead of default ``__init__``::
|
||||
|
||||
>>> package = AURPackage.from_json(metadata) # load package from json dump
|
||||
>>> # ...or alternatively...
|
||||
>>> package = AURPackage.from_repo(metadata) # load package from official repository RPC
|
||||
>>> # properties of the class are built based on ones from AUR RPC, thus additional method is required
|
||||
>>>
|
||||
>>>
|
||||
>>> from ahriman.core.alpm.pacman import Pacman
|
||||
>>>
|
||||
>>> pacman = Pacman(configuration)
|
||||
>>> metadata = pacman.get("pacman")
|
||||
>>> package = AURPackage.from_pacman(next(metadata)) # load package from pyalpm wrapper
|
||||
"""
|
||||
|
||||
id: int
|
||||
|
@ -51,6 +51,25 @@ class Package:
|
||||
Filled only on load from archive
|
||||
remote(Optional[RemoteSource]): package remote source if applicable
|
||||
version(str): package full version
|
||||
|
||||
Examples:
|
||||
Different usages of this class may generate different (incomplete) data, e.g. if instantiating class from json::
|
||||
|
||||
>>> package = Package.from_json(dump)
|
||||
|
||||
it will contain every data available in the json body. Otherwise, if generate package from local archive::
|
||||
|
||||
>>> package = Package.from_archive(local_path, pacman, remote=None)
|
||||
|
||||
it will probably miss file descriptions (in case if there are multiple packages which belong to the base).
|
||||
|
||||
The specific class load method must be defined based on the source provided. The following methods (mostly) must
|
||||
be used: ``from_archive``, ``from_aur``, ``from_build``, ``from_official`` for sources
|
||||
``PackageSource.Archive``, ``PackageSource.AUR``, ``PackageSource.Local`` and ``PackageSource.Repository``
|
||||
repsectively:
|
||||
|
||||
>>> ahriman_package = Package.from_aur("ahriman", pacman)
|
||||
>>> pacman_package = Package.from_official("pacman", pacman)
|
||||
"""
|
||||
|
||||
base: str
|
||||
@ -145,7 +164,7 @@ class Package:
|
||||
Package: package properties
|
||||
"""
|
||||
package = AUR.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_remote(PackageSource.AUR, package.package_base, package.repository)
|
||||
remote = RemoteSource.from_source(PackageSource.AUR, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
|
||||
@classmethod
|
||||
@ -207,7 +226,7 @@ class Package:
|
||||
Package: package properties
|
||||
"""
|
||||
package = OfficialSyncdb.info(name, pacman=pacman) if use_syncdb else Official.info(name, pacman=pacman)
|
||||
remote = RemoteSource.from_remote(PackageSource.Repository, package.package_base, package.repository)
|
||||
remote = RemoteSource.from_source(PackageSource.Repository, package.package_base, package.repository)
|
||||
return cls(package.package_base, package.version, remote, {package.name: PackageDescription()})
|
||||
|
||||
@staticmethod
|
||||
|
@ -44,6 +44,23 @@ class PackageDescription:
|
||||
licenses(List[str]): package licenses list
|
||||
provides(List[str]): list of provided packages
|
||||
url(Optional[str]): package url
|
||||
|
||||
Examples:
|
||||
Unlike the ``Package`` class, this implementation only holds properties. The recommended way to deal with it is
|
||||
to read data based on the source type - either json or ``pyalpm.Package`` instance::
|
||||
|
||||
>>> description = PackageDescription.from_json(dump)
|
||||
>>>
|
||||
>>>
|
||||
>>> from pathlib import Path
|
||||
>>> from ahriman.core.alpm.pacman import Pacman
|
||||
>>> from ahriman.core.configuration import Configuration
|
||||
>>>
|
||||
>>> configuration = Configuration()
|
||||
>>> pacman = Pacman(configuration)
|
||||
>>> pyalpm_description = next(package for package in pacman.get("pacman"))
|
||||
>>> description = PackageDescription.from_package(
|
||||
>>> pyalpm_description, Path("/var/cache/pacman/pkg/pacman-6.0.1-4-x86_64.pkg.tar.zst"))
|
||||
"""
|
||||
|
||||
architecture: Optional[str] = None
|
||||
|
@ -38,6 +38,13 @@ class PackageSource(str, Enum):
|
||||
Local(PackageSource): (class attribute) source is locally stored PKGBUILD
|
||||
Remote(PackageSource): (class attribute) source is remote (http, ftp etc) link
|
||||
Repository(PackageSource): (class attribute) source is official repository
|
||||
|
||||
Examples:
|
||||
In case if source is unknown the ``resolve()`` and the source descriptor is available method must be used::
|
||||
|
||||
>>> real_source = PackageSource.Auto.resolve("ahriman")
|
||||
|
||||
the code above will ensure that the presudo-source ``PackageSource.Auto`` will not be processed later.
|
||||
"""
|
||||
|
||||
Auto = "auto"
|
||||
|
@ -81,7 +81,7 @@ class RemoteSource:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def from_remote(cls: Type[RemoteSource], source: PackageSource, package_base: str,
|
||||
def from_source(cls: Type[RemoteSource], source: PackageSource, package_base: str,
|
||||
repository: str) -> Optional[RemoteSource]:
|
||||
"""
|
||||
generate remote source from the package base
|
||||
|
@ -37,6 +37,20 @@ class RepositoryPaths:
|
||||
Attributes:
|
||||
root(Path): repository root (i.e. ahriman home)
|
||||
architecture(str): repository architecture
|
||||
|
||||
Examples:
|
||||
This class can be used in order to access the repository tree structure::
|
||||
|
||||
>>> paths = RepositoryPaths(Path("/var/lib/ahriman"), "x86_64")
|
||||
|
||||
Additional methods can be used in order to ensure that tree is created::
|
||||
|
||||
>>> paths.tree_create()
|
||||
|
||||
Access to directories inside can be done by either using properties or specifying the package base::
|
||||
|
||||
>>> cache_dir = paths.cache
|
||||
>>> ahriman_cache_dir = paths.cache_for("ahriman")
|
||||
"""
|
||||
|
||||
root: Path
|
||||
|
@ -36,6 +36,28 @@ class User:
|
||||
username(str): username
|
||||
password(str): hashed user password with salt
|
||||
access(UserAccess): user role
|
||||
|
||||
Examples:
|
||||
Simply create user from database data and perform required validation::
|
||||
|
||||
>>> password = User.generate_password(24)
|
||||
>>> user = User("ahriman", password, UserAccess.Write)
|
||||
|
||||
Since the password supplied may be plain text, the ``hash_password`` method can be used to hash the password::
|
||||
|
||||
>>> user.password = user.hash_password("salt")
|
||||
|
||||
Having the user instance and password, it can be validated::
|
||||
|
||||
>>> if user.check_credentials(password, "salt"):
|
||||
>>> print("password is valid")
|
||||
>>> else:
|
||||
>>> print("password is invalid")
|
||||
|
||||
...and finally access can be verified::
|
||||
|
||||
>>> if user.verify_access(UserAccess.Read):
|
||||
>>> print(f"user {user.username} has read access")
|
||||
"""
|
||||
|
||||
username: str
|
||||
|
@ -37,11 +37,11 @@ class AddView(BaseView):
|
||||
"""
|
||||
add new package
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
>>> {
|
||||
>>> "packages": "ahriman" # either list of packages or package name as in AUR
|
||||
>>> }
|
||||
{
|
||||
"packages": "ahriman" # either list of packages or package name as in AUR
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
|
@ -37,11 +37,11 @@ class RemoveView(BaseView):
|
||||
"""
|
||||
remove existing packages
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
>>> {
|
||||
>>> "packages": "ahriman", # either list of packages or package name
|
||||
>>> }
|
||||
{
|
||||
"packages": "ahriman", # either list of packages or package name
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
|
@ -37,11 +37,11 @@ class RequestView(BaseView):
|
||||
"""
|
||||
request to add new package
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
>>> {
|
||||
>>> "packages": "ahriman" # either list of packages or package name as in AUR
|
||||
>>> }
|
||||
{
|
||||
"packages": "ahriman" # either list of packages or package name as in AUR
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
|
@ -41,7 +41,7 @@ class SearchView(BaseView):
|
||||
"""
|
||||
search packages in AUR
|
||||
|
||||
search string (non empty) must be supplied as `for` parameter
|
||||
search string (non empty) must be supplied as ``for`` parameter
|
||||
|
||||
Returns:
|
||||
Response: 200 with found package bases and descriptions sorted by base
|
||||
|
@ -50,11 +50,11 @@ class AhrimanView(BaseView):
|
||||
"""
|
||||
update service status
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
>>> {
|
||||
>>> "status": "unknown", # service status string, must be valid `BuildStatusEnum`
|
||||
>>> }
|
||||
{
|
||||
"status": "unknown", # service status string, must be valid ``BuildStatusEnum``
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
|
@ -81,13 +81,13 @@ class PackageView(BaseView):
|
||||
"""
|
||||
update package build status
|
||||
|
||||
JSON body must be supplied, the following model is used:
|
||||
JSON body must be supplied, the following model is used::
|
||||
|
||||
>>> {
|
||||
>>> "status": "unknown", # package build status string, must be valid `BuildStatusEnum`
|
||||
>>> "package": {} # package body (use `dataclasses.asdict` to generate one), optional.
|
||||
>>> # Must be supplied in case if package base is unknown
|
||||
>>> }
|
||||
{
|
||||
"status": "unknown", # package build status string, must be valid ``BuildStatusEnum``
|
||||
"package": {} # package body (use ``dataclasses.asdict`` to generate one), optional.
|
||||
# Must be supplied in case if package base is unknown
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPBadRequest: if bad data is supplied
|
||||
|
@ -71,12 +71,12 @@ class LoginView(BaseView):
|
||||
"""
|
||||
login user to service
|
||||
|
||||
either JSON body or form data must be supplied the following fields are required:
|
||||
either JSON body or form data must be supplied the following fields are required::
|
||||
|
||||
>>> {
|
||||
>>> "username": "username" # username to use for login
|
||||
>>> "password": "pa55w0rd" # password to use for login
|
||||
>>> }
|
||||
{
|
||||
"username": "username", # username to use for login
|
||||
"password": "pa55w0rd" # password to use for login
|
||||
}
|
||||
|
||||
Raises:
|
||||
HTTPFound: on success response
|
||||
|
Reference in New Issue
Block a user