1 #define PY_SSIZE_T_CLEAN
3 #include <attr/xattr.h>
6 /* Compatibility with python 2.4 regarding python size type (PEP 353) */
7 #if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
8 typedef int Py_ssize_t;
9 #define PY_SSIZE_T_MAX INT_MAX
10 #define PY_SSIZE_T_MIN INT_MIN
13 /* the estimated (startup) attribute buffer size in
15 #define ESTIMATE_ATTR_SIZE 256
17 typedef enum {T_FD, T_PATH, T_LINK} target_e;
27 /** Converts from a string, file or int argument to what we need. */
28 static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) {
30 if(PyString_Check(myobj)) {
31 tgt->type = nofollow ? T_LINK : T_PATH;
32 tgt->name = PyString_AS_STRING(myobj);
33 } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) {
37 PyErr_SetString(PyExc_TypeError, "argument must be string or int");
43 /* Combine a namespace string and an attribute name into a
44 fully-qualified name */
45 static const char* merge_ns(const char *ns, const char *name, char **buf) {
48 size_t new_size = strlen(ns) + 1 + strlen(name) + 1;
49 if((*buf = PyMem_Malloc(new_size)) == NULL) {
53 cnt = snprintf(*buf, new_size, "%s.%s", ns, name);
54 if(cnt > new_size || cnt < 0) {
55 PyErr_SetString(PyExc_ValueError,
56 "can't format the attribute name");
67 static ssize_t _list_obj(target_t *tgt, char *list, size_t size) {
69 return flistxattr(tgt->fd, list, size);
70 else if (tgt->type == T_LINK)
71 return llistxattr(tgt->name, list, size);
73 return listxattr(tgt->name, list, size);
76 static ssize_t _get_obj(target_t *tgt, const char *name, void *value,
79 return fgetxattr(tgt->fd, name, value, size);
80 else if (tgt->type == T_LINK)
81 return lgetxattr(tgt->name, name, value, size);
83 return getxattr(tgt->name, name, value, size);
86 static int _set_obj(target_t *tgt, const char *name,
87 const void *value, size_t size, int flags) {
89 return fsetxattr(tgt->fd, name, value, size, flags);
90 else if (tgt->type == T_LINK)
91 return lsetxattr(tgt->name, name, value, size, flags);
93 return setxattr(tgt->name, name, value, size, flags);
96 static int _remove_obj(target_t *tgt, const char *name) {
98 return fremovexattr(tgt->fd, name);
99 else if (tgt->type == T_LINK)
100 return lremovexattr(tgt->name, name);
102 return removexattr(tgt->name, name);
106 Checks if an attribute name matches an optional namespace.
108 If the namespace is NULL, it will return the name itself. If the
109 namespace is non-NULL and the name matches, it will return a
110 pointer to the offset in the name after the namespace and the
111 separator. If however the name doesn't match the namespace, it will
114 const char *matches_ns(const char *ns, const char *name) {
118 ns_size = strlen(ns);
120 if (strlen(name) > (ns_size+1) && !strncmp(name, ns, ns_size) &&
121 name[ns_size] == '.')
122 return name + ns_size + 1;
126 /* Wrapper for getxattr */
127 static char __pygetxattr_doc__[] =
128 "Get the value of a given extended attribute (deprecated).\n"
131 " - a string representing filename, or a file-like object,\n"
132 " or a file descriptor; this represents the file on \n"
134 " - a string, representing the attribute whose value to retrieve;\n"
135 " usually in form of system.posix_acl or user.mime_type\n"
136 " - (optional) a boolean value (defaults to false), which, if\n"
137 " the file name given is a symbolic link, makes the\n"
138 " function operate on the symbolic link itself instead\n"
140 "@deprecated: since version 0.4, this function has been deprecated\n"
141 " by the L{get} function\n"
145 pygetxattr(PyObject *self, PyObject *args)
152 ssize_t nalloc, nret;
155 /* Parse the arguments */
156 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
158 if(!convertObj(myarg, &tgt, nofollow))
161 /* Find out the needed size of the buffer */
162 if((nalloc = _get_obj(&tgt, attrname, NULL, 0)) == -1) {
163 return PyErr_SetFromErrno(PyExc_IOError);
166 /* Try to allocate the memory, using Python's allocator */
167 if((buf = PyMem_Malloc(nalloc)) == NULL) {
172 /* Now retrieve the attribute value */
173 if((nret = _get_obj(&tgt, attrname, buf, nalloc)) == -1) {
175 return PyErr_SetFromErrno(PyExc_IOError);
178 /* Create the string which will hold the result */
179 res = PyString_FromStringAndSize(buf, nret);
181 /* Free the buffer, now it is no longer needed */
184 /* Return the result */
188 /* Wrapper for getxattr */
189 static char __get_doc__[] =
190 "Get the value of a given extended attribute.\n"
192 "@param item: the item to query; either a string representing the\n"
193 " filename, or a file-like object, or a file descriptor\n"
194 "@param name: the attribute whose value to set; usually in form of\n"
195 " system.posix_acl or user.mime_type\n"
196 "@type name: string\n"
197 "@param nofollow: if given and True, and the function is passed a\n"
198 " filename that points to a symlink, the function will act on the\n"
199 " symlink itself instead of its target\n"
200 "@type nofollow: boolean\n"
201 "@param namespace: if given, the attribute must not contain the\n"
202 " namespace itself, but instead the namespace will be taken from\n"
204 "@type namespace: string\n"
205 "@return: the value of the extended attribute (can contain NULLs)\n"
207 "@raise EnvironmentError: caused by any system errors\n"
212 xattr_get(PyObject *self, PyObject *args, PyObject *keywds)
217 char *attrname, *namebuf;
218 const char *fullname;
221 ssize_t nalloc, nret;
223 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
225 /* Parse the arguments */
226 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
227 &myarg, &attrname, &nofollow, &ns))
229 if(!convertObj(myarg, &tgt, nofollow))
232 fullname = merge_ns(ns, attrname, &namebuf);
234 /* Find out the needed size of the buffer */
235 if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) {
236 return PyErr_SetFromErrno(PyExc_IOError);
239 /* Try to allocate the memory, using Python's allocator */
240 if((buf = PyMem_Malloc(nalloc)) == NULL) {
246 /* Now retrieve the attribute value */
247 if((nret = _get_obj(&tgt, fullname, buf, nalloc)) == -1) {
250 return PyErr_SetFromErrno(PyExc_IOError);
253 /* Create the string which will hold the result */
254 res = PyString_FromStringAndSize(buf, nret);
256 /* Free the buffers, they are no longer needed */
260 /* Return the result */
264 /* Wrapper for getxattr */
265 static char __get_all_doc__[] =
266 "Get all the extended attributes of an item.\n"
268 "This function performs a bulk-get of all extended attribute names\n"
269 "and the corresponding value.\n"
271 " >>> xattr.get_all('/path/to/file')\n"
272 " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n"
273 " ('system.posix_acl_access', '\\x02\\x00...')]\n"
274 " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n"
275 " [('mime-type', 'plain/text'), ('comment', 'test')]\n"
277 "@param item: the item to query; either a string representing the\n"
278 " filename, or a file-like object, or a file descriptor\n"
279 "@keyword namespace: an optional namespace for filtering the\n"
280 " attributes; for example, querying all user attributes can be\n"
281 " accomplished by passing namespace=L{NS_USER}\n"
282 "@type namespace: string\n"
283 "@keyword nofollow: if passed and true, if the target file is a\n"
284 " symbolic link, the attributes for the link itself will be\n"
285 " returned, instead of the attributes of the target\n"
286 "@type nofollow: boolean\n"
287 "@return: list of tuples (name, value); note that if a namespace\n"
288 " argument was passed, it (and the separator) will be stripped from\n"
289 " the names returned\n"
291 "@raise EnvironmentError: caused by any system errors\n"
292 "@note: Since reading the whole attribute list is not an atomic\n"
293 " operation, it might be possible that attributes are added\n"
294 " or removed between the initial query and the actual reading\n"
295 " of the attributes; the returned list will contain only the\n"
296 " attributes that were present at the initial listing of the\n"
297 " attribute names and that were still present when the read\n"
298 " attempt for the value is made.\n"
303 get_all(PyObject *self, PyObject *args, PyObject *keywds)
308 char *buf_list, *buf_val;
310 size_t nalloc, nlist, nval;
313 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
315 /* Parse the arguments */
316 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
317 &myarg, &dolink, &ns))
319 if(!convertObj(myarg, &tgt, dolink))
322 /* Compute first the list of attributes */
324 /* Find out the needed size of the buffer for the attribute list */
325 nalloc = _list_obj(&tgt, NULL, 0);
328 return PyErr_SetFromErrno(PyExc_IOError);
331 /* Try to allocate the memory, using Python's allocator */
332 if((buf_list = PyMem_Malloc(nalloc)) == NULL) {
337 /* Now retrieve the list of attributes */
338 nlist = _list_obj(&tgt, buf_list, nalloc);
341 PyErr_SetFromErrno(PyExc_IOError);
345 /* Create the list which will hold the result */
346 mylist = PyList_New(0);
347 nalloc = ESTIMATE_ATTR_SIZE;
348 if((buf_val = PyMem_Malloc(nalloc)) == NULL) {
353 /* Create and insert the attributes as strings in the list */
354 for(s = buf_list; s - buf_list < nlist; s += strlen(s) + 1) {
359 if((name=matches_ns(ns, s))==NULL)
361 /* Now retrieve the attribute value */
364 nval = _get_obj(&tgt, s, buf_val, nalloc);
367 if(errno == ERANGE) {
368 nval = _get_obj(&tgt, s, NULL, 0);
369 if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL)
373 } else if(errno == ENODATA || errno == ENOATTR) {
374 /* this attribute has gone away since we queried
375 the attribute list */
385 my_tuple = Py_BuildValue("ss#", name, buf_val, nval);
387 PyList_Append(mylist, my_tuple);
391 /* Free the buffers, now they are no longer needed */
393 PyMem_Free(buf_list);
395 /* Return the result */
398 PyErr_SetFromErrno(PyExc_IOError);
403 PyMem_Free(buf_list);
408 static char __pysetxattr_doc__[] =
409 "Set the value of a given extended attribute (deprecated).\n"
410 "Be carefull in case you want to set attributes on symbolic\n"
411 "links, you have to use all the 5 parameters; use 0 for the \n"
412 "flags value if you want the default behavior (create or "
416 " - a string representing filename, or a file-like object,\n"
417 " or a file descriptor; this represents the file on \n"
419 " - a string, representing the attribute whose value to set;\n"
420 " usually in form of system.posix_acl or user.mime_type\n"
421 " - a string, possibly with embedded NULLs; note that there\n"
422 " are restrictions regarding the size of the value, for\n"
423 " example, for ext2/ext3, maximum size is the block size\n"
424 " - (optional) flags; if 0 or ommited the attribute will be \n"
425 " created or replaced; if XATTR_CREATE, the attribute \n"
426 " will be created, giving an error if it already exists;\n"
427 " of XATTR_REPLACE, the attribute will be replaced,\n"
428 " giving an error if it doesn't exists;\n"
429 " - (optional) a boolean value (defaults to false), which, if\n"
430 " the file name given is a symbolic link, makes the\n"
431 " function operate on the symbolic link itself instead\n"
433 "@deprecated: since version 0.4, this function has been deprecated\n"
434 " by the L{set} function\n"
437 /* Wrapper for setxattr */
439 pysetxattr(PyObject *self, PyObject *args)
450 /* Parse the arguments */
451 if (!PyArg_ParseTuple(args, "Oss#|bi", &myarg, &attrname,
452 &buf, &bufsize, &flags, &nofollow))
454 if(!convertObj(myarg, &tgt, nofollow))
457 /* Set the attribute's value */
458 if((nret = _set_obj(&tgt, attrname, buf, bufsize, flags)) == -1) {
459 return PyErr_SetFromErrno(PyExc_IOError);
462 /* Return the result */
466 static char __set_doc__[] =
467 "Set the value of a given extended attribute.\n"
469 "@param item: the item to query; either a string representing the\n"
470 " filename, or a file-like object, or a file descriptor\n"
471 "@param name: the attribute whose value to set; usually in form of\n"
472 " system.posix_acl or user.mime_type\n"
473 "@type name: string\n"
474 "@param value: a string, possibly with embedded NULLs; note that there\n"
475 " are restrictions regarding the size of the value, for\n"
476 " example, for ext2/ext3, maximum size is the block size\n"
477 "@type value: string\n"
478 "@param flags: if 0 or ommited the attribute will be\n"
479 " created or replaced; if L{XATTR_CREATE}, the attribute\n"
480 " will be created, giving an error if it already exists;\n"
481 " if L{XATTR_REPLACE}, the attribute will be replaced,\n"
482 " giving an error if it doesn't exists;\n"
483 "@type flags: integer\n"
484 "@param nofollow: if given and True, and the function is passed a\n"
485 " filename that points to a symlink, the function will act on the\n"
486 " symlink itself instead of its target\n"
487 "@type nofollow: boolean\n"
488 "@param namespace: if given, the attribute must not contain the\n"
489 " namespace itself, but instead the namespace will be taken from\n"
491 "@type namespace: string\n"
493 "@raise EnvironmentError: caused by any system errors\n"
497 /* Wrapper for setxattr */
499 xattr_set(PyObject *self, PyObject *args, PyObject *keywds)
511 const char *full_name;
512 static char *kwlist[] = {"item", "name", "value", "flags",
513 "nofollow", "namespace", NULL};
515 /* Parse the arguments */
516 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oss#|iiz", kwlist,
518 &buf, &bufsize, &flags, &nofollow, &ns))
520 if(!convertObj(myarg, &tgt, nofollow))
523 full_name = merge_ns(ns, attrname, &newname);
524 /* Set the attribute's value */
525 nret = _set_obj(&tgt, full_name, buf, bufsize, flags);
529 return PyErr_SetFromErrno(PyExc_IOError);
532 /* Return the result */
537 static char __pyremovexattr_doc__[] =
538 "Remove an attribute from a file (deprecated)\n"
541 " - a string representing filename, or a file-like object,\n"
542 " or a file descriptor; this represents the file on \n"
544 " - a string, representing the attribute to be removed;\n"
545 " usually in form of system.posix_acl or user.mime_type\n"
546 " - (optional) a boolean value (defaults to false), which, if\n"
547 " the file name given is a symbolic link, makes the\n"
548 " function operate on the symbolic link itself instead\n"
550 "@deprecated: since version 0.4, this function has been deprecated\n"
555 /* Wrapper for removexattr */
557 pyremovexattr(PyObject *self, PyObject *args)
565 /* Parse the arguments */
566 if (!PyArg_ParseTuple(args, "Os|i", &myarg, &attrname, &nofollow))
569 if(!convertObj(myarg, &tgt, nofollow))
572 /* Remove the attribute */
573 if((nret = _remove_obj(&tgt, attrname)) == -1) {
574 return PyErr_SetFromErrno(PyExc_IOError);
577 /* Return the result */
581 static char __remove_doc__[] =
582 "Remove an attribute from a file\n"
584 "@param item: the item to query; either a string representing the\n"
585 " filename, or a file-like object, or a file descriptor\n"
586 "@param name: the attribute whose value to set; usually in form of\n"
587 " system.posix_acl or user.mime_type\n"
588 "@type name: string\n"
589 "@param nofollow: if given and True, and the function is passed a\n"
590 " filename that points to a symlink, the function will act on the\n"
591 " symlink itself instead of its target\n"
592 "@type nofollow: boolean\n"
593 "@param namespace: if given, the attribute must not contain the\n"
594 " namespace itself, but instead the namespace will be taken from\n"
596 "@type namespace: string\n"
599 "@raise EnvironmentError: caused by any system errors\n"
602 /* Wrapper for removexattr */
604 xattr_remove(PyObject *self, PyObject *args, PyObject *keywds)
608 char *attrname, *name_buf;
610 const char *full_name;
613 static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL};
615 /* Parse the arguments */
616 if (!PyArg_ParseTupleAndKeywords(args, keywds, "Os|iz", kwlist,
617 &myarg, &attrname, &nofollow, &ns))
620 if(!convertObj(myarg, &tgt, nofollow))
622 full_name = merge_ns(ns, attrname, &name_buf);
623 if(full_name == NULL)
626 /* Remove the attribute */
627 nret = _remove_obj(&tgt, full_name);
628 PyMem_Free(name_buf);
630 return PyErr_SetFromErrno(PyExc_IOError);
633 /* Return the result */
637 static char __pylistxattr_doc__[] =
638 "Return the list of attribute names for a file (deprecated)\n"
641 " - a string representing filename, or a file-like object,\n"
642 " or a file descriptor; this represents the file to \n"
644 " - (optional) a boolean value (defaults to false), which, if\n"
645 " the file name given is a symbolic link, makes the\n"
646 " function operate on the symbolic link itself instead\n"
648 "@deprecated: since version 0.4, this function has been deprecated\n"
653 /* Wrapper for listxattr */
655 pylistxattr(PyObject *self, PyObject *args)
659 ssize_t nalloc, nret;
666 /* Parse the arguments */
667 if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow))
669 if(!convertObj(myarg, &tgt, nofollow))
672 /* Find out the needed size of the buffer */
673 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
674 return PyErr_SetFromErrno(PyExc_IOError);
677 /* Try to allocate the memory, using Python's allocator */
678 if((buf = PyMem_Malloc(nalloc)) == NULL) {
683 /* Now retrieve the list of attributes */
684 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
686 return PyErr_SetFromErrno(PyExc_IOError);
689 /* Compute the number of attributes in the list */
690 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
694 /* Create the list which will hold the result */
695 mylist = PyList_New(nattrs);
697 /* Create and insert the attributes as strings in the list */
698 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
699 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(s));
703 /* Free the buffer, now it is no longer needed */
706 /* Return the result */
710 static char __list_doc__[] =
711 "Return the list of attribute names for a file\n"
714 " >>> xattr.list('/path/to/file')\n"
715 " ['user.test', 'user.comment', 'system.posix_acl_access']\n"
716 " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n"
717 " ['test', 'comment']\n"
719 "@param item: the item to query; either a string representing the\n"
720 " filename, or a file-like object, or a file descriptor\n"
721 "@param nofollow: if given and True, and the function is passed a\n"
722 " filename that points to a symlink, the function will act on the\n"
723 " symlink itself instead of its target\n"
724 "@type nofollow: boolean\n"
725 "@param namespace: if given, the attribute must not contain the\n"
726 " namespace itself, but instead the namespace will be taken from\n"
728 "@type namespace: string\n"
729 "@return: list of strings; note that if a namespace argument was\n"
730 " passed, it (and the separator) will be stripped from the names\n"
733 "@raise EnvironmentError: caused by any system errors\n"
737 /* Wrapper for listxattr */
739 xattr_list(PyObject *self, PyObject *args, PyObject *keywds)
743 ssize_t nalloc, nret;
750 static char *kwlist[] = {"item", "nofollow", "namespace", NULL};
752 /* Parse the arguments */
753 if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist,
754 &myarg, &nofollow, &ns))
756 if(!convertObj(myarg, &tgt, nofollow))
759 /* Find out the needed size of the buffer */
760 if((nalloc = _list_obj(&tgt, NULL, 0)) == -1) {
761 return PyErr_SetFromErrno(PyExc_IOError);
764 /* Try to allocate the memory, using Python's allocator */
765 if((buf = PyMem_Malloc(nalloc)) == NULL) {
770 /* Now retrieve the list of attributes */
771 if((nret = _list_obj(&tgt, buf, nalloc)) == -1) {
773 return PyErr_SetFromErrno(PyExc_IOError);
776 /* Compute the number of attributes in the list */
777 for(s = buf, nattrs = 0; (s - buf) < nret; s += strlen(s) + 1) {
778 if(matches_ns(ns, s)!=NULL)
781 /* Create the list which will hold the result */
782 mylist = PyList_New(nattrs);
784 /* Create and insert the attributes as strings in the list */
785 for(s = buf, nattrs = 0; s - buf < nret; s += strlen(s) + 1) {
786 const char *name = matches_ns(ns, s);
788 PyList_SET_ITEM(mylist, nattrs, PyString_FromString(name));
793 /* Free the buffer, now it is no longer needed */
796 /* Return the result */
800 static PyMethodDef xattr_methods[] = {
801 {"getxattr", pygetxattr, METH_VARARGS, __pygetxattr_doc__ },
802 {"get", (PyCFunction) xattr_get, METH_VARARGS | METH_KEYWORDS,
804 {"get_all", (PyCFunction) get_all, METH_VARARGS | METH_KEYWORDS,
806 {"setxattr", pysetxattr, METH_VARARGS, __pysetxattr_doc__ },
807 {"set", (PyCFunction) xattr_set, METH_VARARGS | METH_KEYWORDS,
809 {"removexattr", pyremovexattr, METH_VARARGS, __pyremovexattr_doc__ },
810 {"remove", (PyCFunction) xattr_remove, METH_VARARGS | METH_KEYWORDS,
812 {"listxattr", pylistxattr, METH_VARARGS, __pylistxattr_doc__ },
813 {"list", (PyCFunction) xattr_list, METH_VARARGS | METH_KEYWORDS,
815 {NULL, NULL, 0, NULL} /* Sentinel */
818 static char __xattr_doc__[] = \
819 "Access extended filesystem attributes\n"
821 "This module gives access to the extended attributes present\n"
822 "in some operating systems/filesystems. You can list attributes,\n"
823 "get, set and remove them.\n"
825 "The module exposes two sets of functions:\n"
826 " - the 'old' L{listxattr}, L{getxattr}, L{setxattr}, L{removexattr}\n"
827 " functions which are deprecated since version 0.4\n"
828 " - the new L{list}, L{get}, L{get_all}, L{set}, L{remove} functions\n"
829 " which expose a namespace-aware API and simplify a bit the calling\n"
830 " model by using keyword arguments\n"
833 " >>> import xattr\n"
834 " >>> xattr.listxattr(\"file.txt\")\n"
835 " ['user.mime_type']\n"
836 " >>> xattr.getxattr(\"file.txt\", \"user.mime_type\")\n"
838 " >>> xattr.setxattr(\"file.txt\", \"user.comment\", "
839 "\"Simple text file\")\n"
840 " >>> xattr.listxattr(\"file.txt\")\n"
841 " ['user.mime_type', 'user.comment']\n"
842 " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n"
844 "@note: Most or all errors reported by the system while using the xattr\n"
845 "library will be reported by raising a L{EnvironmentError}; under Linux,\n"
846 "the following C{errno} values are used:\n"
847 " - C{ENOATTR} and C{ENODATA} mean that the attribute name is invalid\n"
848 " - C{ENOTSUP} and C{EOPNOTSUPP} mean that the filesystem does not\n"
849 " support extended attributes, or that the namespace is invalid\n"
850 " - C{E2BIG} mean that the attribute value is too big\n"
851 " - C{ERANGE} mean that the attribute name is too big (it might also\n"
852 " mean an error in the xattr module itself)\n"
853 " - C{ENOSPC} and C{EDQUOT} are documented as meaning out of disk space\n"
854 " or out of disk space because of quota limits\n"
856 "@group Deprecated API: *xattr\n"
857 "@group Namespace constants: NS_*\n"
858 "@group set function flags: XATTR_CREATE, XATTR_REPLACE\n"
859 "@sort: list, get, get_all, set, remove, listxattr, getxattr, setxattr\n"
866 PyObject *m = Py_InitModule3("xattr", xattr_methods, __xattr_doc__);
868 PyModule_AddStringConstant(m, "__author__", _XATTR_AUTHOR);
869 PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION);
870 PyModule_AddStringConstant(m, "__docformat__", "epytext en");
872 PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE);
873 PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE);
875 /* namespace constants */
876 PyModule_AddStringConstant(m, "NS_SECURITY", "security");
877 PyModule_AddStringConstant(m, "NS_SYSTEM", "system");
878 PyModule_AddStringConstant(m, "NS_TRUSTED", "trusted");
879 PyModule_AddStringConstant(m, "NS_USER", "user");