diff --git a/setup.py b/setup.py index 1312fea7..9a9a3cad 100644 --- a/setup.py +++ b/setup.py @@ -24,6 +24,7 @@ def get_requirements(): version="2.13.3", packages=find_packages("src"), package_dir={"": "src"}, + package_data={"pushsource": ["py.typed"]}, include_package_data=True, url="https://github.com/release-engineering/pushsource", license="GNU General Public License", diff --git a/src/pushsource/_impl/backend/errata_source/errata_client.pyi b/src/pushsource/_impl/backend/errata_source/errata_client.pyi new file mode 100644 index 00000000..89fc9754 --- /dev/null +++ b/src/pushsource/_impl/backend/errata_source/errata_client.pyi @@ -0,0 +1,29 @@ +import xmlrpc.client +from collections import Sequence +from typing import Any, TypeVar + +from pushsource._impl.type_aliases import JsonObject + +ErrataRaw_co = TypeVar("ErrataRaw_co", bound="ErrataRaw", covariant=True) + +class ErrataRaw(object): + advisory_cdn_metadata: JsonObject + advisory_cdn_file_list: JsonObject + advisory_cdn_docker_file_list: JsonObject + ftp_paths: JsonObject + def __init__( + self, + advisory_cdn_metadata: JsonObject, + advisory_cdn_file_list: JsonObject, + advisory_cdn_docker_file_list: JsonObject, + ftp_paths: JsonObject, + ) -> None: ... + +class ErrataClient(object): + _errata_service: xmlrpc.client.ServerProxy + def __init__(self, threads: int, url: str, **retry_args: Any) -> None: ... + def shutdown(self) -> None: ... + def _log_queried_et(self, response: JsonObject, advisory_id: str) -> JsonObject: ... + def get_raw_f(self, advisory_id: str) -> Sequence[ErrataRaw_co]: ... + # TODO: narrow return type if possible: JsonObject maybe? + def _call_et(self, method_name: str, advisory_id: str) -> Any: ... diff --git a/src/pushsource/_impl/backend/errata_source/errata_source.pyi b/src/pushsource/_impl/backend/errata_source/errata_source.pyi new file mode 100644 index 00000000..d4d6a8c0 --- /dev/null +++ b/src/pushsource/_impl/backend/errata_source/errata_source.pyi @@ -0,0 +1,67 @@ +from collections import Mapping, Iterator +from collections.abc import Sequence +from types import TracebackType +from typing import Optional, Type, List, Any + +from pushsource import ( + Source, + ErratumPushItem, + ContainerImagePushItem, +) +from pushsource._impl.backend.errata_source.errata_client import ErrataRaw +from pushsource._impl.model.base import PushItem_co, PushItem_contra +from pushsource._impl.type_aliases import MaybeString + +# TODO: is the value type a model type or just a Mapping? +DockerFileList = Mapping[str, Any] + +# TODO: is the value type a model type or just a Mapping? +RpmList = Mapping[str, Mapping[Any, ...]] + +class ErrataSource(Source): + _errata_service_url: str + _advisory_ids: List[str] + def __init__( + self, + url: str, + errata: MaybeString, + koji_source: Optional[str] = ..., + rpm_filter_arch: Optional[MaybeString] = ..., + legacy_container_repos: bool = ..., + threads: int = ..., + timeout: int = ..., + ) -> None: ... + def __enter__(self) -> "ErrataSource": ... + def __exit__( + self, + exc_type: Type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: ... + def __iter__(self) -> Iterator[PushItem_co]: ... + def _koji_source(**kwargs: Any) -> Source: ... + def _push_items_from_raw(self, raw: ErrataRaw) -> Sequence[PushItem_co]: ... + def _push_items_from_container_manifests( + self, erratum: ErratumPushItem, docker_file_list: DockerFileList + ) -> Sequence[PushItem_co]: ... + def _enrich_container_push_item( + self, + erratum: ErratumPushItem, + docker_file_list: DockerFileList, + item: ContainerImagePushItem, + ) -> ContainerImagePushItem: ... + def _push_items_from_rpms( + self, erratum: ErratumPushItem, rpm_list: RpmList + ) -> Sequence[PushItem_co]: ... + def _module_push_items_from_build( + self, erratum: ErratumPushItem, build_nvr: str, build_info: Mapping[Any, ...] + ) -> Sequence[PushItem_co]: ... + def _filter_rpms_by_arch( + self, erratum: ErratumPushItem, rpm_filenames: Sequence[str] + ) -> Sequence[str]: ... + def _rpm_push_items_from_build( + self, erratum: ErratumPushItem, build_nvr: str, build_info: Mapping[Any, ...] + ) -> Sequence[PushItem_co]: ... + def _add_ftp_paths( + self, items: Sequence[PushItem_contra], erratum: ErratumPushItem, raw: ErrataRaw + ) -> Sequence[PushItem_co]: ... diff --git a/src/pushsource/_impl/backend/koji_containers.pyi b/src/pushsource/_impl/backend/koji_containers.pyi new file mode 100644 index 00000000..410d7e0c --- /dev/null +++ b/src/pushsource/_impl/backend/koji_containers.pyi @@ -0,0 +1,24 @@ +from collections import Mapping, Sequence +from typing import Final, Any + +from pushsource import ContainerImagePullInfo +from pushsource._impl.model.container import ContainerImagePullSpec_co + +MIME_TYPE_MANIFEST_LIST: Final[str] = ... + +# TODO: a lot of the properties of this class are ambiguous +# Mapping[Any] (i.e. Mapping[Any, Any]): can this be narrowed? +# Perhaps the JsonObject type would be more helpful. +class ContainerArchiveHelper(object): + build_image: Mapping[Any] + build_index: Mapping[Any] + archive_extra: Mapping[Any] + archive_docker: Mapping[Any] + source_tags: Sequence[str] + arch: str + labels: Mapping[Any] + pull_info: ContainerImagePullInfo + +def get_tag_specs(raw_specs: Sequence[str]) -> Sequence[ContainerImagePullSpec_co]: ... +def get_digest_specs(raw_specs: Sequence[str], digests_map): + Sequence[ContainerImagePullSpec_co]: ... diff --git a/src/pushsource/_impl/backend/koji_source.pyi b/src/pushsource/_impl/backend/koji_source.pyi new file mode 100644 index 00000000..ee2c9f37 --- /dev/null +++ b/src/pushsource/_impl/backend/koji_source.pyi @@ -0,0 +1,101 @@ +from collections import Mapping, Sequence, Iterator, Iterable, MutableSequence +from concurrent.futures import Executor, Future +from queue import Queue +from threading import RLock as ReentrantLock +from types import TracebackType +from typing import Any, Optional, Union, Type, ClassVar, Final, NoReturn + +from koji import ClientSession, VirtualCall + +from pushsource import Source, RpmPushItem, OperatorManifestPushItem +from pushsource._impl.model.base import PushItem_co +from pushsource._impl.type_aliases import MaybeString, JsonObject + +Id = Union[str, int] + +CACHE_LOCK: ReentrantLock + +RETRY_ARGS: Mapping[str, Any] + +class ListArchivesCommand(object): + def __init__(self, build: Mapping[str, Any]) -> None: ... + build: Mapping[str, Any] + # TODO: is VirtualCall correct for this type? + call: Optional[VirtualCall] = ... + def execute(self, source: "KojiSource", session: ClientSession) -> int: ... + def save(self, source: "KojiSource", koji_queue: Queue) -> None: ... + +class GetBuildCommand(object): + def __init__(self, ident: int, list_archives: bool = ...) -> None: ... + indent: int + list_archives: bool = ... + # TODO: is VirtualCall correct for this type? + call: Optional[VirtualCall] = ... + def execute(self, source: "KojiSource", session: ClientSession) -> int: ... + def save(self, source: "KojiSource", koji_queue: Queue) -> None: ... + +class GetRpmCommand(object): + def __init__(self, indent: int) -> None: ... + indent: int + # TODO: is VirtualCall correct for this type? + call: Optional[VirtualCall] = ... + def execute(self, source: "KojiSource", session: ClientSession) -> int: ... + def save(self, source: "KojiSource", koji_queue: Queue) -> None: ... + +class KojiSource(Source): + _BATCH_SIZE: Final[ClassVar[int]] + _koji_session: ClientSession + def __init__( + self, + url: str, + dest: MaybeString, + rpm: Optional[Sequence[Id]] = ..., + module_build: Optional[Sequence[Id]] = ..., + module_filter_filename: Optional[Sequence[str]] = ..., + container_build: Optional[Sequence[Id]] = ..., + signing_key: Optional[Sequence[str]] = ..., + basedir: Optional[str] = ..., + threads: int = ..., + timeout: int = ..., + cache: Optional[MutableMapping[str, Any]] = ..., + executor: Optional[Executor] = ..., + ) -> None: ... + def __enter__(self) -> "KojiSource": ... + def __exit__( + self, + exc_type: Type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: ... + def __iter__(self) -> Iterator[PushItem_co]: ... + # TODO: the return type here feels hacky, but is accurate + def _koji_check(self) -> None: ... + def _koji_get_version(self) -> str: ... + # TODO: reasonably sure based on the Fedora Koji XML-RPC + # docs for getRPM, getBuild, and getArchive that what is + # the map returned by those API calls is what's stored in + # the cache. Since it is not unlike a JSON object I am sticking + # with a JsonObject return type for now + def _get_rpm(self, rpm: str) -> JsonObject: ... + def _get_build(self, build_id: Id) -> JsonObject: ... + def _get_archives(self, build_id: Id) -> JsonObject: ... + # TODO: maybe JsonObject is appropriate for the meta parameter type + def _push_items_from_rpm_meta( + self, rpm: str, meta: Mapping[str, Any] + ) -> Sequence[RpmPushItem]: ... + def _module_filtered(self, file_path: str) -> bool: ... + def _get_module_name(self, nvr: str, file_path: str) -> str: ... + # TODO: maybe JsonObject is appropriate for the meta parameter type + def _push_items_from_module_build( + self, nvr: str, meta: Mapping[str, Any] + ) -> Sequence[PushItem_co]: ... + def _push_items_from_container_build( + self, nvr: str, meta: Mapping[str, Any] + ) -> Sequence[PushItem_co]: ... + def _get_operator_item( + self, nvr: str, meta: Mapping[str, Any], archives: Iterable[Mapping[str, Any]] + ) -> OperatorManifestPushItem: ... + def _rpm_futures(self) -> Sequence[Future[PushItem_co]]: ... + def _modulemd_futures(self) -> Sequence[Future[PushItem_co]]: ... + def _container_futures(self) -> Sequence[Future[PushItem_co]]: ... + def _do_fetch(self, koji_queue: Queue, exceptions: MutableSequence) -> None: ... diff --git a/src/pushsource/_impl/backend/modulemd.pyi b/src/pushsource/_impl/backend/modulemd.pyi new file mode 100644 index 00000000..266d4522 --- /dev/null +++ b/src/pushsource/_impl/backend/modulemd.pyi @@ -0,0 +1,9 @@ +class Module(object): + name: str + stream: str + version: str + context: str + arch: str + nsvca: str + @classmethod + def from_file(cls, fname: str) -> "Module": ... diff --git a/src/pushsource/_impl/backend/registry_source.pyi b/src/pushsource/_impl/backend/registry_source.pyi new file mode 100644 index 00000000..1de920a4 --- /dev/null +++ b/src/pushsource/_impl/backend/registry_source.pyi @@ -0,0 +1,27 @@ +from collections import Iterator +from re import Pattern +from types import TracebackType +from typing import Optional, Type + +from pushsource import Source, PushItem +from pushsource._impl.model.base import PushItem_co +from pushsource._impl.type_aliases import MaybeString + +IMAGE_URI_REGEX: Pattern + +class RegistrySource(Source): + def __init__( + self, + image: MaybeString, + dest: Optional[MaybeString] = ..., + dest_signing_key: Optional[MaybeString] = ..., + ) -> None: ... + def __enter__(self) -> "RegistrySource": ... + def __exit__( + self, + exc_type: Type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: ... + def __iter__(self) -> Iterator[PushItem_co]: ... + def _push_item_from_registry_uri(self, uri: str, signing_key: str) -> PushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_ami.pyi b/src/pushsource/_impl/backend/staged/staged_ami.pyi new file mode 100644 index 00000000..33f3da45 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_ami.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from pushsource import AmiPushItem +from pushsource._impl.backend.staged.staged_base import StagedBaseMixin +from pushsource._impl.backend.staged.staged_utils import StagingLeafDir, StagingMetadata + +class StagedAmiMixin(StagedBaseMixin): + def __push_item( + self, leafdir: StagingLeafDir, metadata: StagingMetadata, entry: DirEntry + ) -> AmiPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_base.pyi b/src/pushsource/_impl/backend/staged/staged_base.pyi new file mode 100644 index 00000000..4ceda743 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_base.pyi @@ -0,0 +1,39 @@ +from collections import Callable, Sequence +from os import DirEntry +from typing import Final, ClassVar, MutableMapping, Any + +from pushsource import PushItem +from pushsource._impl.backend.staged.staged_utils import StagingLeafDir, StagingMetadata +from pushsource._impl.model.base import PushItem_co + +# Typically "self" isn't annotated, but in this case +# "unbound" methods are being stashed in a dict and +# the "self" parameter is not fulfilled until later via +# partial application. This type annotation accounts for +# the type of unbound "self" parameter in the callable signature. +UnboundTypeHandlerDelegate = Callable[ + ["StagedBaseMixin", StagingLeafDir, StagingMetadata, DirEntry], PushItem +] +# A type for an UnboundTypeHandlerDelegate where "self" has +# been bound to an instance of StagedBaseMixin +BoundTypeHandlerDelegate = Callable[ + [StagingLeafDir, StagingMetadata, DirEntry], PushItem +] +# A partially applied type handler callable +PartialTypeHandler = Callable[[StagingLeafDir, StagingMetadata], Sequence[PushItem_co]] + +class TypeHandler(object): + HANDLERS: Final[ClassVar[MutableMapping[str, UnboundTypeHandlerDelegate]]] + type_name: str + def __init__(self, type_name: str) -> None: ... + def __call__(self, fn: UnboundTypeHandlerDelegate) -> None: ... + +class StagedBaseMixin(object): + _FILE_TYPES = Final[ClassVar[MutableMapping[str, PartialTypeHandler]]] + def __init__(self, *args: Any, **kwargs: Any) -> None: ... + def __mixin_push_items( + self, + leafdir: StagingLeafDir, + metadata: StagingMetadata, + delegate: BoundTypeHandlerDelegate, + ) -> Sequence[PushItem_co]: ... diff --git a/src/pushsource/_impl/backend/staged/staged_compsxml.pyi b/src/pushsource/_impl/backend/staged/staged_compsxml.pyi new file mode 100644 index 00000000..0bc0c536 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_compsxml.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from pushsource import CompsXmlPushItem +from pushsource._impl.backend.staged.staged_base import StagedBaseMixin +from pushsource._impl.backend.staged.staged_utils import StagingLeafDir, StagingMetadata + +class StagedCompsXmlMixin(StagedBaseMixin): + def __push_item( + self, leafdir: StagingLeafDir, _: StagingMetadata, entry: DirEntry + ) -> CompsXmlPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_errata.pyi b/src/pushsource/_impl/backend/staged/staged_errata.pyi new file mode 100644 index 00000000..e4b94df9 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_errata.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from pushsource import ErratumPushItem +from pushsource._impl.backend.staged.staged_base import StagedBaseMixin +from pushsource._impl.backend.staged.staged_utils import StagingMetadata, StagingLeafDir + +class StagedErrataMixin(StagedBaseMixin): + def __make_push_item( + self, leafdir: StagingLeafDir, metadata: StagingMetadata, entry: DirEntry + ) -> ErratumPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_files.pyi b/src/pushsource/_impl/backend/staged/staged_files.pyi new file mode 100644 index 00000000..6bec545e --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_files.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from pushsource import FilePushItem +from pushsource._impl.backend.staged.staged_base import StagedBaseMixin +from pushsource._impl.backend.staged.staged_utils import StagingLeafDir, StagingMetadata + +class StagedFilesMixin(StagedBaseMixin): + def __file_push_item( + self, leafdir: StagingLeafDir, metadata: StagingMetadata, entry: DirEntry + ) -> FilePushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_modulemd.pyi b/src/pushsource/_impl/backend/staged/staged_modulemd.pyi new file mode 100644 index 00000000..f9dadc63 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_modulemd.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from pushsource import ModuleMdPushItem +from pushsource._impl.backend.staged.staged_base import StagedBaseMixin +from pushsource._impl.backend.staged.staged_utils import StagingLeafDir, StagingMetadata + +class StagedModuleMdMixin(StagedBaseMixin): + def __push_item( + self, leafdir: StagingLeafDir, metadata: StagingMetadata, entry: DirEntry + ) -> ModuleMdPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_productid.pyi b/src/pushsource/_impl/backend/staged/staged_productid.pyi new file mode 100644 index 00000000..0c8f0b07 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_productid.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from .staged_utils import StagingLeafDir, StagingMetadata +from ...model import ProductIdPushItem +from .staged_base import StagedBaseMixin + +class StagedProductIdMixin(StagedBaseMixin): + def __push_item( + self, leafdir: StagingLeafDir, _: StagingMetadata, entry: DirEntry + ) -> ProductIdPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_rpm.pyi b/src/pushsource/_impl/backend/staged/staged_rpm.pyi new file mode 100644 index 00000000..0a4576b8 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_rpm.pyi @@ -0,0 +1,10 @@ +from os import DirEntry + +from .staged_base import StagedBaseMixin +from .staged_utils import StagingLeafDir, StagingMetadata +from ...model import RpmPushItem + +class StagedRpmMixin(StagedBaseMixin): + def __push_item( + self, leafdir: StagingLeafDir, _: StagingMetadata, entry: DirEntry + ) -> RpmPushItem: ... diff --git a/src/pushsource/_impl/backend/staged/staged_source.pyi b/src/pushsource/_impl/backend/staged/staged_source.pyi new file mode 100644 index 00000000..bcb952ed --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_source.pyi @@ -0,0 +1,50 @@ +from collections import Sequence, Mapping, Iterator, MutableMapping, Generator +from threading import RLock as ReentrantLock +from types import TracebackType +from typing import Final, ClassVar, Type + +from .staged_base import PartialTypeHandler +from .staged_utils import StagingMetadata, StagingLeafDir +from ...model.base import PushItem_co, PushItem +from ...source import Source + +from .staged_ami import StagedAmiMixin +from .staged_compsxml import StagedCompsXmlMixin +from .staged_errata import StagedErrataMixin +from .staged_files import StagedFilesMixin +from .staged_modulemd import StagedModuleMdMixin +from .staged_productid import StagedProductIdMixin +from .staged_rpm import StagedRpmMixin +from .staged_unsupported import StagedUnsupportedMixin + +METADATA_FILES: Final[Sequence[str]] +CACHE_LOCK: Final[ReentrantLock] + +class StagedSource( + Source, + StagedAmiMixin, + StagedFilesMixin, + StagedErrataMixin, + StagedCompsXmlMixin, + StagedModuleMdMixin, + StagedProductIdMixin, + StagedRpmMixin, + StagedUnsupportedMixin, +): + _FILE_TYPES = Final[ClassVar[MutableMapping[str, PartialTypeHandler]]] + def __init__( + self, url: Sequence[str], threads: int = ..., timeout: int = ... + ) -> None: ... + def __enter__(self) -> "Source": ... + def __exit__( + self, + exc_type: Type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ): ... + def __iter__(self) -> Iterator[PushItem_co]: ... + def _load_metadata(self, topdir: str) -> StagingMetadata: ... + def _push_items_for_leafdir( + self, leafdir: StagingLeafDir, metadata: StagingMetadata + ) -> Sequence[PushItem_co]: ... + def _push_items_for_topdir(self, topdir: str) -> Iterator[PushItem_co]: ... diff --git a/src/pushsource/_impl/backend/staged/staged_unsupported.pyi b/src/pushsource/_impl/backend/staged/staged_unsupported.pyi new file mode 100644 index 00000000..1e5fcbae --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_unsupported.pyi @@ -0,0 +1,9 @@ +from os import DirEntry + +from .staged_base import StagedBaseMixin +from .staged_utils import StagingLeafDir, StagingMetadata + +class StagedUnsupportedMixin(StagedBaseMixin): + def __push_item( + self, _leafdir: StagingLeafDir, _metadata: StagingMetadata, entry: DirEntry + ) -> None: ... diff --git a/src/pushsource/_impl/backend/staged/staged_utils.pyi b/src/pushsource/_impl/backend/staged/staged_utils.pyi new file mode 100644 index 00000000..07d5c147 --- /dev/null +++ b/src/pushsource/_impl/backend/staged/staged_utils.pyi @@ -0,0 +1,48 @@ +from collections import Mapping +from typing import Final, Any, Optional + +from pushsource._impl.validator import Validator +from pushsource._impl.type_aliases import JsonObject + +REQUIRED_VERSION: Final[str] = ... +VALIDATOR: Final[Validator] = ... + +class StagingFileMetadata(object): + attributes: Mapping[Any] + filename: str + relative_path: str + sha256sum: str + version: str + def __init__( + self, + attributes: Mapping[Any], + filename: str, + relative_path: str, + sha256sum: str, + version: str, + ) -> None: ... + +class StagingMetadata(object): + filename: Optional[str] = ... + file_metadata: Optional[Mapping[str, StagingFileMetadata]] = ... + def __init__( + self, + filename: Optional[str] = ..., + file_metadata: Optional[Mapping[str, StagingFileMetadata]] = ..., + ) -> None: ... + def file_metadata_or_die(self, relative_path: str) -> StagingFileMetadata: ... + @classmethod + def from_data(cls, data: JsonObject, filename: str = ...) -> "StagingMetadata": ... + +class StagingLeafDir(object): + file_type: str + dest: str + path: str + topdir: str + def __init__( + self, + file_type: str, + dest: str, + path: str, + topdir: str, + ) -> None: ... diff --git a/src/pushsource/_impl/model/ami.pyi b/src/pushsource/_impl/model/ami.pyi new file mode 100644 index 00000000..e43192d8 --- /dev/null +++ b/src/pushsource/_impl/model/ami.pyi @@ -0,0 +1,33 @@ +from collections import Sequence +from typing import Text, Optional + +from pushsource import PushItem +from pushsource._impl.type_aliases import Date + + +class AmiRelease(object): + product: Text + date: Date + arch: Text + respin: int + version: Optional[Text] = ... + base_product: Optional[Text] = ... + base_version: Optional[Text] = ... + variant: Optional[Text] = ... + type: Optional[Text] = ... + +class AmiBillingCodes(object): + name: Text = ... + codes: Sequence[Text] = ... + +class AmiPushItem(PushItem): + release: Optional[AmiRelease] = ... + type: Optional[Text] = ... + region: Optional[Text] = ... + virtualization: Optional[Text] = ... + volume: Optional[Text] = ... + root_device: Optional[Text] = ... + description: Optional[Text] = ... + sriov_net_support: Optional[Text] = ... + ena_support: Optional[bool] = ... + billing_codes: Optional[AmiBillingCodes] = ... diff --git a/src/pushsource/_impl/model/base.pyi b/src/pushsource/_impl/model/base.pyi new file mode 100644 index 00000000..54e40931 --- /dev/null +++ b/src/pushsource/_impl/model/base.pyi @@ -0,0 +1,24 @@ +from typing import Text, TypeVar, Type, Sequence, Optional + +PushItem_co = TypeVar("PushItem_co", bound="PushItem", covariant=True) +PushItem_contra = TypeVar("PushItem_contra", bound="PushItem", contravariant=True) + +class KojiBuildInfo(object): + name: Text + version: Text + release: Text + @classmethod + def _from_nvr(cls: Type["KojiBuildInfo"], nvr_str: Text) -> "KojiBuildInfo": ... + +class PushItem(object): + name: Text + state: Text = ... + src: Optional[Text] = ... + dest: Sequence[Text] = ... + md5sum: Optional[Text] = ... + sha256sum: Optional[Text] = ... + origin: Optional[Text] = ... + build: Optional[Text] = ... + build_info: KojiBuildInfo = ... + signing_key: Optional[Text] = ... + def with_checksums(self) -> "PushItem": ... diff --git a/src/pushsource/_impl/model/comps.pyi b/src/pushsource/_impl/model/comps.pyi new file mode 100644 index 00000000..b5bc3937 --- /dev/null +++ b/src/pushsource/_impl/model/comps.pyi @@ -0,0 +1 @@ +class CompsXmlPushItem(PushItem): ... diff --git a/src/pushsource/_impl/model/container.pyi b/src/pushsource/_impl/model/container.pyi new file mode 100644 index 00000000..e3fb1574 --- /dev/null +++ b/src/pushsource/_impl/model/container.pyi @@ -0,0 +1,53 @@ +from collections import Sequence, Mapping +from typing import Text, AnyStr, TypeVar, Optional, Type + +from pushsource import PushItem + +ContainerImagePullSpec_co = TypeVar( + "ContainerImagePullSpec_co", bound="ContainerImagePullSpec", covariant=True +) +ContainerImagePullSpec_contra = TypeVar( + "ContainerImagePullSpec_contra", bound="ContainerImagePullSpec", contravariant=True +) + +class ContainerImagePullSpec(object): + registry: Text + repository: Text + @classmethod + def _from_str(cls, pull_spec: AnyStr) -> ContainerImagePullSpec_co: ... + +class ContainerImageTagPullSpec(ContainerImagePullSpec): + tag: Text + media_types: Sequence[Text] + +class ContainerImageDigestPullSpec(ContainerImagePullSpec): + digest: Text + media_type: Optional[Text] = ... + +def specs_converter( + specs: Sequence[ContainerImagePullSpec_contra], expected_class: Type +) -> Sequence[ContainerImagePullSpec_co]: ... +def tag_specs_converter( + specs: Sequence[ContainerImagePullSpec_contra], +) -> Sequence[ContainerImageTagPullSpec]: ... +def digest_specs_converter( + specs: Sequence[ContainerImagePullSpec_contra], +) -> Sequence[ContainerImageDigestPullSpec]: ... + +class ContainerImagePullInfo(object): + tag_specs: Sequence[ContainerImageTagPullSpec] + digest_specs: Sequence[ContainerImageDigestPullSpec] + media_types: Sequence[Text] = ... + def digest_spec_for_type( + self, media_type: Text + ) -> Optional[ContainerImageDigestPullSpec]: ... + +class ContainerImagePushItem(PushItem): + dest_signing_key: Optional[Text] = ... + source_tags: Sequence[Text] = ... + labels: Mapping[Text, Text] = ... + arch: Optional[Text] = ... + pull_info: Optional[ContainerImagePullInfo] = ... + +class SourceContainerImagePushItem(ContainerImagePushItem): ... +class OperatorManifestPushItem(PushItem): ... diff --git a/src/pushsource/_impl/model/erratum.pyi b/src/pushsource/_impl/model/erratum.pyi new file mode 100644 index 00000000..8f665446 --- /dev/null +++ b/src/pushsource/_impl/model/erratum.pyi @@ -0,0 +1,78 @@ +from collections import Mapping, Sequence, MutableSequence +from typing import Text, overload, List, Optional + +from pushsource import PushItem + +class ErratumReference(object): + href: Text + id: Text + title: Text + type: Text = ... + @classmethod + @overload + def _from_data( + cls, data: Sequence[Mapping[Text, ...]] + ) -> MutableSequence["ErratumReference"]: ... + @classmethod + @overload + def _from_data(cls, data: Mapping[Text, ...]) -> "ErratumReference": ... + +class ErratumModule(object): + name: Text + stream: Text + version: Text + context: Text + arch: Text + @classmethod + def _from_data(cls, data: Mapping[Text, ...]) -> Optional["ErratumModule"]: ... + +class ErratumPackage(object): + arch: Text + filename: Text + epoch: Text + name: Text + version: Text + release: Text + src: Text + reboot_suggested: bool = ... + md5sum: Optional[Text] = ... + sha1sum: Optional[Text] = ... + sha256sum: Optional[Text] = ... + +class ErratumPackageCollection(object): + name: Text = ... + packages: Sequence[ErratumPackage] = ... + short: Text = ... + module: Optional[ErratumModule] = ... + @classmethod + @overload + def _from_data( + cls, data: Sequence[Mapping[Text, ...]] + ) -> MutableSequence["ErratumPackageCollection"]: ... + @classmethod + @overload + def _from_data(cls, data: Mapping[Text, ...]) -> "ErratumPackageCollection": ... + +def errata_type_converter(value: Text) -> Text: ... + +class ErratumPushItem(PushItem): + type: Text = ... + release: Text = ... + status: Text = ... + pushcount: Text = ... + reboot_suggested: bool = ... + references: Sequence[ErratumReference] = ... + pkglist: Sequence[ErratumPackageCollection] = ... + from_: Optional[Text] = ... + rights: Optional[Text] = ... + title: Optional[Text] = ... + description: Optional[Text] = ... + version: Text = ... + updated: Text = ... + issued: Optional[Text] = ... + severity: Optional[Text] = ... + summary: Optional[Text] = ... + solution: Optional[Text] = ... + content_types: Sequence[Text] = ... + @classmethod + def _from_data(cls, data: Mapping[Text, ...]) -> "ErratumPushItem": ... diff --git a/src/pushsource/_impl/model/file.pyi b/src/pushsource/_impl/model/file.pyi new file mode 100644 index 00000000..1a9008e8 --- /dev/null +++ b/src/pushsource/_impl/model/file.pyi @@ -0,0 +1,7 @@ +from typing import Optional, Text + +from pushsource import PushItem + +class FilePushItem(PushItem): + description: Optional[Text] = ... + version: Optional[Text] = ... diff --git a/src/pushsource/_impl/model/modulemd.pyi b/src/pushsource/_impl/model/modulemd.pyi new file mode 100644 index 00000000..33af5c5d --- /dev/null +++ b/src/pushsource/_impl/model/modulemd.pyi @@ -0,0 +1,4 @@ +from pushsource import PushItem + +class ModuleMdPushItem(PushItem): ... +class ModuleMdSourcePushItem(PushItem): ... diff --git a/src/pushsource/_impl/model/productid.pyi b/src/pushsource/_impl/model/productid.pyi new file mode 100644 index 00000000..1f0e7680 --- /dev/null +++ b/src/pushsource/_impl/model/productid.pyi @@ -0,0 +1,3 @@ +from pushsource import PushItem + +class ProductIdPushItem(PushItem): ... diff --git a/src/pushsource/_impl/model/rpm.pyi b/src/pushsource/_impl/model/rpm.pyi new file mode 100644 index 00000000..a16f56c8 --- /dev/null +++ b/src/pushsource/_impl/model/rpm.pyi @@ -0,0 +1,6 @@ +from typing import Text, Optional + +from pushsource import PushItem + +class RpmPushItem(PushItem): + module_build: Optional[Text] = ... diff --git a/src/pushsource/_impl/source.pyi b/src/pushsource/_impl/source.pyi new file mode 100644 index 00000000..4a66dd47 --- /dev/null +++ b/src/pushsource/_impl/source.pyi @@ -0,0 +1,42 @@ +from collections import Mapping, Callable, Iterator +from types import TracebackType +from typing import Type, TypeVar, Any + +from pushsource._impl.model.base import PushItem_co + +SourceFactory = Callable[[], "Source"] + +# TODO: See the following example from PEP-484 to demonstrate +# why I think this could be a good idea for Source#get +# and Source#__enter__ annotations. +# https://www.python.org/dev/peps/pep-0484/#id33 +# I could easily be convinced that Source can be used +# in both of those instances without issue. +S = TypeVar("S", bound="Source") + +class SourceWrapper(object): + def __init__(self, delegate: "Source") -> None: ... + @classmethod + def _maybe_wrap(cls, delegate: "Source") -> "SourceWrapper": ... + +class Source(object): + _BACKENDS: Mapping[str, SourceFactory] + _BACKENDS_BUILTIN: Mapping[str, SourceFactory] + def __enter__(self) -> "Source": ... + def __exit__( + self, + exc_type: Type[BaseException], + exc_val: BaseException, + exc_tb: TracebackType, + ) -> None: ... + def __iter__(self) -> Iterator[PushItem_co]: ... + @classmethod + def get(cls, source_url: str, **kwargs: Any) -> "Source": ... + @classmethod + def get_partial(cls, source_url: str, **kwargs: Any) -> SourceFactory: ... + @classmethod + def register_backend(cls, name: str, factory: SourceFactory) -> None: ... + @classmethod + def _register_backend_builtin(cls, name: str, factory: SourceFactory) -> None: ... + @classmethod + def reset(cls) -> None: ... diff --git a/src/pushsource/_impl/type_aliases.pyi b/src/pushsource/_impl/type_aliases.pyi new file mode 100644 index 00000000..fa778367 --- /dev/null +++ b/src/pushsource/_impl/type_aliases.pyi @@ -0,0 +1,17 @@ +import datetime +from collections import Mapping, Sequence +from numbers import Number +from typing import Union, Collection, Optional + +Date = datetime.date +DateTime = datetime.datetime + +MaybeString = Union[str, Collection[str]] + +# First attempt at a JsonObject type based on the spec from +# https://www.json.org/json-en.html +# from the spec a Value can be a string, number, object, array, true, false, or null +# the "null" case is covered by Optional[] +_JsonValue = Optional[Union[str, Number, object, "_JsonArray", bool]] +_JsonArray = Sequence[_JsonValue] +JsonObject = Mapping[str, _JsonValue] diff --git a/src/pushsource/py.typed b/src/pushsource/py.typed new file mode 100644 index 00000000..e69de29b