Commit 7fb7b2b6 authored by Steven Murray's avatar Steven Murray
Browse files

Updated the interface of the castor::tape::python utilities package

parent ee4a89c4
......@@ -22,6 +22,6 @@
# @author Sebastien Ponce, sebastien.ponce@cern.ch
#
SUBDIRS = aggregator format tapegateway tpcp utils
SUBDIRS = aggregator format python tapegateway tpcp utils
/******************************************************************************
* castor/tape/python/PythonUtils.hpp
* castor/tape/python/Constants.hpp
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
......@@ -18,43 +18,25 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
*
* @author Giulia Taurelli
*****************************************************************************/
#ifndef CASTOR_TAPE_PYTHON_PYTHON_UTILS
#define CASTOR_TAPE_PYTHON_PYTHON_UTILS
#include <Python.h>
#include "castor/exception/Exception.hpp"
namespace castor {
namespace tape {
namespace python {
const char *const CASTOR_POLICIES_DIRECTORY = "/etc/castor/policies";
void startThreadSupportInit();
PyObject* loadPythonPolicy(const char *const policyModuleName)
throw(castor::exception::Exception);
void endThreadSupportInit();
PyObject* getPythonFunction(PyObject* pyDict,const char *const functionName)throw(castor::exception::Exception);
#ifndef CASTOR_TAPE_PYTHON_CONSTANTS_HPP
#define CASTOR_TAPE_PYTHON_CONSTANTS_HPP 1
void appendDirectoryToPYTHONPATH(const char *const directory)throw();
PyObject* importPythonModule(const char *const moduleName)
throw(castor::exception::Exception);
void checkPolicyModuleFileExists(const char *moduleName)
throw(castor::exception::Exception);
namespace castor {
namespace tape {
namespace python {
/**
* The directory that contains the CASTOR policies implemented as Python
* modules.
*/
const char *const CASTOR_POLICIES_DIRECTORY = "/etc/castor/policies";
} // namespace python
} // namespace tape
} // namespace castor
#endif // CASTOR_TAPE_PYTHON_PYTHON_UTILS
#endif // CASTOR_TAPE_PYTHON_CONSTANTS_HPP
#
# castor/tape/pythonUtils/Imakefile
# castor/tape/python/Imakefile
#
# This file is part of the Castor project.
# See http://castor.web.cern.ch/castor
......@@ -18,7 +18,7 @@
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
#
# @author Nicola.Bessone@cern.ch Steven.Murray@cern.ch
# @author Giulia Taurelli
#
INCLUDES = -I../../../h -I../../..
......@@ -28,12 +28,12 @@ LD = g++
LIBS = $(PYTHONLIBS)
TAPEUTILSLIB_SRCS = \
Utils.cpp \
TAPEPYTHONLIB_SRCS = \
python.cpp \
ScopedPythonLock.cpp \
SmartPyObjectPtr.cpp
TAPEUTILSLIB_OBJS = $(TAPEUTILSLIB_SRCS:.cpp=.Osuf)
TAPEPYTHONLIB_OBJS = $(TAPEPYTHONLIB_SRCS:.cpp=.Osuf)
DependsOnLibrary(..,castorclient)
SharedLibraryTarget(castorpythonutils,$(PYTHONUTILSLIB_OBJS),$(DEPLIB),$(LIBS))
SharedLibraryTarget(castorpython,$(TAPEPYTHONLIB_OBJS),$(DEPLIB),$(LIBS))
/******************************************************************************
* castor/tape/python/utils.cpp
* castor/tape/python/python.cpp
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
......@@ -18,26 +18,81 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
*
* @author Giulia Taurelli
* @author Giulia Taurelli, Nicola Bessone and Steven Murray
*****************************************************************************/
#include "castor/tape/python/ScopedLock.hpp"
// Include python.hpp before any standard headers because python.hpp includes
// Python.h which may define some pre-processor definitions which affect the
// standard headers
#include "castor/tape/python/python.hpp"
#include "castor/tape/python/Constants.hpp"
#include "castor/tape/python/SmartPyObjectPtr.hpp"
#include "castor/tape/python/utils.hpp"
#include "castor/tape/utils/utils.hpp"
#include "h/serrno.h"
#include <errno.h>
#include <unistd.h>
//---------------------------------------------------------------------------
// initPython
//---------------------------------------------------------------------------
void castor::tape::python::initPython() throw(castor::exception::Exception) {
// Append the CASTOR policies directory to the end of the PYTHONPATH
// environment variable so the PyImport_ImportModule() function can find the
// stream and migration policy modules
appendDirectoryToPYTHONPATH(CASTOR_POLICIES_DIRECTORY);
// Initialize Python throwing an exception if a Python error occurs
Py_Initialize();
if(PyErr_Occurred()) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
"Py_Initialize() call failed"
": A Python error occured";
throw ex;
}
// Initialize thread support
//
// Please note that PyEval_InitThreads() takes a lock on the global Python
// interpreter
PyEval_InitThreads();
if(PyErr_Occurred()) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
"PyEval_InitThreads() call failed"
": A Python error occured";
throw ex;
}
// Release the lock on the Python global interpreter that was taken by the
// preceding PyEval_InitThreads() call
PyEval_ReleaseLock();
if(PyErr_Occurred()) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
"PyEval_ReleaseLock() call failed"
": A Python error occured";
throw ex;
}
}
//------------------------------------------------------------------------------
// appendDirectoryToPYTHONPATH
//------------------------------------------------------------------------------
void castor::tape::python::utils::appendDirectoryToPYTHONPATH(const char *const directory)
throw() {
void castor::tape::python::appendDirectoryToPYTHONPATH(
const char *const directory) throw() {
// Get the current value of PYTHONPATH (there may not be one)
const char *const currentPath = getenv("PYTHONPATH");
......@@ -57,148 +112,135 @@ void castor::tape::python::utils::appendDirectoryToPYTHONPATH(const char *const
}
//------------------------------------------------------------------------------
// importPythonModule
//------------------------------------------------------------------------------
PyObject *castor::tape::python::utils::importPythonModule(const char *const moduleName)
throw(castor::exception::Exception) {
PyObject *const module = PyImport_ImportModule((char *)moduleName);
if(module == NULL) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
"PyImport_ImportModule() call failed"
": moduleName=" << moduleName;
throw(ex);
//---------------------------------------------------------------------------
// importPolicyPythonModule
//---------------------------------------------------------------------------
PyObject * castor::tape::python::importPolicyPythonModule(
const char *const moduleName) throw(castor::exception::Exception) {
// Check the module file exists in the CASTOR_POLICIES_DIRECTORY as it is
// difficult to obtain errors from the embedded Python interpreter
{
std::string fullPathname(CASTOR_POLICIES_DIRECTORY);
fullPathname += "/";
fullPathname += moduleName;
fullPathname += ".py";
struct stat buf;
try {
utils::statFile(fullPathname.c_str(), buf);
} catch(castor::exception::Exception &ex) {
castor::exception::Exception ex1(ex.code());
ex.getMessage() <<
"Failed to get information about the CASTOR-policy Python-module"
": " << ex.getMessage().str();
}
// Throw an exception if the module file is not a regular file
if(!S_ISREG(buf.st_mode)) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
fullPathname << " is not a regular file";
throw(ex);
}
}
return module;
// Load in the CASTOR-policy Python-module and return its associated Python
// dictionary object
return importPythonModule(moduleName);
}
//------------------------------------------------------------------------------
// checkPolicyModuleFileExists
// importPythonModule
//------------------------------------------------------------------------------
void castor::tape::python::utils::checkPolicyModuleFileExists(const char *moduleName)
PyObject *castor::tape::python::importPythonModule(const char *const moduleName)
throw(castor::exception::Exception) {
std::string fullPathname(CASTOR_POLICIES_DIRECTORY);
fullPathname += "/";
fullPathname += moduleName;
fullPathname += ".py";
struct stat buf;
const int rc = stat(fullPathname.c_str(), &buf);
const int savedErrno = errno;
// Throw an exception if the stat() call failed
if(rc != 0) {
castor::exception::Exception ex(savedErrno);
if(moduleName == NULL) {
castor::exception::InvalidArgument ex;
ex.getMessage() <<
"Failed to stat() policy module-file"
": filename=" << fullPathname <<
": " << sstrerror(savedErrno);
"moduleName parameter is NULL";
throw(ex);
}
// Throw an exception if the file is not a regular file
if(!S_ISREG(buf.st_mode)) {
castor::exception::Exception ex(savedErrno);
castor::tape::python::SmartPyObjectPtr
module(PyImport_ImportModule((char *)moduleName));
if(module.get() == NULL) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
fullPathname << " is not a regular file";
"PyImport_ImportModule() call failed"
": moduleName=" << moduleName;
throw(ex);
}
}
//---------------------------------------------------------------------------
// Inizialize Python
//---------------------------------------------------------------------------
void castor::tape::python::utils::startThreadSupportInit(){
// Append the CASTOR policies directory to the end of the PYTHONPATH
// environment variable so the PyImport_ImportModule() function can find the
// stream and migration policy modules
appendDirectoryToPYTHONPATH(CASTOR_POLICIES_DIRECTORY);
// Initialize Python
Py_Initialize();
// Initialize thread support
//
// Please note that PyEval_InitThreads() takes a lock on the global Python
// interpreter
PyEval_InitThreads();
// Release the lock on the Python global interpreter that was taken by the
// preceding PyEval_InitThreads() call
PyEval_ReleaseLock();
PyObject *const pyDict = PyModule_GetDict(module.get());
module.release();
return(pyDict);
}
//---------------------------------------------------------------------------
// loadPythonCastorPolicy
//---------------------------------------------------------------------------
PyObject * castor::tape::python::utils::loadPythonCastorPolicy(const char *const policyModuleName) throw(castor::exception::Exception){
// Load in the policy Python module and get a handle on its associated Python dictionary object
castor::tape::python::utils::SmartPyObjectPtr policyModule;
PyObject *policyDict = NULL;
if(policyModuleName != NULL) {
checkPolicyModuleFileExists(policyModuleName);
policyModule.reset(importPythonModule(policyModuleName));
policyDict = PyModule_GetDict(policyModule.get());
}
return policyDict;
}
//---------------------------------------------------------------------------
// getPythonFunction
//---------------------------------------------------------------------------
PyObject * castor::tape::python::utils::getPythonFunction(PyObject* pyDict,const char *const functionName)
PyObject * castor::tape::python::getPythonFunction(PyObject *const pyDict,
const char *const functionName)
throw(castor::exception::Exception){
if (pyDict==NULL || functionName == NULL)
return NULL;
if(pyDict == NULL) {
castor::exception::InvalidArgument ex;
// Get a pointer to the migration-policy Python-function
ex.getMessage() <<
"pyDict parameter is NULL";
PyObject *pyFunc = PyDict_GetItemString(pyDict,functionName);
throw(ex);
}
// Throw an exception if the migration-policy Python-function does not exist
if(pyFunc == NULL) {
if(functionName == NULL) {
castor::exception::InvalidArgument ex;
ex.getMessage() <<
"Python function does not exist"
": functionName=" << functionName;
"functionName parameter is NULL";
throw(ex);
}
// Get a pointer to the Python-function object
PyObject *pyFunc = PyDict_GetItemString(pyDict, functionName);
// Throw an exception if the Python-function object was not found due to a
// Python error occurring as opposed to function simply not being in the
// dictionary
if(pyFunc == NULL && PyErr_Occurred()) {
castor::exception::Exception ex(ECANCELED);
ex.getMessage() <<
"PyDict_GetItemString() call failed"
": functionName=" << functionName <<
": A Python error occured";
throw ex;
}
// Throw an exception if the migration-policy Python-function is not callable
if (!PyCallable_Check(pyFunc)) {
// Throw an exception if a non-callable object was found with the same name
// as the function
if(pyFunc != NULL && !PyCallable_Check(pyFunc)) {
castor::exception::InvalidArgument ex;
ex.getMessage() <<
"Python function cannot be called"
"Found non-callable Python object"
": functionName=" << functionName;
throw ex;
......@@ -206,4 +248,3 @@ PyObject * castor::tape::python::utils::getPythonFunction(PyObject* pyDict,const
return pyFunc;
}
/******************************************************************************
* castor/tape/python/python.hpp
*
* This file is part of the Castor project.
* See http://castor.web.cern.ch/castor
*
* Copyright (C) 2003 CERN
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
*
*
* @author Giulia Taurelli
*****************************************************************************/
#ifndef CASTOR_TAPE_PYTHON_PYTHON_HPP
#define CASTOR_TAPE_PYTHON_PYTHON_HPP
// Include Python.h before any standard headers because Python.h may define
// some pre-processor definitions which affect the standard headers
#include <Python.h>
#include "castor/exception/Exception.hpp"
namespace castor {
namespace tape {
namespace python {
/**
* Initializes the embedded Python interpreter for multi-threaded use and
* append the CASTOR_POLICIES_DIRECTORY to the PYTHONPATH environment
* variable.
*
* This function should be once and only once for the entire duration of the
* calling program.
*
* When this function returns there will be no lock taken on the global Python
* interpeter. All threads, including the main thread must therefore take a
* lock using a ScopedPythonLock object before acsessing the API of the
* embedded Python interpreter.
*/
void initPython() throw(castor::exception::Exception);
/**
* Appends the specified directory to the value of the PYTHONPATH environment
* variable.
*
* @param directory The full pathname of the directory to be appended to the
* value of the PYTHONPATH environment variable.
*/
void appendDirectoryToPYTHONPATH(const char *const directory) throw();
/**
* Imports a CASTOR-policy implemented as a Python module from the
* Python-module search path which includes the
* castor::tape::python::CASTOR_POLICIES_DIRECTORY directory.
*
* Please note that initPython() must be called before this function is called.
*
* @param moduleName The name of the CASTOR-policy Python-module.
* @return The dictionary object of the imported library. In the
* terms of the embedded Python interpreter the returned
* library is a "borrowed reference" return value. This
* means the caller does not need to call Py_XDECREF on the
* dictionary when it is no longer required.
*/
PyObject* importPolicyPythonModule(const char *const moduleName)
throw(castor::exception::Exception);
/**
* Imports the specified module into the embedded Python interpreter and
* returns its associated Python dictionary object.
*
* Please note that initPython() must be called before this function is called.
*
* @param moduleName The name of the Python module to be imported.
* @return The dictionary object of the imported library. In the
* terms of the embedded Python interpreter the returned
* library is a "borrowed reference" return value. This
* means the caller does not need to call Py_XDECREF on the
* dictionary when it is no longer required.
*/
PyObject* importPythonModule(const char *const moduleName)
throw(castor::exception::Exception);
/**
* Get the Python function object for the specified function within the
* specified Python dictionary.
*
* Please note that initPython() must be called before this function is called.
*
* @param pyDict The Python dictionary in which the specified function is
* to be found.
* @param functionName The name of the Python function.
* @return The Python function object representing the specified
* function or NULL if the named function cannot be found
* in the specified dictionary.
*/
PyObject* getPythonFunction(PyObject *const pyDict,
const char *const functionName) throw(castor::exception::Exception);
} // namespace python
} // namespace tape
} // namespace castor
#endif // CASTOR_TAPE_PYTHON_PYTHON_HPP
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment