Skip to content
Snippets Groups Projects
SimplePythonAPI.cpp 24.9 KiB
Newer Older
//-----------------------------------
//Includes
//-----------------------------------
#include <vector>
#include <set>
#include <string>
#include <fstream>
#include <sstream>
#include <iomanip>
#include "MantidPythonAPI/SimplePythonAPI.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AlgorithmFactory.h"
#include "MantidAPI/Algorithm.h"
#include "MantidKernel/ConfigService.h"
#include "Poco/DirectoryIterator.h"
#include "Poco/Path.h"
#include "Poco/File.h"

namespace Mantid
{

  namespace PythonAPI
  {

Nick Draper's avatar
Nick Draper committed
    /// The module filename
    std::string SimplePythonAPI::g_module_name = "mantidsimple.py";

    //------------------------------
    //Public methods
    //------------------------------
    /**
    * Return the name of the Python module to be created
    * @returns A string containing the name of the module file
    */
    std::string SimplePythonAPI::getModuleFilename()
    {
      return Poco::Path(Mantid::Kernel::ConfigService::Instance().getOutputDir()).append(Poco::Path(g_module_name)).toString(); 
    }

    /**
    * Create the python module with function definitions in the
    * file whose name is returned by getModule()
    * @param gui If this is true create the necessary framework to use the dialog
    * boxes in qtiplot
    */
    void SimplePythonAPI::createModule(bool gui)
    {
      std::ofstream module(getModuleFilename().c_str());

      // Need to import definitions from main Python API
#ifdef _WIN32
      module << "from MantidPythonAPI import *\n";
#else
      module << "from libMantidPythonAPI import *\n";
#endif

      module << "from MantidFramework import *\n";

      //If in gui mode also need sys and qti module
      if( gui )
      {
        module << "import qti\n";
      }
      //Need string and os module regardless
      module << "import os\n";
      module << "import string\n\n";

      // A simple function to change the working directory
      module << "# A wrapper for changing the directory\n"
        << "def setWorkingDirectory(path):\n"
        << "\tos.chdir(path)\n\n";

      // A function to sort out whether the dialog parameters are disabled or not
      if( gui )
      {
        module << "# A utility function for the dialog routines that decides if the parameter\n"
          << "# should be added to the final list of parameters that have their widgets enabled\n"
          << "def convertToPair(param_name, param_value, enabled_list, disabled_list):\n"
          << "\tif param_value == None:\n"
          << "\t\tif not param_name in disabled_list:\n"
          << "\t\t\treturn ('', param_name)\n"
          << "\t\telse:\n"
          << "\t\t\treturn ('', '')\n"
          << "\telse:\n"
          << "\t\tstrval = makeString(param_value)\n"
          << "\t\tif param_name in enabled_list or (len(strval) > 0 and strval[0] == '?'):\n"
          << "\t\t\treturn (param_name + '=' + strval.lstrip('?'), param_name)\n"
          << "\t\telse:\n"
          << "\t\t\treturn (param_name + '=' + strval, '')\n\n";
      }

      // A couple of functions to aid in the formatting of help commands
      module << "def numberRows(descr, fw):\n"
        << "\tdes_len = len(descr)\n"
        << "\tif des_len == 0:\n"
        << "\t\treturn (1, [''])\n"
        << "\tnrows = 0\n"
        << "\ti = 0\n"
        << "\tdescr_split = []\n"
        << "\twhile i < des_len:\n"
        << "\t\tnrows += 1\n"
        << "\t\tdescr_split.append(descr[i:i+fw])\n"
        << "\t\ti += fw\n"
        << "\treturn (nrows, descr_split)\n\n";

      // A rather complicated function to format the help into a table
      module << "def createParamTable(param_list, dialog):\n"
        << "\tflw = 100\n"
        << "\tcol_widths = [flw/5, 6, 8, 6, flw/3, flw/4]\n"
        << "\ttopline = '|' + 'Param Name'.center(col_widths[0]) + '|' + 'In/Out'.center(col_widths[1]) + '|' + 'Type'.center(col_widths[2]) + '|' + 'Req\\'d?'.center(col_widths[3]) + '|' + 'Description'.center(col_widths[4])  + '|' + 'Allowed Values'.center(col_widths[5]) + '\\n'\n"
        << "\trow_delim = '-' * len(topline) + '\\n'\n"
        << "\thelpstr =  row_delim + topline + row_delim\n"
        << "\tif dialog == True:\n"
        << "\t\tparam_list.append(['Message','Input','string','','A message to display', ''])\n"
        << "\t\tparam_list.append(['Enable','Input','string','','Comma-separated list of param names to keep enabled in the dialog', ''])\n"
        << "\t\tparam_list.append(['Disable','Input','string','','Comma-separated list of param names to disable in the dialog', ''])\n"
        << "\tfor pstr in param_list:\n"
        << "\t\tndes, descr_split = numberRows(pstr[4], col_widths[4])\n"
        << "\t\tnall, allow_split = numberRows(pstr[5], col_widths[5])\n"
        << "\t\tif ndes  == 1 and nall == 1:\n"
        << "\t\t\thelpstr += ''.join(['|' + pstr[s].center(col_widths[s]) for s in range(0, 6)]) + '\\n'\n"
        << "\t\telse:\n"
        << "\t\t\tmidline = 0\n"
        << "\t\t\ttot_rows = max(ndes, nall)\n"
        << "\t\t\tif bool(tot_rows % 2):\n"
        << "\t\t\t\tmidline = (tot_rows + 1) / 2\n"
        << "\t\t\telse:\n"
        << "\t\t\t\tmidline = tot_rows / 2\n"
        << "\t\t\tfor r in range(0, tot_rows):\n"
        << "\t\t\t\tline = []\n"
        << "\t\t\t\tif ndes == nall:\n"
        << "\t\t\t\t\tif r != midline - 1:\n"
        << "\t\t\t\t\t\tline = ['','','','',descr_split[r], allow_split[r]]\n"
        << "\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\tline = [pstr[0],pstr[1],pstr[2],pstr[3],descr_split[r], allow_split[r]]\n" 
        << "\t\t\t\telif ndes > nall:\n"
        << "\t\t\t\t\tif r < nall:\n"
        << "\t\t\t\t\t\tif r != midline - 1:\n"
        << "\t\t\t\t\t\t\tline = ['','','','',descr_split[r], allow_split[r]]\n"
        << "\t\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\t\tline = [pstr[0],pstr[1],pstr[2],pstr[3],descr_split[r], allow_split[r]]\n" 
        << "\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\tif r != midline - 1:\n"
        << "\t\t\t\t\t\t\tline = ['','','','',descr_split[r], '']\n"
        << "\t\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\t\tline = [pstr[0],pstr[1],pstr[2],pstr[3],descr_split[r], '']\n"
        << "\t\t\t\telse:\n"
        << "\t\t\t\t\tif r < ndes:\n"
        << "\t\t\t\t\t\tif r != midline - 1:\n"
        << "\t\t\t\t\t\t\tline = ['','','','',descr_split[r], allow_split[r]]\n"
        << "\t\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\t\tline = [pstr[0],pstr[1],pstr[2],pstr[3],descr_split[r], allow_split[r]]\n"
        << "\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\tif r != midline - 1:\n"
        << "\t\t\t\t\t\t\tline = ['','','','', '',allow_split[r]]\n"
        << "\t\t\t\t\t\telse:\n"
        << "\t\t\t\t\t\t\tline = [pstr[0],pstr[1],pstr[2], pstr[3],'', allow_split[r]]\n"
        << "\t\t\t\thelpstr += ''.join(['|' + line[s].center(col_widths[s]) for s in  range(0,6)]) + '\\n'\n"
        << "\t\thelpstr += row_delim\n"
        << "\treturn helpstr\n\n";


      //Algorithm keys
      using namespace Mantid::API;
      //Ensure that a FrameworkManager object has been instantiated to initialise the framework
      FrameworkManager::Instance();
      StringVector algKeys = AlgorithmFactory::Instance().getKeys();
      VersionMap vMap;
      createVersionMap(vMap, algKeys);
      writeGlobalHelp(module, vMap, gui);
      //Function definitions for each algorithm
      IndexVector helpStrings;
      std::map<std::string, std::set<std::string> > categories;
      for( VersionMap::const_iterator vIter = vMap.begin(); vIter != vMap.end();
        ++vIter)
      {
        IAlgorithm_sptr algm = AlgorithmManager::Instance().createUnmanaged(vIter->first);
        algm->initialize();
        PropertyVector orderedProperties(algm->getProperties());
        std::sort(orderedProperties.begin(), orderedProperties.end(), SimplePythonAPI::PropertyOrdering());
        std::string name(vIter->first);
        writeFunctionDef(module, name , orderedProperties, gui);
        if( gui ) 
        {
          writeGUIFunctionDef(module, name, orderedProperties);
        }

        // Help strings
        std::transform(name.begin(), name.end(), name.begin(), tolower);
        helpStrings.push_back(make_pair(name, createHelpString(vIter->first, orderedProperties, false)));
        //The help for the dialog functions if necessary
        if( gui )
        {
          helpStrings.push_back(make_pair(name + "dialog", createHelpString(vIter->first, orderedProperties, true)));
        }

        // Get the category and save it to our map
        std::string category = algm->category();
        std::string::size_type idx = category.find("\\");
        std::string topcategory(category), tail(vIter->first); 
        if( idx != std::string::npos )
        {
          topcategory = category.substr(0, idx);
          tail = category.substr(idx + 1) + "\\" + vIter->first;
        }

        std::map<std::string, std::set<std::string> >::iterator itr = categories.find(topcategory);
        if( itr != categories.end() )
        {
          (*itr).second.insert(tail);
        }
        else
        {
          std::set<std::string> cat_algms;
          cat_algms.insert(tail);
          categories.insert(std::make_pair<std::string, std::set<std::string> >(topcategory, cat_algms));
        }
      }

      //Help strings
      writeFunctionHelp(module, helpStrings, categories);
      module.close();

    }

