Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] Helpful error when incorrectly instantiating Index client #418

Merged
merged 5 commits into from
Nov 18, 2024

Conversation

jhamon
Copy link
Collaborator

@jhamon jhamon commented Nov 15, 2024

Problem

Sometimes users incorrectly instantiate the Index client like this:

import pinecone

# Initialize the Index with the host
index = pinecone.Index(index_name, host=index_host)

Then they will later get an authentication exception when using it, since the Index class does not have the configuration values it needs when attempting to perform vector operations.

ForbiddenException: (403) Reason: Forbidden HTTP response headers: HTTPHeaderDict({'Date': 'Wed, 13 Nov 2024 02:06:45 GMT', 'Content-Type': 'text/plain', 'Content-Length': '9', 'Connection': 'keep-alive', 'x-pinecone-auth-rejected-reason': 'Wrong API key', 'www-authenticate': 'Wrong API key', 'server': 'envoy'}) HTTP response body: Forbidden

Solution

  • Rename the Index implementation to _Index so that people will not accidentally interact with it.
  • Add a new stub implementation for Index that throws an informative message.

Bonus:

  • Move the Index client docstrings off the implementation class and into a related abstract base class. This wasn't strictly necessary, but I was feeling a bit overwhelmed by the size of the index.py file. I copied this approach from the grpc module. pdoc seems to still find and render the docs okay when doing this.

Usage

The error message incorporates the args/kwargs the user was attempting to pass.

One positional arg

>>> import pinecone
>>> i = pinecone.Index('my-index')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jhamon/workspace/pinecone-python-client/pinecone/data/index.py", line 113, in __init__
    raise IndexClientInstantiationError(args, kwargs)
pinecone.data.index.IndexClientInstantiationError: You are attempting to access the Index client directly from the pinecone module. The Index client must be instantiated through the parent Pinecone client instance so that it can inherit shared configurations such as API key.

    INCORRECT USAGE:
        ```
        import pinecone

        pc = pinecone.Pinecone(api_key='your-api-key')
        index = pinecone.Index('my-index')
        ```

    CORRECT USAGE:
        ```
        from pinecone import Pinecone

        pc = Pinecone(api_key='your-api-key')
        index = pc.Index('my-index')
        ```

Multiple positional args

>>> i = pinecone.Index('my-index', 'https://my-index.blahblah.com')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jhamon/workspace/pinecone-python-client/pinecone/data/index.py", line 113, in __init__
    raise IndexClientInstantiationError(args, kwargs)
pinecone.data.index.IndexClientInstantiationError: You are attempting to access the Index client directly from the pinecone module. The Index client must be instantiated through the parent Pinecone client instance so that it can inherit shared configurations such as API key.

    INCORRECT USAGE:
        ```
        import pinecone

        pc = pinecone.Pinecone(api_key='your-api-key')
        index = pinecone.Index('my-index', 'https://my-index.blahblah.com')
        ```

    CORRECT USAGE:
        ```
        from pinecone import Pinecone

        pc = Pinecone(api_key='your-api-key')
        index = pc.Index('my-index', 'https://my-index.blahblah.com')
        ```

One keyword arg:

>>> i = pinecone.Index(host='https://my-index.blahblah.com')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jhamon/workspace/pinecone-python-client/pinecone/data/index.py", line 113, in __init__
    raise IndexClientInstantiationError(args, kwargs)
pinecone.data.index.IndexClientInstantiationError: You are attempting to access the Index client directly from the pinecone module. The Index client must be instantiated through the parent Pinecone client instance so that it can inherit shared configurations such as API key.

    INCORRECT USAGE:
        ```
        import pinecone

        pc = pinecone.Pinecone(api_key='your-api-key')
        index = pinecone.Index(host='https://my-index.blahblah.com')
        ```

    CORRECT USAGE:
        ```
        from pinecone import Pinecone

        pc = Pinecone(api_key='your-api-key')
        index = pc.Index(host='https://my-index.blahblah.com')
        ```

Multiple kwargs

>>> i = pinecone.Index(name='my-index', host='https://my-index.blahblah.com', pool_threads=20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jhamon/workspace/pinecone-python-client/pinecone/data/index.py", line 113, in __init__
    raise IndexClientInstantiationError(args, kwargs)
pinecone.data.index.IndexClientInstantiationError: You are attempting to access the Index client directly from the pinecone module. The Index client must be instantiated through the parent Pinecone client instance so that it can inherit shared configurations such as API key.

    INCORRECT USAGE:
        ```
        import pinecone

        pc = pinecone.Pinecone(api_key='your-api-key')
        index = pinecone.Index(name='my-index', host='https://my-index.blahblah.com', pool_threads=20)
        ```

    CORRECT USAGE:
        ```
        from pinecone import Pinecone

        pc = Pinecone(api_key='your-api-key')
        index = pc.Index(name='my-index', host='https://my-index.blahblah.com', pool_threads=20)
        ```

Mixed args/kwargs

>>> i = pinecone.Index('my-index', host='https://my-index.blahblah.com', pool_threads=20)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/jhamon/workspace/pinecone-python-client/pinecone/data/index.py", line 113, in __init__
    raise IndexClientInstantiationError(args, kwargs)
pinecone.data.index.IndexClientInstantiationError: You are attempting to access the Index client directly from the pinecone module. The Index client must be instantiated through the parent Pinecone client instance so that it can inherit shared configurations such as API key.

    INCORRECT USAGE:
        ```
        import pinecone

        pc = pinecone.Pinecone(api_key='your-api-key')
        index = pinecone.Index('my-index', host='https://my-index.blahblah.com', pool_threads=20)
        ```

    CORRECT USAGE:
        ```
        from pinecone import Pinecone

        pc = Pinecone(api_key='your-api-key')
        index = pc.Index('my-index', host='https://my-index.blahblah.com', pool_threads=20)

Type of Change

  • New feature (non-breaking change which adds functionality)
  • Breaking change

This change is a UX feature that could be considered breaking if someone was importing Index directly and going out of their way to set it up correctly. This was never documented usage, but somebody skilled at reading code could have figured out how to do this.

@jhamon jhamon marked this pull request as ready for review November 15, 2024 20:17
Copy link
Contributor

@austin-denoble austin-denoble left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, this is a smart way to guide the user away from this specific pitfall. Also nice thinking moving the docs stuff into an abstract class to keep things tidier. 🚢

@jhamon jhamon merged commit d9f365b into release-candidate/2024-10 Nov 18, 2024
83 checks passed
@jhamon jhamon deleted the jhamon/client-instantiation-error branch November 18, 2024 16:12
jhamon added a commit that referenced this pull request Dec 16, 2024
## Problem

By renaming the `Index` class to `_Index` in
#418, I
accidentally broke plugin installation.

## Solution

Rename the class back to `Index` and instead alias it to `_Index`
through import statements. We still need to alias it within the
`pinecone/data/__init__.py` module definition to avoid collision with
the Index class designed to give users a more informative error when
they incorrectly import the `Index` class from the `pinecone` module.

I went ahead and also cleaned up some stuff where we were unnecessarily
exposing some internal openapi details in the public interface. Users
should never realistically need to construct openapi request objects.

## Type of Change

- [x] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to not work as expected)
- [ ] This change requires a documentation update
- [ ] Infrastructure change (CI configs, etc)
- [ ] Non-code change (docs, etc)
- [ ] None of the above: (explain here)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants