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.