/* * 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() ; StrPtr *s ; StrBuf msg ; static char *const vars[] = { "tag", "clientFile", "depotFile", "headAction", "headChange" , "headRev", "headType", "headTime", "haveRev", "action", "change" , "unresolved", "otherOpen", "otherLock", "ourLock", "client", "user", "time", "status", "desc", "dir", "path", "unmap" , 0 } ; if (!dict) return ; /* Know variables, 98.2 */ for (i = 0; vars[i]; i += 1) if (s = varList->GetVar(StrRef(vars[i]))) PyDict_SetItemString(dict, vars[i], PyString_FromString(s->Text())) ; /* otherOpen variables (???) */ for (i = 0; ; i += 1) { msg.Clear() ; msg << "otherOpen" << i ; if (!(s = varList->GetVar(msg))) break ; PyDict_SetItemString(dict, msg.Text(), PyString_FromString(s->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()) ; char *out ; 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 ClientApi *client ; /* The Perforce object we're wrapping */ PythonClientUser *user ; /* The object for callbacks */ } 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) ; }
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 153 | Mike Meyer | Expanded on the build instructions to clarify a few points. | ||
#2 | 135 | Mike Meyer |
Changed the P4Clientmodule::OutputInfo to swap the argument order. This allows the same routine to method OutputInfo and OutputText if the level argument is made optional. |
||
#1 | 129 | Mike Meyer | Initial public version. |