From f1bf1c01fecc19cdb380bc4d2adf961308b98e91 Mon Sep 17 00:00:00 2001 From: kasium <15907922+kasium@users.noreply.github.com> Date: Fri, 20 Sep 2024 05:12:09 +0200 Subject: [PATCH] Add stubs for python-jenkins (#12582) Co-authored-by: Jelle Zijlstra --- .../@tests/stubtest_allowlist.txt | 2 + stubs/python-jenkins/METADATA.toml | 3 + stubs/python-jenkins/jenkins/__init__.pyi | 239 ++++++++++++++++++ stubs/python-jenkins/jenkins/plugins.pyi | 15 ++ stubs/python-jenkins/jenkins/version.pyi | 3 + 5 files changed, 262 insertions(+) create mode 100644 stubs/python-jenkins/@tests/stubtest_allowlist.txt create mode 100644 stubs/python-jenkins/METADATA.toml create mode 100644 stubs/python-jenkins/jenkins/__init__.pyi create mode 100644 stubs/python-jenkins/jenkins/plugins.pyi create mode 100644 stubs/python-jenkins/jenkins/version.pyi diff --git a/stubs/python-jenkins/@tests/stubtest_allowlist.txt b/stubs/python-jenkins/@tests/stubtest_allowlist.txt new file mode 100644 index 000000000..787864001 --- /dev/null +++ b/stubs/python-jenkins/@tests/stubtest_allowlist.txt @@ -0,0 +1,2 @@ +# The default value for the timeout parameter is a token from urllib3 which is private and should not be used by end-users +jenkins.Jenkins.__init__ diff --git a/stubs/python-jenkins/METADATA.toml b/stubs/python-jenkins/METADATA.toml new file mode 100644 index 000000000..c6777f45f --- /dev/null +++ b/stubs/python-jenkins/METADATA.toml @@ -0,0 +1,3 @@ +version = "1.8.*" +upstream_repository = "https://opendev.org/jjb/python-jenkins" +requires = ["types-requests"] diff --git a/stubs/python-jenkins/jenkins/__init__.pyi b/stubs/python-jenkins/jenkins/__init__.pyi new file mode 100644 index 000000000..0394676ed --- /dev/null +++ b/stubs/python-jenkins/jenkins/__init__.pyi @@ -0,0 +1,239 @@ +from _typeshed import Incomplete +from collections.abc import Mapping, MutableMapping, Sequence +from re import Pattern +from typing import Any, Final, Literal, overload +from typing_extensions import TypeAlias + +import requests +from requests.models import Request, Response +from requests.sessions import _Auth + +LAUNCHER_SSH: Final[str] +LAUNCHER_COMMAND: Final[str] +LAUNCHER_JNLP: Final[str] +LAUNCHER_WINDOWS_SERVICE: Final[str] +DEFAULT_HEADERS: Final[dict[str, str]] +DEFAULT_TIMEOUT: Final[float] +INFO: Final[str] +PLUGIN_INFO: Final[str] +CRUMB_URL: Final[str] +WHOAMI_URL: Final[str] +JOBS_QUERY: Final[str] +JOBS_QUERY_TREE: Final[str] +JOB_INFO: Final[str] +JOB_NAME: Final[str] +ALL_BUILDS: Final[str] +Q_INFO: Final[str] +Q_ITEM: Final[str] +CANCEL_QUEUE: Final[str] +CREATE_JOB: Final[str] +CONFIG_JOB: Final[str] +DELETE_JOB: Final[str] +ENABLE_JOB: Final[str] +DISABLE_JOB: Final[str] +CHECK_JENKINSFILE_SYNTAX: Final[str] +SET_JOB_BUILD_NUMBER: Final[str] +COPY_JOB: Final[str] +RENAME_JOB: Final[str] +BUILD_JOB: Final[str] +STOP_BUILD: Final[str] +BUILD_WITH_PARAMS_JOB: Final[str] +BUILD_INFO: Final[str] +BUILD_CONSOLE_OUTPUT: Final[str] +BUILD_ENV_VARS: Final[str] +BUILD_TEST_REPORT: Final[str] +BUILD_ARTIFACT: Final[str] +BUILD_STAGES: Final[str] +DELETE_BUILD: Final[str] +WIPEOUT_JOB_WORKSPACE: Final[str] +NODE_LIST: Final[str] +CREATE_NODE: Final[str] +DELETE_NODE: Final[str] +NODE_INFO: Final[str] +NODE_TYPE: Final[str] +TOGGLE_OFFLINE: Final[str] +CONFIG_NODE: Final[str] +VIEW_NAME: Final[str] +VIEW_JOBS: Final[str] +CREATE_VIEW: Final[str] +CONFIG_VIEW: Final[str] +DELETE_VIEW: Final[str] +SCRIPT_TEXT: Final[str] +NODE_SCRIPT_TEXT: Final[str] +PROMOTION_NAME: Final[str] +PROMOTION_INFO: Final[str] +DELETE_PROMOTION: Final[str] +CREATE_PROMOTION: Final[str] +CONFIG_PROMOTION: Final[str] +LIST_CREDENTIALS: Final[str] +CREATE_CREDENTIAL: Final[str] +CONFIG_CREDENTIAL: Final[str] +CREDENTIAL_INFO: Final[str] +QUIET_DOWN: Final[str] +EMPTY_CONFIG_XML: Final[str] +EMPTY_FOLDER_XML: Final[str] +RECONFIG_XML: Final[str] +EMPTY_VIEW_CONFIG_XML: Final[str] +EMPTY_PROMO_CONFIG_XML: Final[str] +PROMO_RECONFIG_XML: Final[str] + +class JenkinsException(Exception): ... +class NotFoundException(JenkinsException): ... +class EmptyResponseException(JenkinsException): ... +class BadHTTPException(JenkinsException): ... +class TimeoutException(JenkinsException): ... + +class WrappedSession(requests.Session): + # merge_environment_settings wraps requests.Session.merge_environment_settings + # w/o changing the type signature + ... + +_JSONValue: TypeAlias = Any # too many possibilities to express +_JSON: TypeAlias = dict[str, _JSONValue] + +class Jenkins: + server: str + auth: _Auth | None + crumb: Mapping[str, Incomplete] | bool | Incomplete + timeout: int + def __init__(self, url: str, username: str | None = None, password: str | None = None, timeout: int = ...) -> None: ... + def maybe_add_crumb(self, req: Request) -> None: ... + def get_job_info(self, name: str, depth: int = 0, fetch_all_builds: bool = False) -> _JSON: ... + def get_job_info_regex( + self, pattern: str | Pattern[str], depth: int = 0, folder_depth: int = 0, folder_depth_per_request: int = 10 + ) -> list[_JSON]: ... + def get_job_name(self, name: str) -> str | None: ... + def debug_job_info(self, job_name: str) -> None: ... + def jenkins_open(self, req: Request, add_crumb: bool = True, resolve_auth: bool = True) -> str: ... + def jenkins_open_stream(self, req: Request, add_crumb: bool = True, resolve_auth: bool = True) -> Response: ... + def jenkins_request( + self, req: Request, add_crumb: bool = True, resolve_auth: bool = True, stream: bool | None = None + ) -> Response: ... + def get_queue_item(self, number: int, depth: int = 0) -> _JSON: ... + def get_build_info(self, name: str, number: int, depth: int = 0) -> _JSON: ... + def get_build_env_vars(self, name: str, number: int, depth: int = 0) -> _JSON | None: ... + def get_build_test_report(self, name: str, number: int, depth: int = 0) -> _JSON | None: ... + def get_build_artifact(self, name: str, number: int, artifact: str) -> _JSON: ... + def get_build_artifact_as_bytes(self, name: str, number: int, artifact: str) -> bytes: ... + def get_build_stages(self, name: str, number: int) -> _JSON: ... + def get_queue_info(self) -> _JSON: ... + def cancel_queue(self, id: int) -> None: ... + def get_info(self, item: str = "", query: str | None = None) -> _JSON: ... + def get_whoami(self, depth: int = 0) -> _JSON: ... + def get_version(self) -> str: ... + def get_plugins_info(self, depth: int = 2) -> _JSON: ... + def get_plugin_info(self, name: str, depth: int = 2) -> _JSON: ... + def get_plugins(self, depth: int = 2) -> _JSON: ... + def get_jobs( + self, folder_depth: int = 0, folder_depth_per_request: int = 10, view_name: str | None = None + ) -> list[dict[str, str]]: ... + def get_all_jobs(self, folder_depth: int | None = None, folder_depth_per_request: int = 10) -> list[dict[str, str]]: ... + def copy_job(self, from_name: str, to_name: str) -> None: ... + def rename_job(self, from_name: str, to_name: str) -> None: ... + def delete_job(self, name: str) -> None: ... + def enable_job(self, name: str) -> None: ... + def disable_job(self, name: str) -> None: ... + def set_next_build_number(self, name: str, number: int) -> None: ... + def job_exists(self, name: str) -> bool: ... + def jobs_count(self) -> int: ... + def assert_job_exists(self, name: str, exception_message: str = "job[%s] does not exist") -> None: ... + def create_folder(self, folder_name: str, ignore_failures: bool = False) -> None: ... + def upsert_job(self, name: str, config_xml: str) -> None: ... + def check_jenkinsfile_syntax(self, jenkinsfile: str) -> list[str]: ... + def create_job(self, name: str, config_xml: str) -> None: ... + def get_job_config(self, name: str) -> str: ... + def reconfig_job(self, name: str, config_xml: str) -> None: ... + @overload + def build_job_url( + self, + name: str, + parameters: Mapping[str, Incomplete] | Sequence[tuple[str, Incomplete]] | None = None, + token: Literal[""] | None = None, + ) -> str: ... + @overload + def build_job_url( + self, name: str, parameters: dict[str, Incomplete] | list[tuple[str, Incomplete]] | None, token: str + ) -> str: ... + @overload + def build_job_url( + self, name: str, parameters: dict[str, Incomplete] | list[tuple[str, Incomplete]] | None = None, *, token: str + ) -> str: ... + @overload + def build_job( + self, + name: str, + parameters: Mapping[str, Incomplete] | Sequence[tuple[str, Incomplete]] | None = None, + token: Literal[""] | None = None, + ) -> int: ... + @overload + def build_job( + self, name: str, parameters: dict[str, Incomplete] | list[tuple[str, Incomplete]] | None, token: str + ) -> int: ... + @overload + def build_job( + self, name: str, parameters: dict[str, Incomplete] | list[tuple[str, Incomplete]] | None = None, *, token: str + ) -> int: ... + def run_script(self, script: str, node: str | None = None) -> str: ... + def install_plugin(self, name: str, include_dependencies: bool = True) -> bool: ... + def stop_build(self, name: str, number: int) -> None: ... + def delete_build(self, name: str, number: int) -> None: ... + def wipeout_job_workspace(self, name: str) -> None: ... + def get_running_builds(self) -> list[_JSON]: ... + def get_nodes(self, depth: int = 0) -> list[_JSON]: ... + def get_node_info(self, name: str, depth: int = 0) -> _JSON: ... + def node_exists(self, name: str) -> bool: ... + def assert_node_exists(self, name: str, exception_message: str = "node[%s] does not exist") -> None: ... + def delete_node(self, name: str) -> None: ... + def disable_node(self, name: str, msg: str = "") -> None: ... + def enable_node(self, name: str) -> None: ... + def create_node( + self, + name: str, + numExecutors: int = 2, + nodeDescription: str | None = None, + remoteFS: str = "/var/lib/jenkins", + labels: str | None = None, + exclusive: bool = False, + launcher: str = "hudson.slaves.CommandLauncher", + launcher_params: MutableMapping[str, Incomplete] = {}, + ) -> None: ... + def get_node_config(self, name: str) -> str: ... + def reconfig_node(self, name: str, config_xml: str) -> None: ... + def get_build_console_output(self, name: str, number: int) -> str: ... + def get_view_name(self, name: str) -> str | None: ... + def assert_view_exists(self, name: str, exception_message: str = "view[%s] does not exist") -> None: ... + def view_exists(self, name: str) -> bool: ... + def get_views(self) -> list[_JSON]: ... + def delete_view(self, name: str) -> None: ... + def create_view(self, name: str, config_xml: str) -> None: ... + def reconfig_view(self, name: str, config_xml: str) -> None: ... + def get_view_config(self, name: str) -> str: ... + def get_promotion_name(self, name: str, job_name: str) -> str | None: ... + def assert_promotion_exists( + self, name: str, job_name: str, exception_message: str = "promotion[%s] does not exist for job[%s]" + ) -> None: ... + def promotion_exists(self, name: str, job_name: str) -> bool: ... + def get_promotions_info(self, job_name: str, depth: int = 0) -> _JSON: ... + def get_promotions(self, job_name: str) -> list[_JSON]: ... + def delete_promotion(self, name: str, job_name: str) -> None: ... + def create_promotion(self, name: str, job_name: str, config_xml: str) -> None: ... + def reconfig_promotion(self, name: str, job_name: str, config_xml: str) -> None: ... + def get_promotion_config(self, name: str, job_name: str) -> str: ... + def assert_folder(self, name: str, exception_message: str = "job[%s] is not a folder") -> None: ... + def is_folder(self, name: str) -> bool: ... + def assert_credential_exists( + self, + name: str, + folder_name: str, + domain_name: str = "_", + exception_message: str = "credential[%s] does not exist in the domain[%s] of [%s]", # noqa: Y053 + ) -> None: ... + def credential_exists(self, name: str, folder_name: str, domain_name: str = "_") -> bool: ... + def get_credential_info(self, name: str, folder_name: str, domain_name: str = "_") -> _JSON: ... + def get_credential_config(self, name: str, folder_name: str, domain_name: str = "_") -> str: ... + def create_credential(self, folder_name: str, config_xml: str, domain_name: str = "_") -> None: ... + def delete_credential(self, name: str, folder_name: str, domain_name: str = "_") -> None: ... + def reconfig_credential(self, folder_name: str, config_xml: str, domain_name: str = "_") -> None: ... + def list_credentials(self, folder_name: str, domain_name: str = "_") -> list[Incomplete]: ... + def quiet_down(self) -> None: ... + def wait_for_normal_op(self, timeout: int) -> bool: ... diff --git a/stubs/python-jenkins/jenkins/plugins.pyi b/stubs/python-jenkins/jenkins/plugins.pyi new file mode 100644 index 000000000..aa3ce52a1 --- /dev/null +++ b/stubs/python-jenkins/jenkins/plugins.pyi @@ -0,0 +1,15 @@ +from typing import Any + +# Any: Union of possible plugin values is too complex +class Plugin(dict[str, Any]): + # __init__ wraps dict.__init__ w/o changing the type signature + def __setitem__(self, key: str, value: Any) -> None: ... + +class PluginVersion(str): + def __init__(self, version: str) -> None: ... + def __le__(self, version: object) -> bool: ... + def __lt__(self, version: object) -> bool: ... + def __ge__(self, version: object) -> bool: ... + def __gt__(self, version: object) -> bool: ... + def __eq__(self, version: object) -> bool: ... + def __ne__(self, version: object) -> bool: ... diff --git a/stubs/python-jenkins/jenkins/version.pyi b/stubs/python-jenkins/jenkins/version.pyi new file mode 100644 index 000000000..7b63c09e3 --- /dev/null +++ b/stubs/python-jenkins/jenkins/version.pyi @@ -0,0 +1,3 @@ +from _typeshed import Incomplete + +version_info: Incomplete # pbr.version.VersionInfo