    /**
    * Construct a map between an algorithm name and it's highest version
    * @param vMap A reference to the vMap
    * @param algKeys A list of strings representing the algorithms mangled with version numbers
    */
    void SimplePythonAPI::createVersionMap(VersionMap & vMap, const StringVector & algKeys)
    {
      for(StringVector::const_iterator sIter = algKeys.begin(); sIter != algKeys.end();
        ++sIter)
      {
        std::string name = extractAlgName(*sIter); 
        VersionMap::iterator vIter = vMap.find(name);
        if( vIter == vMap.end() ) vMap.insert(make_pair(name, 1));
        else ++(vIter->second);
      }
    }

    /**
    * Extract the algorithm name from an algorithm key
    * @param name The algorithm key
    * @returns The name of the algorithm
    */
    std::string SimplePythonAPI::extractAlgName(const std::string & name)
    {
      std::string::size_type idx = name.find('|');
      if( idx != std::string::npos ) return name.substr(0, idx);
      else return name;
    }

    /**
    * Write a Python function defintion
    * @param os The stream to use to write the definition
    * @param algm The name of the algorithm
    * @param properties The list of properties
    * @param async Whether the algorithm should be executed asynchonously or not
    */
    void SimplePythonAPI::writeFunctionDef(std::ostream & os, const std::string & algm,
      const PropertyVector & properties, bool async)
    {
      os << "# Definition of \"" << algm << "\" function.\n";
      //start of definition
      os << "def " << algm;
      os << "(";
      //Iterate through properties
      PropertyVector::const_iterator pIter = properties.begin();
      PropertyVector::const_iterator pEnd = properties.end();
      StringVector sanitizedNames(properties.size());
      unsigned int iMand(0), iarg(0);
      for( ; pIter != pEnd; ++iarg)
      {
        sanitizedNames[iarg] = removeCharacters((*pIter)->name(), "");
        os << sanitizedNames[iarg];

        //properties are optional unless their current value results in an error (isValid != "")
        if( (*pIter)->isValid() != "" ) ++iMand;
        else os  << " = None";
        if( ++pIter != pEnd ) os << ", ";
      }

      //end of function parameters
      os << "):\n"
        << "\talgm = mantid.createAlgorithm(\"" << algm << "\")\n";

      // Redo loop for setting values
      pIter = properties.begin();
      iarg = 0;
      for( ; pIter != pEnd; ++pIter, ++iarg )
      {
        std::string pvalue = sanitizedNames[iarg];
        if( iarg < iMand )
        {
          os << "\talgm.setPropertyValue(\"" << (*pIter)->name() 
            << "\", makeString(" << pvalue << ").lstrip('? '))\n";
        }
        else
        {
          os << "\tif " << pvalue << " != None:\n"
            << "\t\talgm.setPropertyValue(\"" << (*pIter)->name() << "\", makeString(" 
            << pvalue << ").lstrip('? '))\n";
        }
      }

      if( async )
      {
        writeAsyncFunctionCall(os, algm, "\t");
        os << "\tif result == False:\n"
          << "\t\tsys.exit('An error occurred while running " << algm << ". See results log for details.')\n";
      }
      else
      {
        os << "\talgm.execute()\n";
      }

      // Return the IAlgorithm object
      os << "\treturn mtd._createAlgProxy(algm)\n\n";
    }

