@@ -158,15 +158,20 @@ fn get_interpreter_details_with_timeout(
158158 let result = child. wait_with_output ( ) ;
159159 match result {
160160 Ok ( output) => {
161- let output = String :: from_utf8 ( output. stdout ) . unwrap ( ) . trim ( ) . to_string ( ) ;
161+ let output = output. stdout ;
162162 trace ! (
163163 "Executed Python {:?} in {:?} & produced an output {:?}" ,
164164 executable,
165165 start. elapsed( ) ,
166- output
166+ String :: from_utf8_lossy ( & output)
167167 ) ;
168- if let Some ( ( _, output) ) = output. split_once ( PYTHON_INFO_JSON_SEPARATOR ) {
169- if let Ok ( info) = serde_json:: from_str :: < InterpreterInfo > ( output) {
168+ let separator = PYTHON_INFO_JSON_SEPARATOR . as_bytes ( ) ;
169+ if let Some ( position) = output
170+ . windows ( separator. len ( ) )
171+ . position ( |bytes| bytes == separator)
172+ {
173+ let output = & output[ position + separator. len ( ) ..] ;
174+ if let Ok ( info) = serde_json:: from_slice :: < InterpreterInfo > ( output) {
170175 let mut symlinks = vec ! [
171176 PathBuf :: from( executable) ,
172177 PathBuf :: from( info. executable. clone( ) ) ,
@@ -183,14 +188,15 @@ fn get_interpreter_details_with_timeout(
183188 } else {
184189 error ! (
185190 "Python Execution for {:?} produced an output {:?} that could not be parsed as JSON" ,
186- executable, output,
191+ executable, String :: from_utf8_lossy ( output) ,
187192 ) ;
188193 None
189194 }
190195 } else {
191196 error ! (
192197 "Python Execution for {:?} produced an output {:?} without a separator" ,
193- executable, output,
198+ executable,
199+ String :: from_utf8_lossy( & output) ,
194200 ) ;
195201 None
196202 }
@@ -210,6 +216,22 @@ mod tests {
210216 use super :: * ;
211217 use std:: os:: unix:: fs:: PermissionsExt ;
212218
219+ // https://github.com/microsoft/python-environment-tools/issues/525:
220+ // A launcher printing GBK-encoded "文件不存在" must not panic discovery.
221+ #[ test]
222+ fn get_interpreter_details_handles_non_utf8_stdout ( ) -> std:: io:: Result < ( ) > {
223+ let directory = tempfile:: tempdir ( ) ?;
224+ let executable = directory. path ( ) . join ( "python" ) ;
225+ std:: fs:: write (
226+ & executable,
227+ "#!/bin/sh\n printf '\\ 316\\ 304\\ 274\\ 376\\ 262\\ 273\\ 264\\ 346\\ 324\\ 332: -c\\ r\\ n'\n " ,
228+ ) ?;
229+ std:: fs:: set_permissions ( & executable, std:: fs:: Permissions :: from_mode ( 0o755 ) ) ?;
230+ let result = get_interpreter_details_with_timeout ( & executable, Duration :: from_secs ( 5 ) ) ;
231+ assert ! ( result. is_none( ) ) ;
232+ directory. close ( )
233+ }
234+
213235 /// Regression test for #463: a spawn that never exits must not block the
214236 /// resolve path indefinitely. We use a shell script that sleeps far longer
215237 /// than the test timeout and assert that the call returns None promptly
0 commit comments