From 59bceb4426a5f7e9e4f2d2ec7e1136513dc43b78 Mon Sep 17 00:00:00 2001 From: Joe Lee Date: Tue, 31 Oct 2023 09:57:20 +0800 Subject: [PATCH] Update README (#94) * update README.md --- README.md | 10 +++---- api4jenkins/__init__.py | 37 ++++++++++++------------ api4jenkins/__version__.py | 2 +- api4jenkins/node.py | 16 ++--------- docs/requirements.txt | 1 + docs/source/conf.py | 25 ++++++++++------- docs/source/index.rst | 54 ++++++++++++++++++++++++++---------- docs/source/user/example.rst | 2 +- setup.py | 2 +- tests/unit/test_jenkins.py | 18 ++---------- 10 files changed, 84 insertions(+), 83 deletions(-) diff --git a/README.md b/README.md index 9a2bab3..929503b 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -[![Unit Test](https://github.com/joelee2012/api4jenkins/actions/workflows/unittest.yml/badge.svg?branch=2.0)](https://github.com/joelee2012/api4jenkins/actions/workflows/unittest.yml) -[![Integration Test](https://github.com/joelee2012/api4jenkins/actions/workflows/integration.yml/badge.svg?branch=2.0)](https://github.com/joelee2012/api4jenkins/actions/workflows/integration.yml) -![CodeQL](https://github.com/joelee2012/api4jenkins/workflows/CodeQL/badge.svg?branch=2.0) -[![codecov](https://codecov.io/gh/joelee2012/api4jenkins/branch/2.0/graph/badge.svg?token=YGM4CIB149)](https://codecov.io/gh/joelee2012/api4jenkins) +[![Unit Test](https://github.com/joelee2012/api4jenkins/actions/workflows/unittest.yml/badge.svg?branch=main)](https://github.com/joelee2012/api4jenkins/actions/workflows/unittest.yml) +[![Integration Test](https://github.com/joelee2012/api4jenkins/actions/workflows/integration.yml/badge.svg?branch=main)](https://github.com/joelee2012/api4jenkins/actions/workflows/integration.yml) +![CodeQL](https://github.com/joelee2012/api4jenkins/workflows/CodeQL/badge.svg?branch=main) +[![codecov](https://codecov.io/gh/joelee2012/api4jenkins/branch/main/graph/badge.svg?token=YGM4CIB149)](https://codecov.io/gh/joelee2012/api4jenkins) ![PyPI](https://img.shields.io/pypi/v/api4jenkins) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/api4jenkins) ![PyPI - Wheel](https://img.shields.io/pypi/wheel/api4jenkins) @@ -66,7 +66,7 @@ Finished: SUCCESS False >>> build.result 'SUCCESS' - ``` +``` Async example diff --git a/api4jenkins/__init__.py b/api4jenkins/__init__.py index 540cb6f..d837811 100644 --- a/api4jenkins/__init__.py +++ b/api4jenkins/__init__.py @@ -8,11 +8,8 @@ from api4jenkins.mix import UrlMixIn -from .__version__ import (__author__, __author_email__, __copyright__, - __description__, __license__, __title__, __url__, - __version__) from .credential import AsyncCredentials, Credentials -from .exceptions import AuthenticationError, ItemNotFoundError +from .exceptions import ItemNotFoundError from .http import new_async_http_client, new_http_client from .item import AsyncItem, Item from .job import AsyncFolder, AsyncProject, Folder @@ -35,7 +32,7 @@ class Jenkins(Item, UrlMixIn): :param token: (optional) Boolean, Create user token when initialize instance and revoke token once instance is destroied. useful when LDAP server refuse username and password used too much often. Defaults to ``False``. - :param \*\*kwargs: other kwargs are same as `requests.Session.request `_ + :param \*\*kwargs: other kwargs are same as `httpx.Client `_ Usage:: @@ -226,16 +223,16 @@ def _resolve_name(self, full_name): parent, name = self._parse_name(full_name) return Folder(self, self._name2url(parent)), name - def exists(self): - '''Check if Jenkins server is up + # def exists(self): + # '''Check if Jenkins server is up - :returns: True or False - ''' - try: - self.handle_req('GET', '') - return True - except Exception as e: - return isinstance(e, (AuthenticationError, PermissionError)) + # :returns: True or False + # ''' + # try: + # self._request('HEAD', self.url) + # return True + # except Exception as e: + # return isinstance(e, (AuthenticationError, PermissionError)) @property def crumb(self): @@ -390,12 +387,12 @@ def _resolve_name(self, full_name): parent, name = self._parse_name(full_name) return AsyncFolder(self, self._name2url(parent)), name - async def exists(self): - try: - await self.handle_req('GET', '') - return True - except Exception as e: - return isinstance(e, (AuthenticationError, PermissionError)) + # async def exists(self): + # try: + # await self._request('HEAD', self.url) + # return True + # except Exception as e: + # return isinstance(e, (AuthenticationError, PermissionError)) @property async def crumb(self): diff --git a/api4jenkins/__version__.py b/api4jenkins/__version__.py index 38951aa..42acd92 100644 --- a/api4jenkins/__version__.py +++ b/api4jenkins/__version__.py @@ -1,5 +1,5 @@ # encoding: utf-8 -__version__ = '2.0.0' +__version__ = '2.0.1' __title__ = 'api4jenkins' __description__ = 'Jenkins Python Client' __url__ = 'https://github.com/joelee2012/api4jenkins' diff --git a/api4jenkins/node.py b/api4jenkins/node.py index c770bbb..130954e 100644 --- a/api4jenkins/node.py +++ b/api4jenkins/node.py @@ -141,13 +141,7 @@ class SlaveComputer(Node): class KubernetesComputer(Node): - - def exists(self): - try: - self.handle_req('GET', '') - return True - except ItemNotFoundError: - return False + pass class DockerComputer(Node): @@ -225,13 +219,7 @@ class AsyncSlaveComputer(AsyncNode): class AsyncKubernetesComputer(AsyncNode): - - async def exists(self): - try: - await self.handle_req('GET', '') - return True - except ItemNotFoundError: - return False + pass class AsyncDockerComputer(AsyncNode): diff --git a/docs/requirements.txt b/docs/requirements.txt index f7621d1..15f13d2 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1 +1,2 @@ httpx +sphinx \ No newline at end of file diff --git a/docs/source/conf.py b/docs/source/conf.py index e70915e..7a59e1a 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -10,21 +10,26 @@ # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # -import os + import sys +from pathlib import Path -sys.path.insert(0, os.path.abspath('../../')) +repo_root = Path(__file__).parent.parent.parent +sys.path.insert(0, str(repo_root)) # -- Project information ----------------------------------------------------- -import api4jenkins -project = 'api4jenkins' -copyright = '2023, Joe Lee' -author = 'Joe Lee' +about = {} +with open(repo_root.joinpath('api4jenkins', '__version__.py')) as f: + exec(f.read(), about) -# The full version, including alpha/beta/rc tags -release = api4jenkins.__version__ +project = about['__title__'] +copyright = about['__copyright__'] +author = about['__author__'] +# The full version, including alpha/beta/rc tags +release = about['__version__'] +version = about['__version__'] # -- General configuration --------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be @@ -33,7 +38,7 @@ extensions = ["sphinx.ext.autodoc", "sphinx.ext.intersphinx", "sphinx.ext.todo", - "sphinx.ext.viewcode", ] + "sphinx.ext.viewcode",] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] @@ -54,6 +59,6 @@ # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -html_static_path = ['_static'] +# html_static_path = ['_static'] master_doc = 'index' diff --git a/docs/source/index.rst b/docs/source/index.rst index fa6d258..7d522aa 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -26,12 +26,13 @@ Jenkins Python Client Features -------- +- Provides ``sync`` and ``async`` APIs - Object oriented, each Jenkins item has corresponding class, easy to use and extend -- Base on `api/json`, easy to query/filter attribute of item +- Base on ``api/json``, easy to query/filter attribute of item - Setup relationship between class just like Jenkins item - Support api for almost every Jenkins item - Pythonic -- Test with latest Jenkins LTS +- Test with latest `Jenkins LTS `_ Quick start @@ -40,10 +41,11 @@ Quick start Here is an example to create and build job, then monitor progressive output until it's done. +Sync example:: >>> from api4jenkins import Jenkins - >>> j = Jenkins('http://127.0.0.1:8080/', auth=('admin', 'admin')) - >>> j.version + >>> client = Jenkins('http://127.0.0.1:8080/', auth=('admin', 'admin')) + >>> client.version '2.176.2' >>> xml = """ ... @@ -53,21 +55,12 @@ until it's done. ... ... ... """ - >>> j.create_job('freestylejob', xml) - >>> job = j.get_job('freestylejob') - >>> print(job) - - >>> print(job.parent) - - >>> print(job.jenkins) - + >>> client.create_job('path/to/job', xml) >>> import time - >>> item = job.build() + >>> item = client.build_job('path/to/job') >>> while not item.get_build(): ... time.sleep(1) >>> build = item.get_build() - >>> print(build) - >>> for line in build.progressive_output(): ... print(line) ... @@ -84,6 +77,37 @@ until it's done. 'SUCCESS' +Async example:: + + import asyncio + import time + from api4jenkins import AsyncJenkins + + async main(): + client = AsyncJenkins('http://127.0.0.1:8080/', auth=('admin', 'admin')) + print(await client.version) + xml = """ + + + + echo $JENKINS_VERSION + + + """ + await client.create_job('job', xml) + item = await client.build_job('job') + while not await item.get_build(): + time.sleep(1) + build = await item.get_build() + async for line in build.progressive_output(): + print(line) + + print(await build.building) + print(await build.result) + + asyncio.run(main()) + + .. toctree:: :maxdepth: 2 :caption: Contents: diff --git a/docs/source/user/example.rst b/docs/source/user/example.rst index 323f639..5fe7c32 100644 --- a/docs/source/user/example.rst +++ b/docs/source/user/example.rst @@ -23,7 +23,7 @@ object is destoried by garbage collection. .. note:: - Any parameter supported by `requests.Session.request `_ + Any parameter supported by `httpx.Client `_ can be passed to initialize Jenkins object. Now, we have a :class:`Jenkins ` object `j`, let's check diff --git a/setup.py b/setup.py index 8f3902b..63801c3 100644 --- a/setup.py +++ b/setup.py @@ -27,7 +27,7 @@ author_email=about['__author_email__'], packages=['api4jenkins'], package_data={'': ['LICENSE']}, - python_requires='>=3.7', + python_requires='>=3.8', install_requires=requires, license=about['__license__'], keywords=["RESTAPI", "Jenkins"], diff --git a/tests/unit/test_jenkins.py b/tests/unit/test_jenkins.py index 4119b7f..0212f83 100644 --- a/tests/unit/test_jenkins.py +++ b/tests/unit/test_jenkins.py @@ -1,10 +1,8 @@ -import weakref - import pytest -from api4jenkins import Jenkins + from api4jenkins.exceptions import BadRequestError, ItemNotFoundError from api4jenkins.item import new_item, snake -from api4jenkins.job import AsyncFolder, AsyncWorkflowJob, WorkflowJob, Folder +from api4jenkins.job import AsyncFolder, AsyncWorkflowJob, Folder, WorkflowJob class TestJenkins: @@ -125,12 +123,6 @@ def test__url2name_value_error(self, jenkins): def test__name2url(self, jenkins, name, url_entry): assert jenkins._name2url(name) == f'{jenkins.url}{url_entry}' - @pytest.mark.parametrize('status, exist', [(403, True), (200, True), - (404, False), (500, False)]) - def test_exists(self, jenkins, respx_mock, status, exist): - respx_mock.get(jenkins.url).respond(status) - assert jenkins.exists() == exist - def test_iter_jobs(self, jenkins): assert len(list(jenkins.iter(1))) == 5 assert len(list(jenkins)) == 5 @@ -285,12 +277,6 @@ async def test__name2url(self, async_jenkins, name, url_entry): assert async_jenkins._name2url( name) == f'{async_jenkins.url}{url_entry}' - @pytest.mark.parametrize('status, exist', [(403, True), (200, True), - (404, False), (500, False)]) - async def test_exists(self, async_jenkins, respx_mock, status, exist): - respx_mock.get(async_jenkins.url).respond(status) - assert await async_jenkins.exists() == exist - async def test_iter_jobs(self, async_jenkins): assert len([j async for j in async_jenkins]) == 5 assert len([j async for j in async_jenkins]) == 5