Skip to content

Commit 01dc0fe

Browse files
committed
Implement create_namespace
1 parent 3c166cc commit 01dc0fe

File tree

12 files changed

+514
-3
lines changed

12 files changed

+514
-3
lines changed

pinecone/db_data/index.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -698,6 +698,13 @@ def cancel_import(self, id: str):
698698
"""
699699
return self.bulk_import.cancel(id=id)
700700

701+
@validate_and_convert_errors
702+
@require_kwargs
703+
def create_namespace(
704+
self, name: str, schema: Optional[Dict[str, Any]] = None, **kwargs
705+
) -> "NamespaceDescription":
706+
return self.namespace.create(name=name, schema=schema, **kwargs)
707+
701708
@validate_and_convert_errors
702709
@require_kwargs
703710
def describe_namespace(self, namespace: str, **kwargs) -> "NamespaceDescription":

pinecone/db_data/index_asyncio.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -752,6 +752,13 @@ async def cancel_import(self, id: str):
752752
"""
753753
return await self.bulk_import.cancel(id=id)
754754

755+
@validate_and_convert_errors
756+
@require_kwargs
757+
async def create_namespace(
758+
self, name: str, schema: Optional[Dict[str, Any]] = None, **kwargs
759+
) -> "NamespaceDescription":
760+
return await self.namespace.create(name=name, schema=schema, **kwargs)
761+
755762
@validate_and_convert_errors
756763
@require_kwargs
757764
async def describe_namespace(self, namespace: str, **kwargs) -> "NamespaceDescription":

