Skip to content

Commit e7eb450

Browse files
committed
Fix lint and gzip issue.
1 parent 1e1def0 commit e7eb450

File tree

2 files changed

+125
-82
lines changed
  • instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content

2 files changed

+125
-82
lines changed

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/base.py

Lines changed: 42 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,44 @@
2121
from .util import convert_to_response, create_response
2222

2323

24+
# Helper used in "_install_mocks" below.
25+
def _wrap_output(mock_generate_content):
26+
def _wrapped(*args, **kwargs):
27+
return convert_to_response(mock_generate_content(*args, **kwargs))
28+
29+
return _wrapped
30+
31+
32+
# Helper used in "_install_mocks" below.
33+
def _wrap_output_stream(mock_generate_content_stream):
34+
def _wrapped(*args, **kwargs):
35+
for output in mock_generate_content_stream(*args, **kwargs):
36+
yield convert_to_response(output)
37+
38+
return _wrapped
39+
40+
41+
# Helper used in "_install_mocks" below.
42+
def _async_wrapper(mock_generate_content):
43+
async def _wrapped(*args, **kwargs):
44+
return mock_generate_content(*args, **kwargs)
45+
46+
return _wrapped
47+
48+
49+
# Helper used in "_install_mocks" below.
50+
def _async_stream_wrapper(mock_generate_content_stream):
51+
async def _wrapped(*args, **kwargs):
52+
async def _internal_generator():
53+
for result in mock_generate_content_stream(*args, **kwargs):
54+
yield result
55+
56+
return _internal_generator()
57+
58+
return _wrapped
59+
60+
61+
2462
class TestCase(CommonTestCaseBase):
2563
# The "setUp" function is defined by "unittest.TestCase" and thus
2664
# this name must be used. Uncertain why pylint doesn't seem to
@@ -91,46 +129,17 @@ def _default_impl(*args, **kwargs):
91129
return mock
92130