    /**
    * Write the GUI version of the Python function that raises a Qt dialog
    * @param os The stream to use to write the definition
    * @param algm The name of the algorithm
    * @param properties The list of properties
    */
    void SimplePythonAPI::writeGUIFunctionDef(std::ostream & os, const std::string & algm,
      const PropertyVector & properties)
    {
      os << "# Definition of \"" << algm << "\" function.\n";
      //start of definition
      os << "def " << algm << "Dialog(";
      //Iterate through properties
      PropertyVector::const_iterator pIter = properties.begin();
      PropertyVector::const_iterator pEnd = properties.end();
      StringVector sanitizedNames(properties.size());
      for( int iarg = 0; pIter != pEnd; ++pIter, ++iarg)
      {
        sanitizedNames[iarg] = removeCharacters((*pIter)->name(), "");
        os << sanitizedNames[iarg];

        os << " = None,";
      }
      //end of algorithm function parameters but add other arguments
      os << "Message = \"\", Enable=\"\", Disable=\"\"):\n"
        << "\talgm = mantid.createAlgorithm(\"" << algm << "\")\n"
        << "\tenabled_list = [s.lstrip(' ') for s in Enable.split(',')]\n"
        << "\tdisabled_list = [s.lstrip(' ') for s in Disable.split(',')]\n"
        << "\tvalues = '|'\n"
        << "\tfinal_enabled = ''\n\n";

      pIter = properties.begin();
      for( int iarg = 0; pIter != pEnd; ++pIter, ++iarg)
      {
        os << "\tvalpair = convertToPair('" << (*pIter)->name() << "', " << sanitizedNames[iarg]
        << ", enabled_list, disabled_list)\n"
          << "\tvalues += valpair[0] + '|'\n"
          << "\tfinal_enabled += valpair[1] + ','\n\n";
      }

      os << "\tdialog = qti.app.mantidUI.createPropertyInputDialog(\"" << algm 
        << "\" , values, Message, final_enabled)\n"
        << "\tif dialog == True:\n";

      writeAsyncFunctionCall(os, algm, "\t\t");

      os << "\telse:\n"
        << "\t\tsys.exit('Information: Script execution cancelled')\n"
        << "\tif result == False:\n"
        << "\t\tsys.exit('An error occurred while running " << algm << ". See results log for details.')\n"
        << "\treturn mtd._createAlgProxy(algm)\n\n";
    }

