Skip to content

Commit 726c61a

Browse files
committed
refactor: unit tests for pydantic and pinecone deprecations
1 parent 1f91068 commit 726c61a

File tree

5 files changed

+75
-54
lines changed

5 files changed

+75
-54
lines changed

.vscode/settings.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,8 @@
22
"cornflakes.linter.executablePath": "./venv/bin/flake8",
33
"[python]": {
44
"editor.defaultFormatter": "ms-python.black-formatter"
5-
}
5+
},
6+
"python.testing.unittestArgs": ["-v", "-s", "./models", "-p", "*test.py"],
7+
"python.testing.pytestEnabled": false,
8+
"python.testing.unittestEnabled": true
69
}

models/conf.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ def empty_str_to_int_default(v: str, default: int) -> int:
124124
class Settings(BaseSettings):
125125
"""Settings for Lambda functions"""
126126

127+
model_config = {"frozen": True}
128+
127129
_dump: Optional[dict] = None
128130
_pinecone_api_key_source: str = "unset"
129131
_openai_api_key_source: str = "unset"
@@ -170,8 +172,8 @@ def __init__(self, **data: Any):
170172
)
171173

172174
pinecone_api_key: Optional[SecretStr] = Field(SettingsDefaults.PINECONE_API_KEY)
173-
pinecone_environment: Optional[str] = Field(SettingsDefaults.PINECONE_ENVIRONMENT)
174-
pinecone_index_name: Optional[str] = Field(SettingsDefaults.PINECONE_INDEX_NAME)
175+
pinecone_environment: str = Field(SettingsDefaults.PINECONE_ENVIRONMENT)
176+
pinecone_index_name: str = Field(SettingsDefaults.PINECONE_INDEX_NAME)
175177
pinecone_vectorstore_text_key: Optional[str] = Field(SettingsDefaults.PINECONE_VECTORSTORE_TEXT_KEY)
176178
pinecone_metric: Optional[str] = Field(SettingsDefaults.PINECONE_METRIC)
177179
pinecone_dimensions: Optional[int] = Field(SettingsDefaults.PINECONE_DIMENSIONS, gt=0)
@@ -279,12 +281,6 @@ def recursive_sort_dict(d):
279281
self._dump = recursive_sort_dict(self._dump)
280282
return self._dump
281283

282-
# pylint: disable=too-few-public-methods
283-
class Config:
284-
"""Pydantic configuration"""
285-
286-
frozen = True
287-
288284
@field_validator("debug_mode")
289285
def parse_debug_mode(cls, v) -> bool:
290286
"""Parse debug_mode"""

models/pinecone.py

Lines changed: 53 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@
99
import json
1010
import logging
1111
import os
12+
from typing import Optional
1213

1314
from langchain.text_splitter import RecursiveCharacterTextSplitter
1415
from langchain_community.document_loaders.pdf import PyPDFLoader
1516
from langchain_openai import OpenAIEmbeddings
1617
from langchain_pinecone import PineconeVectorStore
1718

1819
# pinecone integration
19-
from pinecone import Pinecone, ServerlessSpec
20+
from pinecone import AwsRegion, CloudProvider, Pinecone, ServerlessSpec, VectorType
21+
from pinecone.core.openapi.db_data.models import (
22+
IndexDescription as PineconeIndexDescription,
23+
)
24+
from pinecone.db_control.models import IndexList
25+
from pinecone.db_data import Index
2026
from pinecone.exceptions import PineconeApiException
21-
from pinecone.models import IndexList
27+
from pydantic import SecretStr
2228

2329
# this project
2430
from models.conf import settings
@@ -31,20 +37,20 @@ class PineconeIndex:
3137
"""Pinecone helper class."""
3238

3339
_pinecone = None
34-
_index: Pinecone.Index = None
35-
_index_name: str = None
36-
_text_splitter: RecursiveCharacterTextSplitter = None
37-
_openai_embeddings: OpenAIEmbeddings = None
38-
_vector_store: PineconeVectorStore = None
40+
_index: Optional[Index] = None
41+
_index_name: Optional[str] = None
42+
_text_splitter: Optional[RecursiveCharacterTextSplitter] = None
43+
_openai_embeddings: Optional[OpenAIEmbeddings] = None
44+
_vector_store: Optional[PineconeVectorStore] = None
3945

