From afc34c5e8e5c0cd189d35824b53cc04cf62f0383 Mon Sep 17 00:00:00 2001
From: Iustin Pop <iusty@k1024.org>
Date: Mon, 1 Mar 2010 21:03:06 +0100
Subject: [PATCH] Imported Upstream version 0.5.0

---
 MANIFEST.in                           |   7 -
 NEWS                                  |   6 +
 PKG-INFO                              |   2 +-
 pyxattr.egg-info/PKG-INFO             |  12 -
 pyxattr.egg-info/SOURCES.txt          |  13 -
 pyxattr.egg-info/dependency_links.txt |   1 -
 pyxattr.egg-info/top_level.txt        |   1 -
 setup.cfg                             |   7 +-
 setup.py                              |  10 +-
 test/test_xattr.py                    |  26 +-
 xattr.c                               | 402 ++++++++++++++++++--------
 11 files changed, 323 insertions(+), 164 deletions(-)
 delete mode 100644 MANIFEST.in
 delete mode 100644 pyxattr.egg-info/PKG-INFO
 delete mode 100644 pyxattr.egg-info/SOURCES.txt
 delete mode 100644 pyxattr.egg-info/dependency_links.txt
 delete mode 100644 pyxattr.egg-info/top_level.txt

diff --git a/MANIFEST.in b/MANIFEST.in
deleted file mode 100644
index efa600a..0000000
--- a/MANIFEST.in
+++ /dev/null
@@ -1,7 +0,0 @@
-include COPYING
-include NEWS
-include README
-include epydoc.conf
-include setup.cfg
-include test/test_xattr.py
-include xattr.c
diff --git a/NEWS b/NEWS
index 6c9ffbb..1b933d7 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,9 @@
+Version 0.5
+===========
+
+Implemented support for python 3. This required a significant change to
+the C module, hence the new version number.
+
 Version 0.4
 ===========
 
diff --git a/PKG-INFO b/PKG-INFO
index 22d8588..85223fe 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
 Metadata-Version: 1.0
 Name: pyxattr
-Version: 0.4.0
+Version: 0.5.0
 Summary: Filesystem extended attributes for python
 Home-page: http://pyxattr.sourceforge.net/
 Author: Iustin Pop
diff --git a/pyxattr.egg-info/PKG-INFO b/pyxattr.egg-info/PKG-INFO
deleted file mode 100644
index 22d8588..0000000
--- a/pyxattr.egg-info/PKG-INFO
+++ /dev/null
@@ -1,12 +0,0 @@
-Metadata-Version: 1.0
-Name: pyxattr
-Version: 0.4.0
-Summary: Filesystem extended attributes for python
-Home-page: http://pyxattr.sourceforge.net/
-Author: Iustin Pop
-Author-email: iusty@k1024.org
-License: LGPL
-Description: This is a C extension module for Python which
-        implements extended attributes manipulation. It is a wrapper on top
-        of the attr C library - see attr(5).
-Platform: UNKNOWN
diff --git a/pyxattr.egg-info/SOURCES.txt b/pyxattr.egg-info/SOURCES.txt
deleted file mode 100644
index 31a43a3..0000000
--- a/pyxattr.egg-info/SOURCES.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-COPYING
-MANIFEST.in
-NEWS
-README
-epydoc.conf
-setup.cfg
-setup.py
-xattr.c
-pyxattr.egg-info/PKG-INFO
-pyxattr.egg-info/SOURCES.txt
-pyxattr.egg-info/dependency_links.txt
-pyxattr.egg-info/top_level.txt
-test/test_xattr.py
\ No newline at end of file
diff --git a/pyxattr.egg-info/dependency_links.txt b/pyxattr.egg-info/dependency_links.txt
deleted file mode 100644
index 8b13789..0000000
--- a/pyxattr.egg-info/dependency_links.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/pyxattr.egg-info/top_level.txt b/pyxattr.egg-info/top_level.txt
deleted file mode 100644
index 18c5e90..0000000
--- a/pyxattr.egg-info/top_level.txt
+++ /dev/null
@@ -1 +0,0 @@
-xattr
diff --git a/setup.cfg b/setup.cfg
index a68b051..256aa5f 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,9 +1,4 @@
 [bdist_rpm]
 release = 1
 requires = libattr
-
-[egg_info]
-tag_build = 
-tag_date = 0
-tag_svn_revision = 0
-
+;build_requires = libattr libattr-devel
diff --git a/setup.py b/setup.py
index 02e4098..e6f77bc 100755
--- a/setup.py
+++ b/setup.py
@@ -1,13 +1,13 @@
 #!/usr/bin/python
 
