diff --git a/src/common/details/ai_python_generative_sdk/AiSdkConsoleGui_PickOrCreate_AiHubProject.cs b/src/common/details/ai_python_generative_sdk/AiSdkConsoleGui_PickOrCreate_AiHubProject.cs index daa23646..1006d90c 100644 --- a/src/common/details/ai_python_generative_sdk/AiSdkConsoleGui_PickOrCreate_AiHubProject.cs +++ b/src/common/details/ai_python_generative_sdk/AiSdkConsoleGui_PickOrCreate_AiHubProject.cs @@ -311,7 +311,7 @@ public static void CreateAiHubProjectConfigJsonFile(string subscription, string var configJson = JsonSerializer.Serialize(configJsonData, new JsonSerializerOptions { WriteIndented = true }); var configJsonFile = new FileInfo("config.json"); - FileHelpers.WriteAllText(configJsonFile.FullName, configJson + "\n", new UTF8Encoding(false)); + FileHelpers.WriteAllText(configJsonFile.FullName, configJson, new UTF8Encoding(false)); Console.WriteLine($"{configJsonFile.Name} (saved at {configJsonFile.Directory})\n"); Console.WriteLine(" " + configJson.Replace("\n", "\n ")); diff --git a/src/common/details/azcli/AzCliConsoleGui_SubscriptionPicker.cs b/src/common/details/azcli/AzCliConsoleGui_SubscriptionPicker.cs index 1e23e351..34836abf 100644 --- a/src/common/details/azcli/AzCliConsoleGui_SubscriptionPicker.cs +++ b/src/common/details/azcli/AzCliConsoleGui_SubscriptionPicker.cs @@ -21,7 +21,7 @@ public partial class AzCliConsoleGui { public static string GetSubscriptionUserName(string subscriptionId) { - if (_subscriptionIdToUsreName.TryGetValue(subscriptionId, out var userName)) + if (_subscriptionIdToUserName.TryGetValue(subscriptionId, out var userName)) { return userName; } @@ -202,9 +202,9 @@ private static void DisplayNameAndId(AzCli.SubscriptionInfo subscription) private static void CacheSubscriptionUserName(AzCli.SubscriptionInfo subscription) { - _subscriptionIdToUsreName[subscription.Id] = subscription.UserName; + _subscriptionIdToUserName[subscription.Id] = subscription.UserName; } - private static Dictionary _subscriptionIdToUsreName = new Dictionary(); + private static Dictionary _subscriptionIdToUserName = new Dictionary(); } } diff --git a/src/common/details/commands/init_command.cs b/src/common/details/commands/init_command.cs index 8969f1a7..1afc57a4 100644 --- a/src/common/details/commands/init_command.cs +++ b/src/common/details/commands/init_command.cs @@ -112,17 +112,18 @@ private async Task DoInitRootAsync() private async Task DoInitRootVerifyConfigFileAsync(bool interactive, string fileName) { - ParseConfigJson(fileName, out string subscription, out string groupName, out string projectName); - - var (hubName, openai, search) = await VerifyProjectAsync(interactive, subscription, groupName, projectName); - if (openai != null && search != null) - { - await DoInitRootConfirmVerifiedProjectResources(interactive, subscription, projectName, hubName, openai.Value, search.Value); - } - else + bool success = ParseConfigJson(fileName, out string subscription, out string groupName, out string projectName); + if (success) { - await DoInitRootMenuPick(); + var (hubName, openai, search) = await VerifyProjectAsync(interactive, subscription, groupName, projectName); + if (openai != null && search != null) + { + await DoInitRootConfirmVerifiedProjectResources(interactive, subscription, projectName, hubName, openai.Value, search.Value); + return; + } } + + await DoInitRootMenuPick(); } private async Task<(string, AzCli.CognitiveServicesResourceInfo?, AzCli.CognitiveSearchResourceInfo?)> VerifyProjectAsync(bool interactive, string subscription, string groupName, string projectName) diff --git a/src/common/details/helpers/file_helpers.cs b/src/common/details/helpers/file_helpers.cs index ab78c7e6..a77393fe 100644 --- a/src/common/details/helpers/file_helpers.cs +++ b/src/common/details/helpers/file_helpers.cs @@ -793,13 +793,11 @@ public static void EnsureDirectoryForFileExists(string fileName) if (IsStandardInputReference(fileName)) return; if (IsStandardOutputReference(fileName)) return; - var s1 = fileName.LastIndexOf(Path.DirectorySeparatorChar); - var s2 = fileName.LastIndexOf(Path.AltDirectorySeparatorChar); - var sep = Math.Max(s1, s2); - if (sep <= 0) return; - - var dir = fileName.Substring(0, sep); - if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); + string dir = Path.GetDirectoryName(fileName); + if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir)) + { + Directory.CreateDirectory(dir); + } } public static void CopyFile(string path1, string file1, string path2, string file2 = null, bool verbose = true) diff --git a/src/common/details/helpers/process_helpers.cs b/src/common/details/helpers/process_helpers.cs index 4f866452..84abc836 100644 --- a/src/common/details/helpers/process_helpers.cs +++ b/src/common/details/helpers/process_helpers.cs @@ -113,11 +113,19 @@ public static async Task RunShellCommandAsync(string command, str } }; - var process = TryCatchHelpers.TryCatchNoThrow(() => StartShellCommandProcess(command, arguments, addToEnvironment, redirectOutput), null, out Exception processException); - if (process == null) + Process process; + try + { + process = StartShellCommandProcess(command, arguments, addToEnvironment, redirectOutput); + } + catch (Exception processException) { SHELL_DEBUG_TRACE($"ERROR: {processException}"); - return new ProcessOutput() { StdError = processException.ToString() }; + return new ProcessOutput() + { + ExitCode = -1, + StdError = processException.ToString() + }; } if (redirectOutput) @@ -137,10 +145,10 @@ public static async Task RunShellCommandAsync(string command, str } var output = new ProcessOutput(); - output.StdOutput = process != null ? sbOut.ToString().Trim(' ', '\r', '\n') : ""; - output.StdError = process != null ? sbErr.ToString().Trim(' ', '\r', '\n') : processException.ToString(); - output.MergedOutput = process != null ? sbMerged.ToString().Trim(' ', '\r', '\n') : ""; - output.ExitCode = process != null ? process.ExitCode : -1; + output.StdOutput = sbOut.ToString().Trim(' ', '\r', '\n'); + output.StdError = sbErr.ToString().Trim(' ', '\r', '\n'); + output.MergedOutput = sbMerged.ToString().Trim(' ', '\r', '\n'); + output.ExitCode = process.ExitCode; if (!string.IsNullOrEmpty(output.StdOutput)) SHELL_DEBUG_TRACE($"---\nSTDOUT\n---\n{output.StdOutput}"); if (!string.IsNullOrEmpty(output.StdError)) SHELL_DEBUG_TRACE($"---\nSTDERR\n---\n{output.StdError}"); diff --git a/src/common/details/helpers/python_runner.cs b/src/common/details/helpers/python_runner.cs index 799a8f44..15fd09cf 100644 --- a/src/common/details/helpers/python_runner.cs +++ b/src/common/details/helpers/python_runner.cs @@ -25,11 +25,13 @@ public static async Task RunPythonScriptAsync(string script, stri return new ProcessOutput() { ExitCode = -1 }; } - var tempFile = Path.GetTempFileName() + ".py"; - FileHelpers.WriteAllText(tempFile, script, new UTF8Encoding(false)); + string tempFile = null; try { + tempFile = Path.GetTempFileName() + ".py"; + FileHelpers.WriteAllText(tempFile, script, new UTF8Encoding(false)); + args = args != null ? $"\"{tempFile}\" {args}" : $"\"{tempFile}\""; @@ -37,7 +39,8 @@ public static async Task RunPythonScriptAsync(string script, stri } finally { - File.Delete(tempFile); + if (tempFile != null) + File.Delete(tempFile); } } @@ -72,8 +75,8 @@ public static string RunEmbeddedPythonScript(ICommandValues values, string scrip { AI.DBG_TRACE_WARNING($"RunEmbeddedPythonScript: exit={exit}"); - output = output.Trim('\r', '\n', ' '); - output = "\n\n " + output.Replace("\n", "\n "); + output = output?.Trim('\r', '\n', ' '); + output = "\n\n " + output?.Replace("\n", "\n "); var info = new List(); @@ -178,7 +181,7 @@ public static string RunEmbeddedPythonScript(ICommandValues values, string scrip private static string ParseOutputAndSkipLinesUntilStartsWith(string output, string startsWith) { - var lines = output.Split('\n'); + var lines = output.Split(new[] { "\r\n", "\n", "\r" }, StringSplitOptions.None); var sb = new StringBuilder(); var skip = true; foreach (var line in lines) @@ -200,7 +203,6 @@ private static string EnsureFindPython() if (_pythonBinary == null) { _pythonBinary = FindPython(); - AI.DBG_TRACE_VERBOSE($"Python found: {_pythonBinary}"); } return _pythonBinary; @@ -208,9 +210,26 @@ private static string EnsureFindPython() private static string FindPython() { - var lastTry = FindPythonBinaryInOsPath(); - var process = ProcessHelpers.RunShellCommandAsync(lastTry, "--version").Result; - if (process.ExitCode == 0 && process.MergedOutput.Contains("Python 3.")) return lastTry; + string fullPath = FindPythonBinaryInOsPath(); + string pythonExec = fullPath; + if (OperatingSystem.IsWindows()) + { + // TODO FIXME Longer term we really shouldn't be wrapping calls to python in cmd /c python + // and instead just calling python directly + + // since we found the python executable in our standard search path, we can skip passing + // the entire path since it may contain spaces (e.g. C:\Program Files\Python312\python.exe) + // which can cause irritating errors requiring complex escaping. Instead, we can just pass + // the executable name and let Windows will handle querying the OS search path for us + pythonExec = Path.GetFileName(fullPath); + } + + var process = ProcessHelpers.RunShellCommandAsync(pythonExec, "--version").Result; + if (process.ExitCode == 0 && process.MergedOutput.Contains("Python 3.")) + { + AI.DBG_TRACE_VERBOSE($"Python found: {fullPath}"); + return pythonExec; + } return null; }