Skip to content

Commit dc877fc

Browse files
committed
feat(backcompat): plumb Pinecone(pool_threads=N) through to Index (BC-0110)
- Add `_LegacyIndexKwargs(IndexKwargs)` TypedDict in indexes_helpers.py with `pool_threads: NotRequired[int]`; keeps `IndexKwargs` clean for async client - Store `_legacy_pool_threads: int | None` on `Pinecone` instance (replaces silent debug log); `_build_index_kwargs` returns `_LegacyIndexKwargs` and conditionally includes `pool_threads` so `Index(**kwargs)` receives it and installs async_req - Add `tests/unit/test_pinecone_pool_threads.py` with five unit tests covering: attribute capture, kwarg propagation, field presence/absence, no-warning, and end-to-end install-hook firing via `Pinecone.index(host=...)`
1 parent f841f35 commit dc877fc

4 files changed

Lines changed: 74 additions & 22 deletions

File tree

‎pinecone/_client.py‎

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
from __future__ import annotations
44

5-
import logging
65
from dataclasses import replace
76
from typing import TYPE_CHECKING, Any, cast
87

98
from pinecone._internal.config import PineconeConfig, RetryConfig
109
from pinecone._internal.constants import CONTROL_PLANE_API_VERSION, DEFAULT_BASE_URL
11-
from pinecone._internal.indexes_helpers import IndexKwargs, poll_index_until_ready
10+
from pinecone._internal.indexes_helpers import _LegacyIndexKwargs, poll_index_until_ready
1211
from pinecone._internal.validation import require_non_empty
1312
from pinecone.errors.exceptions import ValidationError
1413

@@ -47,8 +46,6 @@
4746
)
4847
from pinecone.preview import Preview
4948

50-
logger = logging.getLogger(__name__)
51-
5249

5350
class Pinecone:
5451
"""Synchronous Pinecone client for control-plane operations.
@@ -108,13 +105,6 @@ def __init__(
108105
legacy_pool_threads = kwargs.pop("pool_threads", None)
109106
if kwargs:
110107
raise TypeError(f"Pinecone() got unexpected keyword arguments: {sorted(kwargs)!r}")
111-
if legacy_pool_threads is not None:
112-
logger.debug(
113-
"Pinecone(pool_threads=%r) is accepted for backcompat but no "
114-
"longer used; the new client uses httpx connection pooling. "
115-
"Tune connection_pool_maxsize= instead.",
116-
legacy_pool_threads,
117-
)
118108
config = PineconeConfig(
119109
api_key=api_key or "",
120110
host=host or "",
@@ -153,6 +143,7 @@ def __init__(
153143
self._assistants: Assistants | None = None
154144
self._host_cache: dict[str, str] = {}
155145
self._preview: Preview | None = None
146+
self._legacy_pool_threads: int | None = legacy_pool_threads
156147

157148
def __repr__(self) -> str:
158149
masked = f"...{self._config.api_key[-4:]}" if len(self._config.api_key) >= 4 else "***"
@@ -372,9 +363,9 @@ def index(
372363

373364
return _Index(**self._build_index_kwargs(resolved_host))
374365

375-
def _build_index_kwargs(self, host: str) -> IndexKwargs:
376-
"""Return the kwargs dict for constructing an Index or AsyncIndex."""
377-
return IndexKwargs(
366+
def _build_index_kwargs(self, host: str) -> _LegacyIndexKwargs:
367+
"""Return the kwargs dict for constructing an Index."""
368+
kwargs: _LegacyIndexKwargs = _LegacyIndexKwargs(
378369
host=host,
379370
api_key=self._config.api_key,
380371
additional_headers=dict(self._config.additional_headers),
@@ -386,6 +377,9 @@ def _build_index_kwargs(self, host: str) -> IndexKwargs:
386377
source_tag=self._config.source_tag,
387378
connection_pool_maxsize=self._config.connection_pool_maxsize,
388379
)
380+
if self._legacy_pool_threads is not None:
381+
kwargs["pool_threads"] = self._legacy_pool_threads
382+
return kwargs
389383

390384
def _resolve_index_host(self, *, name: str, host: str) -> str:
391385
"""Resolve the data plane host from explicit host, cache, or describe call.

‎pinecone/_internal/indexes_helpers.py‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from typing import TYPE_CHECKING, Any, TypedDict
1515

1616
import msgspec
17+
from typing_extensions import NotRequired
1718