-#import distutils
-#from distutils.core import setup, Extension
-from setuptools import setup, Extension
+import distutils
+from distutils.core import setup, Extension
+#from setuptools import setup, Extension
 
 long_desc = """This is a C extension module for Python which
 implements extended attributes manipulation. It is a wrapper on top
 of the attr C library - see attr(5)."""
-version = "0.4.0"
+version = "0.5.0"
 author = "Iustin Pop"
 author_email = "iusty@k1024.org"
 macros = [
@@ -26,5 +26,5 @@ setup(name = "pyxattr",
       ext_modules = [Extension("xattr", ["xattr.c"],
                                libraries=["attr"],
                                define_macros=macros)],
-      test_suite = "test/test_xattr",
+      #test_suite = "test/test_xattr",
       )
diff --git a/test/test_xattr.py b/test/test_xattr.py
index 2f4abb0..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),
@@ -354,3 +371,6 @@ class xattrTest(unittest.TestCase):
                                  [(self.USER_ATTR, self.USER_VAL)])
             self.failUnlessEqual(xattr.get_all(fh, namespace=NS_USER),
                                  [(self.USER_NN, self.USER_VAL)])
+
+if __name__ == "__main__":
+    unittest.main()
diff --git a/xattr.c b/xattr.c
index 9609fa3..370324a 100644
--- a/xattr.c
+++ b/xattr.c
@@ -1,3 +1,25 @@
+/*
+    xattr - a python module for manipulating filesystem extended attributes
+
+    Copyright (C) 2002, 2003, 2006, 2008 Iustin Pop <iusty@k1024.org>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library 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
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+    02110-1301  USA
+
+*/
+
 #define PY_SSIZE_T_CLEAN
 #include <Python.h>
 #include <attr/xattr.h>
@@ -10,6 +32,15 @@ typedef int Py_ssize_t;
 #define PY_SSIZE_T_MIN INT_MIN
 #endif
 
+#if PY_MAJOR_VERSION >= 3
+#define IS_PY3K
+#else
+#define PyBytes_Check PyString_Check
+#define PyBytes_AS_STRING PyString_AS_STRING
+#define PyBytes_FromStringAndSize PyString_FromStringAndSize
+#define PyBytes_FromString PyString_FromString
+#endif
+
 /* the estimated (startup) attribute buffer size in
    multi-operations */
 #define ESTIMATE_ATTR_SIZE 256
@@ -22,14 +53,31 @@ typedef struct {
         const char *name;
         int fd;
     };
+    PyObject *tmp;
 } target_t;
 
+/* Cleans up a tgt structure */
+static void free_tgt(target_t *tgt) {
+    if (tgt->tmp != NULL) {
+        Py_DECREF(tgt->tmp);
+    }
+}
+
 /** 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 = PyBytes_AS_STRING(myobj);
+    } else if(PyUnicode_Check(myobj)) {
         tgt->type = nofollow ? T_LINK : T_PATH;
-        tgt->name = PyString_AS_STRING(myobj);
+        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;
@@ -146,40 +194,48 @@ pygetxattr(PyObject *self, PyObject *args)
 {
     PyObject *myarg;
     target_t tgt;
-    int nofollow=0;
-    char *attrname;
+    int nofollow = 0;
+    char *attrname = NULL;
     char *buf;
     ssize_t nalloc, nret;
     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 = PyErr_NoMemory();
+        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;
@@ -219,8 +275,8 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
 {
     PyObject *myarg;
     target_t tgt;
-    int nofollow=0;
-    char *attrname, *namebuf;
+    int nofollow = 0;
+    char *attrname = NULL, *namebuf;
     const char *fullname;
     char *buf;
     char *ns = NULL;
@@ -229,39 +285,46 @@ 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 = PyErr_NoMemory();
+        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;
@@ -308,12 +371,12 @@ static char __get_all_doc__[] =
 static PyObject *
 get_all(PyObject *self, PyObject *args, PyObject *keywds)
 {
-    PyObject *myarg;
+    PyObject *myarg, *res;
     int dolink=0;
     char *ns = NULL;
     char *buf_list, *buf_val;
     char *s;
-    size_t nalloc, nlist, nval;
+    ssize_t nalloc, nlist, nval;
     PyObject *mylist;
     target_t tgt;
     static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
@@ -331,20 +394,21 @@ 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 = PyErr_NoMemory();
+        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;
     }
 
@@ -352,8 +416,9 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds)
     mylist = PyList_New(0);
     nalloc = ESTIMATE_ATTR_SIZE;
     if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
-        PyErr_NoMemory();
-        goto free_list;
+        Py_DECREF(mylist);
+        res = PyErr_NoMemory();
+        goto free_buf_list;
     }
 
     /* Create and insert the attributes as strings in the list */
