Skip to content

Commit a96a3d0

Browse files
committed
refactor: improve documentation and formatting
1 parent b85b7c3 commit a96a3d0

File tree

1 file changed

+57
-51
lines changed

1 file changed

+57
-51
lines changed

kanboard.py

Lines changed: 57 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from urllib import request as http
3030

3131

32-
DEFAULT_AUTH_HEADER = 'Authorization'
32+
DEFAULT_AUTH_HEADER = "Authorization"
3333
ASYNC_FUNCNAME_MARKER = "_async"
3434

3535

@@ -39,43 +39,51 @@ class ClientError(Exception):
3939

4040
class Client:
4141
"""
42-
Kanboard API client
42+
Kanboard API client for interacting with the Kanboard JSON-RPC API.
4343
44-
Example:
44+
This client provides both synchronous and asynchronous access to all Kanboard API methods.
45+
Methods are dynamically resolved based on the Kanboard API method names, using snake_case for Python calls.
4546
47+
Example usage:
4648
from kanboard import Client
4749
4850
kb = Client(url="http://localhost/jsonrpc.php",
4951
username="jsonrpc",
5052
password="your_api_token")
5153
5254
project_id = kb.create_project(name="My project")
53-
5455
"""
5556

56-
def __init__(self,
57-
url: str,
58-
username: str,
59-
password: str,
60-
auth_header: str = DEFAULT_AUTH_HEADER,
61-
cafile: Optional[str] = None,
62-
insecure: bool = False,
63-
ignore_hostname_verification: bool = False,
64-
user_agent: str = 'Kanboard Python API Client',
65-
loop: Optional[asyncio.AbstractEventLoop] = None):
57+
def __init__(
58+
self,
59+
url: str,
60+
username: str,
61+
password: str,
62+
auth_header: str = DEFAULT_AUTH_HEADER,
63+
cafile: Optional[str] = None,
64+
insecure: bool = False,
65+
ignore_hostname_verification: bool = False,
66+
user_agent: str = "Kanboard Python API Client",
67+
loop: Optional[asyncio.AbstractEventLoop] = None,
68+
):
6669
"""
67-
Constructor
70+
Initialize a new Kanboard API client instance.
6871
6972
Args:
70-
url: API url endpoint
71-
username: API username or real username
72-
password: API token or user password
73-
auth_header: API HTTP header
74-
cafile: Path to a custom CA certificate
75-
insecure: Ignore SSL certificate errors and ignore hostname mismatches
76-
ignore_hostname_verification: Ignore SSL certificate hostname verification
77-
user_agent: Use a personalized user agent
78-
loop: An asyncio event loop. Default: asyncio.get_event_loop()
73+
url (str): The Kanboard JSON-RPC API endpoint URL (e.g.,
74+
'http://localhost/jsonrpc.php').
75+
username (str): Kanboard API username or real username.
76+
password (str): Kanboard API token or user password.
77+
auth_header (str, optional): HTTP header for authentication. Defaults to 'Authorization'.
78+
cafile (Optional[str], optional): Path to a custom CA certificate file. Defaults to None.
79+
insecure (bool, optional): If True, ignore SSL certificate errors and hostname mismatches.
80+
Defaults to False.
81+
ignore_hostname_verification (bool, optional): If True, skip SSL certificate hostname verification.
82+
Defaults to False.
83+
user_agent (str, optional): Custom User-Agent string for HTTP requests. Defaults to
84+
'Kanboard Python API Client'.
85+
loop (Optional[asyncio.AbstractEventLoop], optional): Asyncio event loop to use. If None, uses the
86+
current event loop or creates a new one.
7987
"""
8088
self._url = url
8189
self._username = username
@@ -94,16 +102,21 @@ def __init__(self,
94102

95103
def __getattr__(self, name: str):
96104
if self.is_async_method_name(name):
105+
97106
async def function(*args, **kwargs):
98107
return await self._event_loop.run_in_executor(
99108
None,
100109
functools.partial(
101-
self.execute,
102-
method=self._to_camel_case(self.get_funcname_from_async_name(name)), **kwargs))
110+
self.execute, method=self._to_camel_case(self.get_funcname_from_async_name(name)), **kwargs
111+
),
112+
)
113+
103114
return function
104115
else:
116+
105117
def function(*args, **kwargs):
106118
return self.execute(method=self._to_camel_case(name), **kwargs)
119+
107120
return function
108121