93131
def _install_mocks(self):
94-
output_wrapped = self._wrap_output(self._generate_content_mock)
95-
output_wrapped_stream = self._wrap_output_stream(
132+
output_wrapped = _wrap_output(self._generate_content_mock)
133+
output_wrapped_stream = _wrap_output_stream(
96134
self._generate_content_stream_mock
97135
)
98136
Models.generate_content = output_wrapped
99137
Models.generate_content_stream = output_wrapped_stream
100-
AsyncModels.generate_content = self._async_wrapper(output_wrapped)
101-
AsyncModels.generate_content_stream = self._async_stream_wrapper(
138+
AsyncModels.generate_content = _async_wrapper(output_wrapped)
139+
AsyncModels.generate_content_stream = _async_stream_wrapper(
102140
output_wrapped_stream
103141
)
104142

105-
def _wrap_output(self, mock_generate_content):
106-
def _wrapped(*args, **kwargs):
107-
return convert_to_response(mock_generate_content(*args, **kwargs))
108-
109-
return _wrapped
110-
111-
def _wrap_output_stream(self, mock_generate_content_stream):
112-
def _wrapped(*args, **kwargs):
113-
for output in mock_generate_content_stream(*args, **kwargs):
114-
yield convert_to_response(output)
115-
116-
return _wrapped
117-
118-
def _async_wrapper(self, mock_generate_content):
119-
async def _wrapped(*args, **kwargs):
120-
return mock_generate_content(*args, **kwargs)
121-
122-
return _wrapped
123-
124-
def _async_stream_wrapper(self, mock_generate_content_stream):
125-
async def _wrapped(*args, **kwargs):
126-
async def _internal_generator():
127-
for result in mock_generate_content_stream(*args, **kwargs):
128-
yield result
129-
130-
return _internal_generator()
131-
132-
return _wrapped
133-
134143
def tearDown(self):
135144
super().tearDown()
136145
if self._generate_content_mock is None:

instrumentation-genai/opentelemetry-instrumentation-google-genai/tests/generate_content/test_e2e.py

Lines changed: 83 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import os
2929
import subprocess
3030

31+
import gzip
3132
import google.auth
3233
import google.auth.credentials
3334
import google.genai
@@ -57,7 +58,7 @@ def _get_project_from_env():
5758
def _get_project_from_gcloud_cli():
5859
try:
5960
gcloud_call_result = subprocess.run(
60-
"gcloud config get project", shell=True, capture_output=True
61+
"gcloud config get project", shell=True, capture_output=True, check=True
6162
)
6263
except subprocess.CalledProcessError:
6364
return None
@@ -145,8 +146,8 @@ def _before_record_response(response):
145146
return response
146147

147148

148-
@pytest.fixture(scope="module")
149-
def vcr_config():
149+
@pytest.fixture(name="vcr_config", scope="module")
150+
def fixture_vcr_config():
150151
return {
151152
"filter_query_parameters": [
152153
"key",
@@ -169,7 +170,8 @@ def vcr_config():
169170
"x-goog-api-key",
170171
"authorization",
171172
"server",
172-
"Server" "Server-Timing",
173+
"Server",
174+
"Server-Timing",
173175
"Date",
174176
],
175177
"before_record_request": _before_record_request,
@@ -191,8 +193,8 @@ def _literal_block_scalar_presenter(dumper, data):
191193
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
192194

193195

194-
@pytest.fixture(scope="module", autouse=True)
195-
def setup_yaml_pretty_formattinmg():
196+
@pytest.fixture(name="internal_setup_yaml_pretty_formatting", scope="module", autouse=True)
197+
def fixture_setup_yaml_pretty_formatting():
196198
yaml.add_representer(_LiteralBlockScalar, _literal_block_scalar_presenter)
197199

198200

@@ -229,6 +231,35 @@ def _convert_body_to_literal(data):
229231
return data
230232

231233

234+
# Helper for enforcing GZIP compression where it was originally.
235+
def _ensure_gzip_single_response(data: bytes):
236+
try:
237+
# Attempt to decompress, first, to avoid double compression.
238+
gzip.decompress(data)
239+
return data
240+
except gzip.BadGzipFile:
241+
# It must not have been compressed in the first place.
242+
return gzip.compress(data)
243+
244+
245+
# VCRPy automatically decompresses responses before saving them, but it may forget to
246+
# re-encode them when the data is loaded. This can create issues with decompression.
247+
# This is why we re-encode on load; to accurately replay what was originally sent.
248+
#
249+
# https://vcrpy.readthedocs.io/en/latest/advanced.html#decode-compressed-response
250+
def _ensure_casette_gzip(loaded_casette):
251+
for interaction in loaded_casette["interactions"]:
252+
response = interaction["response"]
253+
headers = response["headers"]
254+
if "content-encoding" not in headers and "Content-Encoding" not in headers:
255+
continue
256+
if "content-encoding" in headers and "gzip" not in headers["content-encoding"]:
257+
continue
258+
if "Content-Encoding" in headers and "gzip" not in headers["Content-Encoding"]:
259+
continue
260+
response["body"]["string"] = _ensure_gzip_single_response(response["body"]["string"].encode())
261+
262+
232263
class _PrettyPrintJSONBody:
233264
"""This makes request and response body recordings more readable."""
234265

@@ -241,55 +272,58 @@ def serialize(cassette_dict):
241272

242273
@staticmethod
243274
def deserialize(cassette_string):
244-
return yaml.load(cassette_string, Loader=yaml.Loader)
275+
result = yaml.load(cassette_string, Loader=yaml.Loader)
276+
_ensure_casette_gzip(result)
277+
return result
245278

246279

247-
@pytest.fixture(scope="module", autouse=True)
280+
@pytest.fixture(name="fully_initialized_vcr", scope="module", autouse=True)
248281
def setup_vcr(vcr):
249282
vcr.register_serializer("yaml", _PrettyPrintJSONBody)
283+
vcr.serializer = "yaml"
250284
return vcr
251285

252286

253-
@pytest.fixture
254-
def instrumentor():
287+
@pytest.fixture(name="instrumentor")
288+
def fixture_instrumentor():
255289
return GoogleGenAiSdkInstrumentor()
256290

257291

258-
@pytest.fixture(autouse=True)
259-
def setup_instrumentation(instrumentor):
292+
@pytest.fixture(name="internal_instrumentation_setup", autouse=True)
293+
def fixture_setup_instrumentation(instrumentor):
260294
instrumentor.instrument()
261295
yield
262296
instrumentor.uninstrument()
263297

264298

265-
@pytest.fixture(autouse=True)
266-
def otel_mocker():
299+
@pytest.fixture(name="otel_mocker", autouse=True)
300+
def fixture_otel_mocker():
267301
result = OTelMocker()
268302
result.install()
269303
yield result
270304
result.uninstall()
271305

272306

273-
@pytest.fixture(autouse=True, params=["logcontent", "excludecontent"])
274-
def setup_content_recording(request):
307+
@pytest.fixture(name="setup_content_recording", autouse=True, params=["logcontent", "excludecontent"])
308+
def fixture_setup_content_recording(request):
275309
enabled = request.param == "logcontent"
276310
os.environ["OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT"] = str(
277311
enabled
278312
)
279313

280314

281-
@pytest.fixture
282-
def vcr_record_mode(vcr):
315+
@pytest.fixture(name="vcr_record_mode")
316+
def fixture_vcr_record_mode(vcr):
283317
return vcr.record_mode
284318

285319

286-
@pytest.fixture
287-
def in_replay_mode(vcr_record_mode):
320+
@pytest.fixture(name="in_replay_mode")
321+
def fixture_in_replay_mode(vcr_record_mode):
288322
return vcr_record_mode == RecordMode.NONE
289323

290324

291-
@pytest.fixture(autouse=True)
292-
def gcloud_project(in_replay_mode):
325+
@pytest.fixture(name="gcloud_project", autouse=True)
326+
def fixture_gcloud_project(in_replay_mode):
293327
if in_replay_mode:
294328
return _FAKE_PROJECT
295329
result = _get_real_project()
@@ -298,15 +332,15 @@ def gcloud_project(in_replay_mode):
298332
return result
299333

300334

301-
@pytest.fixture
302-
def gcloud_location(in_replay_mode):
335+
@pytest.fixture(name="gcloud_location")
336+
def fixture_gcloud_location(in_replay_mode):
303337
if in_replay_mode:
304338
return _FAKE_LOCATION
305339
return _get_real_location()
306340

307341

308-
@pytest.fixture
309-
def gcloud_credentials(in_replay_mode):
342+
@pytest.fixture(name="gcloud_credentials")
343+
def fixture_gcloud_credentials(in_replay_mode):
310344
if in_replay_mode:
311345
return FakeCredentials()
312346
creds, _ = google.auth.default()
@@ -315,30 +349,30 @@ def gcloud_credentials(in_replay_mode):
315349
)
316350

317351

318-
@pytest.fixture
319-
def gemini_api_key(in_replay_mode):
352+
@pytest.fixture(name="gemini_api_key")
353+
def fixture_gemini_api_key(in_replay_mode):
320354
if in_replay_mode:
321355
return _FAKE_API_KEY
322356
return os.getenv("GEMINI_API_KEY")
323357

324358

325-
@pytest.fixture(autouse=True)
326-
def gcloud_api_key(gemini_api_key):
359+
@pytest.fixture(name="gcloud_api_key", autouse=True)
360+
def fixture_gcloud_api_key(gemini_api_key):
327361
if "GOOGLE_API_KEY" not in os.environ:
328362
os.environ["GOOGLE_API_KEY"] = gemini_api_key
329363
return os.getenv("GOOGLE_API_KEY")
330364

331365

332-
@pytest.fixture
333-
def nonvertex_client_factory(gemini_api_key):
366+
@pytest.fixture(name="nonvertex_client_factory")
367+
def fixture_nonvertex_client_factory(gemini_api_key):
334368
def _factory():
335-
return google.genai.Client(api_key=gemini_api_key)
369+
return google.genai.Client(api_key=gemini_api_key, vertexai=False)
336370

337371
return _factory
338372

339373

340-
@pytest.fixture
341-
def vertex_client_factory(gcloud_project, gcloud_location, gcloud_credentials):
374+
@pytest.fixture(name="vertex_client_factory")
375+
def fixture_vertex_client_factory(gcloud_project, gcloud_location, gcloud_credentials):
342376
def _factory():
343377
return google.genai.Client(
344378
vertexai=True,
@@ -350,37 +384,37 @@ def _factory():
350384
return _factory
351385

352386

353-
@pytest.fixture(params=["vertexaiapi"])
354-
def genai_sdk_backend(request):
387+
@pytest.fixture(name="genai_sdk_backend", params=["vertexaiapi"])
388+
def fixture_genai_sdk_backend(request):
355389
return request.param
356390

357391

358-
@pytest.fixture(autouse=True)
359-
def use_vertex(genai_sdk_backend):
392+
@pytest.fixture(name="use_vertex", autouse=True)
393+
def fixture_use_vertex(genai_sdk_backend):
360394
result = bool(genai_sdk_backend == "vertexaiapi")
361395
os.environ["GOOGLE_GENAI_USE_VERTEXAI"] = "1" if result else "0"
362396
return result
363397

364398

365-
@pytest.fixture
366-
def client(vertex_client_factory, nonvertex_client_factory, use_vertex):
399+
@pytest.fixture(name="client")
400+
def fixture_client(vertex_client_factory, nonvertex_client_factory, use_vertex):
367401
if use_vertex:
368402
return vertex_client_factory()
369403
return nonvertex_client_factory()
370404

371405

372-
@pytest.fixture(params=["sync", "async"])
373-
def is_async(request):
406+
@pytest.fixture(name="is_async", params=["sync", "async"])
407+
def fixture_is_async(request):
374408
return request.param == "async"
375409

376410

377-
@pytest.fixture(params=["gemini-1.5-flash-002"])
378-
def model(request):
411+
@pytest.fixture(name="model", params=["gemini-1.5-flash-002"])
412+
def fixture_model(request):
379413
return request.param
380414

381415

382-
@pytest.fixture
383-
def generate_content(client, is_async):
416+
@pytest.fixture(name="generate_content")
417+
def fixture_generate_content(client, is_async):
384418
def _sync_impl(*args, **kwargs):
385419
return client.models.generate_content(*args, **kwargs)
386420

@@ -392,8 +426,8 @@ def _async_impl(*args, **kwargs):
392426
return _sync_impl
393427

394428

395-
@pytest.fixture
396-
def generate_content_stream(client, is_async):
429+
@pytest.fixture(name="generate_content_stream")
430+
def fixture_generate_content_stream(client, is_async):
397431
def _sync_impl(*args, **kwargs):
398432
results = []
399433
for result in client.models.generate_content_stream(*args, **kwargs):

0 commit comments

Comments
 (0)