40-
def __init__(self, index_name: str = None):
46+
def __init__(self, index_name: Optional[str] = None):
4147
self.init()
4248
self.index_name = index_name or settings.pinecone_index_name
4349
logging.debug("PineconeIndex initialized with index_name: %s", self.index_name)
4450
logging.debug(self.index_stats)
4551

4652
@property
47-
def index_name(self) -> str:
53+
def index_name(self) -> Optional[str]:
4854
"""index name."""
4955
return self._index_name
5056

@@ -57,24 +63,30 @@ def index_name(self, value: str) -> None:
5763
self.init_index()
5864

5965
@property
60-
def index(self) -> Pinecone.Index:
66+
def index(self) -> Optional[Index]:
6167
"""pinecone.Index lazy read-only property."""
6268
if self._index is None:
6369
self.init_index()
64-
self._index = self.pinecone.Index(name=self.index_name)
70+
if isinstance(self.pinecone, Pinecone) and isinstance(self.index_name, str):
71+
self._index = self.pinecone.Index(name=self.index_name)
72+
6573
return self._index
6674

6775
@property
68-
def index_stats(self) -> dict:
76+
def index_stats(self) -> str:
6977
"""index stats."""
70-
retval = self.index.describe_index_stats()
71-
return json.dumps(retval.to_dict(), indent=4)
78+
if self.index is not None:
79+
retval: PineconeIndexDescription = self.index.describe_index_stats()
80+
return json.dumps(retval.to_dict(), indent=4)
81+
return "Index not initialized."
7282

7383
@property
7484
def initialized(self) -> bool:
7585
"""initialized read-only property."""
76-
indexes = self.pinecone.list_indexes()
77-
return self.index_name in indexes.names()
86+
if isinstance(self.pinecone, Pinecone) and isinstance(self.index_name, str):
87+
indexes = self.pinecone.list_indexes()
88+
return self.index_name in indexes.names()
89+
return False
7890

7991
@property
8092
def vector_store(self) -> PineconeVectorStore:
@@ -95,19 +107,20 @@ def openai_embeddings(self) -> OpenAIEmbeddings:
95107
if self._openai_embeddings is None:
96108
# pylint: disable=no-member
97109
self._openai_embeddings = OpenAIEmbeddings(
98-
api_key=settings.openai_api_key.get_secret_value(),
110+
api_key=settings.openai_api_key,
99111
organization=settings.openai_api_organization,
100112
)
101113
return self._openai_embeddings
102114

103115
@property
104-
def pinecone(self) -> Pinecone:
116+
def pinecone(self) -> Optional[Pinecone]:
105117
"""Pinecone lazy read-only property."""
106118
if self._pinecone is None:
107119
print("Initializing Pinecone...")
108-
api_key = settings.pinecone_api_key.get_secret_value()
109-
print(f"API Key: {api_key[:12]}****------")
110-
self._pinecone = Pinecone(api_key=api_key)
120+
if isinstance(settings.pinecone_api_key, SecretStr):
121+
api_key = settings.pinecone_api_key.get_secret_value()
122+
print(f"API Key: {api_key[:12]}****------")
123+
self._pinecone = Pinecone(api_key=api_key)
111124
return self._pinecone
112125

113126
@property
@@ -119,11 +132,11 @@ def text_splitter(self) -> RecursiveCharacterTextSplitter:
119132

120133
def init_index(self):
121134
"""Verify that an index named self.index_name exists in Pinecone. If not, create it."""
122-
indexes: IndexList = None
123-
indexes = self.pinecone.list_indexes()
124-
if self.index_name not in indexes.names():
125-
logging.debug("Index does not exist.")
126-
self.create()
135+
if isinstance(self.pinecone, Pinecone):
136+
indexes: IndexList = self.pinecone.list_indexes()
137+
if self.index_name not in indexes.names():
138+
logging.debug("Index does not exist.")
139+
self.create()
127140

