diff --git a/src/Plugins/SimplnxCore/CMakeLists.txt b/src/Plugins/SimplnxCore/CMakeLists.txt index 18f980ca44..8023a7b851 100644 --- a/src/Plugins/SimplnxCore/CMakeLists.txt +++ b/src/Plugins/SimplnxCore/CMakeLists.txt @@ -368,18 +368,6 @@ string(HEX ${PYTHON_PLUGIN_TEMPLATE} PYTHON_PLUGIN_TEMPLATE) string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE ${PYTHON_PLUGIN_TEMPLATE}) string(APPEND PYTHON_PLUGIN_TEMPLATE "0x00") -if(WINDOWS) - file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.bat" PYTHON_PLUGIN_TEMPLATE_BAT) - string(HEX ${PYTHON_PLUGIN_TEMPLATE_BAT} PYTHON_PLUGIN_TEMPLATE_BAT) - string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE_BAT ${PYTHON_PLUGIN_TEMPLATE_BAT}) - string(APPEND PYTHON_PLUGIN_TEMPLATE_BAT "0x00") -else() - file(READ "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginTemplate.sh" PYTHON_PLUGIN_TEMPLATE_BAT) - string(HEX ${PYTHON_PLUGIN_TEMPLATE_BAT} PYTHON_PLUGIN_TEMPLATE_BAT) - string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\0," PYTHON_PLUGIN_TEMPLATE_BAT ${PYTHON_PLUGIN_TEMPLATE_BAT}) - string(APPEND PYTHON_PLUGIN_TEMPLATE_BAT "0x00") -endif() - cmpConfigureFileWithMD5Check(CONFIGURED_TEMPLATE_PATH "${${PLUGIN_NAME}_SOURCE_DIR}/src/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.in.hpp" GENERATED_FILE_PATH "${${PLUGIN_NAME}_BINARY_DIR}/generated/${PLUGIN_NAME}/utils/PythonPluginSourceTemplate.hpp") diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.cpp index f434961b4d..3aabb40867 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.cpp @@ -35,7 +35,6 @@ Result<> GeneratePythonSkeleton::operator()() } else { - return nx::core::WritePythonPluginFiles(m_InputValues->pluginOutputDir, m_InputValues->pluginName, m_InputValues->pluginName, "Description", m_InputValues->filterNames, - m_InputValues->createBatchShellScript, m_InputValues->anacondaEnvName); + return nx::core::WritePythonPluginFiles(m_InputValues->pluginOutputDir, m_InputValues->pluginName, m_InputValues->pluginName, "Description", m_InputValues->filterNames); } } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp index 12cdc5072b..cc12e92604 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/Algorithms/GeneratePythonSkeleton.hpp @@ -20,8 +20,6 @@ struct SIMPLNXCORE_EXPORT GeneratePythonSkeletonInputValues std::string pluginName; std::string pluginHumanName; std::string filterNames; - bool createBatchShellScript; - std::string anacondaEnvName; }; /** diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.cpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.cpp index 10bc6555e3..7b208d263b 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.cpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.cpp @@ -66,15 +66,10 @@ Parameters GeneratePythonSkeletonFilter::parameters() const params.insert( std::make_unique(k_PluginFilterNames, "Filter Names (comma-separated)", "The names of filters that will be created, separated by commas (,).", "FirstFilter,SecondFilter")); - params.insertLinkableParameter( - std::make_unique(k_CreateBatchFile_Key, "Create Anaconda Init Batch/Shell Script", "Generates a script file that can be used to export needed environment variables", false)); - params.insert(std::make_unique(k_AnacondaEnvName_Key, "Anaconda Environment Name", "The name of the Anaconda environment.", "nxpython")); - params.linkParameters(k_UseExistingPlugin_Key, k_PluginName_Key, false); params.linkParameters(k_UseExistingPlugin_Key, k_PluginHumanName_Key, false); params.linkParameters(k_UseExistingPlugin_Key, k_PluginOutputDirectory_Key, false); params.linkParameters(k_UseExistingPlugin_Key, k_PluginInputDirectory_Key, true); - params.linkParameters(k_CreateBatchFile_Key, k_AnacondaEnvName_Key, true); return params; } @@ -99,20 +94,41 @@ IFilter::PreflightResult GeneratePythonSkeletonFilter::preflightImpl(const DataS auto filterList = StringUtilities::split(filterNames, ','); + std::stringstream preflightUpdatedValue; + std::string pluginPath = fmt::format("{}{}{}", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName); if(useExistingPlugin) { pluginPath = pluginInputDir.string(); } + std::string fullPath = fmt::format("{}{}{}{}Plugin.py", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName, std::string{fs::path::preferred_separator}); + if(std::filesystem::exists({fullPath})) + { + fullPath = "[REPLACE]: " + fullPath; + } + else + { + fullPath = "[New]: " + fullPath; + } + preflightUpdatedValue << fullPath << '\n'; - std::stringstream preflightUpdatedValue; + fullPath = fmt::format("{}{}{}{}__init__.py", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName, std::string{fs::path::preferred_separator}); + if(std::filesystem::exists({fullPath})) + { + fullPath = "[REPLACE]: " + fullPath; + } + else + { + fullPath = "[New]: " + fullPath; + } + preflightUpdatedValue << fullPath << '\n'; for(const auto& filterName : filterList) { - std::string fullPath = fmt::format("{}{}{}.py", pluginPath, std::string{fs::path::preferred_separator}, filterName); + fullPath = fmt::format("{}{}{}.py", pluginPath, std::string{fs::path::preferred_separator}, filterName); if(std::filesystem::exists({fullPath})) { - fullPath = "[EXISTS]: " + fullPath; + fullPath = "[REPLACE]: " + fullPath; } else { @@ -139,8 +155,6 @@ Result<> GeneratePythonSkeletonFilter::executeImpl(DataStructure& dataStructure, inputValues.pluginName = filterArgs.value(k_PluginName_Key); inputValues.pluginHumanName = filterArgs.value(k_PluginHumanName_Key); inputValues.filterNames = filterArgs.value(k_PluginFilterNames); - inputValues.createBatchShellScript = filterArgs.value(k_CreateBatchFile_Key); - inputValues.anacondaEnvName = filterArgs.value(k_AnacondaEnvName_Key); return GeneratePythonSkeleton(dataStructure, messageHandler, shouldCancel, &inputValues)(); } diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.hpp index 1e125394bf..d062e4b582 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/Filters/GeneratePythonSkeletonFilter.hpp @@ -29,8 +29,6 @@ class SIMPLNXCORE_EXPORT GeneratePythonSkeletonFilter : public IFilter static inline constexpr StringLiteral k_PluginHumanName_Key = "plugin_human_name"; static inline constexpr StringLiteral k_PluginInputDirectory_Key = "plugin_input_directory"; static inline constexpr StringLiteral k_PluginOutputDirectory_Key = "plugin_output_directory"; - static inline constexpr StringLiteral k_CreateBatchFile_Key = "create_batch_shell_script"; - static inline constexpr StringLiteral k_AnacondaEnvName_Key = "anaconda_env_name"; static inline constexpr StringLiteral k_PluginFilterNames = "filter_names"; /** diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonFilterTemplate.py b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonFilterTemplate.py index 1f1cc848fa..5068ed61ef 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonFilterTemplate.py +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonFilterTemplate.py @@ -2,11 +2,6 @@ import simplnx as nx class #PYTHON_FILTER_NAME#: - """ - This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name - of the value should be ALL_CAPITOL_KEY - """ - TEST_KEY = 'test' # ----------------------------------------------------------------------------- # These methods should not be edited @@ -59,35 +54,91 @@ def default_tags(self) -> List[str]: :rtype: list """ return ['python', '#PYTHON_FILTER_HUMAN_NAME#'] - + + + """ + This section should contain the 'keys' that store each parameter. The value of the key should be snake_case. The name + of the value should be ALL_CAPITOL_KEY + """ + ARRAY_PATH_KEY = 'output_array_path' + NUM_TUPLES_KEY = 'num_tuples' + def parameters(self) -> nx.Parameters: - """This function defines the parameters that are needed by the filter. Parameters collect the values from the user - or through a pipeline file. + """This function defines the parameters that are needed by the filter. Parameters collect the values from the user interface + and pack them up into a dictionary for use in the preflight and execute methods. """ params = nx.Parameters() - params.insert(nx.Float64Parameter(#PYTHON_FILTER_NAME#.TEST_KEY, 'Test', '', 0.0)) + params.insert(nx.ArrayCreationParameter(#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY, 'Created Array', 'Array storing the data', nx.DataPath())) + + params.insert(nx.UInt64Parameter(#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY, 'Num Tuples', 'The number of tuples the array will have', 0)) return params def preflight_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.PreflightResult: """This method preflights the filter and should ensure that all inputs are sanity checked as best as possible. Array - sizes can be checked if the arrays are actually know at preflight time. Some filters will not be able to report output - array sizes during preflight (segmentation filters for example). + sizes can be checked if the array sizes are actually known at preflight time. Some filters will not be able to report output + array sizes during preflight (segmentation filters for example). If in doubt, set the tuple dimensions of an array to [1]. :returns: :rtype: nx.IFilter.PreflightResult """ - value: float = args[#PYTHON_FILTER_NAME#.TEST_KEY] - message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Preflight: {value}')) - return nx.IFilter.PreflightResult() + + # Extract the values from the user interface from the 'args' + data_array_path: nx.DataPath = args[#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY] + num_tuples: int = args[#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY] + + # Create an OutputActions object to hold any DataStructure modifications that we are going to make + output_actions = nx.OutputActions() + + # Create the Errors and Warnings Lists to commuicate back to the user if anything has gone wrong + # errors = [] + # warnings = [] + # preflight_values = [] + + # Validate that the number of tuples > 0, otherwise return immediately with an error message + if num_tuples == 0: + return nx.IFilter.PreflightResult(nx.OutputActions(), [nx.Error(-65020, "The number of tuples should be at least 1.")]) + + # Append a "CreateArrayAction" + output_actions.append_action(nx.CreateArrayAction(nx.DataType.float32, [num_tuples], [1], data_array_path)) + + # Send back any messages that will appear in the "Output" widget in the UI. This is optional. + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f"Creating array at: '{data_array_path.to_string('/')}' with {num_tuples} tuples")) + + # Return the output_actions so the changes are reflected in the User Interface. + return nx.IFilter.PreflightResult(output_actions=output_actions, errors=None, warnings=None, preflight_values=None) def execute_impl(self, data_structure: nx.DataStructure, args: dict, message_handler: nx.IFilter.MessageHandler, should_cancel: nx.AtomicBoolProxy) -> nx.IFilter.ExecuteResult: """ This method actually executes the filter algorithm and reports results. :returns: :rtype: nx.IFilter.ExecuteResult """ + # Extract the values from the user interface from the 'args' + # This is basically repeated from the preflight because the variables are scoped to the method() + data_array_path: nx.DataPath = args[#PYTHON_FILTER_NAME#.ARRAY_PATH_KEY] + num_tuples: int = args[#PYTHON_FILTER_NAME#.NUM_TUPLES_KEY] + + # At this point the array has been allocated with the proper number of tuples and components. And we can access + # the data array through a numpy view. + data_array_view = data_structure[data_array_path].npview() + # Now you can go off and use numpy or anything else that can use a numpy view to modify the data + # or use the data in another calculation. Any operation that works on the numpy view in-place + # has an immediate effect within the DataStructure + + # ----------------------------------------------------------------------------- + # If you want to send back progress on your filter, you can use the message_handler + # ----------------------------------------------------------------------------- + message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Information Message: Num_Tuples = {num_tuples}')) + + # ----------------------------------------------------------------------------- + # If you have a long running process, check the should_cancel to see if the user cancelled the filter + # ----------------------------------------------------------------------------- + if not should_cancel: + return nx.Result() + - value: float = args[#PYTHON_FILTER_NAME#.TEST_KEY] - message_handler(nx.IFilter.Message(nx.IFilter.Message.Type.Info, f'Execute: {value}')) return nx.Result() + + + diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginInitTemplate.py b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginInitTemplate.py index 3cd33c9511..d0a232247e 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginInitTemplate.py +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginInitTemplate.py @@ -1,9 +1,16 @@ + +""" +Insert documentation here for #PLUGIN_NAME# +""" from #PLUGIN_NAME#.Plugin import #PLUGIN_NAME# -#PLUGIN_IMPORT_CODE## FILTER_INCLUDE_INSERT +__all__ = ['#PLUGIN_NAME#', 'get_plugin'] -def get_plugin(): - return #PLUGIN_NAME#() +""" +This section conditionally tries to import each filter +""" -__all__ = [#PLUGIN_FILTER_LIST#] # FILTER_NAME_INSERT +#PLUGIN_IMPORT_CODE# +def get_plugin(): + return #PLUGIN_NAME#() diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp index e73e41ec6b..727c4f5ac6 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginSourceTemplate.in.hpp @@ -37,13 +37,4 @@ inline const std::string PluginInitPythonFile() return {k_PluginInitPythonFileCharArray}; } -// clang-format off -static const char k_PluginBatchFileCharArray[] = {@PYTHON_PLUGIN_TEMPLATE_BAT@}; -// clang-format on - -inline const std::string PluginBatchFile() -{ - return {k_PluginBatchFileCharArray}; -} - }; // namespace nx::core \ No newline at end of file diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.bat b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.bat deleted file mode 100644 index 374c5147b5..0000000000 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.bat +++ /dev/null @@ -1,27 +0,0 @@ -:: This batch file can be run to activate a specific python virtual environment -:: and set the necessary envionment variables to load the DREAM3D-NX Plugin - -:: If you launch an Anaconda Prompt you can then run this batch file to -:: activate your environment and set the needed variables. If you have installed -:: your anaconda environment into a non-default location then you will need to -:: modify the below line with this command instead, being sure to substitue -:: the proper path to the environment. -:: conda activate --prefix C:/conda3/envs/pyd3d - -:: conda activate @ANACONDA_ENV_NAME@ - -:: ---------------------------------------------------------------------------- -:: The first variable to set is the PYTHONPATH which should point to a -:: directory or directories that contains the top level python plugin -:: folders. If you need to list mulitple directories to search for plugins -:: you can use the ";" character to separate those paths. - -set PYTHONPATH=@PYTHONPATH@ - -:: ---------------------------------------------------------------------------- -:: The next variable is a list of all plugins that you would like to -:: load when DREAM3D-NX (or NXRunner) is run. If you have multiple plugins that -:: you would like to load, list them all separated by a ";" character. - -set SIMPLNX_PYTHON_PLUGINS=@SIMPLNX_PYTHON_PLUGINS@ - diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.py b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.py index 9d443d32e2..adfdfd605d 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.py +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.py @@ -2,26 +2,51 @@ Insert documentation here. """ -#PLUGIN_IMPORT_CODE## FILTER_INCLUDE_INSERT +_filters = [] + +""" +This section conditionally tries to import each filter +""" + +#PLUGIN_IMPORT_CODE# import simplnx as nx class #PLUGIN_NAME#: + """ + This class defines the plugin's basic information. + """ def __init__(self) -> None: pass def id(self) -> nx.Uuid: + """This returns the UUID of the filter. Each Plugin has a unique UUID value. DO NOT change this. + :return: The Plugins's Uuid value + :rtype: string + """ return nx.Uuid('#PLUGIN_UUID#') def name(self) -> str: + """The returns the name of plugin. DO NOT Change this + :return: The name of the plugin + :rtype: string + """ return '#PLUGIN_NAME#' def description(self) -> str: + """This returns the description of the plugin. Feel free to edit this. + :return: The plugin's descriptive text + :rtype: string + """ return '#PLUGIN_SHORT_NAME#' def vendor(self) -> str: + """This returns the name of the organization that is writing the plugin. Feel free to edit this. + :return: The plugin's organization + :rtype: string + """ return '#PLUGIN_DESCRIPTION#' def get_filters(self): - return [#PLUGIN_FILTER_LIST#] # FILTER_NAME_INSERT + return _filters diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.sh b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.sh deleted file mode 100644 index dd631a57aa..0000000000 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplate.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -# This batch file can be run to activate a specific python virtual environment -# and set the necessary envionment variables to load the DREAM3D-NX Plugin - -# If you launch an Anaconda Prompt you can then run this batch file to -# activate your environment and set the needed variables. If you have installed -# your anaconda environment into a non-default location then you will need to -# modify the below line with this command instead, being sure to substitue -# the proper path to the environment. -# conda activate --prefix /opt/local/conda3/envs/pyd3d - -conda activate @ANACONDA_ENV_NAME@ - -# ---------------------------------------------------------------------------- -# The first variable to set is the PYTHONPATH which should point to a -# directory or directories that contains the top level python plugin -# folders. If you need to list mulitple directories to search for plugins -# you can use the ":" character to separate those paths. - -export PYTHONPATH=@PYTHONPATH@ - -# ---------------------------------------------------------------------------- -# The next variable is a list of all plugins that you would like to -# load when DREAM3D-NX (or NXRunner) is run. If you have multiple plugins that -# you would like to load, list them all separated by a ":" character. - -export SIMPLNX_PYTHON_PLUGINS=@SIMPLNX_PYTHON_PLUGINS@ diff --git a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp index a3a02062e8..316478ef9d 100644 --- a/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp +++ b/src/Plugins/SimplnxCore/src/SimplnxCore/utils/PythonPluginTemplateFile.hpp @@ -81,30 +81,28 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi // Create the output file by opening the same file for OVER WRITE. std::ofstream outFile = std::ofstream(initPyPath.string(), std::ios_base::binary | std::ios_base::trunc); + std::string filterMarkerLine = fmt::format("# FILTER_START: {}", filterName); + std::string lastMarkerLine = "def get_plugin():"; + std::string filterImportToken = fmt::format("from {0}.{1} import {1}", pluginName, filterName); bool insertToken = true; for(auto& line : lines) { - // If the line is the exact same as the generated import statement mark false - if(line == filterImportToken) + if(line == filterMarkerLine) { insertToken = false; } - // If we hit the include marker comment, then possibly write the newly generated token - if(nx::core::StringUtilities::starts_with(line, k_FilterIncludeInsertToken)) + if(line == lastMarkerLine && insertToken) { - if(insertToken) - { - outFile << filterImportToken << '\n'; - } + outFile << "# FILTER_START: " << filterName << "\n" + << "try:\n" + << " from " << pluginName << "." << filterName << " import " << filterName << "\n" + << " __all__.append('" << filterName << "')\n" + << "except ImportError:\n" + << " pass\n" + << "# FILTER_END: " << filterName << "\n\n"; } - // Do we need to append the filter to the list of filters - if(nx::core::StringUtilities::contains(line, k_FilterNameInsertToken) && insertToken) - { - line = nx::core::StringUtilities::replace(line, "__all__ = [", fmt::format("__all__ = ['{}', ", filterName)); - } - outFile << line << '\n'; // Write the line out to the file } outFile.close(); @@ -125,6 +123,9 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi // Create the output file by opening the same file for OVER WRITE. std::ofstream outFile = std::ofstream(pluginPyPath.string(), std::ios_base::binary | std::ios_base::trunc); + std::string filterMarkerLine = fmt::format("# FILTER_START: {}", filterName); + std::string lastMarkerLine = "import simplnx as nx"; + std::string filterImportToken = fmt::format("from {0}.{1} import {1}", pluginName, filterName); std::string filterInsertToken = fmt::format("'{}'", filterName); @@ -132,24 +133,20 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi for(auto& line : lines) { // If the line is the exact same as the generated import statement mark false - if(line == filterImportToken) + if(line == filterMarkerLine) { insertToken = false; } - // If we hit the include marker comment, then possibly write the newly generated token - if(nx::core::StringUtilities::starts_with(line, k_FilterIncludeInsertToken)) - { - if(insertToken) - { - outFile << filterImportToken << '\n'; - } - } - // Do we need to append the filter to the list of filters - if(nx::core::StringUtilities::contains(line, k_FilterNameInsertToken) && insertToken) + if(line == lastMarkerLine && insertToken) { - line = nx::core::StringUtilities::replace(line, "return [", fmt::format("return [{}, ", filterName)); + outFile << "# FILTER_START: " << filterName << "\n" + << "try:\n" + << " from " << pluginName << "." << filterName << " import " << filterName << "\n" + << " _filters_.append(" << filterName << ")\n" + << "except ImportError:\n" + << " pass\n" + << "# FILTER_END: " << filterName << "\n\n"; } - outFile << line << '\n'; // Write the line out to the file } outFile.close(); @@ -279,13 +276,19 @@ inline std::string GeneratePythonPlugin(const std::string& pluginName, const std auto filterList = StringUtilities::split(pluginFilterList, ','); content = StringUtilities::replace(content, "#PLUGIN_FILTER_LIST#", fmt::format("{}", fmt::join(filterList, ", "))); - std::string importStatements; + std::stringstream ss; + for(const auto& name : filterList) { - importStatements.append(fmt::format("from {}.{} import {}\n", pluginName, name, name)); + ss << "# FILTER_START: " << name << "\n" + << "try:\n" + << " from " << pluginName << "." << name << " import " << name << "\n" + << " _filters.append(" << name << ")\n" + << "except ImportError:\n" + << " pass\n" + << "# FILTER_END: " << name << "\n\n"; } - - content = StringUtilities::replace(content, "#PLUGIN_IMPORT_CODE#", importStatements); + content = StringUtilities::replace(content, "#PLUGIN_IMPORT_CODE#", ss.str()); return content; } @@ -301,7 +304,7 @@ inline std::string GeneratePythonPlugin(const std::string& pluginName, const std * @return */ inline Result<> WritePythonPluginFiles(const std::filesystem::path& outputDirectory, const std::string& pluginName, const std::string& pluginShortName, const std::string& pluginDescription, - const std::string& pluginFilterList, bool createBatchShellScript, const std::string& anacondaEnvName) + const std::string& pluginFilterList) { auto pluginRootPath = outputDirectory / pluginName; @@ -336,40 +339,6 @@ inline Result<> WritePythonPluginFiles(const std::filesystem::path& outputDirect } } - if(createBatchShellScript) - { -#ifdef __WIN32__ - outputPath = pluginRootPath / "init_evn.bat"; -#else - outputPath = pluginRootPath / "init_evn.sh"; -#endif - AtomicFile initTempFile(outputPath.string()); - auto creationResult = initTempFile.getResult(); - if(creationResult.invalid()) - { - return creationResult; - } - { - // Scope this so that the file closes first before we then 'commit' with the atomic file - std::ofstream fout(initTempFile.tempFilePath(), std::ios_base::out | std::ios_base::binary); - if(!fout.is_open()) - { - return MakeErrorResult(-74100, fmt::format("Error creating and opening output file at path: {}", initTempFile.tempFilePath().string())); - } - std::string content = PluginBatchFile(); - - content = StringUtilities::replace(content, "@PYTHONPATH@", outputDirectory.string()); - content = StringUtilities::replace(content, "@SIMPLNX_PYTHON_PLUGINS@", pluginName); - content = StringUtilities::replace(content, "@ANACONDA_ENV_NAME@", anacondaEnvName); - - fout << content; - } - if(!initTempFile.commit()) - { - return initTempFile.getResult(); - } - } - // Write the __init__.py file outputPath = pluginRootPath / "__init__.py"; { @@ -403,13 +372,18 @@ inline Result<> WritePythonPluginFiles(const std::filesystem::path& outputDirect aList.append("'get_plugin'"); content = StringUtilities::replace(content, "#PLUGIN_FILTER_LIST#", aList); - std::string importStatements; + std::stringstream ss; for(const auto& name : filterList) { - importStatements.append(fmt::format("from {}.{} import {}\n", pluginName, name, name)); + ss << "# FILTER_START: " << name << "\n" + << "try:\n" + << " from " << pluginName << "." << name << " import " << name << "\n" + << " __all__.append('" << name << "')\n" + << "except ImportError:\n" + << " pass\n" + << "# FILTER_END: " << name << "\n\n"; } - - content = StringUtilities::replace(content, "#PLUGIN_IMPORT_CODE#", importStatements); + content = StringUtilities::replace(content, "#PLUGIN_IMPORT_CODE#", ss.str()); fout << content; }