pinecone/db_data/index_asyncio_interface.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,49 @@ async def search_records(
863863
"""Alias of the search() method."""
864864
pass
865865

866+
@abstractmethod
867+
@require_kwargs
868+
async def create_namespace(
869+
self, name: str, schema: Optional[Dict[str, Any]] = None, **kwargs
870+
) -> NamespaceDescription:
871+
"""Create a namespace in a serverless index.
872+
873+
Args:
874+
name (str): The name of the namespace to create
875+
schema (Optional[Dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional]
876+
877+
Returns:
878+
NamespaceDescription: Information about the created namespace including vector count
879+
880+
Create a namespace in a serverless index. For guidance and examples, see
881+
`Manage namespaces <https://docs.pinecone.io/guides/manage-data/manage-namespaces>`_.
882+
883+
**Note:** This operation is not supported for pod-based indexes.
884+
885+
Examples:
886+
887+
.. code-block:: python
888+
889+
>>> # Create a namespace with just a name
890+
>>> import asyncio
891+
>>> from pinecone import Pinecone
892+
>>>
893+
>>> async def main():
894+
... pc = Pinecone()
895+
... async with pc.IndexAsyncio(host="example-index-dojoi3u.svc.eu-west1-gcp.pinecone.io") as idx:
896+
... namespace = await idx.create_namespace(name="my-namespace")
897+
... print(f"Created namespace: {namespace.name}, Vector count: {namespace.vector_count}")
898+
>>>
899+
>>> asyncio.run(main())
900+
901+
>>> # Create a namespace with schema configuration
902+
>>> from pinecone.core.openapi.db_data.model.create_namespace_request_schema import CreateNamespaceRequestSchema
903+
>>> schema = CreateNamespaceRequestSchema(fields={...})
904+
>>> namespace = await idx.create_namespace(name="my-namespace", schema=schema)
905+
906+
"""
907+
pass
908+
866909
@abstractmethod
867910
@require_kwargs
868911
async def describe_namespace(self, namespace: str, **kwargs) -> NamespaceDescription:

pinecone/db_data/interfaces.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -837,6 +837,40 @@ def list(self, **kwargs):
837837
"""
838838
pass
839839

840+
@abstractmethod
841+
@require_kwargs
842+
def create_namespace(
843+
self, name: str, schema: Optional[Dict[str, Any]] = None, **kwargs
844+
) -> NamespaceDescription:
845+
"""Create a namespace in a serverless index.
846+
847+
Args:
848+
name (str): The name of the namespace to create
849+
schema (Optional[Dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional]
850+
851+
Returns:
852+
NamespaceDescription: Information about the created namespace including vector count
853+
854+
Create a namespace in a serverless index. For guidance and examples, see
855+
`Manage namespaces <https://docs.pinecone.io/guides/manage-data/manage-namespaces>`_.
856+
857+
**Note:** This operation is not supported for pod-based indexes.
858+
859+
Examples:
860+
861+
.. code-block:: python
862+
863+
>>> # Create a namespace with just a name
864+
>>> namespace = index.create_namespace(name="my-namespace")
865+
>>> print(f"Created namespace: {namespace.name}, Vector count: {namespace.vector_count}")
866+
867+
>>> # Create a namespace with schema configuration
868+
>>> from pinecone.core.openapi.db_data.model.create_namespace_request_schema import CreateNamespaceRequestSchema
869+
>>> schema = CreateNamespaceRequestSchema(fields={...})
870+
>>> namespace = index.create_namespace(name="my-namespace", schema=schema)
871+
"""
872+
pass
873+
840874
@abstractmethod
841875
@require_kwargs
842876
def describe_namespace(self, namespace: str, **kwargs) -> NamespaceDescription:

pinecone/db_data/resources/asyncio/namespace_asyncio.py

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, AsyncIterator
1+
from typing import Optional, AsyncIterator, Any
22

33
from pinecone.core.openapi.db_data.api.namespace_operations_api import AsyncioNamespaceOperationsApi
44
from pinecone.core.openapi.db_data.models import ListNamespacesResponse, NamespaceDescription
@@ -15,6 +15,26 @@ class NamespaceResourceAsyncio:
1515
def __init__(self, api_client) -> None:
1616
self.__namespace_operations_api = AsyncioNamespaceOperationsApi(api_client)
1717

18+
@require_kwargs
19+
async def create(
20+
self, name: str, schema: Optional[Any] = None, **kwargs
21+
) -> NamespaceDescription:
22+
"""
23+
Args:
24+
name (str): The name of the namespace to create
25+
schema (Optional[Any]): Optional schema configuration for the namespace. Can be a dictionary or CreateNamespaceRequestSchema object. [optional]
26+
27+
Returns:
28+
``NamespaceDescription``: Information about the created namespace including vector count
29+
30+
Create a namespace in a serverless index. For guidance and examples, see
31+
`Manage namespaces <https://docs.pinecone.io/guides/manage-data/manage-namespaces>`_.
32+
33+
**Note:** This operation is not supported for pod-based indexes.
34+
"""
35+
args = NamespaceRequestFactory.create_namespace_args(name=name, schema=schema, **kwargs)
36+
return await self.__namespace_operations_api.create_namespace(**args)
37+
1838
@require_kwargs
1939
async def describe(self, namespace: str, **kwargs) -> NamespaceDescription:
2040
"""

pinecone/db_data/resources/sync/namespace.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from typing import Optional, Iterator
1+
from typing import Optional, Iterator, Any
22

33
from pinecone.core.openapi.db_data.api.namespace_operations_api import NamespaceOperationsApi
44
from pinecone.core.openapi.db_data.models import ListNamespacesResponse, NamespaceDescription
@@ -25,6 +25,24 @@ def __init__(self, api_client, config, openapi_config, pool_threads: int) -> Non
2525
self.__namespace_operations_api = NamespaceOperationsApi(api_client)
2626
super().__init__()
2727

28+
@require_kwargs
29+
def create(self, name: str, schema: Optional[Any] = None, **kwargs) -> NamespaceDescription:
30+
"""
31+
Args:
32+
name (str): The name of the namespace to create
33+
schema (Optional[Any]): Optional schema configuration for the namespace. Can be a dictionary or CreateNamespaceRequestSchema object. [optional]
34+
35+
Returns:
36+
``NamespaceDescription``: Information about the created namespace including vector count
37+
38+
Create a namespace in a serverless index. For guidance and examples, see
39+
`Manage namespaces <https://docs.pinecone.io/guides/manage-data/manage-namespaces>`_.
40+
41+
**Note:** This operation is not supported for pod-based indexes.
42+
"""
43+
args = NamespaceRequestFactory.create_namespace_args(name=name, schema=schema, **kwargs)
44+
return self.__namespace_operations_api.create_namespace(**args)
45+
2846
@require_kwargs
2947
def describe(self, namespace: str, **kwargs) -> NamespaceDescription:
3048
"""

pinecone/db_data/resources/sync/namespace_request_factory.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
from typing import Optional, TypedDict, Any, cast
1+
from typing import Optional, TypedDict, Any, cast, Dict, Union
22

33
from pinecone.utils import parse_non_empty_args
4+
from pinecone.core.openapi.db_data.model.create_namespace_request import CreateNamespaceRequest
5+
from pinecone.core.openapi.db_data.model.create_namespace_request_schema import (
6+
CreateNamespaceRequestSchema,
7+
)
48

59

610
class DescribeNamespaceArgs(TypedDict, total=False):
@@ -11,6 +15,10 @@ class DeleteNamespaceArgs(TypedDict, total=False):
1115
namespace: str
1216

1317

18+
class CreateNamespaceArgs(TypedDict, total=False):
19+
create_namespace_request: CreateNamespaceRequest
20+
21+
1422
class NamespaceRequestFactory:
1523
@staticmethod
1624
def describe_namespace_args(namespace: str, **kwargs) -> DescribeNamespaceArgs:
@@ -26,6 +34,30 @@ def delete_namespace_args(namespace: str, **kwargs) -> DeleteNamespaceArgs:
2634
base_args = {"namespace": namespace}
2735
return cast(DeleteNamespaceArgs, {**base_args, **kwargs})
2836

37+
@staticmethod
38+
def create_namespace_args(
39+
name: str,
40+
schema: Optional[Union[CreateNamespaceRequestSchema, Dict[str, Any]]] = None,
41+
**kwargs,
42+
) -> CreateNamespaceArgs:
43+
if not isinstance(name, str):
44+
raise ValueError("name must be string")
45+
if name.strip() == "":
46+
raise ValueError("name must not be empty")
47+
48+
request_kwargs: Dict[str, Any] = {"name": name}
49+
if schema is not None:
50+
if isinstance(schema, dict):
51+
schema_obj = CreateNamespaceRequestSchema(**schema)
52+
request_kwargs["schema"] = schema_obj
53+
else:
54+
# schema is already CreateNamespaceRequestSchema
55+
request_kwargs["schema"] = cast(CreateNamespaceRequestSchema, schema)
56+
57+
create_namespace_request = CreateNamespaceRequest(**request_kwargs)
58+
base_args = {"create_namespace_request": create_namespace_request}
59+
return cast(CreateNamespaceArgs, {**base_args, **kwargs})
60+
2961
@staticmethod
3062
def list_namespaces_args(
3163
limit: Optional[int] = None, pagination_token: Optional[str] = None, **kwargs

pinecone/grpc/index_grpc.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,9 @@
5050
DescribeNamespaceRequest,
5151
DeleteNamespaceRequest,
5252
ListNamespacesRequest,
53+
CreateNamespaceRequest,
54+
MetadataSchema,
55+
MetadataFieldProperties,
5356
)
5457
from pinecone.core.grpc.protos.db_data_2025_10_pb2_grpc import VectorServiceStub
5558
from pinecone import Vector, SparseValues
@@ -769,6 +772,65 @@ def describe_index_stats(
769772
json_response = json_format.MessageToDict(response)
770773
return parse_stats_response(json_response)
771774

775+
@require_kwargs
776+
def create_namespace(
777+
self, name: str, schema: Optional[Dict[str, Any]] = None, async_req: bool = False, **kwargs
778+
) -> Union[NamespaceDescription, PineconeGrpcFuture]:
779+
"""
780+
The create_namespace operation creates a namespace in a serverless index.
781+
782+
Examples:
783+
784+
.. code-block:: python
785+
786+
>>> index.create_namespace(name='my_namespace')
787+
788+
>>> # Create namespace asynchronously
789+
>>> future = index.create_namespace(name='my_namespace', async_req=True)
790+
>>> namespace = future.result()
791+
792+
Args:
793+
name (str): The name of the namespace to create.
794+
schema (Optional[Dict[str, Any]]): Optional schema configuration for the namespace as a dictionary. [optional]
795+
async_req (bool): If True, the create_namespace operation will be performed asynchronously. [optional]
796+
797+
Returns: NamespaceDescription object which contains information about the created namespace, or a PineconeGrpcFuture object if async_req is True.
798+
"""
799+
timeout = kwargs.pop("timeout", None)
800+
801+
# Build MetadataSchema from dict if provided
802+
metadata_schema = None
803+
if schema is not None:
804+
if isinstance(schema, dict):
805+
# Convert dict to MetadataSchema
806+
fields = {}
807+
for key, value in schema.get("fields", {}).items():
808+
if isinstance(value, dict):
809+
filterable = value.get("filterable", False)
810+
fields[key] = MetadataFieldProperties(filterable=filterable)
811+
else:
812+
# If value is already a MetadataFieldProperties, use it directly
813+
fields[key] = value
814+
metadata_schema = MetadataSchema(fields=fields)
815+
else:
816+
# Assume it's already a MetadataSchema
817+
metadata_schema = schema
818+
819+
request_kwargs: Dict[str, Any] = {"name": name}
820+
if metadata_schema is not None:
821+
request_kwargs["schema"] = metadata_schema
822+
823+
request = CreateNamespaceRequest(**request_kwargs)
824+
825+
if async_req:
826+
future = self.runner.run(self.stub.CreateNamespace.future, request, timeout=timeout)
827+
return PineconeGrpcFuture(
828+
future, timeout=timeout, result_transformer=parse_namespace_description
829+
)
830+
831+
response = self.runner.run(self.stub.CreateNamespace, request, timeout=timeout)
832+
return parse_namespace_description(response)
833+
772834
@require_kwargs
773835
def describe_namespace(self, namespace: str, **kwargs) -> NamespaceDescription:
774836
"""

tests/integration/data/test_namespace.py

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,66 @@ def delete_all_namespaces(index):
4343

4444

4545
class TestNamespaceOperations:
46+
def test_create_namespace(self, idx):
47+
"""Test creating a namespace"""
48+
test_namespace = "test_create_namespace_sync"
49+
50+
try:
51+
# Ensure namespace doesn't exist first
52+
if verify_namespace_exists(idx, test_namespace):
53+
idx.delete_namespace(namespace=test_namespace)
54+
time.sleep(10)
55+
56+
# Create namespace
57+
description = idx.create_namespace(name=test_namespace)
58+
59+
# Verify namespace was created
60+
assert isinstance(description, NamespaceDescription)
61+
assert description.name == test_namespace
62+
# New namespace should have 0 records (record_count may be None, 0, or "0" as string)
63+
assert (
64+
description.record_count is None
65+
or description.record_count == 0
66+
or description.record_count == "0"
67+
)
68+
69+
# Verify namespace exists by describing it
70+
verify_description = idx.describe_namespace(namespace=test_namespace)
71+
assert verify_description.name == test_namespace
72+
73+
finally:
74+
# Cleanup
75+
if verify_namespace_exists(idx, test_namespace):
76+
idx.delete_namespace(namespace=test_namespace)
77+
time.sleep(10)
78+
79+
def test_create_namespace_duplicate(self, idx):
80+
"""Test creating a duplicate namespace raises an error"""
81+
test_namespace = "test_create_duplicate_sync"
82+
83+
try:
84+
# Ensure namespace doesn't exist first
85+
if verify_namespace_exists(idx, test_namespace):
86+
idx.delete_namespace(namespace=test_namespace)
87+
time.sleep(10)
88+
89+
# Create namespace first time
90+
description = idx.create_namespace(name=test_namespace)
91+
assert description.name == test_namespace
92+
93+
# Try to create duplicate namespace - should raise an error
94+
import pytest
95+
from pinecone.exceptions import PineconeApiException
96+
97+
with pytest.raises(PineconeApiException):
98+
idx.create_namespace(name=test_namespace)
99+
100+
finally:
101+
# Cleanup
102+
if verify_namespace_exists(idx, test_namespace):
103+
idx.delete_namespace(namespace=test_namespace)
104+
time.sleep(10)
105+
46106
def test_describe_namespace(self, idx):
47107
"""Test describing a namespace"""
48108
# Setup test data

0 commit comments

Comments
 (0)