/*
* Python wrapper for the Perforce ClientApi object.
*
* Copyright 1999, Mike Meyer. All rights reserved
* See license at the end for redistribution permission.
*
* Build instructions: Put this file in the Modules subdirectory of your
* Python source tree. Add a line that looks like:
*
* P4Client P4Clientmodule.cc -I<PERFORCE> -L<PERFORCE> -lclient -lrpc -lsupp
*
* to the Setup file in that directory. <PERFORCE> is the path to the
* directory the where you put the contents of the p4api.tar file. You may
* need to add the C++ standard library(ies) at the end of the line as well.
* For gcc 2.7.2, that means adding -lgcc to the end of that line.
*
* License:
* This file and any derivatives or translations of it may be freely
* copied and redistributed so long as:
* 1) This license and copyright notice are not changed.
* 2) Any fixes or enhancements are reported back to either the
* author (mwm@phone.net).
* and any of:
* a) The source is redistributed with it.
* b) The source is compiled unmodified, and instructions for finding
* the original source are included.
* c) The source is made available by some other means.
*/
#include "clientapi.h"
#include "Python.h"
/*
* The modules variable:
* P4Error, the exception that gets raised if there's an error from the
* perforce error.
*/
static PyObject *P4Error;
/*
* The ClientApi object expects an object to make callbacks to. The
* PythonClientUser object maps those to calls to a Python object that
* was handed to the P4Client create function - the user PyObject above.
*/
class PythonClientUser : public ClientUser {
public:
void InputData( StrBuf *strbuf, Error *e ) {
PyObject *res = PyObject_CallMethod(Py_Client, "InputData", NULL);
if (!res) return; /* Error of some kind - pass it on? */
if (PyString_Check(res))
strbuf->Set(PyString_AS_STRING(res));
else
PyErr_SetString(PyExc_TypeError, "InputData must return a string");
}
void OutputError(char *errBuf) {
PyObject_CallMethod(Py_Client, "OutputError", "(s)", errBuf);
}
void HandleError(Error *err) {
StrBuf msg;
err->Fmt(&msg);
PyObject_CallMethod(Py_Client, "HandleError", "(si)", msg.Text(),
err->GetSeverity());
}
void OutputInfo(char level, char *data) {
PyObject_CallMethod(Py_Client, "OutputInfo", "(sc)", data, level);
}
void OutputBinary(char *data, int length) {
PyObject_CallMethod(Py_Client, "OutputBinary", "(s#)", data, length);
}
void OutputText(char *data, int length) {
PyObject_CallMethod(Py_Client, "OutputText", "(s#)", data, length);
}
void OutputStat(StrDict *varList) {
int i;
PyObject *dict = PyDict_New();
StrBuf msg;
if (!dict) return;
StrRef key, value;
for (i = 0; varList->GetVar(i, key, value); i += 1)
{
PyDict_SetItemString(dict, key.Text(), PyString_FromString(value.Text()));
}
/* If something broke in the above, skip it... */
if (PyErr_Occurred()) return;
/* Now, do the thing */
PyObject_CallMethod(Py_Client, "OutputStat", "(O)", dict);
Py_DECREF(dict);
}
void Prompt(const StrPtr &msg, StrBuf &buf, int bufsiz, Error *e) {
PyObject *res = PyObject_CallMethod(Py_Client, "Prompt", "(s)",
msg.Text());
if (res && PyString_Check(res)) buf.Set(PyString_AS_STRING(res));
}
void ErrorPause(char *errBuf, Error *e) {
PyObject_CallMethod(Py_Client, "ErrorPause", "(s)", errBuf);
}
void Edit(FileSys *f1, Error *e) {
PyObject_CallMethod(Py_Client, "Edit", "(s)", f1->Name());
}
void Diff(FileSys *f1, FileSys *f2, int doPage, char *diffFlags, Error *e) {
PyObject_CallMethod(Py_Client, "Diff", "(ssis)", f1->Name(), f2->Name(),
doPage, diffFlags);
}
void Merge(FileSys *base, FileSys *leg1, FileSys *leg2, FileSys *result, Error *e)
{
PyObject_CallMethod(Py_Client, "Merge", "(ssss)", base->Name(),
leg1->Name(), leg2->Name(), result->Name());
}
void Help(char *const *help) {
PyObject *list = PyList_New(0);
if (!list) return;
while (*help) {
PyList_Append(list, PyString_FromString(*help));
help += 1;
if (PyErr_Occurred()) {
Py_DECREF(list);
return;
}
}
PyObject_CallMethod(Py_Client, "Help", "(O)", list);
}
PyObject *Py_Client;
};
/*
* The P4Client Object, which is what this is really here to wrap.
*/
/* C container for the object */
typedef struct {
PyObject_HEAD
/* The Perforce object we're wrapping */
ClientApi *client;
/* The object for callbacks */
PythonClientUser *user;
} P4ClientObject;
/*
* Object Client methods:
* Init - establish the connection
* Run - run a P4 command
* Final - close the connection, return error count.
* Dropped - check if the connection is still open.
*/
static PyObject *
init(P4ClientObject *me, PyObject *args) {
Error error;
if (!PyArg_ParseTuple(args,":Init")) return NULL;
me->client->Init(&error);
if (error.Test()) {
StrBuf *buffer = new StrBuf;
error.Fmt(buffer);
PyErr_SetString(P4Error, buffer->Text());
return NULL;
}
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
run(P4ClientObject *me, PyObject *args) {
char *command;
if (!PyArg_ParseTuple(args, "s:Run", &command)) return NULL;
me->client->Run(command);
if (PyErr_Occurred()) return NULL;
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
final(P4ClientObject *me, PyObject *args) {
int i;
Error error;
if (!PyArg_ParseTuple(args, ":Final")) return NULL;
i = me->client->Final(&error);
if (error.Test()) {
StrBuf *buffer = new StrBuf;
error.Fmt(buffer);
PyErr_SetString(P4Error, buffer->Text());
return NULL;
}
return Py_BuildValue("i", i);
}
static PyObject *
dropped(P4ClientObject *me, PyObject *args) {
int i;
if (!PyArg_ParseTuple(args, ":Dropped")) return NULL;
i = me->client->Dropped();
return Py_BuildValue("i", i);
}
static PyObject *
protocol(P4ClientObject *me, PyObject *args) {
char *p, *v;
if (!PyArg_ParseTuple(args, "ss:SetProtocol", &p, &v)) return NULL;
me->client->SetProtocol(p, v);
Py_INCREF(Py_None);
return Py_None;
}
static PyObject *
setargs(P4ClientObject *me, PyObject *args) {
int i;
PyObject *Py_argv;
if (!PyArg_ParseTuple(args, "O:SetArgv", &Py_argv)) return NULL;
if (!PySequence_Check(Py_argv)) {
PyErr_SetString(P4Error, "Argument to SetArgv must be a sequence");
return NULL;
}
int count = PySequence_Length(Py_argv);
char **argv = (char **) malloc(count * sizeof(char *));
for (i = 0; i < count; i += 1) {
PyObject *item = PySequence_GetItem(Py_argv, i);
if (PyString_Check(item)) {
argv[i] = PyString_AS_STRING(item);
} else {
PyErr_SetString(PyExc_TypeError, "P4Client arguments must be strings");
free(argv);
return NULL;
}
}
me->client->SetArgv(count, argv);
Py_INCREF(Py_None);
return Py_None;
}
/*
* Method pointer table for the object methods.
*/
static struct PyMethodDef methods[] = {
{"Init", (PyCFunction) init, METH_VARARGS},
{"Run", (PyCFunction) run, METH_VARARGS},
{"Final", (PyCFunction) final, METH_VARARGS},
{"Dropped", (PyCFunction) dropped, METH_VARARGS},
{"SetProtocol", (PyCFunction) protocol, METH_VARARGS},
{"SetArgv", (PyCFunction) setargs, METH_VARARGS},
{NULL, NULL}
};
/*
* P4 Client attributes:
* Port - host:port to connect to in Init method. (*)
* Cwd - current working directory.
* Client - client name. (*)
* Password - password to use for connection. (*)
* User - user name to run as. (*)
* Os - Os we are running on, R/O.
*
* *) Ok, these have a "Define" attached to them, that set something in the
* registry. What's it do?
*/
static PyObject *
getattr(P4ClientObject *me, char *name) {
if (!strcmp(name, "Port"))
return PyString_FromString(me->client->GetPort().Text());
if (!strcmp(name, "Cwd"))
return PyString_FromString(me->client->GetCwd().Text());
if (!strcmp(name, "Client"))
return PyString_FromString(me->client->GetClient().Text());
if (!strcmp(name, "Password"))
return PyString_FromString(me->client->GetPassword().Text());
if (!strcmp(name, "User"))
return PyString_FromString(me->client->GetUser().Text());
if (!strcmp(name, "Os"))
return PyString_FromString(me->client->GetOs().Text());
/* attributes list */
if (!strcmp(name, "__members__"))
return Py_BuildValue("[s,s,s,s,s,s]",
"Cwd", "Client", "Port", "Password", "User", "Os");
return Py_FindMethod(methods, (PyObject *) me, name);
}
static int
setattr(P4ClientObject *me, char *name, PyObject *v) {
char *value;
if (!PyString_Check(v)) {
PyErr_SetString(PyExc_TypeError,
"P4Client attribute values must be strings");
return -1;
}
value = PyString_AS_STRING(v);
if (!strcmp(name, "Port")) {
me->client->SetPort(value);
} else if (!strcmp(name, "Cwd")) {
me->client->SetCwd(value);
} else if (!strcmp(name, "Client")) {
me->client->SetClient(value);
} else if (!strcmp(name, "Password")) {
me->client->SetPassword(value);
} else if (!strcmp(name, "User")) {
me->client->SetUser(value);
} else if (!strcmp(name, "Os")) {
PyErr_SetString(PyExc_ValueError, "P4Client attribute 'Os' is read-only");
return -1;
} else {
PyErr_SetString(PyExc_NameError, name);
return -1;
}
return 0;
}
/*
* The object destroyer.
*/
static void
dealloc(P4ClientObject *gone) {
Py_XDECREF(gone->user->Py_Client);
delete gone->client;
PyMem_DEL(gone);
}
/* PyObject object for the P4ClientObject */
static PyTypeObject P4ClientType = {
PyObject_HEAD_INIT(&PyType_Type)
0,
"P4Client", /* name */
sizeof(P4ClientObject), /* basicsize */
NULL, /* itemsize */
(destructor) dealloc, /* dealloc */
NULL, /* print */
(getattrfunc) getattr, /* getattr */
(setattrfunc) setattr, /* setattr */
NULL, /* compare */
NULL, /* repr */
NULL, /* number methods */
NULL, /* sequence methods */
NULL /* mapping methods */
};
/*
* The object creator.
*/
static PyObject *
create(P4ClientObject *dummy, PyObject *args, PyObject *dict) {
P4ClientObject *newp4;
PyObject *Py_ui;
PythonClientUser *ui = new PythonClientUser;
static char *keywords[] = {"ClientUser", "Port", "User", "Password",
"Client", "Cwd", NULL};
char *port = NULL,
*cwd = NULL,
*clientname = NULL,
*password = NULL,
*user = NULL;
if (!PyArg_ParseTupleAndKeywords(args, dict, "O|sssss:P4Client", keywords,
&Py_ui, &port, &user, &password,
&clientname, &cwd))
return NULL;
/* Create the object, and fill it out */
Py_XINCREF(Py_ui);
ui->Py_Client = Py_ui;
if (!(newp4 = PyObject_NEW(P4ClientObject, &P4ClientType))) return NULL;
newp4->client = new ClientApi(ui);
newp4->user = ui;
/* As a convenience, do attribute setting for the user from keyword args */
if (port) newp4->client->SetPort(port);
if (user) newp4->client->SetUser(user);
if (password) newp4->client->SetPassword(password);
if (clientname) newp4->client->SetClient(clientname);
if (cwd) newp4->client->SetCwd(cwd);
return (PyObject *) newp4;
}
static struct PyMethodDef functions[] = {
{"P4Client", (PyCFunction) create, METH_VARARGS | METH_KEYWORDS},
{NULL, NULL}
};
/* helper function for initialization function, lifted from syslogmodule.c */
static void
ins(PyObject *d, char *s, long x) {
PyObject *v = PyInt_FromLong(x);
if (v) {
PyDict_SetItemString(d, s, v);
Py_DECREF(v);
}
}
/*
* The functions the outside world can see.
*/
extern "C" void
initP4Client() {
PyObject *m, *d;
m = Py_InitModule4("P4Client", functions, "P4 Client Object",
NULL, PYTHON_API_VERSION);
d = PyModule_GetDict(m);
P4Error = PyErr_NewException("P4Client.error", NULL, NULL);
PyDict_SetItemString(d, "error", P4Error);
/* Now add the error constants to the module */
ins(d, "ERROR_EMPTY", E_EMPTY);
ins(d, "ERROR_INFO", E_INFO);
ins(d, "ERROR_WARN", E_WARN);
ins(d, "ERROR_FAILED", E_FAILED);
ins(d, "ERROR_FATAL", E_FATAL);
}