    /**
    * Write a global help command called mtdHelp, which takes no arguments
    * @param os The stream to use to write the command
    * @param vMap The map of algorithm names to highest version numbers
    * @param gui Create GUI help message with extra information about dialog functions
    */
    void SimplePythonAPI::writeGlobalHelp(std::ostream & os, const VersionMap & vMap, bool gui)
    {
      os << "# The help command with no parameters\n";
      os << "def mtdGlobalHelp():\n";
      os << "\thelpmsg =  \"The algorithms available are:\\n\"\n";
      VersionMap::const_iterator vIter = vMap.begin();
      for( ; vIter != vMap.end(); ++vIter )
      {
        if( vIter->second == 1 )
          os << "\thelpmsg += \"\\t" << vIter->first << "\\n\"\n"; 
        else {
          os << "\thelpmsg += \"\\t" << vIter->first << " "; 
          int i = 0;
          while( ++i <= vIter->second )
          {
            os << "v" << i << " ";
          }
          os << "\\n\"\n";
        }
      }
      os << "\thelpmsg += \"For help with a specific command type: mantidHelp(\\\"cmd\\\")\\n\"\n";
      if( gui )
      {
        os << "\thelpmsg += \"Note: Each command also has a counterpart with the word 'Dialog'"
          << " appended to it, which when run will bring up a property input dialog for that algorithm.\\n\"\n";
      }
      os << "\tprint helpmsg,\n"
        << "\n";
    }