1819
from pinecone._internal.validation import require_non_empty
1920

@@ -53,6 +54,12 @@ class IndexKwargs(TypedDict):
5354
connection_pool_maxsize: int
5455

5556

57+
class _LegacyIndexKwargs(IndexKwargs):
58+
"""IndexKwargs extended with the legacy pool_threads field (sync client only)."""
59+
60+
pool_threads: NotRequired[int]
61+
62+
5663
def resolve_enum_value(value: Any) -> Any:
5764
"""Extract ``.value`` from enum-like objects, pass through otherwise."""
5865
return value.value if hasattr(value, "value") else value

‎tests/unit/test_pinecone_class.py‎

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
from __future__ import annotations
44

5-
import logging
65
import warnings
76
from unittest.mock import MagicMock, patch
87

@@ -489,13 +488,9 @@ def test_pool_threads_kwarg_accepted_silently(self) -> None:
489488
pc = Pinecone(api_key="x", pool_threads=4)
490489
assert pc is not None
491490

492-
def test_pool_threads_kwarg_emits_debug_log(self, caplog: pytest.LogCaptureFixture) -> None:
493-
with caplog.at_level(logging.DEBUG, logger="pinecone._client"):
494-
Pinecone(api_key="x", pool_threads=4)
495-
assert any(
496-
"pool_threads" in r.message and "connection_pool_maxsize" in r.message
497-
for r in caplog.records
498-
)
491+
def test_pool_threads_kwarg_stored_as_attribute(self) -> None:
492+
pc = Pinecone(api_key="x", pool_threads=4)
493+
assert pc._legacy_pool_threads == 4 # type: ignore[attr-defined]
499494

500495
def test_pool_threads_kwarg_does_not_warn(self) -> None:
501496
with warnings.catch_warnings(record=True) as record:
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Unit tests for Pinecone(pool_threads=N) propagation to Index (BC-0110)."""
2+
3+
from __future__ import annotations
4+
5+
import sys
6+
import warnings
7+
8+
import pytest
9+
10+
from pinecone import Pinecone
11+
12+
13+
def test_pinecone_without_pool_threads_does_not_import_legacy(
14+
monkeypatch: pytest.MonkeyPatch,
15+
) -> None:
16+
monkeypatch.delitem(sys.modules, "pinecone._legacy.async_req", raising=False)
17+
Pinecone(api_key="k")
18+
assert "pinecone._legacy.async_req" not in sys.modules
19+
20+
21+
def test_pinecone_pool_threads_propagates_to_attribute() -> None:
22+
pc = Pinecone(api_key="k")
23+
assert pc._legacy_pool_threads is None # type: ignore[attr-defined]
24+
25+
pc2 = Pinecone(api_key="k", pool_threads=8)
26+
assert pc2._legacy_pool_threads == 8 # type: ignore[attr-defined]
27+
28+
29+
def test_pinecone_index_factory_passes_pool_threads() -> None:
30+
pc = Pinecone(api_key="k", pool_threads=4)
31+
kwargs = pc._build_index_kwargs("test.svc.pinecone.io") # type: ignore[attr-defined]
32+
assert kwargs["pool_threads"] == 4
33+
34+
35+
def test_pinecone_without_pool_threads_omits_field() -> None:
36+
pc = Pinecone(api_key="k")
37+
kwargs = pc._build_index_kwargs("test.svc.pinecone.io") # type: ignore[attr-defined]
38+
assert "pool_threads" not in kwargs
39+
40+
41+
def test_pinecone_pool_threads_emits_no_warning() -> None:
42+
with warnings.catch_warnings(record=True) as record:
43+
warnings.simplefilter("always")
44+
Pinecone(api_key="k", pool_threads=4)
45+
assert [w for w in record if issubclass(w.category, DeprecationWarning)] == []
46+
47+
48+
def test_pinecone_index_with_pool_threads_installs_async_req() -> None:
49+
pc = Pinecone(api_key="k")
50+
idx = pc.index(host="test.svc.pinecone.io")
51+
assert not hasattr(idx, "_legacy_async_pool")
52+
53+
pc2 = Pinecone(api_key="k", pool_threads=4)
54+
idx2 = pc2.index(host="test.svc.pinecone.io")
55+
assert hasattr(idx2, "_legacy_async_pool")
56+
assert idx2._legacy_async_pool_threads == 4 # type: ignore[attr-defined]

0 commit comments

Comments
 (0)