pinecone.config
1import logging 2import sys 3from typing import NamedTuple, List 4import os 5 6import certifi 7import requests 8import configparser 9import socket 10 11from urllib3.connection import HTTPConnection 12 13from pinecone.core.client.exceptions import ApiKeyError 14from pinecone.core.api_action import ActionAPI, WhoAmIResponse 15from pinecone.core.utils import warn_deprecated, check_kwargs 16from pinecone.core.utils.constants import ( 17 CLIENT_VERSION, 18 PARENT_LOGGER_NAME, 19 DEFAULT_PARENT_LOGGER_LEVEL, 20 TCP_KEEPIDLE, 21 TCP_KEEPINTVL, 22 TCP_KEEPCNT, 23) 24from pinecone.core.client.configuration import Configuration as OpenApiConfiguration 25 26__all__ = ["Config", "init"] 27 28_logger = logging.getLogger(__name__) 29_parent_logger = logging.getLogger(PARENT_LOGGER_NAME) 30_parent_logger.setLevel(DEFAULT_PARENT_LOGGER_LEVEL) 31 32 33class ConfigBase(NamedTuple): 34 environment: str = "" 35 api_key: str = "" 36 project_name: str = "" 37 controller_host: str = "" 38 openapi_config: OpenApiConfiguration = None 39 40 41class _CONFIG: 42 """ 43 44 Order of configs to load: 45 46 - configs specified explicitly in reset 47 - environment variables 48 - configs specified in the INI file 49 - default configs 50 """ 51 52 def __init__(self): 53 self.reset() 54 55 def validate(self): 56 if not self._config.api_key: # None or empty string invalid 57 raise ApiKeyError("You haven't specified an Api-Key.") 58 59 def reset(self, config_file=None, **kwargs): 60 config = ConfigBase() 61 62 # Load config from file 63 file_config = self._load_config_file(config_file) 64 65 # Get the environment first. Make sure that it is not overwritten in subsequent config objects. 66 environment = ( 67 kwargs.pop("environment", None) 68 or os.getenv("PINECONE_ENVIRONMENT") 69 or file_config.pop("environment", None) 70 or "us-west1-gcp" 71 ) 72 config = config._replace(environment=environment) 73 74 # Set default config 75 default_config = ConfigBase( 76 controller_host="https://controller.{0}.pinecone.io".format(config.environment), 77 ) 78 config = config._replace(**self._preprocess_and_validate_config(default_config._asdict())) 79 80 # Set INI file config 81 config = config._replace(**self._preprocess_and_validate_config(file_config)) 82 83 # Set environment config 84 env_config = ConfigBase( 85 project_name=os.getenv("PINECONE_PROJECT_NAME"), 86 api_key=os.getenv("PINECONE_API_KEY"), 87 controller_host=os.getenv("PINECONE_CONTROLLER_HOST"), 88 ) 89 config = config._replace(**self._preprocess_and_validate_config(env_config._asdict())) 90 91 # Set explicit config 92 config = config._replace(**self._preprocess_and_validate_config(kwargs)) 93 94 self._config = config 95 96 # load project_name etc. from whoami api 97 action_api = ActionAPI(host=config.controller_host, api_key=config.api_key) 98 try: 99 whoami_response = action_api.whoami() 100 except requests.exceptions.RequestException: 101 # proceed with default values; reset() may be called later w/ correct values 102 whoami_response = WhoAmIResponse() 103 104 if not self._config.project_name: 105 config = config._replace( 106 **self._preprocess_and_validate_config({"project_name": whoami_response.projectname}) 107 ) 108 109 self._config = config 110 111 # Set OpenAPI client config 112 default_openapi_config = OpenApiConfiguration.get_default_copy() 113 default_openapi_config.ssl_ca_cert = certifi.where() 114 openapi_config = kwargs.pop("openapi_config", None) or default_openapi_config 115 116 openapi_config.socket_options = self._get_socket_options() 117 118 config = config._replace(openapi_config=openapi_config) 119 self._config = config 120 121 def _preprocess_and_validate_config(self, config: dict) -> dict: 122 """Normalize, filter, and validate config keys/values. 123 124 Trims whitespace, removes invalid keys (and the "environment" key), 125 and raises ValueError in case an invalid value was specified. 126 """ 127 # general preprocessing and filtering 128 result = {k: v for k, v in config.items() if k in ConfigBase._fields if v is not None} 129 result.pop("environment", None) 130 # validate api key 131 api_key = result.get("api_key") 132 # if api_key: 133 # try: 134 # uuid.UUID(api_key) 135 # except ValueError as e: 136 # raise ValueError(f"Pinecone API key \"{api_key}\" appears invalid. " 137 # f"Did you specify it correctly?") from e 138 return result 139 140 def _load_config_file(self, config_file: str) -> dict: 141 """Load from INI config file.""" 142 config_obj = {} 143 if config_file: 144 full_path = os.path.expanduser(config_file) 145 if os.path.isfile(full_path): 146 parser = configparser.ConfigParser() 147 parser.read(full_path) 148 if "default" in parser.sections(): 149 config_obj = {**parser["default"]} 150 return config_obj 151 152 @staticmethod 153 def _get_socket_options( 154 do_keep_alive: bool = True, 155 keep_alive_idle_sec: int = TCP_KEEPIDLE, 156 keep_alive_interval_sec: int = TCP_KEEPINTVL, 157 keep_alive_tries: int = TCP_KEEPCNT, 158 ) -> List[tuple]: 159 """ 160 Returns the socket options to pass to OpenAPI's Rest client 161 Args: 162 do_keep_alive: Whether to enable TCP keep alive mechanism 163 keep_alive_idle_sec: Time in seconds of connection idleness before starting to send keep alive probes 164 keep_alive_interval_sec: Interval time in seconds between keep alive probe messages 165 keep_alive_tries: Number of failed keep alive tries (unanswered KA messages) before terminating the connection 166 167 Returns: 168 A list of socket options for the Rest client's connection pool 169 """ 170 # Source: https://www.finbourne.com/blog/the-mysterious-hanging-client-tcp-keep-alives 171 172 socket_params = HTTPConnection.default_socket_options 173 if not do_keep_alive: 174 return socket_params 175 176 socket_params += [(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)] 177 178 # TCP Keep Alive Probes for different platforms 179 platform = sys.platform 180 # TCP Keep Alive Probes for Linux 181 if ( 182 platform == "linux" 183 and hasattr(socket, "TCP_KEEPIDLE") 184 and hasattr(socket, "TCP_KEEPINTVL") 185 and hasattr(socket, "TCP_KEEPCNT") 186 ): 187 socket_params += [(socket.IPPROTO_TCP, socket.TCP_KEEPIDLE, keep_alive_idle_sec)] 188 socket_params += [(socket.IPPROTO_TCP, socket.TCP_KEEPINTVL, keep_alive_interval_sec)] 189 socket_params += [(socket.IPPROTO_TCP, socket.TCP_KEEPCNT, keep_alive_tries)] 190 191 # TCP Keep Alive Probes for Windows OS 192 # NOTE: Changing TCP KA params on windows is done via a different mechanism which OpenAPI's Rest client doesn't expose. 193 # Since the default values work well, it seems setting `(socket.SO_KEEPALIVE, 1)` is sufficient. 194 # Leaving this code here for future reference. 195 # elif platform == 'win32' and hasattr(socket, "SIO_KEEPALIVE_VALS"): 196 # socket.ioctl((socket.SIO_KEEPALIVE_VALS, (1, keep_alive_idle_sec * 1000, keep_alive_interval_sec * 1000))) 197 198 # TCP Keep Alive Probes for Mac OS 199 elif platform == "darwin": 200 TCP_KEEPALIVE = 0x10 201 socket_params += [(socket.IPPROTO_TCP, TCP_KEEPALIVE, keep_alive_interval_sec)] 202 203 return socket_params 204 205 @property 206 def ENVIRONMENT(self): 207 return self._config.environment 208 209 @property 210 def API_KEY(self): 211 return self._config.api_key 212 213 @property 214 def PROJECT_NAME(self): 215 return self._config.project_name 216 217 @property 218 def CONTROLLER_HOST(self): 219 return self._config.controller_host 220 221 @property 222 def OPENAPI_CONFIG(self): 223 return self._config.openapi_config 224 225 @property 226 def LOG_LEVEL(self): 227 """ 228 Deprecated since v2.0.2 [Will be removed in v3.0.0]; use the standard logging module logger "pinecone" instead. 229 """ 230 warn_deprecated( 231 description='LOG_LEVEL is deprecated. Use the standard logging module logger "pinecone" instead.', 232 deprecated_in="2.0.2", 233 removal_in="3.0.0", 234 ) 235 return logging.getLevelName(logging.getLogger("pinecone").level) 236 237 238def init( 239 api_key: str = None, 240 host: str = None, 241 environment: str = None, 242 project_name: str = None, 243 log_level: str = None, 244 openapi_config: OpenApiConfiguration = None, 245 config: str = "~/.pinecone", 246 **kwargs 247): 248 """Initializes the Pinecone client. 249 250 :param api_key: Required if not set in config file or by environment variable ``PINECONE_API_KEY``. 251 :param host: Optional. Controller host. 252 :param environment: Optional. Deployment environment. 253 :param project_name: Optional. Pinecone project name. Overrides the value that is otherwise looked up and used from the Pinecone backend. 254 :param openapi_config: Optional. Set OpenAPI client configuration. 255 :param config: Optional. An INI configuration file. 256 :param log_level: Deprecated since v2.0.2 [Will be removed in v3.0.0]; use the standard logging module to manage logger "pinecone" instead. 257 """ 258 check_kwargs(init, kwargs) 259 Config.reset( 260 project_name=project_name, 261 api_key=api_key, 262 controller_host=host, 263 environment=environment, 264 openapi_config=openapi_config, 265 config_file=config, 266 **kwargs 267 ) 268 if log_level: 269 warn_deprecated( 270 description='log_level is deprecated. Use the standard logging module to manage logger "pinecone" instead.', 271 deprecated_in="2.0.2", 272 removal_in="3.0.0", 273 ) 274 275 276Config = _CONFIG() 277 278# Init 279init()
Config =
<pinecone.config._CONFIG object>
def
init( api_key: str = None, host: str = None, environment: str = None, project_name: str = None, log_level: str = None, openapi_config: pinecone.core.client.configuration.Configuration = None, config: str = '~/.pinecone', **kwargs):
239def init( 240 api_key: str = None, 241 host: str = None, 242 environment: str = None, 243 project_name: str = None, 244 log_level: str = None, 245 openapi_config: OpenApiConfiguration = None, 246 config: str = "~/.pinecone", 247 **kwargs 248): 249 """Initializes the Pinecone client. 250 251 :param api_key: Required if not set in config file or by environment variable ``PINECONE_API_KEY``. 252 :param host: Optional. Controller host. 253 :param environment: Optional. Deployment environment. 254 :param project_name: Optional. Pinecone project name. Overrides the value that is otherwise looked up and used from the Pinecone backend. 255 :param openapi_config: Optional. Set OpenAPI client configuration. 256 :param config: Optional. An INI configuration file. 257 :param log_level: Deprecated since v2.0.2 [Will be removed in v3.0.0]; use the standard logging module to manage logger "pinecone" instead. 258 """ 259 check_kwargs(init, kwargs) 260 Config.reset( 261 project_name=project_name, 262 api_key=api_key, 263 controller_host=host, 264 environment=environment, 265 openapi_config=openapi_config, 266 config_file=config, 267 **kwargs 268 ) 269 if log_level: 270 warn_deprecated( 271 description='log_level is deprecated. Use the standard logging module to manage logger "pinecone" instead.', 272 deprecated_in="2.0.2", 273 removal_in="3.0.0", 274 )
Initializes the Pinecone client.
Parameters
- api_key: Required if not set in config file or by environment variable
PINECONE_API_KEY. - host: Optional. Controller host.
- environment: Optional. Deployment environment.
- project_name: Optional. Pinecone project name. Overrides the value that is otherwise looked up and used from the Pinecone backend.
- openapi_config: Optional. Set OpenAPI client configuration.
- config: Optional. An INI configuration file.
- log_level: Deprecated since v2.0.2 [Will be removed in v3.0.0]; use the standard logging module to manage logger "pinecone" instead.