128141
# pylint: disable=no-member
129142
def init(self):
@@ -140,24 +153,27 @@ def delete(self):
140153
if not self.initialized:
141154
logging.debug("Index does not exist. Nothing to delete.")
142155
return
143-
print("Deleting index...")
144-
self.pinecone.delete_index(self.index_name)
156+
if isinstance(self.pinecone, Pinecone) and isinstance(self.index_name, str):
157+
print("Deleting index...")
158+
self.pinecone.delete_index(self.index_name)
145159

146160
def create(self):
147161
"""Create index."""
148162
print("Creating index. This may take a few minutes...")
149163
serverless_spec = ServerlessSpec(
150-
cloud="aws",
151-
region="us-east-1",
164+
cloud=CloudProvider.AWS,
165+
region=AwsRegion.US_EAST_1,
152166
)
153167
try:
154-
self.pinecone.create_index(
155-
name=self.index_name,
156-
dimension=settings.pinecone_dimensions,
157-
metric=settings.pinecone_metric,
158-
spec=serverless_spec,
159-
)
160-
print("Index created.")
168+
if isinstance(self.pinecone, Pinecone) and isinstance(self.index_name, str):
169+
self.pinecone.create_index(
170+
name=self.index_name,
171+
dimension=settings.pinecone_dimensions,
172+
metric=settings.pinecone_metric,
173+
spec=serverless_spec,
174+
vector_type=VectorType.DENSE,
175+
)
176+
print("Index created.")
161177
except PineconeApiException:
162178
pass
163179

models/tests/test_pinecone.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66

77
import os
88

9-
import pinecone as oem_pinecone
10-
import pytest # pylint: disable=unused-import
9+
import pytest
1110
from pinecone import Pinecone
11+
from pinecone.db_data import Index
12+
from pydantic import SecretStr
1213

1314
from models.conf import settings
1415
from models.pinecone import PineconeIndex
@@ -47,13 +48,15 @@ def test_04_initialize(self):
4748
pinecone.initialize()
4849
except Exception as e:
4950
assert False, f"Pinecone.initialize() failed with exception: {e}"
50-
assert isinstance(pinecone.index, oem_pinecone.Index)
51+
assert isinstance(pinecone.index, Index)
5152

5253
def test_05_delete(self):
5354
"""Test that the index can be deleted."""
5455
pinecone_index = PineconeIndex()
5556

56-
# pylint: disable=E1101
57+
if not isinstance(settings.pinecone_api_key, SecretStr):
58+
raise ValueError("Pinecone API key is not a SecretStr")
59+
# pylint: disable=no-member
5760
api_key = settings.pinecone_api_key.get_secret_value()
5861
pinecone = Pinecone(api_key=api_key)
5962
indexes = pinecone.list_indexes().names()
@@ -68,7 +71,10 @@ def test_06_create(self):
6871
"""Test that the index can be created."""
6972
pinecone_index = PineconeIndex()
7073

71-
# pylint: disable=E1101
74+
if not isinstance(settings.pinecone_api_key, SecretStr):
75+
raise ValueError("Pinecone API key is not a SecretStr")
76+
77+
# pylint: disable=no-member
7278
api_key = settings.pinecone_api_key.get_secret_value()
7379
pinecone = Pinecone(api_key=api_key)
7480

@@ -81,7 +87,7 @@ def test_06_create(self):
8187
pinecone_index.create()
8288
except Exception as e:
8389
assert False, f"Pinecone.create() failed with exception: {e}"
84-
assert isinstance(pinecone_index.index, oem_pinecone.Index)
90+
assert isinstance(pinecone_index.index, Index)
8591
pinecone_index.delete()
8692

8793
def test_07_load_pdf(self):

requirements/base.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ langsmith==0.3.45
1313
openai==1.88.0
1414
pinecone==7.2.0
1515
pinecone-text==0.10.0
16-
pydantic==2.10.4
16+
pydantic==2.11.7
1717
pydantic-settings==2.9.1
18-
pydantic_core==2.27.2
18+
pydantic_core==2.33.2
1919
python-decouple==3.8
2020
python-dotenv==1.1.0
2121
pypdf==5.6.0

0 commit comments

Comments
 (0)