Skip to content

Commit

Permalink
fix Nodes type hints (sync & async)
Browse files Browse the repository at this point in the history
  • Loading branch information
pbelskiy committed Nov 13, 2023
1 parent f76fa69 commit 91edbc1
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 66 deletions.
19 changes: 18 additions & 1 deletion ujenkins/adapters/aio.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import asyncio

from http import HTTPStatus
from typing import Any, Callable, List, Optional, Union
from typing import Any, Callable, Coroutine, List, Optional, Union

from aiohttp import (
BasicAuth,
Expand All @@ -14,6 +14,15 @@

from ujenkins.adapters import CRUMB_ISSUER_URL
from ujenkins.core import Jenkins, JenkinsError, JenkinsNotFoundError, Response
from ujenkins.endpoints import (
Builds,
Jobs,
Nodes,
Plugins,
Queue,
System,
Views,
)


class RetryClientSession:
Expand Down Expand Up @@ -109,6 +118,14 @@ def __init__(self,
"""
super().__init__()

self.builds = Builds(self)
self.jobs = Jobs(self)
self.nodes = Nodes[Coroutine[Any, Any, str]](self)
self.plugins = Plugins(self)
self.queue = Queue(self)
self.system = System(self)
self.views = Views(self)

self.host = url.rstrip('/')
self.crumb = None # type: Any

Expand Down
17 changes: 17 additions & 0 deletions ujenkins/adapters/sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@

from ujenkins.adapters import CRUMB_ISSUER_URL
from ujenkins.core import Jenkins, Response
from ujenkins.endpoints import (
Builds,
Jobs,
Nodes,
Plugins,
Queue,
System,
Views,
)
from ujenkins.exceptions import JenkinsError, JenkinsNotFoundError


Expand Down Expand Up @@ -94,6 +103,14 @@ def __init__(self,
"""
super().__init__()

self.builds = Builds(self)
self.jobs = Jobs(self)
self.nodes = Nodes[str](self)
self.plugins = Plugins(self)
self.queue = Queue(self)
self.system = System(self)
self.views = Views(self)

self.host = url.rstrip('/')
self.session = Session()

Expand Down
18 changes: 0 additions & 18 deletions ujenkins/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,6 @@
from multidict import CIMultiDictProxy
from requests.structures import CaseInsensitiveDict

from ujenkins.endpoints import (
Builds,
Jobs,
Nodes,
Plugins,
Queue,
System,
Views,
)
from ujenkins.exceptions import JenkinsError, JenkinsNotFoundError


Expand All @@ -27,15 +18,6 @@ class Response(NamedTuple):

class Jenkins:

def __init__(self) -> None:
self.builds = Builds(self)
self.jobs = Jobs(self)
self.nodes = Nodes(self)
self.plugins = Plugins(self)
self.queue = Queue(self)
self.system = System(self)
self.views = Views(self)

@staticmethod
def _process(response: Response, callback: Optional[Callable] = None) -> Any:
if response.status == HTTPStatus.NOT_FOUND:
Expand Down
98 changes: 51 additions & 47 deletions ujenkins/endpoints/nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@
import xml.etree.ElementTree

from functools import partial
from typing import Any, Dict, List
from typing import Any, Generic, List, TypeVar, cast

from ujenkins.exceptions import JenkinsError, JenkinsNotFoundError
from ujenkins.helpers import parse_build_url

R = TypeVar('R')


def _parse_rss(rss: str) -> List[dict]:
builds = []
Expand All @@ -28,7 +30,7 @@ def _parse_rss(rss: str) -> List[dict]:
return list(reversed(builds))


class Nodes:
class Nodes(Generic[R]):

def __init__(self, jenkins) -> None:
self.jenkins = jenkins
Expand All @@ -40,7 +42,7 @@ def _normalize_name(name: str) -> str:
return '(master)'
return name

def get(self) -> Dict[str, dict]:
def get(self) -> R: # Dict[str, dict]
"""
Get all available nodes on server.
Expand All @@ -61,13 +63,13 @@ def callback(response):
nodes = json.loads(response.text)
return {v['displayName']: v for v in nodes['computer']}

return self.jenkins._request(
return cast(R, self.jenkins._request(
'GET',
'/computer/api/json',
_callback=callback,
)
))

def get_failed_builds(self, name: str) -> List[dict]:
def get_failed_builds(self, name: str) -> R: # List[dict]
"""
Return list of detalizied failed builds for node name. Actually it
parsed from RSS feed. usefull for build restart. Ascending builds sort.
Expand All @@ -94,13 +96,13 @@ def callback(response) -> List[dict]:

name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'GET',
f'/computer/{name}/rssFailed',
_callback=callback
)
))

def get_all_builds(self, name: str) -> List[dict]:
def get_all_builds(self, name: str) -> R: # List[dict]
"""
Return list of all detalizied builds for node name, actually it parsed
from RSS feed. Ascending builds sort.
Expand All @@ -127,13 +129,13 @@ def callback(response) -> List[dict]:

name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'GET',
f'/computer/{name}/rssAll',
_callback=callback
)
))

def get_info(self, name: str) -> dict:
def get_info(self, name: str) -> R: # dict
"""
Get node detailed information.
Expand All @@ -159,9 +161,9 @@ def callback2(response: Any):
return response