109122
@staticmethod
@@ -112,31 +125,29 @@ def is_async_method_name(funcname: str) -> bool:
112125

113126
@staticmethod
114127
def get_funcname_from_async_name(funcname: str) -> str:
115-
return funcname[:len(funcname) - len(ASYNC_FUNCNAME_MARKER)]
128+
return funcname[: len(funcname) - len(ASYNC_FUNCNAME_MARKER)]
116129

117130
@staticmethod
118131
def _to_camel_case(snake_str: str) -> str:
119-
components = snake_str.split('_')
120-
return components[0] + ''.join(x.title() for x in components[1:])
132+
components = snake_str.split("_")
133+
return components[0] + "".join(x.title() for x in components[1:])
121134

122135
@staticmethod
123136
def _parse_response(response: bytes):
124137
try:
125-
body = json.loads(response.decode(errors='ignore'))
138+
body = json.loads(response.decode(errors="ignore"))
126139

127-
if 'error' in body:
128-
message = body.get('error').get('message')
140+
if "error" in body:
141+
message = body.get("error").get("message")
129142
raise ClientError(message)
130143

131-
return body.get('result')
144+
return body.get("result")
132145
except ValueError:
133146
return None
134147

135148
def _do_request(self, headers: Dict[str, str], body: Dict):
136149
try:
137-
request = http.Request(self._url,
138-
headers=headers,
139-
data=json.dumps(body).encode())
150+
request = http.Request(self._url, headers=headers, data=json.dumps(body).encode())
140151

141152
ssl_context = ssl.create_default_context(cafile=self._cafile)
142153
if self._insecure:
@@ -153,31 +164,26 @@ def _do_request(self, headers: Dict[str, str], body: Dict):
153164

154165
def execute(self, method: str, **kwargs):
155166
"""
156-
Call remote API procedure
167+
Call a remote Kanboard API procedure.
157168
158169
Args:
159-
method: Procedure name
160-
kwargs: Procedure named arguments
170+
method (str): The Kanboard API method name in camelCase (e.g., 'createProject').
171+
**kwargs: Named arguments to pass to the API method.
161172
162173
Returns:
163-
Procedure result
174+
The result returned by the Kanboard API for the requested method.
164175
165176
Raises:
166-
urllib.error.HTTPError: Any HTTP error (Python 3)
177+
ClientError: If the API returns an error or if a network/HTTP error occurs.
167178
"""
168-
payload = {
169-
'id': 1,
170-
'jsonrpc': '2.0',
171-
'method': method,
172-
'params': kwargs
173-
}
179+
payload = {"id": 1, "jsonrpc": "2.0", "method": method, "params": kwargs}
174180

175-
credentials = base64.b64encode('{}:{}'.format(self._username, self._password).encode())
176-
auth_header_prefix = 'Basic ' if self._auth_header == DEFAULT_AUTH_HEADER else ''
181+
credentials = base64.b64encode("{}:{}".format(self._username, self._password).encode())
182+
auth_header_prefix = "Basic " if self._auth_header == DEFAULT_AUTH_HEADER else ""
177183
headers = {
178184
self._auth_header: auth_header_prefix + credentials.decode(),
179-
'Content-Type': 'application/json',
180-
'User-Agent': self._user_agent,
185+
"Content-Type": "application/json",
186+
"User-Agent": self._user_agent,
181187
}
182188

183189
return self._do_request(headers, payload)

0 commit comments

Comments
 (0)