From cc84e466f63906d32ec1bf4a4fcae6a7bce9a4c8 Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Sun, 14 Dec 2008 18:47:51 +0100 Subject: [PATCH] Add python 3 support This big patch does a few things (which are hard to split): - uniformize all function w.r.t. the error exit path (all functions now use only one exit point) - move from 's' to 'et' format in ParseArgs so that we can accept both binary and text, in both 2.x and 3.0 - update the unittest for py3k --- test/test_xattr.py | 23 +++- xattr.c | 278 +++++++++++++++++++++++++++++++-------------- 2 files changed, 215 insertions(+), 86 deletions(-) diff --git a/test/test_xattr.py b/test/test_xattr.py index 126d2c1..cd25594 100644 --- a/test/test_xattr.py +++ b/test/test_xattr.py @@ -1,6 +1,7 @@ # # +import sys import unittest import tempfile import os @@ -9,15 +10,25 @@ import errno import xattr from xattr import NS_USER, XATTR_CREATE, XATTR_REPLACE +if sys.hexversion >= 0x03000000: + PY3K = True +else: + PY3K = False + TEST_DIR = os.environ.get("TESTDIR", ".") class xattrTest(unittest.TestCase): USER_NN = "test" - USER_ATTR = "%s.%s" % (NS_USER, USER_NN) + USER_ATTR = NS_USER.decode() + "." + USER_NN USER_VAL = "abc" MANYOPS_COUNT = 131072 + if PY3K: + USER_NN = USER_NN.encode() + USER_VAL = USER_VAL.encode() + USER_ATTR = USER_ATTR.encode() + def setUp(self): """set up function""" self.rmfiles = [] @@ -67,7 +78,8 @@ class xattrTest(unittest.TestCase): XATTR_REPLACE) try: xattr.setxattr(item, self.USER_ATTR, self.USER_VAL, 0, symlink) - except IOError, err: + except IOError: + err = sys.exc_info()[1] if err.errno == errno.EPERM and symlink: # symlinks may fail, in which case we abort the rest # of the test for this case @@ -105,7 +117,8 @@ class xattrTest(unittest.TestCase): else: xattr.set(item, self.USER_ATTR, self.USER_VAL, nofollow=symlink) - except IOError, err: + except IOError: + err = sys.exc_info()[1] if err.errno == errno.EPERM and symlink: # symlinks may fail, in which case we abort the rest # of the test for this case @@ -297,6 +310,8 @@ class xattrTest(unittest.TestCase): fh, fname = self._getfile() os.close(fh) BINVAL = "abc" + '\0' + "def" + if PY3K: + BINVAL = BINVAL.encode() xattr.setxattr(fname, self.USER_ATTR, BINVAL) self.failUnlessEqual(xattr.listxattr(fname), [self.USER_ATTR]) self.failUnlessEqual(xattr.getxattr(fname, self.USER_ATTR), BINVAL) @@ -308,6 +323,8 @@ class xattrTest(unittest.TestCase): fh, fname = self._getfile() os.close(fh) BINVAL = "abc" + '\0' + "def" + if PY3K: + BINVAL = BINVAL.encode() xattr.set(fname, self.USER_ATTR, BINVAL) self.failUnlessEqual(xattr.list(fname), [self.USER_ATTR]) self.failUnlessEqual(xattr.list(fname, namespace=NS_USER), diff --git a/xattr.c b/xattr.c index 041313c..d82ca33 100644 --- a/xattr.c +++ b/xattr.c @@ -66,9 +66,18 @@ static void free_tgt(target_t *tgt) { /** Converts from a string, file or int argument to what we need. */ static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) { int fd; - if(PyString_Check(myobj)) { + tgt->tmp = NULL; + if(PyBytes_Check(myobj)) { tgt->type = nofollow ? T_LINK : T_PATH; - tgt->name = PyString_AS_STRING(myobj); + tgt->name = PyBytes_AS_STRING(myobj); + } else if(PyUnicode_Check(myobj)) { + tgt->type = nofollow ? T_LINK : T_PATH; + tgt->tmp = \ + PyUnicode_AsEncodedString(myobj, + Py_FileSystemDefaultEncoding, "strict"); + if(tgt->tmp == NULL) + return 0; + tgt->name = PyBytes_AS_STRING(tgt->tmp); } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) { tgt->type = T_FD; tgt->fd = fd; @@ -192,33 +201,42 @@ pygetxattr(PyObject *self, PyObject *args) PyObject *res; /* Parse the arguments */ - if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow)) - return NULL; - if(!convertObj(myarg, &tgt, nofollow)) + if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow)) return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } /* Find out the needed size of the buffer */ if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freetgt; } /* Try to allocate the memory, using Python's allocator */ if((buf = PyMem_Malloc(nalloc)) == NULL) { PyErr_NoMemory(); - return NULL; + res = NULL; + goto freetgt; } /* Now retrieve the attribute value */ if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) { - PyMem_Free(buf); - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freebuf; } /* Create the string which will hold the result */ - res = PyString_FromStringAndSize(buf, nret); + res = PyBytes_FromStringAndSize(buf, nret); + freebuf: /* Free the buffer, now it is no longer needed */ PyMem_Free(buf); + freetgt: + free_tgt(&tgt); + freearg: + PyMem_Free(attrname); /* Return the result */ return res; @@ -268,39 +286,47 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds) static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist, - &myarg, &attrname, &nofollow, &ns)) - return NULL; - if(!convertObj(myarg, &tgt, nofollow)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iz", kwlist, + &myarg, NULL, &attrname, &nofollow, &ns)) return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } fullname = merge_ns(ns, attrname, &namebuf); /* Find out the needed size of the buffer */ if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freetgt; } /* Try to allocate the memory, using Python's allocator */ if((buf = PyMem_Malloc(nalloc)) == NULL) { - PyMem_Free(namebuf); PyErr_NoMemory(); - return NULL; + res = NULL; + goto freenamebuf; } /* Now retrieve the attribute value */ if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) { - PyMem_Free(buf); - PyMem_Free(namebuf); - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freebuf; } /* Create the string which will hold the result */ - res = PyString_FromStringAndSize(buf, nret); + res = PyBytes_FromStringAndSize(buf, nret); /* Free the buffers, they are no longer needed */ - PyMem_Free(namebuf); + freebuf: PyMem_Free(buf); + freenamebuf: + PyMem_Free(namebuf); + freetgt: + free_tgt(&tgt); + freearg: + PyMem_Free(attrname); /* Return the result */ return res; @@ -370,20 +396,22 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) nalloc = _list_obj(&tgt, NULL, 0); if(nalloc == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freetgt; } /* Try to allocate the memory, using Python's allocator */ if((buf_list = PyMem_Malloc(nalloc)) == NULL) { PyErr_NoMemory(); - return NULL; + res = NULL; + goto freetgt; } /* Now retrieve the list of attributes */ nlist = _list_obj(&tgt, buf_list, nalloc); if(nlist == -1) { - PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); goto free_buf_list; } @@ -392,7 +420,9 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) nalloc = ESTIMATE_ATTR_SIZE; if((buf_val = PyMem_Malloc(nalloc)) == NULL) { PyErr_NoMemory(); - goto free_list; + Py_DECREF(mylist); + res = NULL; + goto free_buf_list; } /* Create and insert the attributes as strings in the list */ @@ -411,8 +441,11 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) if(nval == -1) { if(errno == ERANGE) { nval = _get_obj(&tgt, s, NULL, 0); - if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL) - goto free_list; + if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL) { + res = NULL; + Py_DECREF(mylist); + goto free_buf_list; + } nalloc = nval; continue; } else if(errno == ENODATA || errno == ENOATTR) { @@ -421,32 +454,37 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) missing = 1; break; } - goto exit_errno; + res = PyErr_SetFromErrno(PyExc_IOError); + goto freebufval; } break; } if(missing) continue; +#ifdef IS_PY3K + my_tuple = Py_BuildValue("yy#", name, buf_val, nval); +#else my_tuple = Py_BuildValue("ss#", name, buf_val, nval); +#endif PyList_Append(mylist, my_tuple); Py_DECREF(my_tuple); } - /* Free the buffers, now they are no longer needed */ - PyMem_Free(buf_val); - PyMem_Free(buf_list); + /* Successfull exit */ + res = mylist; - /* Return the result */ - return mylist; - exit_errno: - PyErr_SetFromErrno(PyExc_IOError); + freebufval: PyMem_Free(buf_val); - free_list: - Py_DECREF(mylist); + free_buf_list: PyMem_Free(buf_list); - return NULL; + + freetgt: + free_tgt(&tgt); + + /* Return the result */ + return res; } @@ -494,19 +532,33 @@ pysetxattr(PyObject *self, PyObject *args) target_t tgt; /* Parse the arguments */ - if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname, - &buf, &bufsize, &flags, &nofollow)) - return NULL; - if(!convertObj(myarg, &tgt, nofollow)) + if (!PyArg_ParseTuple(args, "Oetet#|bi", &myarg, NULL, &attrname, + NULL, &buf, &bufsize, &flags, &nofollow)) return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } /* Set the attribute's value */ - if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + nret = _set_obj(&tgt, attrname, buf, bufsize, flags); + + free_tgt(&tgt); + + if(nret == -1) { + res = PyErr_SetFromErrno(PyExc_IOError); + goto freearg; } + Py_INCREF(Py_None); + res = Py_None; + + freearg: + PyMem_Free(attrname); + PyMem_Free(buf); + /* Return the result */ - Py_RETURN_NONE; + return res; } static char __set_doc__[] = @@ -564,24 +616,39 @@ xattr_set(PyObject *self, PyObject *args, PyObject *keywds) "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist, - &myarg, &attrname, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|iiz", kwlist, + &myarg, NULL, &attrname, NULL, &buf, &bufsize, &flags, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) - return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } full_name = merge_ns(ns, attrname, &newname); + /* Set the attribute's value */ nret = _set_obj(&tgt, full_name, buf, bufsize, flags); + if(newname != NULL) PyMem_Free(newname); + + free_tgt(&tgt); + if(nret == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freearg; } + Py_INCREF(Py_None); + res = Py_None; + + freearg: + PyMem_Free(attrname); + PyMem_Free(buf); + /* Return the result */ - Py_RETURN_NONE; + return res; } @@ -614,19 +681,32 @@ pyremovexattr(PyObject *self, PyObject *args) target_t tgt; /* Parse the arguments */ - if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow)) + if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) - return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } /* Remove the attribute */ - if((nret = _remove_obj(&tgt, attrname)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + nret = _remove_obj(&tgt, attrname); + + free_tgt(&tgt); + + if(nret == -1) { + res = PyErr_SetFromErrno(PyExc_IOError); + goto freearg; } + Py_INCREF(Py_None); + res = Py_None; + + freearg: + PyMem_Free(attrname); + /* Return the result */ - Py_RETURN_NONE; + return res; } static char __remove_doc__[] = @@ -667,25 +747,41 @@ xattr_remove(PyObject *self, PyObject *args, PyObject *keywds) static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist, - &myarg, &attrname, &nofollow, &ns)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iz", kwlist, + &myarg, NULL, &attrname, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) - return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } + full_name = merge_ns(ns, attrname, &name_buf); - if(full_name == NULL) - return NULL; + if(full_name == NULL) { + res = NULL; + goto freearg; + } /* Remove the attribute */ nret = _remove_obj(&tgt, full_name); + PyMem_Free(name_buf); + + free_tgt(&tgt); + if(nret == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freearg; } + Py_INCREF(Py_None); + res = Py_None; + + freearg: + PyMem_Free(attrname); + /* Return the result */ - Py_RETURN_NONE; + return res; } static char __pylistxattr_doc__[] = @@ -725,19 +821,21 @@ pylistxattr(PyObject *self, PyObject *args) /* Find out the needed size of the buffer */ if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + mylist = PyErr_SetFromErrno(PyExc_IOError); + goto freetgt; } /* Try to allocate the memory, using Python's allocator */ if((buf = PyMem_Malloc(nalloc)) == NULL) { PyErr_NoMemory(); - return NULL; + mylist = NULL; + goto freetgt; } /* Now retrieve the list of attributes */ if((nret = _list_obj(&tgt, buf, nalloc)) == -1) { - PyMem_Free(buf); - return PyErr_SetFromErrno(PyExc_IOError); + mylist = PyErr_SetFromErrno(PyExc_IOError); + goto freebuf; } /* Compute the number of attributes in the list */ @@ -750,13 +848,17 @@ pylistxattr(PyObject *self, PyObject *args) /* Create and insert the attributes as strings in the list */ for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) { - PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s)); + PyList_SET_ITEM(mylist, nattrs, PyBytes_FromString(s)); nattrs++; } + freebuf: /* Free the buffer, now it is no longer needed */ PyMem_Free(buf); + freetgt: + free_tgt(&tgt); + /* Return the result */ return mylist; } @@ -796,7 +898,7 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) int nofollow = 0; ssize_t nalloc, nret; PyObject *myarg; - PyObject *mylist; + PyObject *res; char *ns = NULL; Py_ssize_t nattrs; char *s; @@ -804,27 +906,31 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) static char *kwlist[] = {"item", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist, - &myarg, &nofollow, &ns)) - return NULL; - if(!convertObj(myarg, &tgt, nofollow)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iet", kwlist, + &myarg, &nofollow, NULL, &ns)) return NULL; + if(!convertObj(myarg, &tgt, nofollow)) { + res = NULL; + goto freearg; + } /* Find out the needed size of the buffer */ if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) { - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freetgt; } /* Try to allocate the memory, using Python's allocator */ if((buf = PyMem_Malloc(nalloc)) == NULL) { PyErr_NoMemory(); - return NULL; + res = NULL; + goto freetgt; } /* Now retrieve the list of attributes */ if((nret = _list_obj(&tgt, buf, nalloc)) == -1) { - PyMem_Free(buf); - return PyErr_SetFromErrno(PyExc_IOError); + res = PyErr_SetFromErrno(PyExc_IOError); + goto freebuf; } /* Compute the number of attributes in the list */ @@ -833,22 +939,28 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) nattrs++; } /* Create the list which will hold the result */ - mylist = PyList_New(nattrs); + res = PyList_New(nattrs); /* Create and insert the attributes as strings in the list */ for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) { const char *name = matches_ns(ns, s); if(name!=NULL) { - PyList_SET_ITEM(mylist, nattrs, PyString_FromString(name)); + PyList_SET_ITEM(res, nattrs, PyBytes_FromString(name)); nattrs++; } } + freebuf: /* Free the buffer, now it is no longer needed */ PyMem_Free(buf); + freetgt: + free_tgt(&tgt); + freearg: + PyMem_Free(ns); + /* Return the result */ - return mylist; + return res; } static PyMethodDef xattr_methods[] = { -- 2.39.2