name = self._normalize_name(name)
return self.jenkins._chain([callback1, callback2])
return cast(R, self.jenkins._chain([callback1, callback2]))

def get_config(self, name: str) -> str:
def get_config(self, name: str) -> R: # str
"""
Return node config in XML format.
Expand All @@ -174,13 +176,13 @@ def get_config(self, name: str) -> str:
"""
name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'GET',
f'/computer/{name}/config.xml',
_callback=self.jenkins._return_text,
)
))

def is_exists(self, name: str) -> bool:
def is_exists(self, name: str) -> R: # bool
"""
Check is node exist.
Expand All @@ -192,7 +194,7 @@ def is_exists(self, name: str) -> bool:
bool: node existing.
"""
if name == '':
return False
return cast(R, False)

def callback1(_) -> Any:
return partial(self.get_info, name)
Expand All @@ -203,9 +205,9 @@ def callback2(response: Any) -> bool:

return True

return self.jenkins._chain([callback1, callback2])
return cast(R, self.jenkins._chain([callback1, callback2]))

def create(self, name: str, config: dict) -> None:
def create(self, name: str, config: dict) -> R: # None
"""
Create new node.
Expand All @@ -225,7 +227,7 @@ def create(self, name: str, config: dict) -> None:
def callback1(_) -> Any:
return partial(self.get)

def callback2(response: Any) -> bool:
def callback2(response: Any) -> R: # bool
# if not check, then we get 400 error with unclear stacktrace
if name in response:
raise JenkinsError(f'Node `{name}` is already exists')
Expand All @@ -241,15 +243,15 @@ def callback2(response: Any) -> bool:
'json': json.dumps(config)
}

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
'/computer/doCreateItem',
params=params,
)
))

return self.jenkins._chain([callback1, callback2])
return cast(R, self.jenkins._chain([callback1, callback2]))

def delete(self, name: str) -> None:
def delete(self, name: str) -> R: # None
"""
Delete node.
Expand All @@ -262,12 +264,12 @@ def delete(self, name: str) -> None:
"""
name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
f'/computer/{name}/doDelete'
)
))

def reconfigure(self, name: str, config: str) -> None:
def reconfigure(self, name: str, config: str) -> R: # None
"""
Reconfigure node.
Expand All @@ -286,14 +288,14 @@ def reconfigure(self, name: str, config: str) -> None:
if name == '(master)':
raise JenkinsError('Cannot reconfigure master node')

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
f'/computer/{name}/config.xml',
data=config,
headers={'Content-Type': 'text/xml'},
)
))

def enable(self, name: str) -> None:
def enable(self, name: str) -> R:
"""
Enable node if it disabled.
Expand All @@ -309,16 +311,18 @@ def enable(self, name: str) -> None:
def callback1(_) -> Any:
return partial(self.get_info, name)

def callback2(response: dict) -> None:
def callback2(response: dict) -> R: # None
# skip if already enabled
if response['temporarilyOffline'] is False:
return None
return cast(R, None)

return self.jenkins._request('POST', f'/computer/{name}/toggleOffline')
return cast(R, self.jenkins._request(
'POST', f'/computer/{name}/toggleOffline'
))

return self.jenkins._chain([callback1, callback2])
return cast(R, self.jenkins._chain([callback1, callback2]))

def disable(self, name: str, message: str = '') -> None:
def disable(self, name: str, message: str = '') -> R: # None
"""
Disable node if it enabled.
Expand All @@ -337,20 +341,20 @@ def disable(self, name: str, message: str = '') -> None:
def callback1(_) -> Any:
return partial(self.get_info, name)

def callback2(response: dict) -> None:
def callback2(response: dict) -> R:
# skip if already disabled
if response['temporarilyOffline'] is True:
return None
return cast(R, None)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
f'/computer/{name}/toggleOffline',
params={'offlineMessage': message},
)
))

return self.jenkins._chain([callback1, callback2])
return cast(R, self.jenkins._chain([callback1, callback2]))

def update_offline_reason(self, name: str, message: str) -> None:
def update_offline_reason(self, name: str, message: str) -> R: # None
"""
Update reason message of disabled node.
Expand All @@ -366,13 +370,13 @@ def update_offline_reason(self, name: str, message: str) -> None:
"""
name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
f'/computer/{name}/changeOfflineCause',
params={'offlineMessage': message}
)
))

def launch_agent(self, name: str) -> None:
def launch_agent(self, name: str) -> R: # None
"""
Launch agent on node, for example in case when disconnected.
Expand All @@ -388,7 +392,7 @@ def launch_agent(self, name: str) -> None:
"""
name = self._normalize_name(name)

return self.jenkins._request(
return cast(R, self.jenkins._request(
'POST',
f'/computer/{name}/launchSlaveAgent',
)
))

0 comments on commit 91edbc1

Please sign in to comment.