22import getpass
33
44class Metabase_API_Async :
5- """
6- Async version of the Metabase API wrapper.
7- Provides asynchronous methods to interact with the Metabase API.
8- """
9-
10- def __init__ (self , domain , email = None , password = None , api_key = None , basic_auth = False , is_admin = True , timeout = None ):
5+ def __init__ (self ,domain ,email = None ,password = None ,api_key = None ,basic_auth = False ,is_admin = True ,timeout = None ,
6+ * ,trust_env = False ,http2 = False ,limits = None ,verify = True ):
117 assert email is not None or api_key is not None
12- self .domain = domain .rstrip ('/' )
8+ self .domain = domain .rstrip ("/" )
139 self .email = email
1410 self .auth = None
1511 self .password = None
@@ -19,62 +15,70 @@ def __init__(self, domain, email=None, password=None, api_key=None, basic_auth=F
1915 self .timeout = timeout
2016
2117 if email :
22- self .password = getpass .getpass (prompt = 'Please enter your password: ' ) if password is None else password
23- if basic_auth :
24- self .auth = True # We'll use aiohttp.BasicAuth in the request methods
25- else :
26- self .auth = None
18+ self .password = getpass .getpass (prompt = "Please enter your password: " ) if password is None else password
19+ self .auth = True if basic_auth else None
2720 else :
2821 self .header = {"X-API-KEY" : api_key }
29-
22+
23+ # Connection pooling and keep-alive
24+ self ._limits = limits or httpx .Limits (max_connections = 20 , max_keepalive_connections = 20 )
25+ self ._client = httpx .AsyncClient (
26+ base_url = self .domain ,
27+ timeout = self .timeout ,
28+ trust_env = trust_env ,
29+ http2 = http2 ,
30+ limits = self ._limits ,
31+ verify = verify ,
32+ headers = self .header , # default headers; can be updated after auth
33+ )
34+
3035 if not self .is_admin :
31- print ('''
32- Ask your Metabase admin to disable "Friendly Table and Field Names" (in Admin Panel > Settings > General).
33- Without this some of the functions of the current package may not work as expected.
34- ''' )
35-
36- async def authenticate_async (self ):
37- """Asynchronously get a Session ID"""
38- conn_header = {
39- 'username' : self .email ,
40- 'password' : self .password
41- }
36+ print (
37+ """
38+ Ask your Metabase admin to disable "Friendly Table and Field Names" (in Admin Panel > Settings > General).
39+ Without this some of the functions of the current package may not work as expected.
40+ """
41+ )
42+
43+ async def aclose (self ):
44+ await self ._client .aclose ()
45+
46+ async def __aenter__ (self ):
47+ return self
4248
49+ async def __aexit__ (self , exc_type , exc , tb ):
50+ await self .aclose ()
51+
52+ async def authenticate_async (self ):
53+ conn_header = {"username" : self .email , "password" : self .password }
4354 auth = (self .email , self .password ) if self .auth else None
44- async with httpx .AsyncClient () as client :
45- res = await client .post (
46- self .domain + '/api/session' ,
47- json = conn_header ,
48- auth = auth
49- )
50- if res .status_code != 200 :
51- raise Exception (f"Authentication failed with status { res .status_code } " )
5255
53- data = res .json ()
54- self .session_id = data ['id' ]
55- self .header = {'X-Metabase-Session' : self .session_id }
56+ res = await self ._client .post ("/api/session" , json = conn_header , auth = auth )
57+ if res .status_code != 200 :
58+ raise Exception (f"Authentication failed with status { res .status_code } " )
59+
60+ self .session_id = res .json ()["id" ]
61+ self .header = {"X-Metabase-Session" : self .session_id }
62+
63+ # Update default headers used by the pooled client
64+ self ._client .headers .clear ()
65+ self ._client .headers .update (self .header )
5666
5767 async def validate_session_async (self ):
58- """Asynchronously get a new session ID if the previous one has expired"""
5968 if not self .email : # Using API key
6069 return
6170
62- if not self .session_id : # First request
71+ if not self .session_id :
6372 return await self .authenticate_async ()
6473
6574 auth = (self .email , self .password ) if self .auth else None
66- async with httpx .AsyncClient () as client :
67- res = await client .get (
68- self .domain + '/api/user/current' ,
69- headers = self .header ,
70- auth = auth
71- )
72- if res .status_code == 200 :
73- return True
74- elif res .status_code == 401 : # unauthorized
75- return await self .authenticate_async ()
76- else :
77- raise Exception (f"Session validation failed with status { res .status_code } " )
75+ res = await self ._client .get ("/api/user/current" , auth = auth )
76+
77+ if res .status_code == 200 :
78+ return True
79+ if res .status_code == 401 :
80+ return await self .authenticate_async ()
81+ raise Exception (f"Session validation failed with status { res .status_code } " )
7882
7983
8084
0 commit comments