55# ------------------------------------
66import csv , os , pytest , re , inspect , sys
77import importlib .util
8+ import io
89import unittest .mock as mock
910from azure .core .exceptions import HttpResponseError
1011from devtools_testutils .aio import recorded_by_proxy_async
1718class SampleExecutor :
1819 """Helper class for executing sample files with proper environment setup and credential mocking."""
1920
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+
2036 def __init__ (
2137 self , test_instance : "AzureRecordedTestCase" , sample_path : str , env_var_mapping : dict [str , str ], ** kwargs
2238 ):
@@ -46,17 +62,43 @@ def __init__(
4662
4763 self .module = importlib .util .module_from_spec (spec )
4864 self .spec = spec
65+ self ._original_open = open
4966
5067 def _capture_print (self , * args , ** kwargs ):
5168 """Capture print calls while still outputting to console."""
5269 self .print_calls .append (" " .join (str (arg ) for arg in args ))
5370 self ._original_print (* args , ** kwargs )
5471
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+
5595 def execute (self ):
5696 """Execute a synchronous sample with proper mocking and environment setup."""
97+
5798 with (
5899 MonkeyPatch .context () as mp ,
59100 mock .patch ("builtins.print" , side_effect = self ._capture_print ),
101+ mock .patch ("builtins.open" , side_effect = self ._patched_open ),
60102 mock .patch ("azure.identity.DefaultAzureCredential" ) as mock_credential ,
61103 ):
62104 for var_name , var_value in self .env_vars .items ():
@@ -77,6 +119,7 @@ async def execute_async(self):
77119 with (
78120 MonkeyPatch .context () as mp ,
79121 mock .patch ("builtins.print" , side_effect = self ._capture_print ),
122+ mock .patch ("builtins.open" , side_effect = self ._patched_open ),
80123 mock .patch ("azure.identity.aio.DefaultAzureCredential" ) as mock_credential ,
81124 ):
82125 for var_name , var_value in self .env_vars .items ():
0 commit comments