diff --git a/semantic_router/index/base.py b/semantic_router/index/base.py index fee3c4e20a587bedbe4e9c20dd57c8b1243322eb..7c88a1e56ef6b8b84eff77b1c3c50a945442be70 100644 --- a/semantic_router/index/base.py +++ b/semantic_router/index/base.py @@ -196,10 +196,7 @@ class BaseIndex(BaseModel): return config def lock( - self, - value: bool, - wait: int = 0, - scope: str | None = None + self, value: bool, wait: int = 0, scope: str | None = None ) -> ConfigParameter: """Lock/unlock the index for a given scope (if applicable). If index already locked/unlocked, raises ValueError. @@ -221,7 +218,9 @@ class BaseIndex(BaseModel): # wait for 2.5 seconds before checking again time.sleep(2.5) else: - raise ValueError(f"Index is already {'locked' if value else 'unlocked'}.") + raise ValueError( + f"Index is already {'locked' if value else 'unlocked'}." + ) lock_param = ConfigParameter( field="sr_lock", value=str(value), diff --git a/semantic_router/schema.py b/semantic_router/schema.py index d4f89aa868596dccdf5679269913bcce06009af7..fb4b6825221f1ab3c83fe97d0529109c47fb21e6 100644 --- a/semantic_router/schema.py +++ b/semantic_router/schema.py @@ -63,7 +63,9 @@ class ConfigParameter(BaseModel): field: str value: str scope: Optional[str] = None - created_at: str = Field(default_factory=lambda: datetime.now(datetime.UTC).isoformat()) + created_at: str = Field( + default_factory=lambda: datetime.now(datetime.UTC).isoformat() + ) def to_pinecone(self, dimensions: int): namespace = self.scope or "" diff --git a/tests/unit/test_sync.py b/tests/unit/test_sync.py index a4e4539cfa92cd2292ddf2a4f752b1ae829f535c..9333793f49b1657ff3692c39b7415afb19da2c8a 100644 --- a/tests/unit/test_sync.py +++ b/tests/unit/test_sync.py @@ -491,7 +491,9 @@ class TestSemanticRouter: @pytest.mark.skipif( os.environ.get("PINECONE_API_KEY") is None, reason="Pinecone API key required" ) - def test_sync_lock_prevents_concurrent_sync(self, openai_encoder, routes, index_cls): + def test_sync_lock_prevents_concurrent_sync( + self, openai_encoder, routes, index_cls + ): """Test that sync lock prevents concurrent synchronization operations""" index = init_index(index_cls) route_layer = SemanticRouter( @@ -500,17 +502,17 @@ class TestSemanticRouter: index=index, auto_sync=None, ) - + # Acquire sync lock route_layer.index.lock(value=True) - + # Attempt to sync while lock is held should raise exception with pytest.raises(RuntimeError, match="Sync operation already in progress"): route_layer.sync("local") - + # Release lock route_layer.index.lock(value=False) - + # Should succeed after lock is released route_layer.sync("local") if index_cls is PineconeIndex: @@ -529,12 +531,12 @@ class TestSemanticRouter: index=index, auto_sync=None, ) - + # Initial sync should acquire and release lock route_layer.sync("local") if index_cls is PineconeIndex: time.sleep(PINECONE_SLEEP) - + # Lock should be released, allowing another sync route_layer.sync("local") # Should not raise exception if index_cls is PineconeIndex: @@ -553,18 +555,20 @@ class TestSemanticRouter: index=index, auto_sync=None, ) - + # Force an error during sync by temporarily breaking the index original_sync = route_layer.index.sync - route_layer.index.sync = lambda *args, **kwargs: (_ for _ in ()).throw(Exception("Forced sync error")) - + route_layer.index.sync = lambda *args, **kwargs: (_ for _ in ()).throw( + Exception("Forced sync error") + ) + # Sync should fail but release the lock with pytest.raises(Exception, match="Forced sync error"): route_layer.sync("local") - + # Restore original sync method route_layer.index.sync = original_sync - + # Should be able to sync again since lock was released route_layer.sync("local") if index_cls is PineconeIndex: