2929from urllib import request as http
3030
3131
32- DEFAULT_AUTH_HEADER = ' Authorization'
32+ DEFAULT_AUTH_HEADER = " Authorization"
3333ASYNC_FUNCNAME_MARKER = "_async"
3434
3535
@@ -39,43 +39,46 @@ class ClientError(Exception):
3939
4040class 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., 'http://localhost/jsonrpc.php').
74+ username (str): Kanboard API username or real username.
75+ password (str): Kanboard API token or user password.
76+ auth_header (str, optional): HTTP header for authentication. Defaults to 'Authorization'.
77+ cafile (Optional[str], optional) : Path to a custom CA certificate file. Defaults to None.
78+ insecure (bool, optional): If True, ignore SSL certificate errors and hostname mismatches. Defaults to False.
79+ ignore_hostname_verification (bool, optional): If True, skip SSL certificate hostname verification. Defaults to False.
80+ user_agent (str, optional): Custom User-Agent string for HTTP requests. Defaults to 'Kanboard Python API Client'.
81+ loop (Optional[ asyncio.AbstractEventLoop], optional): Asyncio event loop to use. If None, uses the current event loop or creates a new one.
7982 """
8083 self ._url = url
8184 self ._username = username
@@ -94,16 +97,21 @@ def __init__(self,
9497
9598 def __getattr__ (self , name : str ):
9699 if self .is_async_method_name (name ):
100+
97101 async def function (* args , ** kwargs ):
98102 return await self ._event_loop .run_in_executor (
99103 None ,
100104 functools .partial (
101- self .execute ,
102- method = self ._to_camel_case (self .get_funcname_from_async_name (name )), ** kwargs ))
105+ self .execute , method = self ._to_camel_case (self .get_funcname_from_async_name (name )), ** kwargs
106+ ),
107+ )
108+
103109 return function
104110 else :
111+
105112 def function (* args , ** kwargs ):
106113 return self .execute (method = self ._to_camel_case (name ), ** kwargs )
114+
107115 return function
108116
109117 @staticmethod
@@ -112,31 +120,29 @@ def is_async_method_name(funcname: str) -> bool:
112120
113121 @staticmethod
114122 def get_funcname_from_async_name (funcname : str ) -> str :
115- return funcname [:len (funcname ) - len (ASYNC_FUNCNAME_MARKER )]
123+ return funcname [: len (funcname ) - len (ASYNC_FUNCNAME_MARKER )]
116124
117125 @staticmethod
118126 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 :])
127+ components = snake_str .split ("_" )
128+ return components [0 ] + "" .join (x .title () for x in components [1 :])
121129
122130 @staticmethod
123131 def _parse_response (response : bytes ):
124132 try :
125- body = json .loads (response .decode (errors = ' ignore' ))
133+ body = json .loads (response .decode (errors = " ignore" ))
126134
127- if ' error' in body :
128- message = body .get (' error' ).get (' message' )
135+ if " error" in body :
136+ message = body .get (" error" ).get (" message" )
129137 raise ClientError (message )
130138
131- return body .get (' result' )
139+ return body .get (" result" )
132140 except ValueError :
133141 return None
134142
135143 def _do_request (self , headers : Dict [str , str ], body : Dict ):
136144 try :
137- request = http .Request (self ._url ,
138- headers = headers ,
139- data = json .dumps (body ).encode ())
145+ request = http .Request (self ._url , headers = headers , data = json .dumps (body ).encode ())
140146
141147 ssl_context = ssl .create_default_context (cafile = self ._cafile )
142148 if self ._insecure :
@@ -153,31 +159,26 @@ def _do_request(self, headers: Dict[str, str], body: Dict):
153159
154160 def execute (self , method : str , ** kwargs ):
155161 """
156- Call remote API procedure
162+ Call a remote Kanboard API procedure.
157163
158164 Args:
159- method: Procedure name
160- kwargs: Procedure named arguments
165+ method (str): The Kanboard API method name in camelCase (e.g., 'createProject').
166+ ** kwargs: Named arguments to pass to the API method.
161167
162168 Returns:
163- Procedure result
169+ The result returned by the Kanboard API for the requested method.
164170
165171 Raises:
166- urllib.error.HTTPError: Any HTTP error (Python 3)
172+ ClientError: If the API returns an error or if a network/HTTP error occurs.
167173 """
168- payload = {
169- 'id' : 1 ,
170- 'jsonrpc' : '2.0' ,
171- 'method' : method ,
172- 'params' : kwargs
173- }
174+ payload = {"id" : 1 , "jsonrpc" : "2.0" , "method" : method , "params" : kwargs }
174175
175- credentials = base64 .b64encode (' {}:{}' .format (self ._username , self ._password ).encode ())
176- auth_header_prefix = ' Basic ' if self ._auth_header == DEFAULT_AUTH_HEADER else ''
176+ credentials = base64 .b64encode (" {}:{}" .format (self ._username , self ._password ).encode ())
177+ auth_header_prefix = " Basic " if self ._auth_header == DEFAULT_AUTH_HEADER else ""
177178 headers = {
178179 self ._auth_header : auth_header_prefix + credentials .decode (),
179- ' Content-Type' : ' application/json' ,
180- ' User-Agent' : self ._user_agent ,
180+ " Content-Type" : " application/json" ,
181+ " User-Agent" : self ._user_agent ,
181182 }
182183
183184 return self ._do_request (headers , payload )
0 commit comments