    /**
    * Construct a  help command for a specific algorithm
    * @param algm The name of the algorithm
    * @param properties The list of properties
    * @param dialog A boolean indicating whether this is a dialog function or not
    * @return A help string for users
    */
    std::string SimplePythonAPI::createHelpString(const std::string & algm, const PropertyVector & properties, bool dialog)
    {
      std::ostringstream os;
      os << "\t\tparams_list = [";
      std::string argument_list(algm);
      if( dialog )
      {
        argument_list += "Dialog";
      }
      argument_list += "(";

      PropertyVector::const_iterator pIter = properties.begin();
      PropertyVector::const_iterator pEnd = properties.end();
      for( ; pIter != pEnd ; )
      {
        Mantid::Kernel::Property *prop = *pIter;
        argument_list += removeCharacters((*pIter)->name(), "");
        os << "['" << prop->name() << "','" << Mantid::Kernel::Direction::asText(prop->direction()) << "', '" 
          << prop->type() << "','";
        if( !prop->isValid().empty() )
        {
          os << "X";
        }
        os << "', '" << removeCharacters(prop->documentation(),"\n\r", true) << "','";
        std::set<std::string> allowed = prop->allowedValues();
        if( !allowed.empty() )
        {
          std::set<std::string>::const_iterator sIter = allowed.begin();
          std::set<std::string>::const_iterator sEnd = allowed.end();
          for( ; sIter != sEnd ; )
          {
            os << (*sIter);
            if( ++sIter != sEnd ) os << ", ";
          }
        }
        os << "']";
        if( ++pIter != pEnd ) 
        {
          os << ",";
          argument_list += ",";
        }
      }
      if( dialog )
      {
        argument_list += ",Message,Enable,Disable";
      }
      argument_list += ")";

      os << "]\n"
        << "\t\thelpstring = '\\nUsage: ' + '" << argument_list << "\\n\\n'\n"
        << "\t\thelpstring += createParamTable(params_list,";
      if( dialog )
      {
        os << "True";
      }
      else
      {
        os << "False";
      }
      os << ")\n"
        << "\t\tprint helpstring,\n";
      return os.str();
    }

    /**
    * Write the help function that takes a command as an argument
    * @param os The stream to use for the output
    * @param helpStrings A map of function names to help strings
    * @param categories The list categories and their associated algorithms
    */
    void SimplePythonAPI::writeFunctionHelp(std::ostream & os, const IndexVector & helpStrings, 
      const std::map<std::string, std::set<std::string> > & categories)
    {
      if ( helpStrings.empty() ) return;

      os << "def mtdHelp(cmd = None):\n";

      os << "\tif cmd == None or cmd == '':\n"
        << "\t\tmtdGlobalHelp()\n"
        << "\t\treturn\n";
      os << "\n\ttry:\n"
          << "\t\tcmd = cmd.func_name\n"
          << "\texcept ValueError:\n"
          << "\t\tpass\n";
      os << "\n\tcmd = string.lower(cmd)\n";
      //Functons help
      SimplePythonAPI::IndexVector::const_iterator mIter = helpStrings.begin();
      SimplePythonAPI::IndexVector::const_iterator mEnd = helpStrings.end();
      os << "\tif cmd == '" << (*mIter).first << "':\n" 
        << (*mIter).second;
      while( ++mIter != mEnd )
      {
        os << "\telif cmd == '" << (*mIter).first << "':\n" 
          << (*mIter).second;
      }

      //Categories
      std::map<std::string, std::set<std::string> >::const_iterator cat_end = categories.end();
      for( std::map<std::string, std::set<std::string> >::const_iterator cat_itr = categories.begin(); cat_itr != cat_end; ++cat_itr )
      {
        std::string topcategory = cat_itr->first;
        std::string lowercase = topcategory;
        std::transform(lowercase.begin(), lowercase.end(), lowercase.begin(), tolower);
        os << std::string("\telif cmd == '") << lowercase << std::string("':\n\t\thelpstr = 'The algorithms in the ") 
          << topcategory << std::string(" category are:\\n'\n");		
        std::set<std::string>::const_iterator alg_end = cat_itr->second.end();
        for( std::set<std::string>::const_iterator alg_itr = cat_itr->second.begin(); alg_itr != alg_end; ++alg_itr )
        {
          os << std::string("\t\thelpstr += '\t") << (*alg_itr) << "\\n'\n";
        }
        os << "\t\tprint helpstr,\n";
      }



      // Finally add a "default" clause if the name cannot be found
      os << "\telse:\n"
        << "\t\tprint 'mtdHelp() - ' + cmd + ' not found in help list'\n\n";

      //Aliases
      os << "# Help function aliases\n"
        << "mtdhelp = mtdHelp\n"
        << "Mtdhelp = mtdHelp\n"
        << "MtdHelp = mtdHelp\n"
        << "mantidhelp = mtdHelp\n"
        << "mantidHelp = mtdHelp\n"
        << "MantidHelp = mtdHelp\n";
    }

