11 #include <QDirIterator>
14 using namespace pybind11::literals;
23 plgPrint() <<
"Searching for custom plugins (checking metadata in site-package)";
24 const py::object versionInfo = py::module::import(
"sys").attr(
"version_info");
25 const py::object metadata = py::module::import(
"importlib.metadata");
26 std::vector<py::object> all_entries;
28 auto collectEntries = [&](
const std::string &group)
31 if (versionInfo < py::make_tuple(3, 10))
33 const py::dict entries_dict = metadata.attr(
"entry_points")();
34 if (entries_dict.contains(group))
36 entries = entries_dict[group.c_str()];
40 plgVerbose() <<
"No custom plugin registered in site-packages";
46 entries = metadata.attr(
"entry_points")(
"group"_a = group.c_str());
49 if (py::len(entries) == 0)
51 plgPrint() <<
"No entries found for group: " << QString::fromStdString(group);
55 for (
auto &entry : entries)
57 all_entries.push_back(py::cast<py::object>(entry));
62 collectEntries(
"cloudviewer.plugins");
63 collectEntries(
"cloudcompare.plugins");
65 if (all_entries.empty())
67 plgVerbose() <<
"No custom plugin registered in site-packages";
72 plgVerbose() <<
"Found the following plugins:";
73 for (
auto &entry : all_entries)
75 QString pluginName = entry.attr(
"name").cast<QString>();
80 for (
auto &entry : all_entries)
82 QString pluginName = entry.attr(
"name").cast<QString>();
83 QString entry_point = entry.attr(
"value").cast<QString>();
84 QStringList entry_pieces = entry_point.split(
':');
85 if (entry_pieces.size() != 2)
87 plgWarning() <<
"Malformed entry point specification for '" << pluginName <<
"':'"
88 << entry_point <<
"'";
96 py::object plugin_class = py::module::import(entry_pieces[0].toStdString().c_str())
97 .attr(entry_pieces[1].toStdString().c_str());
100 m_plugins.push_back(plugin);
101 plgPrint() <<
"\tLoaded plugin: '" << pluginName <<
"'";
103 catch (
const std::exception &e)
105 plgWarning() <<
"\tFailed to instantiate plugin named '" << pluginName
106 <<
"'\nThe error was:\n"
124 plgPrint() <<
"Searching for custom plugins (in custom paths)";
125 py::object appendToPythonSysPath = py::module::import(
"sys").attr(
"path").attr(
"append");
126 for (
const QString &
path : paths)
129 appendToPythonSysPath(
path);
130 QDirIterator iter(
path);
131 while (iter.hasNext())
134 QFileInfo entry = iter.fileInfo();
135 QString fileName = entry.fileName();
137 if (fileName ==
"." || fileName ==
".." || fileName ==
"__pycache__")
142 QString nameToImport = fileName;
143 if (!entry.isDir() && fileName.endsWith(
".py"))
145 nameToImport = fileName.left(fileName.size() - 3);
148 const std::string nameToImportStd = nameToImport.toStdString();
151 py::module::import(nameToImportStd.c_str());
152 plgVerbose() <<
"\tLoaded python module '" << nameToImportStd.c_str() <<
"'";
154 catch (
const std::exception &e)
156 plgWarning() <<
"\tFailed to python module '" << nameToImportStd.c_str()
162 py::list subClassTypes =
163 py::module::import(
"pycc_runtime").attr(
"PythonPluginInterface").attr(
"__subclasses__")();
164 for (
auto &subClassType : subClassTypes)
167 if (hasattr(subClassType,
"__name__"))
169 pluginName = subClassType.attr(
"__name__").cast<QString>();
175 m_plugins.push_back(plugin);
176 plgPrint() <<
"\tLoaded plugin: '" << pluginName <<
"'";
178 catch (
const std::exception &e)
180 plgWarning() <<
"\tFailed to instantiate plugin named '" << pluginName
181 <<
"'\nThe error was:\n"
PluginLogger< CVLog::LOG_WARNING > plgWarning
PluginLogger< CVLog::LOG_STANDARD > plgPrint
PluginLogger< CVLog::LOG_VERBOSE > plgVerbose
void loadPluginsFrom(const QStringList &paths)
const std::vector< Runtime::RegisteredPlugin > & plugins() const
Returns the currently loaded plugins.
void loadPluginsFromEntryPoints()
void unloadPlugins()
This MUST be called before finalizing the interpreter.
static const std::string path
static RegisteredPlugin InstanciatePlugin(pybind11::object class_type, const QString &name) noexcept(false)
Instanciate a plugin with a known name.