github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/clients/python-wrapper/lakefs/client.py (about) 1 """ 2 lakeFS Client module 3 4 Handles authentication against the lakeFS server and wraps the underlying lakefs_sdk client. 5 """ 6 7 from __future__ import annotations 8 9 from typing import Optional 10 from threading import Lock 11 12 import lakefs_sdk 13 from lakefs_sdk.client import LakeFSClient 14 15 from lakefs.config import ClientConfig 16 from lakefs.exceptions import NotAuthorizedException, ServerException, api_exception_handler 17 from lakefs.models import ServerStorageConfiguration 18 19 20 class ServerConfiguration: 21 """ 22 Represent a lakeFS server's configuration 23 """ 24 _conf: lakefs_sdk.Config 25 _storage_conf: ServerStorageConfiguration 26 27 def __init__(self, client: Optional[Client] = None): 28 try: 29 self._conf = client.sdk_client.config_api.get_config() 30 self._storage_conf = ServerStorageConfiguration(**self._conf.storage_config.dict()) 31 except lakefs_sdk.exceptions.ApiException as e: 32 if isinstance(e, lakefs_sdk.exceptions.ApiException): 33 raise NotAuthorizedException(e.status, e.reason) from e 34 raise ServerException(e.status, e.reason) from e 35 36 @property 37 def version(self) -> str: 38 """ 39 Return the lakeFS server version 40 """ 41 return self._conf.version_config.version 42 43 @property 44 def storage_config(self) -> ServerStorageConfiguration: 45 """ 46 Returns the lakeFS server storage configuration 47 """ 48 return self._storage_conf 49 50 51 class Client: 52 """ 53 Wrapper around lakefs_sdk's client object 54 Takes care of instantiating it from the environment 55 56 Example of initializing a Client object: 57 58 .. code-block:: python 59 60 from lakefs import Client 61 62 client = Client(username="<access_key_id>", password="<secret_access_key>", host="<lakefs_endpoint>") 63 print(client.version) 64 65 """ 66 67 _client: Optional[LakeFSClient] = None 68 _conf: Optional[ClientConfig] = None 69 _server_conf: Optional[ServerConfiguration] = None 70 71 def __init__(self, **kwargs): 72 self._conf = ClientConfig(**kwargs) 73 self._client = LakeFSClient(self._conf, header_name='X-Lakefs-Client', 74 header_value='python-lakefs') 75 76 @property 77 def config(self): 78 """ 79 Return the underlying lakefs_sdk configuration 80 """ 81 return self._conf 82 83 @property 84 def sdk_client(self): 85 """ 86 Return the underlying lakefs_sdk client 87 """ 88 return self._client 89 90 @property 91 def storage_config(self): 92 """ 93 lakeFS SDK storage config object, lazy evaluated. 94 """ 95 if self._server_conf is None: 96 self._server_conf = ServerConfiguration(self) 97 return self._server_conf.storage_config 98 99 @property 100 def version(self) -> str: 101 """ 102 lakeFS Server version, lazy evaluated. 103 """ 104 if self._server_conf is None: 105 self._server_conf = ServerConfiguration(self) 106 return self._server_conf.version 107 108 109 def from_web_identity(code: str, state: str, redirect_uri: str, ttl_seconds: int = 3600, **kwargs) -> Client: 110 """ 111 Authenticate against lakeFS using a code received from an identity provider 112 113 :param code: The code received from the identity provider 114 :param state: The state received from the identity provider 115 :param redirect_uri: The redirect URI used in the authentication process 116 :param ttl_seconds: The token's time-to-live in seconds 117 :param kwargs: Remaining arguments for the Client object 118 :return: The authenticated Client object 119 :raise NotAuthorizedException: if user is not authorized to perform this operation 120 """ 121 client = Client(**kwargs) 122 sts_requests = lakefs_sdk.StsAuthRequest(code=code, state=state, redirect_uri=redirect_uri, ttl_seconds=ttl_seconds) 123 with api_exception_handler(): 124 auth_token = client.sdk_client.experimental_api.sts_login(sts_requests) 125 client.config.access_token = auth_token.token 126 return client 127 128 129 class _BaseLakeFSObject: 130 """ 131 Base class for all lakeFS SDK objects, holds the client object and handles errors where no authentication method 132 found for client. Attempts to reload client dynamically in case of changes in the environment. 133 """ 134 __mutex: Lock = Lock() 135 __client: Optional[Client] = None 136 137 def __init__(self, client: Optional[Client]): 138 self.__client = client 139 140 @property 141 def _client(self): 142 """ 143 If client is None due to missing authentication params, try to init again. If authentication method is still 144 missing - will raise exception 145 :return: The initialized client object 146 :raise NoAuthenticationFound: If no authentication method found to configure the lakeFS client with 147 """ 148 if self.__client is not None: 149 return self.__client 150 151 with _BaseLakeFSObject.__mutex: 152 if _BaseLakeFSObject.__client is None: 153 _BaseLakeFSObject.__client = Client() 154 return _BaseLakeFSObject.__client