Skip to content

Commit 23e25ad

Browse files
committed
Write to temp file instead
1 parent 1d79241 commit 23e25ad

File tree

2 files changed

+62
-43
lines changed

2 files changed

+62
-43
lines changed

sdk/ai/azure-ai-projects/tests/samples/test_samples.py

Lines changed: 3 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -5,34 +5,18 @@
55
# ------------------------------------
66
import csv, os, pytest, re, inspect, sys
77
import importlib.util
8-
import io
98
import unittest.mock as mock
109
from azure.core.exceptions import HttpResponseError
1110
from devtools_testutils.aio import recorded_by_proxy_async
1211
from devtools_testutils import AzureRecordedTestCase, recorded_by_proxy, RecordedTransport
13-
from test_base import servicePreparer
12+
from test_base import servicePreparer, patched_open_crlf_to_lf
1413
from pytest import MonkeyPatch
1514
from azure.ai.projects import AIProjectClient
1615

1716

1817
class SampleExecutor:
1918
"""Helper class for executing sample files with proper environment setup and credential mocking."""
2019

21-
class ConvertedFileWrapper(io.BufferedReader):
22-
"""File wrapper that converts CRLF to LF content while preserving original filename."""
23-
24-
def __init__(self, file_path, converted_content):
25-
# Create BytesIO with converted content
26-
self._bytesio = io.BytesIO(converted_content)
27-
# Initialize BufferedReader with the BytesIO
28-
super().__init__(self._bytesio)
29-
# Override name to be the original file path
30-
self._name = file_path
31-
32-
@property
33-
def name(self):
34-
return self._name
35-
3620
def __init__(
3721
self, test_instance: "AzureRecordedTestCase", sample_path: str, env_var_mapping: dict[str, str], **kwargs
3822
):
@@ -62,43 +46,19 @@ def __init__(
6246

6347
self.module = importlib.util.module_from_spec(spec)
6448
self.spec = spec
65-
self._original_open = open
6649

6750
def _capture_print(self, *args, **kwargs):
6851
"""Capture print calls while still outputting to console."""
6952
self.print_calls.append(" ".join(str(arg) for arg in args))
7053
self._original_print(*args, **kwargs)
7154

72-
def _patched_open(self, *args, **kwargs):
73-
"""Patch open to convert CRLF to LF for text files."""
74-
file_path = args[0] if args else kwargs.get("file")
75-
mode = args[1] if len(args) > 1 else kwargs.get("mode", "r")
76-
77-
# Check if this is binary read mode for text-like files
78-
if "r" in mode and "b" in mode and file_path and isinstance(file_path, str):
79-
# Check file extension to determine if it's a text file
80-
text_extensions = {".txt", ".json", ".jsonl", ".csv", ".md", ".yaml", ".yml", ".xml"}
81-
ext = os.path.splitext(file_path)[1].lower()
82-
if ext in text_extensions:
83-
# Read the original file
84-
with self._original_open(file_path, "rb") as f:
85-
content = f.read()
86-
87-
# Convert CRLF to LF
88-
converted_content = content.replace(b"\r\n", b"\n")
89-
90-
# Return wrapped file-like object with converted content
91-
return SampleExecutor.ConvertedFileWrapper(file_path, converted_content)
92-
93-
return self._original_open(*args, **kwargs)
94-
9555
def execute(self):
9656
"""Execute a synchronous sample with proper mocking and environment setup."""
9757

9858
with (
9959
MonkeyPatch.context() as mp,
10060
mock.patch("builtins.print", side_effect=self._capture_print),
101-
mock.patch("builtins.open", side_effect=self._patched_open),
61+
mock.patch("builtins.open", side_effect=patched_open_crlf_to_lf),
10262
mock.patch("azure.identity.DefaultAzureCredential") as mock_credential,
10363
):
10464
for var_name, var_value in self.env_vars.items():
@@ -119,7 +79,7 @@ async def execute_async(self):
11979
with (
12080
MonkeyPatch.context() as mp,
12181
mock.patch("builtins.print", side_effect=self._capture_print),
122-
mock.patch("builtins.open", side_effect=self._patched_open),
82+
mock.patch("builtins.open", side_effect=patched_open_crlf_to_lf),
12383
mock.patch("azure.identity.aio.DefaultAzureCredential") as mock_credential,
12484
):
12585
for var_name, var_value in self.env_vars.items():

sdk/ai/azure-ai-projects/tests/test_base.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import re
88
import functools
99
import json
10+
import os
11+
import tempfile
1012
from typing import Optional, Any, Dict, Final
1113
from azure.ai.projects.models import (
1214
Connection,
@@ -33,6 +35,9 @@
3335
from azure.ai.projects import AIProjectClient as AIProjectClient
3436
from azure.ai.projects.aio import AIProjectClient as AsyncAIProjectClient
3537

38+
# Store reference to built-in open before any mocking occurs
39+
_BUILTIN_OPEN = open
40+
3641

3742
# Load secrets from environment variables
3843
servicePreparer = functools.partial(
@@ -63,6 +68,60 @@
6368
DEVELOPER_TIER_TRAINING_TYPE: Final[str] = "developerTier"
6469

6570

71+
def patched_open_crlf_to_lf(*args, **kwargs):
72+
"""
73+
Patched open function that converts CRLF to LF for text files.
74+
75+
This function should be used with mock.patch("builtins.open", side_effect=TestBase.patched_open_crlf_to_lf)
76+
to ensure consistent line endings in test files during recording and playback.
77+
"""
78+
# Extract file path - first positional arg or 'file' keyword arg
79+
if args:
80+
file_path = args[0]
81+
elif "file" in kwargs:
82+
file_path = kwargs["file"]
83+
else:
84+
# No file path provided, just pass through
85+
return _BUILTIN_OPEN(*args, **kwargs)
86+
87+
# Extract mode - second positional arg or 'mode' keyword arg
88+
if len(args) > 1:
89+
mode = args[1]
90+
else:
91+
mode = kwargs.get("mode", "r")
92+
93+
# Check if this is binary read mode for text-like files
94+
if "r" in mode and "b" in mode and file_path and isinstance(file_path, str):
95+
# Check file extension to determine if it's a text file
96+
text_extensions = {".txt", ".json", ".jsonl", ".csv", ".md", ".yaml", ".yml", ".xml"}
97+
ext = os.path.splitext(file_path)[1].lower()
98+
if ext in text_extensions:
99+
# Read the original file
100+
with _BUILTIN_OPEN(file_path, "rb") as f:
101+
content = f.read()
102+
103+
# Convert CRLF to LF
104+
converted_content = content.replace(b"\r\n", b"\n")
105+
106+
# Only create temp file if conversion was needed
107+
if converted_content != content:
108+
# Create a temporary file with the converted content
109+
temp_fd, temp_path = tempfile.mkstemp(suffix=ext)
110+
os.write(temp_fd, converted_content)
111+
os.close(temp_fd)
112+
# Replace file path with temp path
113+
if args:
114+
# File path was passed as positional arg
115+
return _BUILTIN_OPEN(temp_path, *args[1:], **kwargs)
116+
else:
117+
# File path was passed as keyword arg
118+
kwargs = kwargs.copy()
119+
kwargs["file"] = temp_path
120+
return _BUILTIN_OPEN(**kwargs)
121+
122+
return _BUILTIN_OPEN(*args, **kwargs)
123+
124+
66125
class TestBase(AzureRecordedTestCase):
67126

68127
test_redteams_params = {

0 commit comments

Comments
 (0)