PythonApplicationManager.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of ArmarX.
3  *
4  * ArmarX is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  *
8  * ArmarX is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU General Public License for more details.
12  *
13  * You should have received a copy of the GNU General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * @package ArmarXCore::ArmarXObjects::PythonApplicationManager
17  * @author Rainer Kartmann ( rainer dot kartmann at kit dot edu )
18  * @date 2021
19  * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20  * GNU General Public License
21  */
22 
24 
25 #include <SimoxUtility/algorithm/string.h>
26 
30 
31 #include <algorithm>
32 
33 namespace armarx
34 {
35 
36  const simox::meta::EnumNames<PythonApplicationManager::VenvType> PythonApplicationManager::VenvTypeNames
37  {
41  };
42 
43 
45  {
46  defs->required(armarxPackageName, prefix + "10_ArmarXPackageName", "Name of the ArmarX package.");
47  defs->optional(armarxPythonPackagesDir, prefix + "11_ArmarXPythonPackagesDir", "Directory where python packages are located (relative to the root of the ArmarX package).");
48 
49  defs->required(pythonPackageName, prefix + "20_PythonPackageName", "Name of the Python package in the ArmarXPackage/python/ directory.");
50  defs->required(pythonScriptPath, prefix + "21_PythonScriptPath", "Path to the python script (inside the python package).");
51  defs->optional(pythonScriptArgumentsString, prefix + "22_PythonScriptArgs", "Whitespace separated list of arguments.");
52  defs->optional(pythonPathEntriesString, prefix + "23_PythonPathEntries", "Colon-separated list of paths to add to the PYTHONPATH.");
53  defs->optional(pythonPoetry, prefix + "24_PythonPoetry", "Use python poetry.");
54 
55  defs->optional(venvName, prefix + "30_venv.Name", "Name of the virtual environment.");
56  {
57  std::stringstream ss;
58  ss << "Type of the virtual environment."
59  << "\n\t- " << VenvTypeNames.to_name(VenvType::Auto) << " \tDerive automatically."
60  << "\n\t- " << VenvTypeNames.to_name(VenvType::Dedicated) << "\tSearch inside the python package root directory."
61  << "\n\t- " << VenvTypeNames.to_name(VenvType::Shared) << " \tSearch in the shared_envs directory."
62  ;
63  defs->optional(venvType, prefix + "31_venv.Type", ss.str()).map(VenvTypeNames);
64  }
65 
66 
67  defs->optional(workingDir, prefix + "40_WorkingDirectory",
68  "If set, this path is used as working directory for the python script (overriding BinaryPathAsWorkingDirectory)."
69  "\n${HOME} for env vars, $C{RobotAPI:BINARY_DIR} for CMakePackageFinder vars");
70  }
71 
72  void PythonApplicationManager::Properties::read(PropertyUser& props, const std::string& prefix)
73  {
75 
76  if (props.getProperty<std::string>(prefix + "40_WorkingDirectory").isSet())
77  {
78  workingDir = props.getProperty<std::string>("WorkingDirectory").getValueAndReplaceAllVars();
79  };
80 
81  pythonScriptArgumentsVector = simox::alg::split(pythonScriptArgumentsString, " ", true, true);
82  pythonPathEntriesVector = simox::alg::split(pythonPathEntriesString, ":", true, true);
83  }
84 
85 
87  {
89 
90  properties.defineProperties(defs, "py.");
91 
92  defs->defineOptionalProperty<bool>("BinaryPathAsWorkingDirectory", false, "If true the path of the binary is set as the working directory.");
93  defs->defineOptionalProperty<bool>("RestartInCaseOfCrash", false, "Whether the application should be restarted in case it crashed.");
94 
95  defs->defineOptionalProperty<bool>("DisconnectInCaseOfCrash", true, "Whether this component should disconnect as long as the application is not running.");
96  defs->defineOptionalProperty<int>("FakeObjectStartDelay", 0, "Delay in ms after which the fake armarx object is started on which other apps can depend. Not used if property FakeObjectDelayedStartKeyword is used.");
97  defs->defineOptionalProperty<std::string>("FakeObjectDelayedStartKeyword", "", "If not empty, the start up of the fake armarx object will be delayed until this keyword is found in the stdout of the subprocess.");
98  defs->defineOptionalProperty<int>("KillDelay", 2000, "Delay ins ms before the subprocess is killed after sending the stop signal");
99  defs->defineOptionalProperty<bool>("PythonUnbuffered", true, "If true, PYTHONUNBUFFERED=1 is added to the environment variables.");
100  defs->defineOptionalProperty<bool>("RedirectToArmarXLog", true, "If true, all outputs from the subprocess are printed with ARMARX_LOG, otherwise with std::cout");
101  defs->defineOptionalProperty<Ice::StringSeq>("AdditionalEnvVars", {}, "Comma-seperated list of env-var assignment, e.g. MYVAR=1,ADDPATH=/tmp");
102  defs->defineOptionalProperty<Ice::StringSeq>("Dependencies", {}, "Comma-seperated list of Ice Object dependencies. The external app will only be started after all dependencies have been found.");
103 
104  return defs;
105  }
106 
107  std::filesystem::path getArmarXCliPath()
108  {
109  CMakePackageFinder finder("ArmarXCore");
110  ARMARX_CHECK(finder.packageFound());
111 
112  std::filesystem::path armarxCoreRootPath = finder.getPackageDir();
113  std::filesystem::path armarxCliPath = armarxCoreRootPath / "etc" / "python";
114  return armarxCliPath;
115  }
116 
117 
119  {
120  ARMARX_TRACE;
121 
122  properties.read(*this, "py.");
123  paths.derive(properties);
124  ARMARX_INFO << paths;
125 
126  {
127  std::stringstream originalPythonPath;
128  if (char* pp = getenv("PYTHONPATH"))
129  {
130  originalPythonPath << pp;
131  }
132  std::vector<std::string> pythonPath = simox::alg::split(originalPythonPath.str(), ":");
133 
134  std::stringstream message;
135  {
136  /* We have to remove the entry of the ArmarX command line interface
137  * (".../armarx/ArmarXCore/etc/python").
138  *
139  * Reason: The ArmarX command line tool (armarx start, armarx gui, ...)
140  * is now running from a proper virtual environment. So inside the "armarx gui"
141  * process, the path of that venv is in the PYTHONPATH.
142  * This can cause "import armarx" to try to import the command line tool
143  * instead of the ArmarX python bindings (which are probably installed
144  * in the called virtual environment).
145  */
146  const std::filesystem::path armarxCliPath = getArmarXCliPath();
147  if (auto it = std::find(pythonPath.begin(), pythonPath.end(), armarxCliPath.string()); it != pythonPath.end())
148  {
149  message << "(Removed '" << *it << "' from PYTHONPATH.)";
150  pythonPath.erase(it);
151  }
152  }
153  pythonPath.push_back(this->paths.pythonPackagePath.string());
154  pythonPath.push_back(properties.pythonPathEntriesString);
155 
156  const std::string newPythonpath = simox::alg::join(pythonPath, ":");
157 
158  ARMARX_INFO << "Setting PYTHONPATH:\n\t" << newPythonpath
159  << "\nOriginal PYTHONPATH:\n\t" << originalPythonPath.str()
160  << "\n" << message.str();
161  ;
162  setenv("PYTHONPATH", newPythonpath.c_str(), 1);
163  }
164 
165  inputOk = true;
166 
168  }
169 
170 
172  {
173  ARMARX_TRACE;
174 
175  if (inputOk)
176  {
178  }
179  }
180 
181 
183  {
184  ARMARX_TRACE;
185 
186  if (inputOk)
187  {
189  }
190  }
191 
192 
194  {
195  ARMARX_TRACE;
196 
197  if (inputOk)
198  {
200  }
201  }
202 
204  {
205  return paths.workingDir;
206  }
207 
209  {
210  return paths.pythonBinPath;
211  }
212 
214  {
215  // Script path
216  args.push_back(paths.pythonScriptPath);
217  // Script args
218  args.insert(args.end(), properties.pythonScriptArgumentsVector.begin(), properties.pythonScriptArgumentsVector.end());
219  }
220 
221  std::ostream& operator<<(std::ostream& os, const PythonApplicationManager::Paths& paths)
222  {
223  os << "ArmarX package path: \t" << paths.armarxPackagePath;
224  os << "\nPython package path: \t" << paths.pythonPackagePath;
225  os << "\nPython script path: \t" << paths.pythonScriptPath;
226  os << "\nVenv path: \t" << paths.venvPath;
227  os << "\nPython binary path: \t" << paths.pythonBinPath;
228  os << "\nWorking directory: \t" << paths.workingDir;
229  return os;
230  }
231 
233  {
234  ARMARX_TRACE;
235 
236  namespace fs = std::filesystem;
237 
238  armarxPackagePath = findArmarXPackagePath(properties);
239  pythonPackagePath = findPythonPackagePath(properties);
240  pythonScriptPath = findPythonScriptPath(properties);
241 
242  if (properties.pythonPoetry)
243  {
244  std::string cmd = std::string("cd ")
245  + reinterpret_cast<const char*>(pythonPackagePath.u8string().c_str())
246  + ";poetry env info -p --ansi" ;
247 
248  std::array<char, 128> buffer;
249  std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
250  if (!pipe)
251  {
252  throw std::runtime_error("popen() failed!");
253  }
254  while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr)
255  {
256  std::string s = buffer.data();
257  s.erase(std::remove(s.begin(), s.end(), '\n'), s.end());
258  venvPath += s;
259  }
260  }
261  else
262  {
263  venvPath = findVenvPath(properties);
264  }
265  pythonBinPath = findPythonBinaryPath(properties);
266 
267  if (properties.workingDir.empty())
268  {
269  workingDir = pythonScriptPath.parent_path();
270  }
271  else
272  {
273  workingDir = properties.workingDir;
274  }
275  }
276 
278  {
280  if (!finder.packageFound())
281  {
282  std::stringstream ss;
283  ss << "Could not find ArmarX package '" << properties.armarxPackageName << "'.";
284  throw armarx::LocalException(ss.str());
285  }
286  return finder.getPackageDir();
287  }
288 
290  {
291  return armarxPackagePath
292  / properties.armarxPythonPackagesDir
293  / properties.pythonPackageName;
294  }
295 
297  {
298  std::vector<path> candidates
299  {
300  pythonPackagePath
301  / properties.pythonPackageName
302  / properties.pythonScriptPath,
303  pythonPackagePath
304  / properties.pythonScriptPath
305  };
306 
307  if (std::optional<path> p = checkCandidateFiles(candidates))
308  {
309  return p.value();
310  }
311  else
312  {
313  std::stringstream ss;
314  ss << "Could not find python script '" << properties.pythonScriptPath << "' "
315  << "in python package '" << properties.pythonPackageName << "' "
316  << "in ArmarX package '" << properties.armarxPackageName << "'. \n"
317  << "Checked candidates: ";
318  for (const path& candidate : candidates)
319  {
320  ss << "\n" << candidate;
321  }
322  throw armarx::LocalException(ss.str());
323  }
324  }
325 
327  {
328  ARMARX_TRACE;
329 
330  path venvPath;
331  switch (properties.venvType)
332  {
333  case VenvType::Dedicated:
334  venvPath = getDedicatedVenvPath(properties);
335  break;
336 
337  case VenvType::Shared:
338  venvPath = getSharedVenvPath(properties);
339  break;
340 
341  case VenvType::Auto:
342  {
343  std::vector<path> candidates
344  {
345  getDedicatedVenvPath(properties),
346  getSharedVenvPath(properties),
347  };
348  if (std::optional<path> p = checkCandidateDirectories(candidates))
349  {
350  venvPath = p.value();
351  }
352  else
353  {
354  std::stringstream ss;
355  ss << "Could not find dedicated or shared venv '" << properties.venvName << "' "
356  << "in python package '" << properties.pythonPackageName << "' "
357  << "in ArmarX package '" << properties.armarxPackageName << "'. \n"
358  << "Checked candidates: ";
359  for (const path& candidate : candidates)
360  {
361  ss << "\n" << candidate;
362  }
363  throw armarx::LocalException(ss.str());
364  }
365  }
366  break;
367  }
368 
369  if (std::filesystem::is_directory(venvPath))
370  {
371  return venvPath;
372  }
373  else
374  {
375  std::stringstream ss;
376  ss << "Could not find " << simox::alg::to_lower(VenvTypeNames.to_name(properties.venvType))
377  << " venv '" << properties.venvName << "' ";
378  switch (properties.venvType)
379  {
380  case VenvType::Auto:
381  break;
382  case VenvType::Dedicated:
383  ss << "in python package '" << properties.pythonPackageName << "' ";
384  break;
385  case VenvType::Shared:
386  break;
387  }
388  ss << "in ArmarX package '" << properties.armarxPackageName << "'. \n";
389  ss << "Expected path: \n" << venvPath;
390  throw armarx::LocalException(ss.str());
391  }
392  }
393 
395  {
396  ARMARX_TRACE;
397 
398  const path pythonBinPath = venvPath / "bin" / "python";
399 
400  if (std::filesystem::is_regular_file(pythonBinPath))
401  {
402  return pythonBinPath;
403  }
404  else
405  {
406  std::stringstream ss;
407  ss << "Could not find python binary "
408  << "in " << simox::alg::to_lower(VenvTypeNames.to_name(properties.venvType))
409  << " venv '" << properties.venvName << "' ";
410  switch (properties.venvType)
411  {
412  case VenvType::Auto:
413  break;
414  case VenvType::Dedicated:
415  ss << "in python package '" << properties.pythonPackageName << "' ";
416  break;
417  case VenvType::Shared:
418  break;
419  }
420  ss << "in ArmarX package '" << properties.armarxPackageName << "'. \n";
421  ss << "Expected path: \n" << pythonBinPath;
422  throw armarx::LocalException(ss.str());
423  }
424  }
425 
427  {
428  return pythonPackagePath
429  / properties.venvName;
430  }
431 
433  {
434  return armarxPackagePath
435  / properties.armarxPythonPackagesDir
436  / sharedVenvsDir
437  / properties.venvName;
438  }
439 
440  std::optional<PythonApplicationManager::Paths::path> PythonApplicationManager::Paths::checkCandidateFiles(const std::vector<PythonApplicationManager::Paths::path>& candidates)
441  {
442  return checkCandidatePaths(candidates, [](const path & p)
443  {
444  return std::filesystem::is_regular_file(p);
445  });
446  }
447 
448  std::optional<PythonApplicationManager::Paths::path> PythonApplicationManager::Paths::checkCandidateDirectories(const std::vector<PythonApplicationManager::Paths::path>& candidates)
449  {
450  return checkCandidatePaths(candidates, [](const path & p)
451  {
452  return std::filesystem::is_directory(p);
453  });
454  }
455 
456  std::optional<PythonApplicationManager::Paths::path> PythonApplicationManager::Paths::checkCandidatePaths(const std::vector<PythonApplicationManager::Paths::path>& candidates, std::function<bool (PythonApplicationManager::Paths::path)> existsFn)
457  {
458  auto it = std::find_if(candidates.begin(), candidates.end(), existsFn);
459  if (it != candidates.end())
460  {
461  return *it;
462  }
463  else
464  {
465  return std::nullopt;
466  }
467  }
468 
469 }
armarx::PythonApplicationManager::Properties::pythonPoetry
bool pythonPoetry
Definition: PythonApplicationManager.h:112
PythonApplicationManager.h
armarx::PythonApplicationManager::Properties::pythonScriptPath
std::string pythonScriptPath
Definition: PythonApplicationManager.h:101
armarx::PythonApplicationManager::onExitComponent
void onExitComponent() override
Definition: PythonApplicationManager.cpp:193
armarx::CMakePackageFinder::packageFound
bool packageFound() const
Returns whether or not this package was found with cmake.
Definition: CMakePackageFinder.cpp:485
LocalException.h
armarx::PythonApplicationManager::Paths::findPythonScriptPath
path findPythonScriptPath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:296
armarx::ExternalApplicationManager::onConnectComponent
void onConnectComponent() override
Definition: ExternalApplicationManager.cpp:200
armarx::PythonApplicationManager::Paths::derive
void derive(const Properties &properties)
Definition: PythonApplicationManager.cpp:232
armarx::PythonApplicationManager::Paths::findArmarXPackagePath
path findArmarXPackagePath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:277
armarx::PythonApplicationManager::Properties::read
void read(PropertyUser &props, const std::string &prefix="")
Definition: PythonApplicationManager.cpp:72
armarx::PythonApplicationManager::VenvTypeNames
static const simox::meta::EnumNames< VenvType > VenvTypeNames
Definition: PythonApplicationManager.h:65
armarx::PythonApplicationManager::Paths::getDedicatedVenvPath
path getDedicatedVenvPath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:426
armarx::CMakePackageFinder
The CMakePackageFinder class provides an interface to the CMake Package finder capabilities.
Definition: CMakePackageFinder.h:53
armarx::PythonApplicationManager::Paths::venvPath
path venvPath
Definition: PythonApplicationManager.h:131
message
message(STATUS "Boost-Library-Dir: " "${Boost_LIBRARY_DIRS}") message(STATUS "Boost-LIBRARIES
Definition: CMakeLists.txt:8
armarx::PythonApplicationManager::VenvType::Auto
@ Auto
armarx::PythonApplicationManager::Properties::defineProperties
void defineProperties(armarx::PropertyDefinitionsPtr defs, const std::string &prefix="")
Definition: PythonApplicationManager.cpp:44
armarx::PythonApplicationManager::VenvType::Dedicated
@ Dedicated
armarx::PythonApplicationManager::Properties::venvType
VenvType venvType
Definition: PythonApplicationManager.h:110
ARMARX_CHECK
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
Definition: ExpressionException.h:82
armarx::PythonApplicationManager::Properties::armarxPackageName
std::string armarxPackageName
Definition: PythonApplicationManager.h:95
armarx::PythonApplicationManager::Paths::pythonPackagePath
path pythonPackagePath
Definition: PythonApplicationManager.h:129
armarx::getArmarXCliPath
std::filesystem::path getArmarXCliPath()
Definition: PythonApplicationManager.cpp:107
armarx::ExternalApplicationManager::onInitComponent
void onInitComponent() override
Definition: ExternalApplicationManager.cpp:153
armarx::PythonApplicationManager::Paths::checkCandidateDirectories
static std::optional< path > checkCandidateDirectories(const std::vector< path > &candidates)
Definition: PythonApplicationManager.cpp:448
ARMARX_TRACE
#define ARMARX_TRACE
Definition: trace.h:69
armarx::PythonApplicationManager::Paths::checkCandidateFiles
static std::optional< path > checkCandidateFiles(const std::vector< path > &candidates)
Definition: PythonApplicationManager.cpp:440
armarx::CMakePackageFinder::getPackageDir
std::string getPackageDir() const
Returns the top level path of a source package.
Definition: CMakePackageFinder.h:144
armarx::PythonApplicationManager::Paths::pythonBinPath
path pythonBinPath
Definition: PythonApplicationManager.h:132
armarx::PythonApplicationManager::Properties
Definition: PythonApplicationManager.h:93
armarx::PythonApplicationManager::deriveApplicationPath
std::string deriveApplicationPath() const override
Definition: PythonApplicationManager.cpp:208
armarx::PythonApplicationManager::createPropertyDefinitions
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Definition: PythonApplicationManager.cpp:86
armarx::PythonApplicationManager::onDisconnectComponent
void onDisconnectComponent() override
Definition: PythonApplicationManager.cpp:182
armarx::PythonApplicationManager::onConnectComponent
void onConnectComponent() override
Definition: PythonApplicationManager.cpp:171
armarx::PythonApplicationManager::Paths::findPythonBinaryPath
path findPythonBinaryPath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:394
armarx::PythonApplicationManager::Paths::findPythonPackagePath
path findPythonPackagePath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:289
armarx::ExternalApplicationManager::onDisconnectComponent
void onDisconnectComponent() override
Definition: ExternalApplicationManager.cpp:207
armarx::PythonApplicationManager::addApplicationArguments
void addApplicationArguments(Ice::StringSeq &args) override
Definition: PythonApplicationManager.cpp:213
armarx::Component::getConfigIdentifier
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition: Component.cpp:74
CMakePackageFinder.h
armarx::PythonApplicationManager::Paths::path
std::filesystem::path path
Definition: PythonApplicationManager.h:122
armarx::PythonApplicationManager::onInitComponent
void onInitComponent() override
Definition: PythonApplicationManager.cpp:118
armarx::PythonApplicationManager::Paths::pythonScriptPath
path pythonScriptPath
Definition: PythonApplicationManager.h:130
armarx::PythonApplicationManager::Properties::armarxPythonPackagesDir
std::string armarxPythonPackagesDir
Definition: PythonApplicationManager.h:96
armarx::ComponentPropertyDefinitions
Default component property definition container.
Definition: Component.h:70
armarx::PythonApplicationManager::Properties::venvName
std::string venvName
Definition: PythonApplicationManager.h:109
armarx::PythonApplicationManager::Paths::checkCandidatePaths
static std::optional< path > checkCandidatePaths(const std::vector< path > &candidates, std::function< bool(path)> existsFn)
Definition: PythonApplicationManager.cpp:456
ARMARX_INFO
#define ARMARX_INFO
Definition: Logging.h:174
armarx::PythonApplicationManager::Paths::findVenvPath
path findVenvPath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:326
armarx::PropertyUser::getProperty
Property< PropertyType > getProperty(const std::string &name)
Property creation and retrieval.
Definition: PropertyUser.h:179
IceUtil::Handle< class PropertyDefinitionContainer >
armarx::operator<<
std::ostream & operator<<(std::ostream &os, const PythonApplicationManager::Paths &paths)
Definition: PythonApplicationManager.cpp:221
armarx::PythonApplicationManager::VenvType::Shared
@ Shared
armarx::ExternalApplicationManager::onExitComponent
void onExitComponent() override
Definition: ExternalApplicationManager.cpp:213
armarx::PythonApplicationManager::deriveWorkingDir
std::string deriveWorkingDir() const override
Definition: PythonApplicationManager.cpp:203
armarx::PythonApplicationManager::Properties::pythonPackageName
std::string pythonPackageName
Definition: PythonApplicationManager.h:100
armarx::PythonApplicationManager::Properties::workingDir
std::string workingDir
Definition: PythonApplicationManager.h:114
armarx::PythonApplicationManager::Paths
Definition: PythonApplicationManager.h:120
armarx::PropertyUser
Abstract PropertyUser class.
Definition: PropertyUser.h:62
armarx::PythonApplicationManager::Properties::pythonScriptArgumentsVector
std::vector< std::string > pythonScriptArgumentsVector
Definition: PythonApplicationManager.h:104
armarx::PythonApplicationManager::Paths::workingDir
path workingDir
Definition: PythonApplicationManager.h:133
armarx::PythonApplicationManager::Paths::armarxPackagePath
path armarxPackagePath
Definition: PythonApplicationManager.h:128
armarx::PythonApplicationManager::Paths::getSharedVenvPath
path getSharedVenvPath(const Properties &properties) const
Definition: PythonApplicationManager.cpp:432
armarx::PythonApplicationManager::Properties::pythonScriptArgumentsString
std::string pythonScriptArgumentsString
Whitespace separated list of arguments.
Definition: PythonApplicationManager.h:103
armarx::ctrlutil::s
double s(double t, double s0, double v0, double a0, double j)
Definition: CtrlUtil.h:33
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28
PluginEnumNames.h
armarx::split
std::vector< std::string > split(const std::string &source, const std::string &splitBy, bool trimElements=false, bool removeEmptyElements=false)
Definition: StringHelpers.cpp:36
armarx::PythonApplicationManager::Properties::pythonPathEntriesString
std::string pythonPathEntriesString
Definition: PythonApplicationManager.h:105