From cc7474217148b51198ff6c6c6675ab119895956f Mon Sep 17 00:00:00 2001 From: Iustin Pop Date: Wed, 16 May 2012 19:37:41 +0200 Subject: [PATCH] Imported Upstream version 0.5.1 --- MANIFEST.in | 10 + Makefile | 38 ++ NEWS | 45 ++- PKG-INFO | 9 +- README | 28 +- doc/conf.py | 252 ++++++++++++ doc/index.rst | 17 + doc/module.rst | 54 +++ doc/news.rst | 1 + epydoc.conf | 27 -- pyxattr.egg-info/PKG-INFO | 13 + pyxattr.egg-info/SOURCES.txt | 18 + pyxattr.egg-info/dependency_links.txt | 1 + pyxattr.egg-info/top_level.txt | 1 + setup.cfg | 7 +- setup.py | 11 +- test/__init__.py | 0 test/test_xattr.py | 261 ++++++------ xattr.c | 555 +++++++++++++++----------- 19 files changed, 925 insertions(+), 423 deletions(-) create mode 100644 MANIFEST.in create mode 100644 Makefile create mode 100644 doc/conf.py create mode 100644 doc/index.rst create mode 100644 doc/module.rst create mode 120000 doc/news.rst delete mode 100644 epydoc.conf create mode 100644 pyxattr.egg-info/PKG-INFO create mode 100644 pyxattr.egg-info/SOURCES.txt create mode 100644 pyxattr.egg-info/dependency_links.txt create mode 100644 pyxattr.egg-info/top_level.txt create mode 100644 test/__init__.py diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9c2be4b --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,10 @@ +include COPYING +include NEWS +include README +include Makefile +include doc/conf.py +include doc/*.rst +include setup.cfg +include test/test_xattr.py +include test/__init__.py +include xattr.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..2fb3d2e --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +SPHINXOPTS = -W +SPHINXBUILD = sphinx-build +DOCDIR = doc +DOCHTML = $(DOCDIR)/html +DOCTREES = $(DOCDIR)/doctrees +ALLSPHINXOPTS = -d $(DOCTREES) $(SPHINXOPTS) $(DOCDIR) + +MODNAME = xattr.so +RSTFILES = doc/index.rst doc/module.rst NEWS README doc/conf.py + +all: doc test + +$(MODNAME): xattr.c + ./setup.py build_ext --inplace + +$(DOCHTML)/index.html: $(MODNAME) $(RSTFILES) + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DOCHTML) + touch $@ + +doc: $(DOCHTML)/index.html + +dist: + fakeroot ./setup.py sdist + +test: + for ver in 2.4 2.5 2.6 2.7 3.0 3.1 3.2; do \ + if type python$$ver >/dev/null; then \ + echo Testing with python$$ver; \ + python$$ver ./setup.py test; \ + fi; \ + done + +clean: + rm -rf $(DOCHTML) $(DOCTREES) + rm -f $(MODNAME) + rm -rf build + +.PHONY: doc test clean dist diff --git a/NEWS b/NEWS index 1b933d7..aace59c 100644 --- a/NEWS +++ b/NEWS @@ -1,14 +1,34 @@ +News +==== + +Version 0.5.1 +------------- + +Bug-fix release. Thanks to Dave Malcolm and his cpychecker tool, a +number of significant bugs (refcount leaks and potential NULL-pointer +dereferences) have been fixed. + +Furthermore, compatibility with Python 3 has been improved; this however +required changing the meaning of the ``namespace`` argument to the +functions: if passed, None is no longer a valid value; pass an empty +string if (due to the structure of your program) you have to pass this +argument but want to specify no namespace. + +Also, the project home page has changed from SourceForge to GitHub, and +the documentation has been converted from epydoc-based to sphinx. + + Version 0.5 -=========== +----------- -Implemented support for python 3. This required a significant change to +Implemented support for Python 3. This required a significant change to the C module, hence the new version number. Version 0.4 -=========== +----------- API ---- +~~~ The old functions ({get,set,list,remove}xattr) are deprecated and replaced with a new API that is namespace-aware and hopefully will allow other OSes (e.g. @@ -18,38 +38,43 @@ Both the old and the new API are supported in the 0.4 versions, however users are encouraged to migrate to the new API. New features ------------- +~~~~~~~~~~~~ A new bulk get function called get_all() has been added that should be somewhat faster in case of querying files which have many attributes. License -------- +~~~~~~~ Since LGPLv3 is not compatible with GPLv2 (which unfortunately I didn't realize before), the license was changed to LGPLv2.1 or later. Internals ---------- +~~~~~~~~~ Unittest coverage was improved. Version 0.3 -=========== +----------- * changed licence from GPL to LGPL (3 or later) * changed listxattr return type from tuple to a list * developer-related: added unittests Version 0.2.2 -============= +------------- * fixed listing symlink xattrs Version 0.2.1 -============= +------------- * fixed a bug when reading symlink EAs (you weren't able to do it, actually) * fixed a possible memory leak when the actual read of the EA failed but the call to get the length of the EA didn't + +.. Local Variables: +.. mode: rst +.. fill-column: 72 +.. End: diff --git a/PKG-INFO b/PKG-INFO index 85223fe..15f09a5 100644 --- a/PKG-INFO +++ b/PKG-INFO @@ -1,12 +1,13 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: pyxattr -Version: 0.5.0 +Version: 0.5.1 Summary: Filesystem extended attributes for python -Home-page: http://pyxattr.sourceforge.net/ +Home-page: http://pyxattr.k1024.org/ Author: Iustin Pop Author-email: iusty@k1024.org License: LGPL +Download-URL: https://github.com/iustin/pyxattr/downloads 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 +Platform: Linux diff --git a/README b/README index 0bbf5e0..dfa7578 100644 --- a/README +++ b/README @@ -1,23 +1,30 @@ pyxattr ======= -About ------ - This is the pyxattr module, a Python extension module which gives access to the extended attributes for filesystem objects available in some operating systems. -For usage details, see the documentation of the module using epydoc. -The project web page is http://pyxattr.sourceforge.net/ +Downloads: go to https://github.com/iustin/pyxattr/downloads. Latest +version is 0.5.1. The source repository is either at +http://git.k1024.org/pyxattr.git or at +https://github.com/iustin/pyxattr. + +Requirements +------------ + +pyxattr has been written and tested on Linux, kernel v2.4 or later, with +XFS filesystems; ext2/ext3 should work also. If any other platform +implements the same behavior, pyxattr could be used. -You need to have setuptools installed in order to build and install the -module. +You need to have the attr library (including development headers; most +distributions should have this, under various names) and setuptools +installed in order to build and install the module. License ------- -pyxattr is Copyright 2002-2008 Iustin Pop. +pyxattr is Copyright 2002-2008, 2012 Iustin Pop. pyxattr 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 @@ -29,4 +36,7 @@ under LGPL version 3 (which, I realized later, is not compatible with GPLv2, hence the change to LGPL 2.1), and even older versions were licensed under GPL v2 or later. -Iustin Pop, +.. Local Variables: +.. mode: rst +.. fill-column: 72 +.. End: diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..0031d00 --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +# +# pyxattr documentation build configuration file, created by +# sphinx-quickstart on Sun May 13 01:05:18 2012. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath('../')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'pyxattr' +copyright = u'2002, 2003, 2006, 2008, 2012, Iustin Pop' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '0.5.1' +# The full version, including alpha/beta/rc tags. +release = '0.5.1' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build', 'html'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +default_domain = 'python' + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + +keep_warnings = True + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +#html_static_path = ['_static'] +html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True +html_domain_indices = False + +# If false, no index is generated. +#html_use_index = True +html_use_index = False + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True +html_show_sourcelink = False + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'pyxattrdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'pyxattr.tex', u'pyxattr Documentation', + u'Iustin Pop', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'pyxattr', u'pyxattr Documentation', + [u'Iustin Pop'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('index', 'pyxattr', u'pyxattr Documentation', + u'Iustin Pop', 'pyxattr', 'One line description of project.', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + +autodoc_member_order = 'alphabetical' diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..336e191 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,17 @@ +====================================== + Welcome to pylibacl's documentation! +====================================== + +.. include:: ../README + :start-line: 2 + +Contents +-------- + +.. toctree:: + :maxdepth: 2 + + module.rst + news.rst + +Also see the :ref:`search`. diff --git a/doc/module.rst b/doc/module.rst new file mode 100644 index 0000000..0231605 --- /dev/null +++ b/doc/module.rst @@ -0,0 +1,54 @@ +Interface to extended filesystem attributes +=========================================== + +.. automodule:: xattr + +Constants +--------- + +.. data:: XATTR_CREATE + + Used as flags value, the target attribute + will be created, giving an error if it already exists. + +.. data:: XATTR_REPLACE + + Used as flags value, the target attribute + will be replaced, giving an error if it doesn't exist. + +.. data:: NS_SECURITY + + The security namespace, used by kernel security modules. + +.. data:: NS_SYSTEM + + The system namespace, used by the kernel to store things such as + ACLs and capabilities. + +.. data:: NS_TRUSTED + + The trusted namespace, visible and accessibly only to trusted + processes, used to implement mechanisms in user space. + +.. data:: NS_USER + + The user namespace; this is the namespace accessible to + non-privileged processes. + +Functions +--------- + +.. autofunction:: list +.. autofunction:: get +.. autofunction:: get_all +.. autofunction:: set +.. autofunction:: remove + + +Deprecated functions +-------------------- + +.. autofunction:: getxattr +.. autofunction:: setxattr +.. autofunction:: listxattr +.. autofunction:: removexattr diff --git a/doc/news.rst b/doc/news.rst new file mode 120000 index 0000000..0fae0f8 --- /dev/null +++ b/doc/news.rst @@ -0,0 +1 @@ +../NEWS \ No newline at end of file diff --git a/epydoc.conf b/epydoc.conf deleted file mode 100644 index f9c154f..0000000 --- a/epydoc.conf +++ /dev/null @@ -1,27 +0,0 @@ -[epydoc] - -modules: xattr - -output: html - -target: html/ - -verbosity: 0 - -debug: 0 - -simple-term: 1 - -parse: no - -introspect: yes - -imports: no - -sourcecode: no - -name: Filesystem extended attributes for python - -url: http://pyxattr.sourceforge.net/ - -frames: yes diff --git a/pyxattr.egg-info/PKG-INFO b/pyxattr.egg-info/PKG-INFO new file mode 100644 index 0000000..15f09a5 --- /dev/null +++ b/pyxattr.egg-info/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 1.1 +Name: pyxattr +Version: 0.5.1 +Summary: Filesystem extended attributes for python +Home-page: http://pyxattr.k1024.org/ +Author: Iustin Pop +Author-email: iusty@k1024.org +License: LGPL +Download-URL: https://github.com/iustin/pyxattr/downloads +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: Linux diff --git a/pyxattr.egg-info/SOURCES.txt b/pyxattr.egg-info/SOURCES.txt new file mode 100644 index 0000000..51b72e6 --- /dev/null +++ b/pyxattr.egg-info/SOURCES.txt @@ -0,0 +1,18 @@ +COPYING +MANIFEST.in +Makefile +NEWS +README +setup.cfg +setup.py +xattr.c +doc/conf.py +doc/index.rst +doc/module.rst +doc/news.rst +pyxattr.egg-info/PKG-INFO +pyxattr.egg-info/SOURCES.txt +pyxattr.egg-info/dependency_links.txt +pyxattr.egg-info/top_level.txt +test/__init__.py +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 new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/pyxattr.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/pyxattr.egg-info/top_level.txt b/pyxattr.egg-info/top_level.txt new file mode 100644 index 0000000..18c5e90 --- /dev/null +++ b/pyxattr.egg-info/top_level.txt @@ -0,0 +1 @@ +xattr diff --git a/setup.cfg b/setup.cfg index 256aa5f..a68b051 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,9 @@ [bdist_rpm] release = 1 requires = libattr -;build_requires = libattr libattr-devel + +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py index e6f77bc..17326bc 100755 --- a/setup.py +++ b/setup.py @@ -1,13 +1,12 @@ #!/usr/bin/python import distutils -from distutils.core import setup, Extension -#from setuptools 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.5.0" +version = "0.5.1" author = "Iustin Pop" author_email = "iusty@k1024.org" macros = [ @@ -21,10 +20,12 @@ setup(name = "pyxattr", long_description = long_desc, author = author, author_email = author_email, - url = "http://pyxattr.sourceforge.net/", + url = "http://pyxattr.k1024.org/", + download_url = "https://github.com/iustin/pyxattr/downloads", license = "LGPL", ext_modules = [Extension("xattr", ["xattr.c"], libraries=["attr"], define_macros=macros)], - #test_suite = "test/test_xattr", + test_suite = "test", + platforms = ["Linux"], ) diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_xattr.py b/test/test_xattr.py index cd25594..8df00b9 100644 --- a/test/test_xattr.py +++ b/test/test_xattr.py @@ -12,8 +12,10 @@ from xattr import NS_USER, XATTR_CREATE, XATTR_REPLACE if sys.hexversion >= 0x03000000: PY3K = True + EMPTY_NS = b'' else: PY3K = False + EMPTY_NS = '' TEST_DIR = os.environ.get("TESTDIR", ".") @@ -72,10 +74,10 @@ class xattrTest(unittest.TestCase): def _checkDeprecated(self, item, symlink=False): """check deprecated list, set, get operations against an item""" - self.failUnlessEqual(xattr.listxattr(item, symlink), []) - self.failUnlessRaises(EnvironmentError, xattr.setxattr, item, - self.USER_ATTR, self.USER_VAL, - XATTR_REPLACE) + self.assertEqual(xattr.listxattr(item, symlink), []) + self.assertRaises(EnvironmentError, xattr.setxattr, item, + self.USER_ATTR, self.USER_VAL, + XATTR_REPLACE) try: xattr.setxattr(item, self.USER_ATTR, self.USER_VAL, 0, symlink) except IOError: @@ -85,30 +87,27 @@ class xattrTest(unittest.TestCase): # of the test for this case return raise - self.failUnlessRaises(EnvironmentError, xattr.setxattr, item, - self.USER_ATTR, self.USER_VAL, - XATTR_CREATE) - self.failUnlessEqual(xattr.listxattr(item, symlink), [self.USER_ATTR]) - self.failUnlessEqual(xattr.getxattr(item, self.USER_ATTR, symlink), - self.USER_VAL) - self.failUnlessEqual(xattr.get_all(item, nofollow=symlink), - [(self.USER_ATTR, self.USER_VAL)]) + self.assertRaises(EnvironmentError, xattr.setxattr, item, + self.USER_ATTR, self.USER_VAL, XATTR_CREATE) + self.assertEqual(xattr.listxattr(item, symlink), [self.USER_ATTR]) + self.assertEqual(xattr.getxattr(item, self.USER_ATTR, symlink), + self.USER_VAL) + self.assertEqual(xattr.get_all(item, nofollow=symlink), + [(self.USER_ATTR, self.USER_VAL)]) xattr.removexattr(item, self.USER_ATTR) - self.failUnlessEqual(xattr.listxattr(item, symlink), []) - self.failUnlessEqual(xattr.get_all(item, nofollow=symlink), []) - self.failUnlessRaises(EnvironmentError, xattr.removexattr, - item, self.USER_ATTR) + self.assertEqual(xattr.listxattr(item, symlink), []) + self.assertEqual(xattr.get_all(item, nofollow=symlink), []) + self.assertRaises(EnvironmentError, xattr.removexattr, + item, self.USER_ATTR) def _checkListSetGet(self, item, symlink=False, use_ns=False): """check list, set, get operations against an item""" - self.failUnlessEqual(xattr.list(item, symlink), []) - self.failUnlessRaises(EnvironmentError, xattr.set, item, - self.USER_ATTR, self.USER_VAL, - flags=XATTR_REPLACE) - self.failUnlessRaises(EnvironmentError, xattr.set, item, - self.USER_NN, self.USER_VAL, - flags=XATTR_REPLACE, - namespace=NS_USER) + self.assertEqual(xattr.list(item, symlink), []) + self.assertRaises(EnvironmentError, xattr.set, item, + self.USER_ATTR, self.USER_VAL, flags=XATTR_REPLACE) + self.assertRaises(EnvironmentError, xattr.set, item, + self.USER_NN, self.USER_VAL, flags=XATTR_REPLACE, + namespace=NS_USER) try: if use_ns: xattr.set(item, self.USER_NN, self.USER_VAL, @@ -124,70 +123,68 @@ class xattrTest(unittest.TestCase): # of the test for this case return raise - self.failUnlessRaises(EnvironmentError, xattr.set, item, - self.USER_ATTR, self.USER_VAL, - flags=XATTR_CREATE) - self.failUnlessRaises(EnvironmentError, xattr.set, item, - self.USER_NN, self.USER_VAL, - flags=XATTR_CREATE, - namespace=NS_USER) - self.failUnlessEqual(xattr.list(item, nofollow=symlink), - [self.USER_ATTR]) - self.failUnlessEqual(xattr.list(item, namespace=NS_USER, - nofollow=symlink), - [self.USER_NN]) - self.failUnlessEqual(xattr.get(item, self.USER_ATTR, nofollow=symlink), - self.USER_VAL) - self.failUnlessEqual(xattr.get(item, self.USER_NN, nofollow=symlink, + self.assertRaises(EnvironmentError, xattr.set, item, + self.USER_ATTR, self.USER_VAL, flags=XATTR_CREATE) + self.assertRaises(EnvironmentError, xattr.set, item, + self.USER_NN, self.USER_VAL, + flags=XATTR_CREATE, namespace=NS_USER) + self.assertEqual(xattr.list(item, nofollow=symlink), + [self.USER_ATTR]) + self.assertEqual(xattr.list(item, nofollow=symlink, + namespace=EMPTY_NS), + [self.USER_ATTR]) + self.assertEqual(xattr.list(item, namespace=NS_USER, nofollow=symlink), + [self.USER_NN]) + self.assertEqual(xattr.get(item, self.USER_ATTR, nofollow=symlink), + self.USER_VAL) + self.assertEqual(xattr.get(item, self.USER_NN, nofollow=symlink, + namespace=NS_USER), self.USER_VAL) + self.assertEqual(xattr.get_all(item, nofollow=symlink), + [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(item, nofollow=symlink, namespace=NS_USER), - self.USER_VAL) - self.failUnlessEqual(xattr.get_all(item, nofollow=symlink), - [(self.USER_ATTR, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(item, nofollow=symlink, - namespace=NS_USER), - [(self.USER_NN, self.USER_VAL)]) + [(self.USER_NN, self.USER_VAL)]) if use_ns: xattr.remove(item, self.USER_NN, namespace=NS_USER) else: xattr.remove(item, self.USER_ATTR) - self.failUnlessEqual(xattr.list(item, symlink), []) - self.failUnlessEqual(xattr.get_all(item, nofollow=symlink), []) - self.failUnlessRaises(EnvironmentError, xattr.remove, - item, self.USER_ATTR, nofollow=symlink) - self.failUnlessRaises(EnvironmentError, xattr.remove, - item, self.USER_NN, namespace=NS_USER, - nofollow=symlink) + self.assertEqual(xattr.list(item, symlink), []) + self.assertEqual(xattr.get_all(item, nofollow=symlink), []) + self.assertRaises(EnvironmentError, xattr.remove, + item, self.USER_ATTR, nofollow=symlink) + self.assertRaises(EnvironmentError, xattr.remove, item, + self.USER_NN, namespace=NS_USER, nofollow=symlink) def testNoXattrDeprecated(self): """test no attributes (deprecated functions)""" fh, fname = self._getfile() - self.failUnlessEqual(xattr.listxattr(fname), []) - self.failUnlessEqual(xattr.get_all(fname), []) + self.assertEqual(xattr.listxattr(fname), []) + self.assertEqual(xattr.get_all(fname), []) dname = self._getdir() - self.failUnlessEqual(xattr.listxattr(dname), []) - self.failUnlessEqual(xattr.get_all(dname), []) + self.assertEqual(xattr.listxattr(dname), []) + self.assertEqual(xattr.get_all(dname), []) _, sname = self._getsymlink() - self.failUnlessEqual(xattr.listxattr(sname, True), []) - self.failUnlessEqual(xattr.get_all(sname, nofollow=True), []) + self.assertEqual(xattr.listxattr(sname, True), []) + self.assertEqual(xattr.get_all(sname, nofollow=True), []) def testNoXattr(self): """test no attributes""" fh, fname = self._getfile() - self.failUnlessEqual(xattr.list(fname), []) - self.failUnlessEqual(xattr.list(fname, namespace=NS_USER), []) - self.failUnlessEqual(xattr.get_all(fname), []) - self.failUnlessEqual(xattr.get_all(fname, namespace=NS_USER), []) + self.assertEqual(xattr.list(fname), []) + self.assertEqual(xattr.list(fname, namespace=NS_USER), []) + self.assertEqual(xattr.get_all(fname), []) + self.assertEqual(xattr.get_all(fname, namespace=NS_USER), []) dname = self._getdir() - self.failUnlessEqual(xattr.list(dname), []) - self.failUnlessEqual(xattr.list(dname, namespace=NS_USER), []) - self.failUnlessEqual(xattr.get_all(dname), []) - self.failUnlessEqual(xattr.get_all(dname, namespace=NS_USER), []) + self.assertEqual(xattr.list(dname), []) + self.assertEqual(xattr.list(dname, namespace=NS_USER), []) + self.assertEqual(xattr.get_all(dname), []) + self.assertEqual(xattr.get_all(dname, namespace=NS_USER), []) _, sname = self._getsymlink() - self.failUnlessEqual(xattr.list(sname, nofollow=True), []) - self.failUnlessEqual(xattr.list(sname, nofollow=True, + self.assertEqual(xattr.list(sname, nofollow=True), []) + self.assertEqual(xattr.list(sname, nofollow=True, namespace=NS_USER), []) - self.failUnlessEqual(xattr.get_all(sname, nofollow=True), []) - self.failUnlessEqual(xattr.get_all(sname, nofollow=True, + self.assertEqual(xattr.get_all(sname, nofollow=True), []) + self.assertEqual(xattr.get_all(sname, nofollow=True, namespace=NS_USER), []) def testFileByNameDeprecated(self): @@ -235,37 +232,34 @@ class xattrTest(unittest.TestCase): """test mixed access to file (deprecated functions)""" fh, fname = self._getfile() fo = os.fdopen(fh) - self.failUnlessEqual(xattr.listxattr(fname), []) + self.assertEqual(xattr.listxattr(fname), []) xattr.setxattr(fname, self.USER_ATTR, self.USER_VAL) - self.failUnlessEqual(xattr.listxattr(fh), [self.USER_ATTR]) - self.failUnlessEqual(xattr.getxattr(fo, self.USER_ATTR), - self.USER_VAL) - self.failUnlessEqual(xattr.get_all(fo), - [(self.USER_ATTR, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(fname), - [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.listxattr(fh), [self.USER_ATTR]) + self.assertEqual(xattr.getxattr(fo, self.USER_ATTR), self.USER_VAL) + self.assertEqual(xattr.get_all(fo), [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fname), + [(self.USER_ATTR, self.USER_VAL)]) + fo.close() def testMixedAccess(self): """test mixed access to file""" fh, fname = self._getfile() fo = os.fdopen(fh) - self.failUnlessEqual(xattr.list(fname), []) + self.assertEqual(xattr.list(fname), []) xattr.set(fname, self.USER_ATTR, self.USER_VAL) - self.failUnlessEqual(xattr.list(fh), [self.USER_ATTR]) - self.failUnlessEqual(xattr.list(fh, namespace=NS_USER), - [self.USER_NN]) - self.failUnlessEqual(xattr.get(fo, self.USER_ATTR), - self.USER_VAL) - self.failUnlessEqual(xattr.get(fo, self.USER_NN, namespace=NS_USER), - self.USER_VAL) - self.failUnlessEqual(xattr.get_all(fo), - [(self.USER_ATTR, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(fo, namespace=NS_USER), - [(self.USER_NN, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(fname), - [(self.USER_ATTR, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(fname, namespace=NS_USER), - [(self.USER_NN, self.USER_VAL)]) + self.assertEqual(xattr.list(fh), [self.USER_ATTR]) + self.assertEqual(xattr.list(fh, namespace=NS_USER), [self.USER_NN]) + self.assertEqual(xattr.get(fo, self.USER_ATTR), self.USER_VAL) + self.assertEqual(xattr.get(fo, self.USER_NN, namespace=NS_USER), + self.USER_VAL) + self.assertEqual(xattr.get_all(fo), [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fo, namespace=NS_USER), + [(self.USER_NN, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fname), + [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fname, namespace=NS_USER), + [(self.USER_NN, self.USER_VAL)]) + fo.close() def testDirOpsDeprecated(self): """test attribute setting on directories (deprecated functions)""" @@ -281,28 +275,28 @@ class xattrTest(unittest.TestCase): def testSymlinkOpsDeprecated(self): """test symlink operations (deprecated functions)""" _, sname = self._getsymlink() - self.failUnlessRaises(EnvironmentError, xattr.listxattr, sname) + self.assertRaises(EnvironmentError, xattr.listxattr, sname) self._checkDeprecated(sname, symlink=True) target, sname = self._getsymlink(dangling=False) xattr.setxattr(target, self.USER_ATTR, self.USER_VAL) - self.failUnlessEqual(xattr.listxattr(target), [self.USER_ATTR]) - self.failUnlessEqual(xattr.listxattr(sname, True), []) - self.failUnlessRaises(EnvironmentError, xattr.removexattr, sname, - self.USER_ATTR, True) + self.assertEqual(xattr.listxattr(target), [self.USER_ATTR]) + self.assertEqual(xattr.listxattr(sname, True), []) + self.assertRaises(EnvironmentError, xattr.removexattr, sname, + self.USER_ATTR, True) xattr.removexattr(sname, self.USER_ATTR, False) def testSymlinkOps(self): """test symlink operations""" _, sname = self._getsymlink() - self.failUnlessRaises(EnvironmentError, xattr.list, sname) + self.assertRaises(EnvironmentError, xattr.list, sname) self._checkListSetGet(sname, symlink=True) self._checkListSetGet(sname, symlink=True, use_ns=True) target, sname = self._getsymlink(dangling=False) xattr.set(target, self.USER_ATTR, self.USER_VAL) - self.failUnlessEqual(xattr.list(target), [self.USER_ATTR]) - self.failUnlessEqual(xattr.list(sname, nofollow=True), []) - self.failUnlessRaises(EnvironmentError, xattr.remove, sname, - self.USER_ATTR, nofollow=True) + self.assertEqual(xattr.list(target), [self.USER_ATTR]) + self.assertEqual(xattr.list(sname, nofollow=True), []) + self.assertRaises(EnvironmentError, xattr.remove, sname, + self.USER_ATTR, nofollow=True) xattr.remove(sname, self.USER_ATTR, nofollow=False) def testBinaryPayloadDeprecated(self): @@ -313,9 +307,9 @@ class xattrTest(unittest.TestCase): 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) - self.failUnlessEqual(xattr.get_all(fname), [(self.USER_ATTR, BINVAL)]) + self.assertEqual(xattr.listxattr(fname), [self.USER_ATTR]) + self.assertEqual(xattr.getxattr(fname, self.USER_ATTR), BINVAL) + self.assertEqual(xattr.get_all(fname), [(self.USER_ATTR, BINVAL)]) xattr.removexattr(fname, self.USER_ATTR) def testBinaryPayload(self): @@ -326,15 +320,14 @@ class xattrTest(unittest.TestCase): 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), - [self.USER_NN]) - self.failUnlessEqual(xattr.get(fname, self.USER_ATTR), BINVAL) - self.failUnlessEqual(xattr.get(fname, self.USER_NN, - namespace=NS_USER), BINVAL) - self.failUnlessEqual(xattr.get_all(fname), [(self.USER_ATTR, BINVAL)]) - self.failUnlessEqual(xattr.get_all(fname, namespace=NS_USER), - [(self.USER_NN, BINVAL)]) + self.assertEqual(xattr.list(fname), [self.USER_ATTR]) + self.assertEqual(xattr.list(fname, namespace=NS_USER), [self.USER_NN]) + self.assertEqual(xattr.get(fname, self.USER_ATTR), BINVAL) + self.assertEqual(xattr.get(fname, self.USER_NN, + namespace=NS_USER), BINVAL) + self.assertEqual(xattr.get_all(fname), [(self.USER_ATTR, BINVAL)]) + self.assertEqual(xattr.get_all(fname, namespace=NS_USER), + [(self.USER_NN, BINVAL)]) xattr.remove(fname, self.USER_ATTR) def testManyOpsDeprecated(self): @@ -343,13 +336,12 @@ class xattrTest(unittest.TestCase): xattr.setxattr(fh, self.USER_ATTR, self.USER_VAL) VL = [self.USER_ATTR] for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.listxattr(fh), VL) + self.assertEqual(xattr.listxattr(fh), VL) for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.getxattr(fh, self.USER_ATTR), - self.USER_VAL) + self.assertEqual(xattr.getxattr(fh, self.USER_ATTR), self.USER_VAL) for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.get_all(fh), - [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fh), + [(self.USER_ATTR, self.USER_VAL)]) def testManyOps(self): """test many ops""" @@ -358,19 +350,24 @@ class xattrTest(unittest.TestCase): VL = [self.USER_ATTR] VN = [self.USER_NN] for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.list(fh), VL) - self.failUnlessEqual(xattr.list(fh, namespace=NS_USER), VN) + self.assertEqual(xattr.list(fh), VL) + self.assertEqual(xattr.list(fh, namespace=EMPTY_NS), VL) + self.assertEqual(xattr.list(fh, namespace=NS_USER), VN) for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.get(fh, self.USER_ATTR), - self.USER_VAL) - self.failUnlessEqual(xattr.get(fh, self.USER_NN, - namespace=NS_USER), - self.USER_VAL) + self.assertEqual(xattr.get(fh, self.USER_ATTR), self.USER_VAL) + self.assertEqual(xattr.get(fh, self.USER_NN, namespace=NS_USER), + self.USER_VAL) for i in range(self.MANYOPS_COUNT): - self.failUnlessEqual(xattr.get_all(fh), - [(self.USER_ATTR, self.USER_VAL)]) - self.failUnlessEqual(xattr.get_all(fh, namespace=NS_USER), - [(self.USER_NN, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fh), + [(self.USER_ATTR, self.USER_VAL)]) + self.assertEqual(xattr.get_all(fh, namespace=NS_USER), + [(self.USER_NN, self.USER_VAL)]) + + def testNoneNamespace(self): + fh, fname = self._getfile() + self.assertRaises(TypeError, xattr.get, fh, self.USER_ATTR, + namespace=None) + if __name__ == "__main__": unittest.main() diff --git a/xattr.c b/xattr.c index 370324a..f88d116 100644 --- a/xattr.c +++ b/xattr.c @@ -1,7 +1,7 @@ /* xattr - a python module for manipulating filesystem extended attributes - Copyright (C) 2002, 2003, 2006, 2008 Iustin Pop + Copyright (C) 2002, 2003, 2006, 2008, 2012 Iustin Pop This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -34,13 +34,65 @@ typedef int Py_ssize_t; #if PY_MAJOR_VERSION >= 3 #define IS_PY3K +#define BYTES_CHAR "y" #else +#define BYTES_CHAR "s" #define PyBytes_Check PyString_Check #define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_FromStringAndSize PyString_FromStringAndSize #define PyBytes_FromString PyString_FromString #endif +#define ITEM_DOC \ + ":param item: a string representing a file-name, or a file-like\n" \ + " object, or a file descriptor; this represents the file on \n" \ + " which to act\n" + +#define NOFOLLOW_DOC \ + ":param nofollow: if true and if\n" \ + " the file name given is a symbolic link, the\n" \ + " function will operate on the symbolic link itself instead\n" \ + " of its target; defaults to false\n" \ + ":type nofollow: boolean, optional\n" \ + +#define NS_DOC \ + ":param namespace: if given, the attribute must not contain the\n" \ + " namespace, but instead it will be taken from this parameter\n" \ + ":type namespace: bytes\n" + +#define NAME_GET_DOC \ + ":param string name: the attribute whose value to retrieve;\n" \ + " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n" + +#define NAME_SET_DOC \ + ":param string name: the attribute whose value to set;\n" \ + " usually in the form of ``system.posix_acl`` or ``user.mime_type``\n" + +#define NAME_REMOVE_DOC \ + ":param string name: the attribute to remove;\n" \ + " usually in the form of ``system.posix_acl`` or \n" \ + " ``user.mime_type``\n" + +#define VALUE_DOC \ + ":param string value: possibly with embedded NULLs; note that there\n" \ + " are restrictions regarding the size of the value, for\n" \ + " example, for ext2/ext3, maximum size is the block size\n" \ + +#define FLAGS_DOC \ + ":param flags: if 0 or omitted the attribute will be\n" \ + " created or replaced; if :const:`XATTR_CREATE`, the attribute\n" \ + " will be created, giving an error if it already exists;\n" \ + " if :const:`XATTR_REPLACE`, the attribute will be replaced,\n" \ + " giving an error if it doesn't exist;\n" \ + ":type flags: integer\n" + +#define NS_CHANGED_DOC \ + ".. versionchanged:: 0.5.1\n" \ + " The namespace argument, if passed, cannot be None anymore; to\n" \ + " explicitly specify an empty namespace, pass an empty\n" \ + " string (byte string under Python 3)." + + /* the estimated (startup) attribute buffer size in multi-operations */ #define ESTIMATE_ATTR_SIZE 256 @@ -63,8 +115,29 @@ 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) { +/* Used for cpychecker: */ +/* The checker automatically defines this preprocessor name when creating + the custom attribute: */ +#if defined(WITH_CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION_ATTRIBUTE) + #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION \ +__attribute__((cpychecker_negative_result_sets_exception)) + #else + #define CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION + #endif + +static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; + +static int merge_ns(const char *ns, const char *name, + const char **result, char **buf) + CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION; + + +/** Converts from a string, file or int argument to what we need. + * + * Returns -1 on failure, 0 on success. + */ +static int convert_obj(PyObject *myobj, target_t *tgt, int nofollow) { int fd; tgt->tmp = NULL; if(PyBytes_Check(myobj)) { @@ -76,40 +149,42 @@ static int convertObj(PyObject *myobj, target_t *tgt, int nofollow) { PyUnicode_AsEncodedString(myobj, Py_FileSystemDefaultEncoding, "strict"); if(tgt->tmp == NULL) - return 0; + return -1; tgt->name = PyBytes_AS_STRING(tgt->tmp); } else if((fd = PyObject_AsFileDescriptor(myobj)) != -1) { tgt->type = T_FD; tgt->fd = fd; } else { PyErr_SetString(PyExc_TypeError, "argument must be string or int"); - return 0; + return -1; } - return 1; + return 0; } /* Combine a namespace string and an attribute name into a fully-qualified name */ -static const char* merge_ns(const char *ns, const char *name, char **buf) { - if(ns != NULL) { +static int merge_ns(const char *ns, const char *name, + const char **result, char **buf) { + if(ns != NULL && *ns != '\0') { int cnt; size_t new_size = strlen(ns) + 1 + strlen(name) + 1; if((*buf = PyMem_Malloc(new_size)) == NULL) { PyErr_NoMemory(); - return NULL; + return -1; } cnt = snprintf(*buf, new_size, "%s.%s", ns, name); if(cnt > new_size || cnt < 0) { PyErr_SetString(PyExc_ValueError, "can't format the attribute name"); PyMem_Free(*buf); - return NULL; + return -1; } - return *buf; + *result = *buf; } else { *buf = NULL; - return name; + *result = name; } + return 0; } static ssize_t _list_obj(target_t *tgt, char *list, size_t size) { @@ -153,15 +228,16 @@ static int _remove_obj(target_t *tgt, const char *name) { /* Checks if an attribute name matches an optional namespace. - If the namespace is NULL, it will return the name itself. If the - namespace is non-NULL and the name matches, it will return a - pointer to the offset in the name after the namespace and the - separator. If however the name doesn't match the namespace, it will - return NULL. + If the namespace is NULL or an empty string, it will return the + name itself. If the namespace is non-NULL and the name matches, it + will return a pointer to the offset in the name after the namespace + and the separator. If however the name doesn't match the namespace, + it will return NULL. + */ const char *matches_ns(const char *ns, const char *name) { size_t ns_size; - if (ns == NULL) + if (ns == NULL || *ns == '\0') return name; ns_size = strlen(ns); @@ -173,20 +249,16 @@ const char *matches_ns(const char *ns, const char *name) { /* Wrapper for getxattr */ static char __pygetxattr_doc__[] = + "getxattr(item, attribute[, nofollow=False])\n" "Get the value of a given extended attribute (deprecated).\n" "\n" - "Parameters:\n" - " - a string representing filename, or a file-like object,\n" - " or a file descriptor; this represents the file on \n" - " which to act\n" - " - a string, representing the attribute whose value to retrieve;\n" - " usually in form of system.posix_acl or user.mime_type\n" - " - (optional) a boolean value (defaults to false), which, if\n" - " the file name given is a symbolic link, makes the\n" - " function operate on the symbolic link itself instead\n" - " of its target;\n" - "@deprecated: since version 0.4, this function has been deprecated\n" - " by the L{get} function\n" + ITEM_DOC + NAME_GET_DOC + NOFOLLOW_DOC + "\n" + ".. deprecated:: 0.4\n" + " this function has been deprecated\n" + " by the :func:`get` function.\n" ; static PyObject * @@ -203,7 +275,7 @@ pygetxattr(PyObject *self, PyObject *args) /* Parse the arguments */ if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } @@ -243,6 +315,7 @@ pygetxattr(PyObject *self, PyObject *args) /* Wrapper for getxattr */ static char __get_doc__[] = + "get(item, name[, nofollow=False, namespace=None])\n" "Get the value of a given extended attribute.\n" "\n" "Example:\n" @@ -251,23 +324,16 @@ static char __get_doc__[] = " >>> xattr.get('/path/to/file', 'comment', namespace=xattr.NS_USER)\n" " 'test'\n" "\n" - "@param item: the item to query; either a string representing the\n" - " filename, or a file-like object, or a file descriptor\n" - "@param name: the attribute whose value to set; usually in form of\n" - " system.posix_acl or user.mime_type\n" - "@type name: string\n" - "@param nofollow: if given and True, and the function is passed a\n" - " filename that points to a symlink, the function will act on the\n" - " symlink itself instead of its target\n" - "@type nofollow: boolean\n" - "@param namespace: if given, the attribute must not contain the\n" - " namespace itself, but instead the namespace will be taken from\n" - " this parameter\n" - "@type namespace: string\n" - "@return: the value of the extended attribute (can contain NULLs)\n" - "@rtype: string\n" - "@raise EnvironmentError: caused by any system errors\n" - "@since: 0.4\n" + ITEM_DOC + NAME_GET_DOC + NOFOLLOW_DOC + NS_DOC + ":return: the value of the extended attribute (can contain NULLs)\n" + ":rtype: string\n" + ":raises EnvironmentError: caused by any system errors\n" + "\n" + ".. versionadded:: 0.4\n" + NS_CHANGED_DOC ; static PyObject * @@ -279,21 +345,24 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds) char *attrname = NULL, *namebuf; const char *fullname; char *buf; - char *ns = NULL; + const char *ns = NULL; ssize_t nalloc, nret; PyObject *res; static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iz", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist, &myarg, NULL, &attrname, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } - fullname = merge_ns(ns, attrname, &namebuf); + if(merge_ns(ns, attrname, &fullname, &namebuf) < 0) { + res = NULL; + goto freearg; + } /* Find out the needed size of the buffer */ if((nalloc = _get_obj(&tgt, fullname, NULL, 0)) == -1) { @@ -332,60 +401,60 @@ xattr_get(PyObject *self, PyObject *args, PyObject *keywds) /* Wrapper for getxattr */ static char __get_all_doc__[] = + "get_all(item[, nofollow=False, namespace=None])\n" "Get all the extended attributes of an item.\n" "\n" "This function performs a bulk-get of all extended attribute names\n" "and the corresponding value.\n" "Example:\n" + "\n" " >>> xattr.get_all('/path/to/file')\n" " [('user.mime-type', 'plain/text'), ('user.comment', 'test'),\n" " ('system.posix_acl_access', '\\x02\\x00...')]\n" " >>> xattr.get_all('/path/to/file', namespace=xattr.NS_USER)\n" " [('mime-type', 'plain/text'), ('comment', 'test')]\n" "\n" - "@param item: the item to query; either a string representing the\n" - " filename, or a file-like object, or a file descriptor\n" - "@keyword namespace: an optional namespace for filtering the\n" - " attributes; for example, querying all user attributes can be\n" - " accomplished by passing namespace=L{NS_USER}\n" - "@type namespace: string\n" - "@keyword nofollow: if passed and true, if the target file is a\n" - " symbolic link, the attributes for the link itself will be\n" - " returned, instead of the attributes of the target\n" - "@type nofollow: boolean\n" - "@return: list of tuples (name, value); note that if a namespace\n" - " argument was passed, it (and the separator) will be stripped from\n" - " the names returned\n" - "@rtype: list\n" - "@raise EnvironmentError: caused by any system errors\n" - "@note: Since reading the whole attribute list is not an atomic\n" - " operation, it might be possible that attributes are added\n" - " or removed between the initial query and the actual reading\n" - " of the attributes; the returned list will contain only the\n" - " attributes that were present at the initial listing of the\n" - " attribute names and that were still present when the read\n" - " attempt for the value is made.\n" - "@since: 0.4\n" + ITEM_DOC + ":keyword namespace: an optional namespace for filtering the\n" + " attributes; for example, querying all user attributes can be\n" + " accomplished by passing namespace=:const:`NS_USER`\n" + ":type namespace: string\n" + NOFOLLOW_DOC + ":return: list of tuples (name, value); note that if a namespace\n" + " argument was passed, it (and the separator) will be stripped from\n" + " the names returned\n" + ":rtype: list\n" + ":raises EnvironmentError: caused by any system errors\n" + "\n" + ".. note:: Since reading the whole attribute list is not an atomic\n" + " operation, it might be possible that attributes are added\n" + " or removed between the initial query and the actual reading\n" + " of the attributes; the returned list will contain only the\n" + " attributes that were present at the initial listing of the\n" + " attribute names and that were still present when the read\n" + " attempt for the value is made.\n" + ".. versionadded:: 0.4\n" + NS_CHANGED_DOC ; static PyObject * get_all(PyObject *self, PyObject *args, PyObject *keywds) { PyObject *myarg, *res; - int dolink=0; - char *ns = NULL; + int nofollow=0; + const char *ns = NULL; char *buf_list, *buf_val; - char *s; + const char *s; ssize_t nalloc, nlist, nval; PyObject *mylist; target_t tgt; static char *kwlist[] = {"item", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iz", kwlist, - &myarg, &dolink, &ns)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist, + &myarg, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, dolink)) + if(convert_obj(myarg, &tgt, nofollow) < 0) return NULL; /* Compute first the list of attributes */ @@ -414,6 +483,11 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) /* Create the list which will hold the result */ mylist = PyList_New(0); + if(mylist == NULL) { + res = NULL; + goto free_buf_list; + } + nalloc = ESTIMATE_ATTR_SIZE; if((buf_val = PyMem_Malloc(nalloc)) == NULL) { Py_DECREF(mylist); @@ -438,7 +512,7 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) if(errno == ERANGE) { nval = _get_obj(&tgt, s, NULL, 0); if((buf_val = PyMem_Realloc(buf_val, nval)) == NULL) { - res = NULL; + res = PyErr_NoMemory(); Py_DECREF(mylist); goto free_buf_list; } @@ -450,6 +524,9 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) missing = 1; break; } + /* else we're dealing with a different error, which we + don't know how to handle nicely, so we abort */ + Py_DECREF(mylist); res = PyErr_SetFromErrno(PyExc_IOError); goto freebufval; } @@ -462,12 +539,16 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) #else my_tuple = Py_BuildValue("ss#", name, buf_val, nval); #endif - + if (my_tuple == NULL) { + Py_DECREF(mylist); + res = NULL; + goto freebufval; + } PyList_Append(mylist, my_tuple); Py_DECREF(my_tuple); } - /* Successfull exit */ + /* Successful exit */ res = mylist; freebufval: @@ -485,33 +566,23 @@ get_all(PyObject *self, PyObject *args, PyObject *keywds) static char __pysetxattr_doc__[] = + "setxattr(item, name, value[, flags=0, nofollow=False])\n" "Set the value of a given extended attribute (deprecated).\n" "\n" - "Be carefull in case you want to set attributes on symbolic\n" + "Be careful in case you want to set attributes on symbolic\n" "links, you have to use all the 5 parameters; use 0 for the \n" - "flags value if you want the default behavior (create or " + "flags value if you want the default behaviour (create or " "replace)\n" "\n" - "Parameters:\n" - " - a string representing filename, or a file-like object,\n" - " or a file descriptor; this represents the file on \n" - " which to act\n" - " - a string, representing the attribute whose value to set;\n" - " usually in form of system.posix_acl or user.mime_type\n" - " - a string, possibly with embedded NULLs; note that there\n" - " are restrictions regarding the size of the value, for\n" - " example, for ext2/ext3, maximum size is the block size\n" - " - (optional) flags; if 0 or ommited the attribute will be \n" - " created or replaced; if XATTR_CREATE, the attribute \n" - " will be created, giving an error if it already exists;\n" - " of XATTR_REPLACE, the attribute will be replaced,\n" - " giving an error if it doesn't exists;\n" - " - (optional) a boolean value (defaults to false), which, if\n" - " the file name given is a symbolic link, makes the\n" - " function operate on the symbolic link itself instead\n" - " of its target;\n" - "@deprecated: since version 0.4, this function has been deprecated\n" - " by the L{set} function\n" + ITEM_DOC + NAME_SET_DOC + VALUE_DOC + FLAGS_DOC + NOFOLLOW_DOC + "\n" + ".. deprecated:: 0.4\n" + " this function has been deprecated\n" + " by the :func:`set` function.\n" ; /* Wrapper for setxattr */ @@ -528,10 +599,10 @@ pysetxattr(PyObject *self, PyObject *args) target_t tgt; /* Parse the arguments */ - if (!PyArg_ParseTuple(args, "Oetet#|bi", &myarg, NULL, &attrname, + if (!PyArg_ParseTuple(args, "Oetet#|ii", &myarg, NULL, &attrname, NULL, &buf, &bufsize, &flags, &nofollow)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } @@ -558,39 +629,26 @@ pysetxattr(PyObject *self, PyObject *args) } static char __set_doc__[] = + "set(item, name, value[, flags=0, namespace=None])\n" "Set the value of a given extended attribute.\n" "\n" "Example:\n" + "\n" " >>> xattr.set('/path/to/file', 'user.comment', 'test')\n" " >>> xattr.set('/path/to/file', 'comment', 'test'," " namespace=xattr.NS_USER)\n" "\n" - "@param item: the item to query; either a string representing the\n" - " filename, or a file-like object, or a file descriptor\n" - "@param name: the attribute whose value to set; usually in form of\n" - " system.posix_acl or user.mime_type\n" - "@type name: string\n" - "@param value: a string, possibly with embedded NULLs; note that there\n" - " are restrictions regarding the size of the value, for\n" - " example, for ext2/ext3, maximum size is the block size\n" - "@type value: string\n" - "@param flags: if 0 or ommited the attribute will be\n" - " created or replaced; if L{XATTR_CREATE}, the attribute\n" - " will be created, giving an error if it already exists;\n" - " if L{XATTR_REPLACE}, the attribute will be replaced,\n" - " giving an error if it doesn't exists;\n" - "@type flags: integer\n" - "@param nofollow: if given and True, and the function is passed a\n" - " filename that points to a symlink, the function will act on the\n" - " symlink itself instead of its target\n" - "@type nofollow: boolean\n" - "@param namespace: if given, the attribute must not contain the\n" - " namespace itself, but instead the namespace will be taken from\n" - " this parameter\n" - "@type namespace: string\n" - "@rtype: None\n" - "@raise EnvironmentError: caused by any system errors\n" - "@since: 0.4\n" + ITEM_DOC + NAME_SET_DOC + VALUE_DOC + FLAGS_DOC + NOFOLLOW_DOC + NS_DOC + ":returns: None\n" + ":raises EnvironmentError: caused by any system errors\n" + "\n" + ".. versionadded:: 0.4\n" + NS_CHANGED_DOC ; /* Wrapper for setxattr */ @@ -605,29 +663,31 @@ xattr_set(PyObject *self, PyObject *args, PyObject *keywds) int nret; int flags = 0; target_t tgt; - char *ns = NULL; + const char *ns = NULL; char *newname; const char *full_name; static char *kwlist[] = {"item", "name", "value", "flags", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|iiz", kwlist, - &myarg, NULL, &attrname, NULL, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oetet#|ii" BYTES_CHAR, + kwlist, &myarg, NULL, &attrname, NULL, &buf, &bufsize, &flags, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } - full_name = merge_ns(ns, attrname, &newname); + if(merge_ns(ns, attrname, &full_name, &newname) < 0) { + res = NULL; + goto freearg; + } /* Set the attribute's value */ nret = _set_obj(&tgt, full_name, buf, bufsize, flags); - if(newname != NULL) - PyMem_Free(newname); + PyMem_Free(newname); free_tgt(&tgt); @@ -649,21 +709,15 @@ xattr_set(PyObject *self, PyObject *args, PyObject *keywds) static char __pyremovexattr_doc__[] = + "removexattr(item, name[, nofollow])\n" "Remove an attribute from a file (deprecated).\n" "\n" - "Parameters:\n" - " - a string representing filename, or a file-like object,\n" - " or a file descriptor; this represents the file on \n" - " which to act\n" - " - a string, representing the attribute to be removed;\n" - " usually in form of system.posix_acl or user.mime_type\n" - " - (optional) a boolean value (defaults to false), which, if\n" - " the file name given is a symbolic link, makes the\n" - " function operate on the symbolic link itself instead\n" - " of its target;\n" - "@deprecated: since version 0.4, this function has been deprecated\n" - " by the L{remove}" - " function\n" + ITEM_DOC + NAME_REMOVE_DOC + NOFOLLOW_DOC + "\n" + ".. deprecated:: 0.4\n" + " this function has been deprecated by the :func:`remove` function.\n" ; /* Wrapper for removexattr */ @@ -680,7 +734,7 @@ pyremovexattr(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "Oet|i", &myarg, NULL, &attrname, &nofollow)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } @@ -706,27 +760,22 @@ pyremovexattr(PyObject *self, PyObject *args) } static char __remove_doc__[] = + "remove(item, name[, nofollow=False, namespace=None])\n" "Remove an attribute from a file.\n" "\n" "Example:\n" + "\n" " >>> xattr.remove('/path/to/file', 'user.comment')\n" "\n" - "@param item: the item to query; either a string representing the\n" - " filename, or a file-like object, or a file descriptor\n" - "@param name: the attribute whose value to set; usually in form of\n" - " system.posix_acl or user.mime_type\n" - "@type name: string\n" - "@param nofollow: if given and True, and the function is passed a\n" - " filename that points to a symlink, the function will act on the\n" - " symlink itself instead of its target\n" - "@type nofollow: boolean\n" - "@param namespace: if given, the attribute must not contain the\n" - " namespace itself, but instead the namespace will be taken from\n" - " this parameter\n" - "@type namespace: string\n" - "@since: 0.4\n" - "@rtype: None\n" - "@raise EnvironmentError: caused by any system errors\n" + ITEM_DOC + NAME_REMOVE_DOC + NOFOLLOW_DOC + NS_DOC + ":returns: None\n" + ":raises EnvironmentError: caused by any system errors\n" + "\n" + ".. versionadded:: 0.4\n" + NS_CHANGED_DOC ; /* Wrapper for removexattr */ @@ -736,24 +785,23 @@ xattr_remove(PyObject *self, PyObject *args, PyObject *keywds) PyObject *myarg, *res; int nofollow = 0; char *attrname = NULL, *name_buf; - char *ns = NULL; + const char *ns = NULL; const char *full_name; int nret; target_t tgt; static char *kwlist[] = {"item", "name", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|iz", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, keywds, "Oet|i" BYTES_CHAR, kwlist, &myarg, NULL, &attrname, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } - full_name = merge_ns(ns, attrname, &name_buf); - if(full_name == NULL) { + if(merge_ns(ns, attrname, &full_name, &name_buf) < 0) { res = NULL; goto freearg; } @@ -781,19 +829,14 @@ xattr_remove(PyObject *self, PyObject *args, PyObject *keywds) } static char __pylistxattr_doc__[] = + "listxattr(item[, nofollow=False])\n" "Return the list of attribute names for a file (deprecated).\n" "\n" - "Parameters:\n" - " - a string representing filename, or a file-like object,\n" - " or a file descriptor; this represents the file to \n" - " be queried\n" - " - (optional) a boolean value (defaults to false), which, if\n" - " the file name given is a symbolic link, makes the\n" - " function operate on the symbolic link itself instead\n" - " of its target;\n" - "@deprecated: since version 0.4, this function has been deprecated\n" - " by the L{list}" - " function\n" + ITEM_DOC + NOFOLLOW_DOC + "\n" + ".. deprecated:: 0.4\n" + " this function has been deprecated by the :func:`list` function.\n" ; /* Wrapper for listxattr */ @@ -812,7 +855,7 @@ pylistxattr(PyObject *self, PyObject *args) /* Parse the arguments */ if (!PyArg_ParseTuple(args, "O|i", &myarg, &nofollow)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) + if(convert_obj(myarg, &tgt, nofollow) < 0) return NULL; /* Find out the needed size of the buffer */ @@ -840,10 +883,18 @@ pylistxattr(PyObject *self, PyObject *args) /* Create the list which will hold the result */ mylist = PyList_New(nattrs); + if(mylist == NULL) + goto freebuf; /* 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, PyBytes_FromString(s)); + PyObject *item = PyBytes_FromString(s); + if(item == NULL) { + Py_DECREF(mylist); + mylist = NULL; + goto freebuf; + } + PyList_SET_ITEM(mylist, nattrs, item); nattrs++; } @@ -859,30 +910,28 @@ pylistxattr(PyObject *self, PyObject *args) } static char __list_doc__[] = + "list(item[, nofollow=False, namespace=None])\n" "Return the list of attribute names for a file.\n" "\n" "Example:\n" + "\n" " >>> xattr.list('/path/to/file')\n" " ['user.test', 'user.comment', 'system.posix_acl_access']\n" " >>> xattr.list('/path/to/file', namespace=xattr.NS_USER)\n" " ['test', 'comment']\n" "\n" - "@param item: the item to query; either a string representing the\n" - " filename, or a file-like object, or a file descriptor\n" - "@param nofollow: if given and True, and the function is passed a\n" - " filename that points to a symlink, the function will act on the\n" - " symlink itself instead of its target\n" - "@type nofollow: boolean\n" - "@param namespace: if given, the attribute must not contain the\n" - " namespace itself, but instead the namespace will be taken from\n" - " this parameter\n" - "@type namespace: string\n" - "@return: list of strings; note that if a namespace argument was\n" - " passed, it (and the separator) will be stripped from the names\n" + ITEM_DOC + NOFOLLOW_DOC + NS_DOC + ":returns: the list of attributes; note that if a namespace \n" + " argument was passed, it (and the separator) will be stripped\n" + " from the names\n" " returned\n" - "@rtype: list\n" - "@raise EnvironmentError: caused by any system errors\n" - "@since: 0.4\n" + ":rtype: list\n" + ":raises EnvironmentError: caused by any system errors\n" + "\n" + ".. versionadded:: 0.4\n" + NS_CHANGED_DOC ; /* Wrapper for listxattr */ @@ -894,17 +943,17 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) ssize_t nalloc, nret; PyObject *myarg; PyObject *res; - char *ns = NULL; + const char *ns = NULL; Py_ssize_t nattrs; char *s; target_t tgt; static char *kwlist[] = {"item", "nofollow", "namespace", NULL}; /* Parse the arguments */ - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|iet", kwlist, - &myarg, &nofollow, NULL, &ns)) + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|i" BYTES_CHAR, kwlist, + &myarg, &nofollow, &ns)) return NULL; - if(!convertObj(myarg, &tgt, nofollow)) { + if(convert_obj(myarg, &tgt, nofollow) < 0) { res = NULL; goto freearg; } @@ -934,12 +983,20 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) } /* Create the list which will hold the result */ res = PyList_New(nattrs); + if(res == NULL) + goto freebuf; /* 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(res, nattrs, PyBytes_FromString(name)); + PyObject *item = PyBytes_FromString(name); + if(item == NULL) { + Py_DECREF(res); + res = NULL; + goto freebuf; + } + PyList_SET_ITEM(res, nattrs, item); nattrs++; } } @@ -951,7 +1008,6 @@ xattr_list(PyObject *self, PyObject *args, PyObject *keywds) freetgt: free_tgt(&tgt); freearg: - PyMem_Free(ns); /* Return the result */ return res; @@ -976,16 +1032,16 @@ static PyMethodDef xattr_methods[] = { }; static char __xattr_doc__[] = \ - "Interface to extended filesystem attributes.\n" - "\n" "This module gives access to the extended attributes present\n" "in some operating systems/filesystems. You can list attributes,\n" "get, set and remove them.\n" "\n" "The module exposes two sets of functions:\n" - " - the 'old' L{listxattr}, L{getxattr}, L{setxattr}, L{removexattr}\n" + " - the 'old' :func:`listxattr`, :func:`getxattr`, :func:`setxattr`,\n" + " :func:`removexattr`\n" " functions which are deprecated since version 0.4\n" - " - the new L{list}, L{get}, L{get_all}, L{set}, L{remove} functions\n" + " - the new :func:`list`, :func:`get`, :func:`get_all`, :func:`set`,\n" + " :func:`remove` functions\n" " which expose a namespace-aware API and simplify a bit the calling\n" " model by using keyword arguments\n" "\n" @@ -1001,23 +1057,23 @@ static char __xattr_doc__[] = \ " ['user.mime_type', 'user.comment']\n" " >>> xattr.removexattr (\"file.txt\", \"user.comment\")\n" "\n" - "@note: Most or all errors reported by the system while using the xattr\n" - "library will be reported by raising a L{EnvironmentError}; under Linux,\n" - "the following C{errno} values are used:\n" - " - C{ENOATTR} and C{ENODATA} mean that the attribute name is invalid\n" - " - C{ENOTSUP} and C{EOPNOTSUPP} mean that the filesystem does not\n" - " support extended attributes, or that the namespace is invalid\n" - " - C{E2BIG} mean that the attribute value is too big\n" - " - C{ERANGE} mean that the attribute name is too big (it might also\n" - " mean an error in the xattr module itself)\n" - " - C{ENOSPC} and C{EDQUOT} are documented as meaning out of disk space\n" - " or out of disk space because of quota limits\n" + ".. note:: Most or all errors reported by the system while using\n" + " the ``xattr`` library will be reported by raising\n" + " a :exc:`EnvironmentError`; under\n" + " Linux, the following ``errno`` values are used:\n" + "\n" + " - ``ENOATTR`` and ``ENODATA`` mean that the attribute name is\n" + " invalid\n" + " - ``ENOTSUP`` and ``EOPNOTSUPP`` mean that the filesystem does not\n" + " support extended attributes, or that the namespace is invalid\n" + " - ``E2BIG`` mean that the attribute value is too big\n" + " - ``ERANGE`` mean that the attribute name is too big (it might also\n" + " mean an error in the xattr module itself)\n" + " - ``ENOSPC`` and ``EDQUOT`` are documented as meaning out of disk\n" + " space or out of disk space because of quota limits\n" + ".. note:: Under Python 3, the namespace argument is a byte string,\n" + " not a unicode string.\n" "\n" - "@group Deprecated API: *xattr\n" - "@group Namespace constants: NS_*\n" - "@group set function flags: XATTR_CREATE, XATTR_REPLACE\n" - "@sort: list, get, get_all, set, remove, listxattr, getxattr, setxattr\n" - " removexattr\n" ; #ifdef IS_PY3K @@ -1041,6 +1097,10 @@ void initxattr(void) #endif { + PyObject *ns_security = NULL; + PyObject *ns_system = NULL; + PyObject *ns_trusted = NULL; + PyObject *ns_user = NULL; #ifdef IS_PY3K PyObject *m = PyModule_Create(&xattrmodule); #else @@ -1054,18 +1114,43 @@ initxattr(void) PyModule_AddStringConstant(m, "__version__", _XATTR_VERSION); PyModule_AddStringConstant(m, "__license__", "GNU Lesser General Public License (LGPL)"); - PyModule_AddStringConstant(m, "__docformat__", "epytext en"); + PyModule_AddStringConstant(m, "__docformat__", "restructuredtext en"); PyModule_AddIntConstant(m, "XATTR_CREATE", XATTR_CREATE); PyModule_AddIntConstant(m, "XATTR_REPLACE", XATTR_REPLACE); /* namespace constants */ - 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")); + if((ns_security = PyBytes_FromString("security")) == NULL) + goto err_out; + if((ns_system = PyBytes_FromString("system")) == NULL) + goto err_out; + if((ns_trusted = PyBytes_FromString("trusted")) == NULL) + goto err_out; + if((ns_user = PyBytes_FromString("user")) == NULL) + goto err_out; + if(PyModule_AddObject(m, "NS_SECURITY", ns_security) < 0) + goto err_out; + ns_security = NULL; + if(PyModule_AddObject(m, "NS_SYSTEM", ns_system) < 0) + goto err_out; + ns_system = NULL; + if(PyModule_AddObject(m, "NS_TRUSTED", ns_trusted) < 0) + goto err_out; + ns_trusted = NULL; + if(PyModule_AddObject(m, "NS_USER", ns_user) < 0) + goto err_out; + ns_user = NULL; #ifdef IS_PY3K return m; +#else + return; #endif + + err_out: + Py_XDECREF(ns_user); + Py_XDECREF(ns_trusted); + Py_XDECREF(ns_system); + Py_XDECREF(ns_security); + INITERROR; } -- 2.39.5