/*
* Python wrapper for the Perforce ClientApi object.
*
* Copyright (c) 1997-2007, Perforce Software, Inc. All rights reserved.
* Portions Copyright (c) 1999, Mike Meyer. All rights reserved.
* Portions Copyright (c) 2004-2007, Robert Cowham. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTR
* IBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL PERFORCE SOFTWARE, INC. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $Id: //depot/r07.3/p4-python/P4API.cpp#1 $
*
* Build instructions:
* Use Distutils - see accompanying setup.py
*
* python setup.py install
*
*/
#include <Python.h>
#include <structmember.h>
#include "undefdups.h"
#include <clientapi.h>
#include <strtable.h>
#include <spec.h>
#include <ident.h>
#include "P4Result.h"
#include "SpecMgr.h"
#include "PythonClientUser.h"
#include "PythonClientAPI.h"
// #include <alloca.h>
#include <iostream>
#include <cstring>
#include <sstream>
#include <vector>
using namespace std;
static Ident ident = {
IdentMagic "P4Python" "/" ID_OS "/" ID_REL "/" ID_PATCH " (" ID_API " API)",
ID_Y "/" ID_M "/" ID_D
};
/* C container for P4Adapter */
typedef struct {
PyObject_HEAD
PythonClientAPI *clientAPI; /* The Perforce object we're wrapping */
} P4Adapter;
static PyObject * P4Error;
/*
* P4Adapter destructor
*/
static void
P4Adapter_dealloc(P4Adapter *self)
{
delete self->clientAPI;
self->ob_type->tp_free((PyObject*)self);
}
/*
* P4Adapter constructor.
*/
static PyObject *
P4Adapter_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
P4Adapter *self = (P4Adapter *) type->tp_alloc(type, 0);
if (self != NULL) {
self->clientAPI = new PythonClientAPI(P4Error);
}
return (PyObject *) self;
}
/*
* P4Adapter initializer.
*/
static int
P4Adapter_init(P4Adapter *self, PyObject *args, PyObject *kwds)
{
if (kwds != NULL && PyDict_Check(kwds)) {
Py_ssize_t pos = 0;
PyObject *key, *value;
while (PyDict_Next(kwds, &pos, &key, &value)) {
const char * name = PyString_AS_STRING(key);
if (PyInt_Check(value)) {
PythonClientAPI::intsetter isetter = self->clientAPI->GetIntSetter(name);
if (isetter) {
return (self->clientAPI->*isetter)(PyInt_AS_LONG(value));
}
else {
ostringstream os;
os << "No integer keyword with name " << name;
PyErr_SetString(PyExc_AttributeError, os.str().c_str());
return -1;
}
}
else
if (PyString_Check(value)) {
PythonClientAPI::strsetter ssetter = self->clientAPI->GetStrSetter(name);
if (ssetter) {
return (self->clientAPI->*ssetter)(PyString_AS_STRING(value));
}
else {
ostringstream os;
os << "No string keyword with name " << name;
PyErr_SetString(PyExc_AttributeError, os.str().c_str());
return -1;
}
}
}
}
return 0;
}
static PyObject *
P4Adapter_repr(P4Adapter *self)
{
return PyString_FromFormat("P4Adapter");
}
// **************************************
// P4Adapter directly implemented methods
// **************************************
static PyObject * P4Adapter_connect(P4Adapter * self)
{
return self->clientAPI->Connect();
}
static PyObject * P4Adapter_connected(P4Adapter * self)
{
return self->clientAPI->Connected();
}
static PyObject * P4Adapter_disconnect(P4Adapter * self)
{
return self->clientAPI->Disconnect();
}
static PyObject * P4Adapter_run(P4Adapter * self, PyObject * args)
{
PyObject * cmd = PyTuple_GetItem(args, 0);
if (cmd == NULL) {
return NULL;
}
// assume the args are flattened already
vector<const char *> argv;
for (Py_ssize_t i = 1; i < PyTuple_Size(args); ++i) {
argv.push_back(PyString_AS_STRING(PyObject_Str(PyTuple_GET_ITEM(args, i))));
}
// this is a bit of a hack: it assumes the storage layout of the vector is continuous
// the other hack is that the API expects (char * const *), but this cannot be stored
// a std::vector<>, because it cannot exchange pointers
return self->clientAPI->Run(PyString_AsString(cmd), argv.size(), (char * const *) &argv[0]);
}
static PyObject * P4API_identify(PyObject * self)
{
StrBuf s;
ident.GetMessage( &s );
return PyString_FromString( s.Text() );
}
static PyObject * P4Adapter_formatSpec(P4Adapter * self, PyObject * args)
{
const char * type;
PyObject * dict;
if ( PyArg_ParseTuple(args, "sO", &type, &dict) ) {
if ( PyDict_Check(dict) ) {
return self->clientAPI->FormatSpec(type, dict);
}
else {
PyErr_SetString(PyExc_TypeError, "Second argument needs to be a dictionary");
return NULL;
}
}
return NULL;
}
static PyObject * P4Adapter_parseSpec(P4Adapter * self, PyObject * args)
{
const char * type;
const char * form;
if ( PyArg_ParseTuple(args, "ss", &type, &form) ) {
return self->clientAPI->ParseSpec(type, form);
}
return NULL;
}
static PyMethodDef P4Adapter_methods[] = {
{"connect", (PyCFunction)P4Adapter_connect, METH_NOARGS,
"Connects to the Perforce Server"},
{"connected", (PyCFunction)P4Adapter_connected, METH_NOARGS,
"Checks whether we are (still) connected"},
{"disconnect", (PyCFunction)P4Adapter_disconnect, METH_NOARGS,
"Closes the connection to the Perforce Server"},
{"run", (PyCFunction)P4Adapter_run, METH_VARARGS,
"Runs a command"},
{"format_spec", (PyCFunction)P4Adapter_formatSpec, METH_VARARGS,
"Converts a dictionary-based form into a string"},
{"parse_spec", (PyCFunction)P4Adapter_parseSpec, METH_VARARGS,
"Converts a string form into a dictionary"},
{NULL} /* Sentinel */
};
static PyMemberDef P4Adapter_members[] = {
// {"first", T_OBJECT_EX, offsetof(Noddy, first), 0,
// "first name"},
{NULL} /* Sentinel */
};
static PyObject * P4Adapter_getattro(P4Adapter *self, PyObject * nameObject)
{
const char * name = PyString_AsString(nameObject);
PythonClientAPI::intgetter igetter = self->clientAPI->GetIntGetter(name);
if (igetter) {
return PyInt_FromLong((self->clientAPI->*igetter)());
}
PythonClientAPI::strgetter sgetter = self->clientAPI->GetStrGetter(name);
if (sgetter) {
return PyString_FromString((self->clientAPI->*sgetter)());
}
PythonClientAPI::objgetter ogetter = self->clientAPI->GetObjGetter(name);
if (ogetter) {
return (self->clientAPI->*ogetter)();
}
return PyObject_GenericGetAttr((PyObject *) self, nameObject);
}
static int P4Adapter_setattro(P4Adapter *self, PyObject * nameObject, PyObject * value)
{
const char * name = PyString_AsString(nameObject);
// Special case first:
// If there is a specific ObjectSetter for this name available use this one
PythonClientAPI::objsetter osetter = self->clientAPI->GetObjSetter(name);
if (osetter) {
return (self->clientAPI->*osetter)(value);
}
else
if (PyInt_Check(value)) {
PythonClientAPI::intsetter isetter = self->clientAPI->GetIntSetter(name);
if (isetter) {
return (self->clientAPI->*isetter)(PyInt_AS_LONG(value));
}
else {
ostringstream os;
os << "No integer attribute with name " << name;
PyErr_SetString(PyExc_AttributeError, os.str().c_str());
return -1;
}
}
else
if (PyString_Check(value)) {
PythonClientAPI::strsetter ssetter = self->clientAPI->GetStrSetter(name);
if (ssetter) {
return (self->clientAPI->*ssetter)(PyString_AS_STRING(value));
}
else {
ostringstream os;
os << "No string attribute with name " << name;
PyErr_SetString(PyExc_AttributeError, os.str().c_str());
return -1;
}
}
// can only set int and string or certain object values -> bail out with exception
ostringstream os;
os << "Cannot set attribute : " << name << " with value " << PyString_AS_STRING(PyObject_Str(value));
PyErr_SetString(PyExc_AttributeError, os.str().c_str());
return -1;
}
/* PyObject object for the P4Adapter */
static PyTypeObject P4AdapterType = {
PyObject_HEAD_INIT(&PyType_Type)
0, /* ob_size */
"P4API.P4Adapter", /* name */
sizeof(P4Adapter), /* basicsize */
0, /* itemsize */
(destructor) P4Adapter_dealloc, /* dealloc */
0, /* print */
0, /* getattr */
0, /* setattr */
0, /* compare */
(reprfunc) P4Adapter_repr, /* repr */
0, /* number methods */
0, /* sequence methods */
0, /* mapping methods */
0, /* tp_hash */
0, /* tp_call*/
0, /* tp_str*/
(getattrofunc) P4Adapter_getattro, /* tp_getattro*/
(setattrofunc) P4Adapter_setattro, /* tp_setattro*/
0, /* tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags*/
"P4Adapter - base class for P4", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
P4Adapter_methods, /* tp_methods */
P4Adapter_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)P4Adapter_init, /* tp_init */
0, /* tp_alloc */
P4Adapter_new, /* tp_new */
};
static struct PyMethodDef P4API_methods[] = {
{"identify", (PyCFunction)P4API_identify, METH_NOARGS,
"Identify module version"},
{NULL} /* Sentinel */
};
PyMODINIT_FUNC
initP4API(void)
{
if (PyType_Ready(&P4AdapterType) < 0)
return;
PyObject * module = Py_InitModule3("P4API", P4API_methods, "P4 Python Adapter Module");
if (module == NULL) return;
Py_INCREF(&P4AdapterType);
PyModule_AddObject(module, "P4Adapter", (PyObject*) &P4AdapterType);
// Get a reference to P4.P4Error
// Not declared as an exception here because of module inconsistencies
// Better for the user to have an exception of type P4.P4Error than
// P4API.P4Error
PyObject * p4Module = PyImport_ImportModule("P4");
PyObject * p4Dict = PyModule_GetDict(p4Module);
P4Error = PyDict_GetItemString(p4Dict, "P4Exception");
if (P4Error)
Py_INCREF(P4Error);
else {
cout << "Could not find P4.P4Exception. Trouble awaits us" << endl;
}
}