    /**
    * Write out Python code required to execute an algorithm asynchronously, ensuring the GIL is in the correct state
    * @param output The stream to contain the code
    * @param alg_name The name of the algorithm
    * @param prefix A prefix to apply to each line
    */
    void SimplePythonAPI::writeAsyncFunctionCall(std::ostream & output, const std::string & alg_name, 
      const std::string & prefix)
    {
      output << prefix << "mtd._setGILRequired(True)\n" 
        << prefix << "result = qti.app.mantidUI.runAlgorithmAsync_PyCallback(\"" << alg_name << "\")\n"
        << prefix << "mtd._setGILRequired(False)\n";
    }

    /**
    * Takes a string and if only EOL characters are present then they are replaced with their string represenations
    * @param value The property value
    * @returns A string containing the sanitized property value
    */
    std::string SimplePythonAPI::convertEOLToString(const std::string & value)
    {
      if( value == "\n\r" )
        return std::string("\\\\") + std::string("n") + std::string("\\\\") + std::string("r");
      if( value == "\n" )
        return std::string("\\\\") + std::string("n");
      return value;
    }

    /**
    * Takes a string and removes the characters given in the optional second argument. If none are given then only alpha-numeric
    * characters are retained.
    * @param value The string to analyse
    * @param cs A string of characters to remove
    * @param eol_to_space Flag signalling whether end of line characters should be changed to a space
    * @returns The sanitized value
    */
    std::string SimplePythonAPI::removeCharacters(const std::string & value, const std::string & cs, bool eol_to_space)
    {
      if (value.empty())
        return value;

      std::string retstring;
      std::string::const_iterator sIter = value.begin();
      std::string::const_iterator sEnd = value.end();

      // No characeters specified, only keep alpha-numeric
      if (cs.empty())
      {
        for (; sIter != sEnd; ++sIter)
        {
          int letter = static_cast<int> (*sIter);
          if ((letter >= 48 && letter <= 57) || (letter >= 97 && letter <= 122) || (letter >= 65 && letter <= 90))
          {
            retstring.push_back(*sIter);
          }
        }
      }
      else
      {
        for (; sIter != sEnd; ++sIter)
        {
          const char letter = (*sIter);
          // If the letter is NOT one to remove
          if (cs.find_first_of(letter) == std::string::npos)
          {
            //This is because I use single-quotes to delimit my strings in the module and if any in strings
            //that I try to write contain these, it will confuse Python so I'll convert them
            if (letter == '\'')
            {
              retstring.push_back('\"');
            }
            // Keep the character
            else
            {
              retstring.push_back(letter);
            }
          }
          else
          {
            if (eol_to_space && letter == '\n')
            {
              retstring.push_back(' ');
            }
          }
        }
      }
      return retstring;
    }

    /**
    * Split the string on the given delimiter
    * @param str The string to split
    * @param delim The delimiter to use
Nick Draper's avatar
Nick Draper committed
    * @return A vector of the sections of the string
    */
    std::vector<std::string> SimplePythonAPI::split(const std::string & str,  const std::string & delim)
    {
      std::vector<std::string> splitlist;
      std::string::size_type idx = str.find(delim);
      if( idx == std::string::npos ) return splitlist;

      splitlist.push_back( str.substr(0, idx) );
      std::string::size_type offset = delim.size();
      std::string::size_type start(idx + offset);
      std::string::size_type end(str.size());
      while( true )
      {
        idx = str.find(delim, start);
        if( idx == std::string::npos ) 
        {
          splitlist.push_back( str.substr(start, end - start) );
          break;
        }
        splitlist.push_back( str.substr(start, idx - start) );
        start = idx + offset;
      }

      return splitlist;
    }

  } //namespace PythonAPI

} //namespace Mantid