Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,17 +15,17 @@ dependencies = [
"requests-oauthlib~=2.0.0",
"pydantic~=2.12.3",
"hatchling~=1.27.0",
"opentelemetry-exporter-otlp-proto-grpc~=1.42.1",
"opentelemetry-exporter-otlp-proto-http~=1.42.1",
"opentelemetry-exporter-otlp-proto-grpc~=1.44.0",
"opentelemetry-exporter-otlp-proto-http~=1.44.0",
"traceloop-sdk~=0.61.0",
"opentelemetry-instrumentation-langchain>=0.61.0,<1",
"httpx>=0.27.0,<1",
"PyJWT>=2.13.0,<3",
"protobuf>=5.0.0,<8",
"protovalidate>=0.13.0,<1",
"protobuf>=6.33.5,<7",
"protovalidate>=1.0.0,<2",
"grpcio>=1.60.0,<2",
"opentelemetry-api>=1.42.1,<2",
"opentelemetry-sdk>=1.42.1,<2",
"opentelemetry-api~=1.44.0",
"opentelemetry-sdk~=1.44.0",
"mcp>=1.1.0,<2",
]

Expand All @@ -44,13 +44,13 @@ packages = ["src/sap_cloud_sdk", "src/buf"]

[dependency-groups]
dev = [
"pytest>=8.4.2,<9",
"pytest>=9.1.1,<10",
"pytest-cov>=7.0.0,<8",
"pytest-asyncio>=1.0.0,<2",
"pytest-bdd>=8.1.0,<9",
"python-dotenv>=1.0.0,<2",
"ty>=0.0.21,<1",
"cryptography>=46.0.3,<47",
"cryptography>=48.0.1,<51",
"ruff>=0.8.0,<1",
"starlette>=0.40.0,<1",
"anyio>=4.5,<5",
Expand Down
3 changes: 2 additions & 1 deletion src/sap_cloud_sdk/adms/_async_http.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
from __future__ import annotations

import asyncio
import inspect
from typing import Any, Callable, Dict, Optional

import httpx
Expand Down Expand Up @@ -216,7 +217,7 @@ async def _bearer_token(self) -> Optional[str]:
"""Resolve the bearer token, handling both sync and async callables."""
if self._get_token is None:
return None
if asyncio.iscoroutinefunction(self._get_token):
if inspect.iscoroutinefunction(self._get_token):
return await self._get_token()
return await asyncio.to_thread(self._get_token)

Expand Down
6 changes: 6 additions & 0 deletions src/sap_cloud_sdk/core/auditlog_ng/buf.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Generated by buf. DO NOT EDIT.
version: v2
deps:
- name: buf.build/bufbuild/protovalidate
commit: 511051f7f4374c3ca873b53ae68a9288
digest: b5:a4a2d4d808a25984cced60769c822c5d496ef0b740f56ac0c9e6b97aaa25b86a9332a00ffd74e0cd202be29e91bd3edfb0bf2ba4dacfe48ff2d8217f9986e3c8
10 changes: 3 additions & 7 deletions src/sap_cloud_sdk/core/data_anonymization/_http_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,16 +182,12 @@ def _post_file_request(
try:
if request.file_path is not None:
file_handle = open(request.file_path, "rb")
file_value = file_handle
file_value: BinaryIO | bytes = file_handle
else:
assert request.file_content is not None
file_value = request.file_content

files = {
"file": (
request.resolved_file_name(),
file_value,
)
}
files = {"file": (request.resolved_file_name(), file_value)}

return self._session.post(
url,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,11 @@ def _normalize_attributes(self, span: ReadableSpan) -> None:
if not hasattr(span, "_attributes") or span._attributes is None:
return

# BoundedAttributes (the default OTEL type) is immutable; copy to a plain dict
# so we can mutate it in-place below.
if not isinstance(span._attributes, dict):
span._attributes = dict(span._attributes)

attrs = cast(MutableMapping[str, Any], span._attributes)

# Only consider spans that have traceloop.* or llm.* or gen_ai.prompt.* or gen_ai.completion.* attributes
Expand Down
16 changes: 9 additions & 7 deletions tests/core/unit/data_anonymization/test_http_transport.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ def test_resolve_cert_from_destination(

cert_path = transport._resolve_cert()

assert isinstance(cert_path, str)
assert Path(cert_path).exists()
assert "BEGIN RSA PRIVATE KEY" in Path(cert_path).read_text(encoding="utf-8")
transport._session = MagicMock()
Expand Down Expand Up @@ -329,6 +330,7 @@ def test_resolve_cert_from_destination_with_base64_bundle(

cert_path = transport._resolve_cert()

assert isinstance(cert_path, str)
assert Path(cert_path).exists()
assert "BEGIN CERTIFICATE" in Path(cert_path).read_text(encoding="utf-8")
transport._session = MagicMock()
Expand Down Expand Up @@ -373,13 +375,13 @@ def test_decode_destination_certificate_content_rejects_missing_key(self) -> Non

def test_resolve_cert_without_config_raises(self) -> None:
transport = object.__new__(HttpTransport)
transport._config = types.SimpleNamespace(
cert=None,
key=None,
cert_path=None,
key_path=None,
destination_name=None,
)
config = object.__new__(DataAnonymizationConfig)
config.cert = None
config.key = None
config.cert_path = None
config.key_path = None
config.destination_name = None
transport._config = config

assert_raises(
AuthenticationError,
Expand Down
4 changes: 2 additions & 2 deletions tests/objectstore/unit/test_s3_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def test_put_object_from_bytes_validation(self, mock_minio_class):
@patch('sap_cloud_sdk.objectstore._s3.Minio')
def test_put_object_from_bytes_s3_error(self, mock_minio_class):
mock_minio = Mock()
s3_error = S3Error("AccessDenied", "Access denied", "test.txt", "123", "456", Mock())
s3_error = S3Error(Mock(), "AccessDenied", "Access denied", "test.txt", "123", "456")
mock_minio.put_object.side_effect = s3_error
mock_minio_class.return_value = mock_minio

Expand Down Expand Up @@ -237,7 +237,7 @@ def test_list_objects_success(self, mock_minio_class):
@patch('sap_cloud_sdk.objectstore._s3.Minio')
def test_list_objects_s3_error(self, mock_minio_class):
mock_minio = Mock()
s3_error = S3Error("AccessDenied", "Access denied", "", "123", "456", Mock())
s3_error = S3Error(Mock(), "AccessDenied", "Access denied", "", "123", "456")
mock_minio.list_objects.side_effect = s3_error
mock_minio_class.return_value = mock_minio

Expand Down
Loading
Loading