Skip to content

Commit

Permalink
BUG: Fix python plugin generation codes (#881)
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Jackson <[email protected]>
  • Loading branch information
imikejackson authored Mar 12, 2024
1 parent 023c989 commit 60a8121
Show file tree
Hide file tree
Showing 6 changed files with 132 additions and 55 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
#include "GeneratePythonSkeleton.hpp"

#include "simplnx/Common/Constants.hpp"
#include "simplnx/Common/RgbColor.hpp"
#include "simplnx/DataStructure/DataArray.hpp"
#include "simplnx/DataStructure/DataGroup.hpp"
#include "simplnx/Utilities/DataArrayUtilities.hpp"

#include <Eigen/Dense>

using namespace nx::core;

// -----------------------------------------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,39 @@ IFilter::PreflightResult GeneratePythonSkeletonFilter::preflightImpl(const DataS
auto useExistingPlugin = filterArgs.value<BoolParameter::ValueType>(k_UseExistingPlugin_Key);
auto pluginOutputDir = filterArgs.value<FileSystemPathParameter::ValueType>(k_PluginOutputDirectory_Key);
auto pluginName = filterArgs.value<StringParameter::ValueType>(k_PluginName_Key);
auto pluginInputDir = filterArgs.value<FileSystemPathParameter::ValueType>(k_PluginInputDirectory_Key);
auto filterNames = filterArgs.value<StringParameter::ValueType>(k_PluginFilterNames);

nx::core::Result<OutputActions> resultOutputActions;
std::vector<PreflightValue> preflightUpdatedValues;

if(!useExistingPlugin)
auto filterList = StringUtilities::split(filterNames, ',');

std::string pluginPath = fmt::format("{}{}{}", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName);
if(useExistingPlugin)
{
pluginPath = pluginInputDir.string();
}

std::stringstream preflightUpdatedValue;

for(const auto& filterName : filterList)
{
preflightUpdatedValues.push_back({"Generated Plugin Directory", fmt::format("", pluginOutputDir.string(), std::string{fs::path::preferred_separator}, pluginName)});
std::string fullPath = fmt::format("{}{}{}.py", pluginPath, std::string{fs::path::preferred_separator}, filterName);
if(std::filesystem::exists({fullPath}))
{
fullPath = "[EXISTS]: " + fullPath;
}
else
{
fullPath = "[New]: " + fullPath;
}
preflightUpdatedValue << fullPath << '\n';
}

preflightUpdatedValues.push_back({"Generated Plugin File(s):", preflightUpdatedValue.str()});
preflightUpdatedValues.push_back({"Warning:", "Any Existing Files Will Be Overwritten"});

return {std::move(resultOutputActions), std::move(preflightUpdatedValues)};
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from #PLUGIN_NAME#.Plugin import #PLUGIN_NAME#

#PLUGIN_IMPORT_CODE#
# FILTER_INCLUDE_INSERT
#PLUGIN_IMPORT_CODE## FILTER_INCLUDE_INSERT

def get_plugin():
return #PLUGIN_NAME#()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@
Insert documentation here.
"""

#PLUGIN_IMPORT_CODE#
# FILTER_INCLUDE_INSERT
#PLUGIN_IMPORT_CODE## FILTER_INCLUDE_INSERT

import simplnx as nx

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi
if(StringUtilities::ends_with(pluginName, "/"))
{
pluginName.pop_back();
std::filesystem::path plugPath(pluginName);
pluginName = plugPath.stem().string();
}
std::filesystem::path plugPath(pluginName);
pluginName = plugPath.stem().string();

fs::path pluginPyPath = pluginPath / "Plugin.py";
if(!fs::exists(pluginPyPath))
Expand All @@ -66,60 +66,93 @@ inline Result<> InsertFilterNameInPluginFiles(const std::filesystem::path& plugi
return MakeErrorResult(-2001, fmt::format("Non-existent plugin file at path: {}", initPyPath.string()));
}

std::vector<fs::path> pluginFilePaths = {pluginPyPath, initPyPath};
for(const auto& pluginFilePath : pluginFilePaths)
// Update the __init__.py file
{
std::ifstream file(pluginFilePath.string());
std::ifstream file(initPyPath.string());
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
std::vector<std::string> lines = nx::core::StringUtilities::split_2(content, "\n", true);
if(lines.back().empty())
{
lines.pop_back();
}
// 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);

// Insert filter import statement
std::string filterImportToken = fmt::format("from {0}.{1} import {1}", pluginName, filterName);
bool insertToken = true;
for(auto& line : lines)
{
std::size_t filterInsertPos = content.find(k_FilterIncludeInsertToken);
if(filterInsertPos == std::string::npos)

// If the line is the exact same as the generated import statement mark false
if(line == filterImportToken)
{
result.warnings().push_back(
{-2002, fmt::format("Plugin file ('{0}') does not contain the filter insert token ('{1}'), so the filter import statement could not be automatically inserted into the plugin "
"file. This plugin file must be manually updated to include the filter import statement.",
pluginFilePath.string(), k_FilterNameInsertToken)});
insertToken = false;
}
else
// If we hit the include marker comment, then possibly write the newly generated token
if(nx::core::StringUtilities::starts_with(line, k_FilterIncludeInsertToken))
{
filterInsertPos--; // Go back one character to insert before the newline

std::string filterImportToken = fmt::format("from {0}.{1} import {1}\n", pluginName, filterName);
content.insert(filterInsertPos, filterImportToken);
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)
{
line = nx::core::StringUtilities::replace(line, "__all__ = [", fmt::format("__all__ = ['{}', ", filterName));
}

outFile << line << '\n'; // Write the line out to the file
}
outFile.close();
}

// Update the Plugin.py file
{
std::ifstream file(pluginPyPath.string());
std::stringstream buffer;
buffer << file.rdbuf();
std::string content = buffer.str();
file.close();
std::vector<std::string> lines = nx::core::StringUtilities::split_2(content, "\n", true);
if(lines.back().empty())
{
lines.pop_back();
}
// 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);

// Insert filter name
std::string filterImportToken = fmt::format("from {0}.{1} import {1}", pluginName, filterName);
std::string filterInsertToken = fmt::format("'{}'", filterName);

bool insertToken = true;
for(auto& line : lines)
{
std::size_t filterInsertPos = content.find(k_FilterNameInsertToken);
if(filterInsertPos == std::string::npos)
// If the line is the exact same as the generated import statement mark false
if(line == filterImportToken)
{
result.warnings().push_back(
{-2002, fmt::format("Plugin file ('{0}') does not contain the filter insert token ('{1}'), so the filter name ('{2}') could not be automatically inserted into the plugin "
"file. This plugin file must be manually updated to include the filter name ('{2}').",
pluginFilePath.string(), k_FilterNameInsertToken, filterName)});
insertToken = false;
}
else
// If we hit the include marker comment, then possibly write the newly generated token
if(nx::core::StringUtilities::starts_with(line, k_FilterIncludeInsertToken))
{
filterInsertPos -= 2; // Go back two characters to insert before the closing brace

std::string filterNameToken = filterName;
if(pluginFilePath.filename() == "__init__.py")
if(insertToken)
{
filterNameToken = fmt::format("'{}'", filterNameToken);
outFile << filterImportToken << '\n';
}
content.insert(filterInsertPos, "," + filterNameToken);
}
}
// 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, "return [", fmt::format("return [{}, ", filterName));
}

std::ofstream out_file(pluginFilePath.string());
out_file << content;
out_file.close();
outFile << line << '\n'; // Write the line out to the file
}
outFile.close();
}

return result;
Expand Down Expand Up @@ -224,7 +257,7 @@ inline std::string GeneratePythonPlugin(const std::string& pluginName, const std
content = StringUtilities::replace(content, "#PLUGIN_DESCRIPTION#", pluginDescription);

auto filterList = StringUtilities::split(pluginFilterList, ',');
content = StringUtilities::replace(content, "#PLUGIN_FILTER_LIST#", fmt::format("{}", fmt::join(filterList, ",")));
content = StringUtilities::replace(content, "#PLUGIN_FILTER_LIST#", fmt::format("{}", fmt::join(filterList, ", ")));

std::string importStatements;
for(const auto& name : filterList)
Expand Down Expand Up @@ -294,7 +327,7 @@ inline Result<> WritePythonPluginFiles(const std::filesystem::path& outputDirect

auto filterList = StringUtilities::split(pluginFilterList, ',');

std::string aList(fmt::format("'{}',", pluginName));
std::string aList(fmt::format("'{}', ", pluginName));
for(const auto& name : filterList)
{
aList.append(fmt::format("'{}', ", name));
Expand Down
43 changes: 34 additions & 9 deletions src/simplnx/Utilities/StringUtilities.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,43 @@ inline std::vector<std::string> split(std::string_view str, char delim)
return split(str, delims, false);
}

inline std::vector<std::string> split_2(const std::string& line, char delimiter)
/**
*
* @param input
* @param delimiter
* @param consecutiveDelimiters
* @return
*/
inline std::vector<std::string> split_2(std::string_view input, nonstd::span<const char> delimiter, bool consecutiveDelimiters)
{
std::stringstream ss(line);

std::vector<std::string> tokens;
std::string tempStr;

while(std::getline(ss, tempStr, delimiter))
std::vector<std::string> result;
std::string current;
for(char ch : input)
{
tokens.push_back(tempStr);
if(ch == delimiter[0])
{
// If consecutive delimiters should lead to empty strings, or if the current string is not empty,
// we add the current string (which could be empty) to the result.
if(consecutiveDelimiters || !current.empty())
{
result.push_back(current);
current.clear(); // Reset current for the next word.
}
// If consecutiveDelimiters is false, we simply skip this part,
// which avoids adding an empty string for consecutive delimiters.
}
else
{
current += ch;
}
}
return tokens;
// Add the last word to the result if it's not empty, or if the last character was a delimiter
// and consecutiveDelimiters is true.
if(!current.empty() || (consecutiveDelimiters && !input.empty() && input.back() == delimiter[0]))
{
result.push_back(current);
}
return result;
}

inline std::string join(nonstd::span<std::string_view> vec, std::string_view delim)
Expand Down

0 comments on commit 60a8121

Please sign in to comment.