/// <summary>
        /// Execute the Python script `update_packages.py`: using a package requirements.txt file, it will install
        /// needed packages and uninstall unused ones
        /// </summary>
        /// <param="requirementsFile">Path to the requirements.txt file</param>
        /// <param="pythonInterpreter">Path to the Python interpreter on wich we run the update packages script</param>
        /// <returns>Standard output of the script</returns>
        internal static string UpdatePackages(string requirementsFile,
                                              string pythonInterpreter = PythonSettings.kDefaultPython)
        {
            PythonRunner.EnsureInitialized();
            using (Py.GIL())
            {
                dynamic spawn_process = PythonEngine.ImportModule("unity_python.common.spawn_process");

                dynamic args = new PyList();
                args.append(updatePackagesScript);
                args.append(requirementsFile);

                dynamic subprocess       = PythonEngine.ImportModule("subprocess");
                var     subprocessKwargs = new PyDict();
                subprocessKwargs["stdout"]             = subprocess.PIPE;
                subprocessKwargs["stderr"]             = subprocess.PIPE;
                subprocessKwargs["universal_newlines"] = true.ToPython();

                dynamic process = spawn_process.spawn_process_in_environment(pythonInterpreter, args,
                                                                             kwargs: subprocessKwargs,
                                                                             wantLogging: false);
                dynamic res = process.communicate();
                (string output, string errors) = (res[0], res[1]);
                if (!string.IsNullOrEmpty(errors))
                {
                    var pipWarningStringBuilder = new StringBuilder();
                    // Split errors lines to filter them individually
                    foreach (var line in Regex.Split(errors, "\r\n|\n|\r"))
                    {
                        if (IsInterestingWarning(line))
                        {
                            pipWarningStringBuilder.AppendLine(line);
                        }
                    }

                    if (pipWarningStringBuilder.Length > 0)
                    {
                        UnityEngine.Debug.LogError(pipWarningStringBuilder.ToString());
                    }
                }

                return(output);
            }
        }
        /// <summary>
        /// Spawns a `python -m pip freeze` subprocess and returns its output
        /// </summary>
        /// <param="pythonInterpreter">Path to the Python interpreter on which we call pip freeze</param>
        /// <param="pythonPath">Override PYTHONPATH with the passed argument; no override if empty string</param>
        /// <returns>Standard output of the pip freeze subprocess</returns>
        internal static string CallPipFreeze(string pythonInterpreter = PythonSettings.kDefaultPython,
                                             string pythonPath        = "")
        {
            PythonRunner.EnsureInitialized();
            using (Py.GIL())
            {
                dynamic subprocess = PythonEngine.ImportModule("subprocess");
                dynamic module     = PythonEngine.ImportModule("unity_python.common.spawn_process");
                dynamic spawn      = module.spawn_process_in_environment;

                dynamic args = new PyList();
                args.append(new PyString("-m"));
                args.append(new PyString("pip"));
                args.append(new PyString("freeze"));
                var subprocessKwargs = new PyDict();
                subprocessKwargs["stdout"]             = subprocess.PIPE;
                subprocessKwargs["stderr"]             = subprocess.PIPE;
                subprocessKwargs["universal_newlines"] = true.ToPython(); // text streams instead of bytes

                dynamic envOverride = new PyDict();
                if (string.IsNullOrEmpty(pythonPath))
                {
                    envOverride["PYTHONPATH"] = new PyString("");
                }

                dynamic process = spawn(pythonInterpreter, args,
                                        kwargs: subprocessKwargs,
                                        env_override: envOverride,
                                        wantLogging: false);
                dynamic res = process.communicate();
                (string output, string errors) = (res[0], res[1]);
                if (errors != null && errors.Length > 0)
                {
                    UnityEngine.Debug.LogError(errors);
                }

                return(output);
            }
        }
Exemple #3
0
        public static void ExecuteCommand(object sender, ExecutedRoutedEventArgs e, string extensionFileName) {
            if (sender is ListBox listBox) {
                using (Py.GIL()) {
                    dynamic comics = new PyList();
                    foreach (var item in listBox.SelectedItems) {
                        if (item is Comic comic) {
                            var pyobject = comic.ToPython();
                            comics.append(pyobject);
                        }
                    }

                    using (var scope = Py.CreateScope()) {
                        /* 
                         * What we have right now is a python script that runs at exactly the authority of the C# code
                         * surrounding this comment. That is too dangerous. We will create a custom object to pass to the
                         * Python script, and retrieve the object, detect changes, and apply them to our works.
                         * 
                         * This not only makes things safer and simpler, but also ensures that if the python script crashes, 
                         * no changes will be made to anything in the database.
                         */
                        try {
                            scope.Exec(System.IO.File.ReadAllText(extensionFileName));
                            dynamic extension_func = scope.Get("extension");
                            string result = extension_func(comics);
                            MessageBox.Show(result);
                        } catch (PythonException error) {
                            MessageBox.Show("An error occured while running the extension: " + error.ToString());
                        } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException) {
                            MessageBox.Show("An error occured while running the extension: The extension must define a function extension(List[Comic]) -> str. Did you define the function wrongly?");
                        }
                    }
                }
            } else {
                MessageBox.Show("Command execution failed! Unable to cast sender of command as ListBox");
            }
        }