@@ -372,8 +437,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) {
@@ -382,32 +450,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;
 }
 
 
@@ -445,29 +518,43 @@ static char __pysetxattr_doc__[] =
 static PyObject *
 pysetxattr(PyObject *self, PyObject *args)
 {
-    PyObject *myarg;
-    int nofollow=0;
-    char *attrname;
-    char *buf;
+    PyObject *myarg, *res;
+    int nofollow = 0;
+    char *attrname = NULL;
+    char *buf = NULL;
     Py_ssize_t bufsize;
     int nret;
     int flags = 0;
     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__[] =
@@ -510,10 +597,10 @@ static char __set_doc__[] =
 static PyObject *
 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
 {
-    PyObject *myarg;
-    int nofollow=0;
-    char *attrname;
-    char *buf;
+    PyObject *myarg, *res;
+    int nofollow = 0;
+    char *attrname = NULL;
+    char *buf = NULL;
     Py_ssize_t bufsize;
     int nret;
     int flags = 0;
@@ -525,24 +612,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;
 }
 
 
@@ -568,26 +670,39 @@ static char __pyremovexattr_doc__[] =
 static PyObject *
 pyremovexattr(PyObject *self, PyObject *args)
 {
-    PyObject *myarg;
-    int nofollow=0;
-    char *attrname;
+    PyObject *myarg, *res;
+    int nofollow = 0;
+    char *attrname = NULL;
     int nret;
     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__[] =
@@ -618,9 +733,9 @@ static char __remove_doc__[] =
 static PyObject *
 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
 {
-    PyObject *myarg;
-    int nofollow=0;
-    char *attrname, *name_buf;
+    PyObject *myarg, *res;
+    int nofollow = 0;
+    char *attrname = NULL, *name_buf;
     char *ns = NULL;
     const char *full_name;
     int nret;
@@ -628,25 +743,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__[] =
@@ -686,19 +817,20 @@ 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 = PyErr_NoMemory();
+        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 */
@@ -711,13 +843,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;
 }
@@ -754,10 +890,10 @@ static PyObject *
 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
 {
     char *buf;
-    int nofollow=0;
+    int nofollow = 0;
     ssize_t nalloc, nret;
     PyObject *myarg;
-    PyObject *mylist;
+    PyObject *res;
     char *ns = NULL;
     Py_ssize_t nattrs;
     char *s;
@@ -765,51 +901,60 @@ 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 = PyErr_NoMemory();
+        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 */
     for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
-        if(matches_ns(ns, s)!=NULL)
+        if(matches_ns(ns, s) != NULL)
             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[] = {
@@ -875,10 +1020,34 @@ static char __xattr_doc__[] = \
     "    removexattr\n"
     ;
 
+#ifdef IS_PY3K
+
+static struct PyModuleDef xattrmodule = {
+    PyModuleDef_HEAD_INIT,
+    "xattr",
+    __xattr_doc__,
+    0,
+    xattr_methods,
+};
+
+#define INITERROR return NULL
+
+PyMODINIT_FUNC
+PyInit_xattr(void)
+
+#else
+#define INITERROR return
 void
 initxattr(void)
+#endif
 {
+#ifdef IS_PY3K
+    PyObject *m = PyModule_Create(&xattrmodule);
+#else
     PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
+#endif
+    if (m==NULL)
+        INITERROR;
 
     PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
     PyModule_AddStringConstant(m, "__contact__", _XATTR_EMAIL);
@@ -891,9 +1060,12 @@ initxattr(void)
     PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
 
     /* namespace constants */
-    PyModule_AddStringConstant(m, "NS_SECURITY", "security");
-    PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
-    PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
-    PyModule_AddStringConstant(m, "NS_USER", "user");
+    PyModule_AddObject(m, "NS_SECURITY", PyBytes_FromString("security"));
+    PyModule_AddObject(m, "NS_SYSTEM", PyBytes_FromString("system"));
+    PyModule_AddObject(m, "NS_TRUSTED", PyBytes_FromString("trusted"));
+    PyModule_AddObject(m, "NS_USER", PyBytes_FromString("user"));
 
+#ifdef IS_PY3K
+    return m;
+#endif
 }
-- 
2.39.5