Skip to content

Commit 2af1edb

Browse files
ChrisWu2024ADO Sync
andauthored
Sync changes from v25-0-1 (#14)
Co-authored-by: ADO Sync <ado-sync@bentley.com>
1 parent c256ee6 commit 2af1edb

File tree

106 files changed

+12283
-1341
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

106 files changed

+12283
-1341
lines changed

InternalAPI/MSPyCommon.h

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -329,58 +329,66 @@ py::class_<bvector<ValueType>, holder_type> bind_PointerVector(py::handle scope,
329329
return cls;
330330
}
331331

332-
// Convert Python list to an existing C++ array
332+
template <typename T>
333+
T ConvertItemFromPyObject(const py::handle& item) {
334+
if (!py::isinstance<T>(item)) {
335+
throw std::invalid_argument("All items in the list must be of the correct item type");
336+
}
337+
return item.cast<T>();
338+
}
339+
340+
inline double ConvertItemFromPyObject(const py::handle& item) {
341+
if (!PyFloat_Check(item.ptr()) && !PyLong_Check(item.ptr())) {
342+
throw std::invalid_argument("Expected all items to be convertible to double");
343+
}
344+
return item.cast<double>();
345+
}
346+
347+
template <typename T>
348+
py::object ConvertItemToPyObject(const T& item) {
349+
return py::cast(item);
350+
}
351+
352+
inline py::object ConvertItemToPyObject(const double& item) {
353+
return py::float_(item);
354+
}
355+
356+
//Convert Python list to an existing C++ array
333357
template <typename arrayType, typename itemType>
334-
void ConvertPyListToCppArray(py::list const& pyList, arrayType& cppArray)
335-
{
358+
void ConvertPyListToCppArray(const py::list& pyList, arrayType& cppArray) {
336359
cppArray.clear();
337-
for (auto item : pyList)
338-
{
339-
if (!py::isinstance<itemType>(item))
340-
{
341-
throw std::invalid_argument("All items in the list must be of the correct item type");
342-
}
343-
auto cppItem = item.cast<itemType>();
360+
for (const auto& item : pyList) {
361+
itemType cppItem = ConvertItemFromPyObject<itemType>(item);
344362
cppArray.push_back(cppItem);
345363
}
346364
}
347365

348366
// Convert Python list to a new C++ array
349367
template <typename arrayType, typename itemType>
350-
arrayType ConvertPyListToCppArray(py::list const& pyList)
351-
{
368+
arrayType ConvertPyListToCppArray(const py::list& pyList) {
352369
arrayType cppArray;
353-
for (auto item : pyList)
354-
{
355-
if (!py::isinstance<itemType>(item))
356-
{
357-
throw std::invalid_argument("All items in the list must be of the correct item type");
358-
}
359-
auto cppItem = item.cast<itemType>();
370+
for (const auto& item : pyList) {
371+
itemType cppItem = ConvertItemFromPyObject<itemType>(item);
360372
cppArray.push_back(cppItem);
361373
}
362374
return cppArray;
363375
}
364376

365377
// Convert C++ array to an existing Python list
366378
template <typename arrayType, typename itemType>
367-
void ConvertCppArrayToPyList(py::list& pyList, arrayType const& cppArray)
368-
{
379+
void ConvertCppArrayToPyList(py::list& pyList, const arrayType& cppArray) {
369380
pyList.attr("clear")();
370-
for (const itemType& item : cppArray)
371-
{
372-
pyList.append(py::cast(item));
381+
for (const itemType& item : cppArray) {
382+
pyList.append(ConvertItemToPyObject(item));
373383
}
374384
}
375385

376386
// Convert C++ array to a new Python list
377387
template <typename arrayType, typename itemType>
378-
py::list ConvertCppArrayToPyList(arrayType const& cppArray)
379-
{
388+
py::list ConvertCppArrayToPyList(const arrayType& cppArray) {
380389
py::list pyList;
381-
for (const itemType& item : cppArray)
382-
{
383-
pyList.append(py::cast(item));
390+
for (const itemType& item : cppArray) {
391+
pyList.append(ConvertItemToPyObject(item));
384392
}
385393
return pyList;
386394
}
@@ -396,4 +404,4 @@ py::list ConvertCppArrayToPyList(arrayType const& cppArray)
396404
ConvertCppArrayToPyList<cppArrayType, cppItemType>(pyList, cppArray);
397405

398406
#define CONVERT_CPPARRAY_TO_NEW_PYLIST(pyList, cppArray, cppArrayType, cppItemType) \
399-
py::list pyList = ConvertCppArrayToPyList<cppArrayType, cppItemType>(cppArray);
407+
py::list pyList = ConvertCppArrayToPyList<cppArrayType, cppItemType>(cppArray);

InternalAPI/MSPythonEngine.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ struct PythonScriptEngine : public ScriptEngine
131131
virtual ScriptValuePtr eval(WCharCP expr, ScriptContext* global = nullptr, ScriptContext* locals = nullptr);
132132
virtual void exec(WCharCP stms, WCharCP funcName, ScriptContext* global = nullptr, ScriptContext* locals = nullptr);
133133
virtual void eval_file(WCharCP fileName, WCharCP funcName, ScriptContext* global = nullptr, ScriptContext* locals = nullptr);
134+
virtual void evalPYC(WStringCR pythonFileName, ScriptContext* global = nullptr, ScriptContext* locals = nullptr);
134135
private:
135136
void InitSearchPath();
136137
};

InternalAPI/OpqueTypes_Geom.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,5 @@ DEFINE_BVECTOR_TYPE(FloatRgb, FloatRgbArray);
5151
DEFINE_BVECTOR_TYPE(RgbFactor, RgbFactorArray);
5252
DEFINE_BVECTOR_TYPE(FacetFaceData, FacetFaceDataArray);
5353
DEFINE_BVECTOR_TYPE(PolyfaceEdgeChain, PolyfaceEdgeChainArray);
54+
DEFINE_BVECTOR_TYPE(GeoPoint, GeoPointArray);
55+
DEFINE_BVECTOR_TYPE(GeoPoint2d, GeoPoint2dArray);

MSPythonCore/ScriptEngineManager/source/MSPythonEngine.cpp

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include "MSPythonPCH.h"
99
#include "..\\MsPythonCore.r.h"
1010
#include <iostream>
11+
#include <fstream>
12+
#include <vector>
1113
// Default entry point function for script
1214
#define DEFAULT_FUNC_NAME L"PyMain"
1315

@@ -697,6 +699,128 @@ void print_dict(const py::dict& dict)
697699
std::cout << std::endl;
698700
}
699701

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+
700824
/*---------------------------------------------------------------------------------**//***
701825
@bsimethod 2/2023
702826
+---------------+---------------+---------------+---------------+---------------+------*/

MSPythonCore/ScriptEngineManager/source/ScriptEngineManager.cpp

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -304,18 +304,24 @@ bool ScriptEngineManager::AddNotifier(ScriptNotifierP notifier)
304304
UstnScriptNotifier* beConsole = dynamic_cast<UstnScriptNotifier*> (notifier);
305305
bool isBeConsole = (nullptr != beConsole) ? true : false;
306306

307+
bool isActiveExist(false);
308+
309+
for (auto& it0 : *m_sinkList)
310+
if (it0->GetActive())
311+
{
312+
isActiveExist = true;
313+
break;
314+
}
315+
307316
if (isBeConsole)
308317
{
309-
notifier->SetActive(true);
318+
if (!isActiveExist)
319+
notifier->SetActive(true);
310320
}
311321
else
312322
{
313323
for (auto& it0 : *m_sinkList)
314-
{
315-
beConsole = dynamic_cast<UstnScriptNotifier*> (it0);
316-
if (nullptr == beConsole)
317-
it0->SetActive(false);
318-
}
324+
it0->SetActive(false);
319325

320326
notifier->SetActive(true);
321327
}

0 commit comments

Comments
 (0)