cvec 1.5.2__tar.gz → 1.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cvec-1.5.2 → cvec-1.5.3}/.github/workflows/ci.yml +1 -1
- {cvec-1.5.2 → cvec-1.5.3}/.github/workflows/license-check-python.yml +1 -1
- cvec-1.5.3/.github/workflows/pyproject-license-check.yml +34 -0
- {cvec-1.5.2 → cvec-1.5.3}/PKG-INFO +2 -2
- {cvec-1.5.2 → cvec-1.5.3}/pyproject.toml +1 -1
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/cvec.py +12 -9
- {cvec-1.5.2 → cvec-1.5.3}/tests/test_cvec.py +89 -0
- {cvec-1.5.2 → cvec-1.5.3}/uv.lock +3 -3
- cvec-1.5.2/.claude/settings.local.json +0 -28
- cvec-1.5.2/.github/workflows/pyproject-license-check.yml +0 -17
- {cvec-1.5.2 → cvec-1.5.3}/.claude/settings.json +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/.gitignore +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/CLAUDE.md +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/LICENSE +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/README.md +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/add_agent_post_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/add_metric_data_arrow_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/add_metric_data_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/add_multiple_metrics_and_get_spans_for_them.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/eav_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_metric_arrow_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_metric_data_objects_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_metrics_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_modeling_metrics_data_arrow_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_modeling_metrics_data_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_modeling_metrics_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/get_spans_example.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/examples/show_eav_schema.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/scripts/csv_import.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/__init__.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/http_cache.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/__init__.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/agent_post.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/eav_column.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/eav_filter.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/eav_table.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/metric.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/models/span.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/py.typed +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/utils/__init__.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/src/cvec/utils/arrow_converter.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/tests/__init__.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/tests/test_http_cache.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/tests/test_http_compression.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/tests/test_modeling.py +0 -0
- {cvec-1.5.2 → cvec-1.5.3}/tests/test_token_refresh.py +0 -0
|
@@ -27,7 +27,7 @@ jobs:
|
|
|
27
27
|
- name: Checkout
|
|
28
28
|
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1
|
|
29
29
|
|
|
30
|
-
- uses: CVector-Energy/python-test@
|
|
30
|
+
- uses: CVector-Energy/python-test@b956bb181831f0ddca3496505d5823c678a926e7 # v1.1.0
|
|
31
31
|
with:
|
|
32
32
|
python-version: ${{ matrix.python-version }}
|
|
33
33
|
src-dirs: .
|
|
@@ -11,7 +11,7 @@ jobs:
|
|
|
11
11
|
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
|
12
12
|
|
|
13
13
|
- name: Check Python licenses
|
|
14
|
-
uses: CVector-Energy/pyproject-license-check@main
|
|
14
|
+
uses: CVector-Energy/pyproject-license-check@977092b2a13e0766082dfdfb7c57b67985c675e8 # main
|
|
15
15
|
with:
|
|
16
16
|
app-id: ${{ vars.APP_ID }}
|
|
17
17
|
app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
name: Python License Check
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
|
|
6
|
+
permissions:
|
|
7
|
+
contents: read
|
|
8
|
+
id-token: write
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
license-check:
|
|
12
|
+
runs-on: ubuntu-24.04
|
|
13
|
+
|
|
14
|
+
steps:
|
|
15
|
+
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
|
16
|
+
|
|
17
|
+
- name: Configure AWS credentials
|
|
18
|
+
uses: aws-actions/configure-aws-credentials@7474bc4690e29a8392af63c5b98e7449536d5c3a # v4.3.1
|
|
19
|
+
with:
|
|
20
|
+
role-to-assume: arn:aws:iam::904233131484:role/CvecCodeArtifactInternalReadOnlyAccess
|
|
21
|
+
aws-region: us-east-1
|
|
22
|
+
|
|
23
|
+
- name: Configure CodeArtifact
|
|
24
|
+
run: |
|
|
25
|
+
CODEARTIFACT_AUTH_TOKEN=$(aws codeartifact get-authorization-token --domain cvector --domain-owner 904233131484 --region us-east-1 --query authorizationToken --output text)
|
|
26
|
+
echo "::add-mask::$CODEARTIFACT_AUTH_TOKEN"
|
|
27
|
+
echo "UV_INDEX_CVECTOR_INTERNAL_USERNAME=aws" >> $GITHUB_ENV
|
|
28
|
+
echo "UV_INDEX_CVECTOR_INTERNAL_PASSWORD=${CODEARTIFACT_AUTH_TOKEN}" >> $GITHUB_ENV
|
|
29
|
+
|
|
30
|
+
- name: Check Python licenses
|
|
31
|
+
uses: CVector-Energy/pyproject-license-check@075858d33ce078e040088fbef523db1e0cfd2d63 # v0.3.0
|
|
32
|
+
with:
|
|
33
|
+
app-id: ${{ vars.APP_ID }}
|
|
34
|
+
app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
@@ -36,6 +36,7 @@ class CVec:
|
|
|
36
36
|
host: Optional[str]
|
|
37
37
|
default_start_at: Optional[datetime]
|
|
38
38
|
default_end_at: Optional[datetime]
|
|
39
|
+
timeout: Optional[float]
|
|
39
40
|
# Supabase authentication
|
|
40
41
|
_access_token: Optional[str]
|
|
41
42
|
_refresh_token: Optional[str]
|
|
@@ -49,10 +50,12 @@ class CVec:
|
|
|
49
50
|
default_start_at: Optional[datetime] = None,
|
|
50
51
|
default_end_at: Optional[datetime] = None,
|
|
51
52
|
api_key: Optional[str] = None,
|
|
53
|
+
timeout: Optional[float] = 30,
|
|
52
54
|
) -> None:
|
|
53
55
|
self.host = host or os.environ.get("CVEC_HOST")
|
|
54
56
|
self.default_start_at = default_start_at
|
|
55
57
|
self.default_end_at = default_end_at
|
|
58
|
+
self.timeout = timeout
|
|
56
59
|
|
|
57
60
|
# Supabase authentication
|
|
58
61
|
self._access_token = None
|
|
@@ -210,7 +213,7 @@ class CVec:
|
|
|
210
213
|
req = Request(
|
|
211
214
|
url, data=request_body, headers=request_headers, method=method
|
|
212
215
|
)
|
|
213
|
-
with urlopen(req) as response:
|
|
216
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
214
217
|
return self._process_response(response, url, method)
|
|
215
218
|
|
|
216
219
|
try:
|
|
@@ -234,7 +237,7 @@ class CVec:
|
|
|
234
237
|
req = Request(
|
|
235
238
|
url, data=request_body, headers=request_headers, method=method
|
|
236
239
|
)
|
|
237
|
-
with urlopen(req) as response:
|
|
240
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
238
241
|
return self._process_response(response, url, method)
|
|
239
242
|
except (HTTPError, URLError, ValueError, KeyError) as refresh_error:
|
|
240
243
|
logger.warning(
|
|
@@ -557,7 +560,7 @@ class CVec:
|
|
|
557
560
|
request_body = json.dumps(payload).encode("utf-8")
|
|
558
561
|
req = Request(supabase_url, data=request_body, headers=headers, method="POST")
|
|
559
562
|
|
|
560
|
-
with urlopen(req) as response:
|
|
563
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
561
564
|
response_data = response.read()
|
|
562
565
|
data = json.loads(response_data.decode("utf-8"))
|
|
563
566
|
|
|
@@ -586,7 +589,7 @@ class CVec:
|
|
|
586
589
|
request_body = json.dumps(payload).encode("utf-8")
|
|
587
590
|
req = Request(supabase_url, data=request_body, headers=headers, method="POST")
|
|
588
591
|
|
|
589
|
-
with urlopen(req) as response:
|
|
592
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
590
593
|
response_data = response.read()
|
|
591
594
|
data = json.loads(response_data.decode("utf-8"))
|
|
592
595
|
|
|
@@ -609,7 +612,7 @@ class CVec:
|
|
|
609
612
|
config_url = f"{self.host}/config"
|
|
610
613
|
req = Request(config_url, method="GET")
|
|
611
614
|
|
|
612
|
-
with urlopen(req) as response:
|
|
615
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
613
616
|
response_data = response.read()
|
|
614
617
|
config_data = json.loads(response_data.decode("utf-8"))
|
|
615
618
|
|
|
@@ -664,7 +667,7 @@ class CVec:
|
|
|
664
667
|
def make_rpc_request() -> Any:
|
|
665
668
|
"""Inner function to make the actual RPC request."""
|
|
666
669
|
req = Request(url, data=request_body, headers=headers, method="POST")
|
|
667
|
-
with urlopen(req) as response:
|
|
670
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
668
671
|
response_data = response.read()
|
|
669
672
|
return json.loads(response_data.decode("utf-8"))
|
|
670
673
|
|
|
@@ -682,7 +685,7 @@ class CVec:
|
|
|
682
685
|
req = Request(
|
|
683
686
|
url, data=request_body, headers=headers, method="POST"
|
|
684
687
|
)
|
|
685
|
-
with urlopen(req) as response:
|
|
688
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
686
689
|
response_data = response.read()
|
|
687
690
|
return json.loads(response_data.decode("utf-8"))
|
|
688
691
|
except (HTTPError, URLError, ValueError, KeyError) as refresh_error:
|
|
@@ -731,7 +734,7 @@ class CVec:
|
|
|
731
734
|
def make_query_request() -> Any:
|
|
732
735
|
"""Inner function to make the actual query request."""
|
|
733
736
|
req = Request(url, headers=headers, method="GET")
|
|
734
|
-
with urlopen(req) as response:
|
|
737
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
735
738
|
response_data = response.read()
|
|
736
739
|
return json.loads(response_data.decode("utf-8"))
|
|
737
740
|
|
|
@@ -747,7 +750,7 @@ class CVec:
|
|
|
747
750
|
|
|
748
751
|
# Retry the request
|
|
749
752
|
req = Request(url, headers=headers, method="GET")
|
|
750
|
-
with urlopen(req) as response:
|
|
753
|
+
with urlopen(req, timeout=self.timeout) as response:
|
|
751
754
|
response_data = response.read()
|
|
752
755
|
return json.loads(response_data.decode("utf-8"))
|
|
753
756
|
except (HTTPError, URLError, ValueError, KeyError) as refresh_error:
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import io
|
|
2
2
|
import os
|
|
3
|
+
import socket
|
|
4
|
+
import threading
|
|
5
|
+
import time
|
|
3
6
|
from datetime import datetime
|
|
4
7
|
from typing import Any
|
|
5
8
|
from unittest.mock import patch
|
|
@@ -897,3 +900,89 @@ class TestCVecSelectFromEAVId:
|
|
|
897
900
|
)
|
|
898
901
|
|
|
899
902
|
assert result == []
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
class TestCVecTimeout:
|
|
906
|
+
"""Test that HTTP requests respect the timeout parameter."""
|
|
907
|
+
|
|
908
|
+
@patch.object(CVec, "_login_with_supabase", return_value=None)
|
|
909
|
+
@patch.object(
|
|
910
|
+
CVec, "_fetch_config", autospec=True, side_effect=mock_fetch_config_side_effect
|
|
911
|
+
)
|
|
912
|
+
def test_default_timeout_is_set(self, mock_fetch_key: Any, mock_login: Any) -> None:
|
|
913
|
+
"""Test that the default timeout is 30 seconds."""
|
|
914
|
+
client = CVec(
|
|
915
|
+
host="test_host",
|
|
916
|
+
api_key="cva_hHs0CbkKALxMnxUdI9hanF0TBPvvvr1HjG6O",
|
|
917
|
+
)
|
|
918
|
+
assert client.timeout == 30
|
|
919
|
+
|
|
920
|
+
@patch.object(CVec, "_login_with_supabase", return_value=None)
|
|
921
|
+
@patch.object(
|
|
922
|
+
CVec, "_fetch_config", autospec=True, side_effect=mock_fetch_config_side_effect
|
|
923
|
+
)
|
|
924
|
+
def test_custom_timeout_is_set(self, mock_fetch_key: Any, mock_login: Any) -> None:
|
|
925
|
+
"""Test that a custom timeout can be provided."""
|
|
926
|
+
client = CVec(
|
|
927
|
+
host="test_host",
|
|
928
|
+
api_key="cva_hHs0CbkKALxMnxUdI9hanF0TBPvvvr1HjG6O",
|
|
929
|
+
timeout=60,
|
|
930
|
+
)
|
|
931
|
+
assert client.timeout == 60
|
|
932
|
+
|
|
933
|
+
@patch.object(CVec, "_login_with_supabase", return_value=None)
|
|
934
|
+
@patch.object(
|
|
935
|
+
CVec, "_fetch_config", autospec=True, side_effect=mock_fetch_config_side_effect
|
|
936
|
+
)
|
|
937
|
+
def test_none_timeout_disables_timeout(
|
|
938
|
+
self, mock_fetch_key: Any, mock_login: Any
|
|
939
|
+
) -> None:
|
|
940
|
+
"""Test that timeout=None disables the timeout."""
|
|
941
|
+
client = CVec(
|
|
942
|
+
host="test_host",
|
|
943
|
+
api_key="cva_hHs0CbkKALxMnxUdI9hanF0TBPvvvr1HjG6O",
|
|
944
|
+
timeout=None,
|
|
945
|
+
)
|
|
946
|
+
assert client.timeout is None
|
|
947
|
+
|
|
948
|
+
def test_make_request_times_out_on_hung_server(self) -> None:
|
|
949
|
+
"""Test that _make_request raises when the server hangs."""
|
|
950
|
+
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
951
|
+
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
952
|
+
srv.bind(("127.0.0.1", 0))
|
|
953
|
+
srv.listen(1)
|
|
954
|
+
port = srv.getsockname()[1]
|
|
955
|
+
|
|
956
|
+
def accept_and_hang() -> None:
|
|
957
|
+
conn, _ = srv.accept()
|
|
958
|
+
time.sleep(30)
|
|
959
|
+
conn.close()
|
|
960
|
+
srv.close()
|
|
961
|
+
|
|
962
|
+
t = threading.Thread(target=accept_and_hang, daemon=True)
|
|
963
|
+
t.start()
|
|
964
|
+
|
|
965
|
+
with (
|
|
966
|
+
patch.object(CVec, "_login_with_supabase", return_value=None),
|
|
967
|
+
patch.object(
|
|
968
|
+
CVec,
|
|
969
|
+
"_fetch_config",
|
|
970
|
+
autospec=True,
|
|
971
|
+
side_effect=mock_fetch_config_side_effect,
|
|
972
|
+
),
|
|
973
|
+
):
|
|
974
|
+
client = CVec(
|
|
975
|
+
host=f"http://127.0.0.1:{port}",
|
|
976
|
+
api_key="cva_hHs0CbkKALxMnxUdI9hanF0TBPvvvr1HjG6O",
|
|
977
|
+
timeout=2,
|
|
978
|
+
)
|
|
979
|
+
client._access_token = "fake_token"
|
|
980
|
+
|
|
981
|
+
start = time.time()
|
|
982
|
+
with pytest.raises(OSError):
|
|
983
|
+
client._make_request("GET", "/api/test")
|
|
984
|
+
elapsed = time.time() - start
|
|
985
|
+
|
|
986
|
+
assert elapsed < 5, f"Expected timeout around 2s, took {elapsed:.1f}s"
|
|
987
|
+
|
|
988
|
+
srv.close()
|
|
@@ -399,11 +399,11 @@ wheels = [
|
|
|
399
399
|
|
|
400
400
|
[[package]]
|
|
401
401
|
name = "pygments"
|
|
402
|
-
version = "2.
|
|
402
|
+
version = "2.20.0"
|
|
403
403
|
source = { registry = "https://pypi.org/simple" }
|
|
404
|
-
sdist = { url = "https://files.pythonhosted.org/packages/
|
|
404
|
+
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
|
|
405
405
|
wheels = [
|
|
406
|
-
{ url = "https://files.pythonhosted.org/packages/
|
|
406
|
+
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
|
407
407
|
]
|
|
408
408
|
|
|
409
409
|
[[package]]
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"permissions": {
|
|
3
|
-
"allow": [
|
|
4
|
-
"Bash(chmod:*)",
|
|
5
|
-
"Bash(python:*)",
|
|
6
|
-
"Bash(git commit:*)",
|
|
7
|
-
"Bash(pytest:*)",
|
|
8
|
-
"Bash(python3 -m pytest:*)",
|
|
9
|
-
"Bash(poetry run pytest:*)",
|
|
10
|
-
"Bash(git add:*)",
|
|
11
|
-
"Bash(poetry run mypy:*)",
|
|
12
|
-
"Bash(git checkout:*)",
|
|
13
|
-
"Bash(poetry run ruff:*)",
|
|
14
|
-
"Bash(poetry install:*)",
|
|
15
|
-
"Bash(tree:*)",
|
|
16
|
-
"Bash(git mv:*)",
|
|
17
|
-
"WebFetch(domain:github.com)",
|
|
18
|
-
"WebFetch(domain:pypi.org)",
|
|
19
|
-
"Bash(poetry run python:*)",
|
|
20
|
-
"Bash(poetry add:*)",
|
|
21
|
-
"Bash(git fetch:*)",
|
|
22
|
-
"Bash(git rebase:*)",
|
|
23
|
-
"Bash(git rm:*)"
|
|
24
|
-
],
|
|
25
|
-
"deny": [],
|
|
26
|
-
"ask": []
|
|
27
|
-
}
|
|
28
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
name: Python License Check
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
|
|
6
|
-
jobs:
|
|
7
|
-
license-check:
|
|
8
|
-
runs-on: ubuntu-24.04
|
|
9
|
-
|
|
10
|
-
steps:
|
|
11
|
-
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
|
|
12
|
-
|
|
13
|
-
- name: Check Python licenses
|
|
14
|
-
uses: CVector-Energy/pyproject-license-check@main
|
|
15
|
-
with:
|
|
16
|
-
app-id: ${{ vars.APP_ID }}
|
|
17
|
-
app-private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|