|
8 | 8 | #include "MSPythonPCH.h" |
9 | 9 | #include "..\\MsPythonCore.r.h" |
10 | 10 | #include <iostream> |
| 11 | +#include <fstream> |
| 12 | +#include <vector> |
11 | 13 | // Default entry point function for script |
12 | 14 | #define DEFAULT_FUNC_NAME L"PyMain" |
13 | 15 |
|
@@ -697,6 +699,128 @@ void print_dict(const py::dict& dict) |
697 | 699 | std::cout << std::endl; |
698 | 700 | } |
699 | 701 |
|
| 702 | + /*---------------------------------------------------------------------------------**//*** |
| 703 | + @bsimethod 2/2023 |
| 704 | + +---------------+---------------+---------------+---------------+---------------+------*/ |
| 705 | +void PythonScriptEngine::evalPYC(WStringCR pythonFileName, ScriptContext* global, ScriptContext* locals) |
| 706 | + { |
| 707 | + ScriptContextPtr globalCtx(global), localCtx(locals); |
| 708 | + py::dict globalDict, localDict; |
| 709 | + |
| 710 | + if (m_engineProcessing) |
| 711 | + { |
| 712 | + ScriptEngineManager::Get().InjectError("Cannot have two instances of Python executing at the same time"); |
| 713 | + return ; |
| 714 | + } |
| 715 | + // Create context when necessary |
| 716 | + if (globalCtx.IsNull()) |
| 717 | + globalCtx = createContext(true); |
| 718 | + if (localCtx.IsNull()) |
| 719 | + localCtx = createContext(false); |
| 720 | + |
| 721 | + // Obtain py::dict from context that is specific for PyEngine |
| 722 | + auto pyGlobalDict = dynamic_cast<PythonScriptContext*>(globalCtx.get()); |
| 723 | + if (nullptr != pyGlobalDict) |
| 724 | + globalDict = pyGlobalDict->m_dict; |
| 725 | + |
| 726 | + auto pyLocalDict = dynamic_cast<PythonScriptContext*>(localCtx.get()); |
| 727 | + if (nullptr != pyLocalDict) |
| 728 | + localDict = pyLocalDict->m_dict; |
| 729 | + m_engineProcessing = true; |
| 730 | + // Evaluate expression and capture return value. |
| 731 | + ScriptEngineManager::Get().ClearException(); |
| 732 | + try |
| 733 | + { |
| 734 | + // Convert WString to std::string for file operations |
| 735 | + std::string pyc_file_path; |
| 736 | + Utf8String temp; |
| 737 | + temp.Assign(pythonFileName.c_str()); |
| 738 | + pyc_file_path = temp.c_str(); |
| 739 | + |
| 740 | + // Get the __main__ module and backup its current dictionary |
| 741 | + py::module main_module = py::module::import("__main__"); |
| 742 | + py::dict original_main_dict = main_module.attr("__dict__"); |
| 743 | + |
| 744 | + // Create a backup of the original dictionary |
| 745 | + py::dict backup_dict = original_main_dict.cast<py::dict>(); |
| 746 | + |
| 747 | + // Temporarily replace __main__.__dict__ with our custom globalDict |
| 748 | + // First, copy essential built-ins to our globalDict if they don't exist |
| 749 | + if (!globalDict.contains("__builtins__")) |
| 750 | + { |
| 751 | + if (original_main_dict.contains("__builtins__")) |
| 752 | + { |
| 753 | + globalDict["__builtins__"] = original_main_dict["__builtins__"]; |
| 754 | + } |
| 755 | + else |
| 756 | + { |
| 757 | + py::module builtins_module = py::module::import("builtins"); |
| 758 | + globalDict["__builtins__"] = builtins_module; |
| 759 | + } |
| 760 | + } |
| 761 | + if (!globalDict.contains("__name__")) |
| 762 | + { |
| 763 | + globalDict["__name__"] = "__main__"; |
| 764 | + } |
| 765 | + if (!globalDict.contains("__file__")) |
| 766 | + { |
| 767 | + globalDict["__file__"] = py::cast(pyc_file_path); |
| 768 | + } |
| 769 | + |
| 770 | + // Replace the main module's dictionary |
| 771 | + main_module.attr("__dict__").attr("clear")(); |
| 772 | + main_module.attr("__dict__").attr("update")(globalDict); |
| 773 | + |
| 774 | + // Open the .pyc file |
| 775 | + FILE* fp = fopen(pyc_file_path.c_str(), "rb"); |
| 776 | + if (!fp) |
| 777 | + { |
| 778 | + // Restore original dictionary before returning |
| 779 | + main_module.attr("__dict__").attr("clear")(); |
| 780 | + main_module.attr("__dict__").attr("update")(backup_dict); |
| 781 | + |
| 782 | + ScriptEngineManager::Get().InjectError("Cannot open PYC file"); |
| 783 | + m_engineProcessing = false; |
| 784 | + return ; |
| 785 | + } |
| 786 | + |
| 787 | + // Set up compilation flags for .pyc files |
| 788 | + PyCompilerFlags flags; |
| 789 | + flags.cf_flags = 0; |
| 790 | + |
| 791 | + // Execute the .pyc file using PyRun_SimpleFileExFlags |
| 792 | + // This will now use our custom globalDict via __main__.__dict__ |
| 793 | + mdlErrno = 0; |
| 794 | + int result = PyRun_SimpleFileExFlags(fp, pyc_file_path.c_str(), 1, &flags); |
| 795 | + |
| 796 | + // Copy any new variables back to our globalDict |
| 797 | + py::dict updated_main_dict = main_module.attr("__dict__"); |
| 798 | + for (auto item : updated_main_dict) |
| 799 | + { |
| 800 | + globalDict[item.first] = item.second; |
| 801 | + } |
| 802 | + |
| 803 | + // Restore the original __main__ dictionary |
| 804 | + main_module.attr("__dict__").attr("clear")(); |
| 805 | + main_module.attr("__dict__").attr("update")(backup_dict); |
| 806 | + |
| 807 | + if (result != 0) |
| 808 | + { |
| 809 | + ScriptEngineManager::Get().InjectError("Error executing PYC file"); |
| 810 | + mdlErrno = MDLERR_PYTHONEXECUTIONERROR; |
| 811 | + } |
| 812 | + } |
| 813 | + catch (py::error_already_set& err) |
| 814 | + { |
| 815 | + ScriptEngineManager::Get().InjectException(err); |
| 816 | + mdlErrno = MDLERR_PYTHONEXECUTIONERROR; |
| 817 | + } |
| 818 | + |
| 819 | + m_engineProcessing = false; |
| 820 | + |
| 821 | + |
| 822 | + } |
| 823 | + |
700 | 824 | /*---------------------------------------------------------------------------------**//*** |
701 | 825 | @bsimethod 2/2023 |
702 | 826 | +---------------+---------------+---------------+---------------+---------------+------*/ |
|
0 commit comments