summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBob Ippolito <bob@redivi.com>2013-07-19 11:28:24 -0700
committerBob Ippolito <bob@redivi.com>2013-07-19 11:28:24 -0700
commit08da8ecca3faeec339eae20f924d3a375e1bee95 (patch)
tree652b45238636528d2569444133f3f975d6f23d88
parent32e7c8b809e9641f01a321274839144d6e0eed9b (diff)
parent8368608472bc3db5755ed4b3904d56902f8fe69d (diff)
downloadxattr-0.7.0.tar.gz
Merge branch 'cleanup-and-cffi'v0.7.0
-rw-r--r--.gitignore2
-rw-r--r--CHANGES.txt5
-rw-r--r--_build/_make_constants.py20
-rw-r--r--ez_setup.py284
-rw-r--r--requirements.txt1
-rw-r--r--setup.py28
-rw-r--r--xattr/__init__.py74
-rw-r--r--xattr/constants.py16
-rw-r--r--xattr/lib.py (renamed from xattr/_xattr.c)639
-rw-r--r--xattr/tests/__init__.py6
-rw-r--r--xattr/tests/test_xattr.py24
-rwxr-xr-xxattr/tool.py33
12 files changed, 295 insertions, 837 deletions
diff --git a/.gitignore b/.gitignore
index b5a500d..0a151a0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
/xattr.egg-info
/build
/dist
+/*.egg
*.pyc
*.so
.\#*
+__pycache__
diff --git a/CHANGES.txt b/CHANGES.txt
index bfcb0ad..4e5f24a 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+Version 0.7.0 released 2013-07-19
+
+* Rewritten to use cffi
+ https://github.com/xattr/xattr/pull/11
+
Version 0.6.4 released 2012-02-01
* Updated README.txt to match setup.py description
diff --git a/_build/_make_constants.py b/_build/_make_constants.py
deleted file mode 100644
index c73e8c1..0000000
--- a/_build/_make_constants.py
+++ /dev/null
@@ -1,20 +0,0 @@
-infile = '/usr/include/sys/xattr.h'
-out = file('Lib/xattr/constants.py', 'w')
-for line in file(infile):
- line = line.rstrip()
- if line.startswith('/*') and line.endswith('*/'):
- print >>out, ''
- print >>out, '# ' + line[2:-2].strip()
- elif line.startswith('#define'):
- if lastblank:
- print >>out, ''
- chunks = line.split(None, 3)
- if len(chunks) == 3:
- print >>out, '%s = %s' % (chunks[1], chunks[2])
- elif len(chunks) == 4:
- comment = chunks[3].replace('/*', '').replace('*/', '').strip()
- print >>out, '%s = %s # %s' % (chunks[1], chunks[2], comment)
- if not line:
- lastblank = True
- else:
- lastblank = False
diff --git a/ez_setup.py b/ez_setup.py
deleted file mode 100644
index b74adc0..0000000
--- a/ez_setup.py
+++ /dev/null
@@ -1,284 +0,0 @@
-#!python
-"""Bootstrap setuptools installation
-
-If you want to use setuptools in your package's setup.py, just include this
-file in the same directory with it, and add this to the top of your setup.py::
-
- from ez_setup import use_setuptools
- use_setuptools()
-
-If you want to require a specific version of setuptools, set a download
-mirror, or use an alternate download directory, you can do so by supplying
-the appropriate options to ``use_setuptools()``.
-
-This file can also be run as a script to install or upgrade setuptools.
-"""
-import sys
-DEFAULT_VERSION = "0.6c11"
-DEFAULT_URL = "http://pypi.python.org/packages/%s/s/setuptools/" % sys.version[:3]
-
-md5_data = {
- 'setuptools-0.6b1-py2.3.egg': '8822caf901250d848b996b7f25c6e6ca',
- 'setuptools-0.6b1-py2.4.egg': 'b79a8a403e4502fbb85ee3f1941735cb',
- 'setuptools-0.6b2-py2.3.egg': '5657759d8a6d8fc44070a9d07272d99b',
- 'setuptools-0.6b2-py2.4.egg': '4996a8d169d2be661fa32a6e52e4f82a',
- 'setuptools-0.6b3-py2.3.egg': 'bb31c0fc7399a63579975cad9f5a0618',
- 'setuptools-0.6b3-py2.4.egg': '38a8c6b3d6ecd22247f179f7da669fac',
- 'setuptools-0.6b4-py2.3.egg': '62045a24ed4e1ebc77fe039aa4e6f7e5',
- 'setuptools-0.6b4-py2.4.egg': '4cb2a185d228dacffb2d17f103b3b1c4',
- 'setuptools-0.6c1-py2.3.egg': 'b3f2b5539d65cb7f74ad79127f1a908c',
- 'setuptools-0.6c1-py2.4.egg': 'b45adeda0667d2d2ffe14009364f2a4b',
- 'setuptools-0.6c10-py2.3.egg': 'ce1e2ab5d3a0256456d9fc13800a7090',
- 'setuptools-0.6c10-py2.4.egg': '57d6d9d6e9b80772c59a53a8433a5dd4',
- 'setuptools-0.6c10-py2.5.egg': 'de46ac8b1c97c895572e5e8596aeb8c7',
- 'setuptools-0.6c10-py2.6.egg': '58ea40aef06da02ce641495523a0b7f5',
- 'setuptools-0.6c11-py2.3.egg': '2baeac6e13d414a9d28e7ba5b5a596de',
- 'setuptools-0.6c11-py2.4.egg': 'bd639f9b0eac4c42497034dec2ec0c2b',
- 'setuptools-0.6c11-py2.5.egg': '64c94f3bf7a72a13ec83e0b24f2749b2',
- 'setuptools-0.6c11-py2.6.egg': 'bfa92100bd772d5a213eedd356d64086',
- 'setuptools-0.6c2-py2.3.egg': 'f0064bf6aa2b7d0f3ba0b43f20817c27',
- 'setuptools-0.6c2-py2.4.egg': '616192eec35f47e8ea16cd6a122b7277',
- 'setuptools-0.6c3-py2.3.egg': 'f181fa125dfe85a259c9cd6f1d7b78fa',
- 'setuptools-0.6c3-py2.4.egg': 'e0ed74682c998bfb73bf803a50e7b71e',
- 'setuptools-0.6c3-py2.5.egg': 'abef16fdd61955514841c7c6bd98965e',
- 'setuptools-0.6c4-py2.3.egg': 'b0b9131acab32022bfac7f44c5d7971f',
- 'setuptools-0.6c4-py2.4.egg': '2a1f9656d4fbf3c97bf946c0a124e6e2',
- 'setuptools-0.6c4-py2.5.egg': '8f5a052e32cdb9c72bcf4b5526f28afc',
- 'setuptools-0.6c5-py2.3.egg': 'ee9fd80965da04f2f3e6b3576e9d8167',
- 'setuptools-0.6c5-py2.4.egg': 'afe2adf1c01701ee841761f5bcd8aa64',
- 'setuptools-0.6c5-py2.5.egg': 'a8d3f61494ccaa8714dfed37bccd3d5d',
- 'setuptools-0.6c6-py2.3.egg': '35686b78116a668847237b69d549ec20',
- 'setuptools-0.6c6-py2.4.egg': '3c56af57be3225019260a644430065ab',
- 'setuptools-0.6c6-py2.5.egg': 'b2f8a7520709a5b34f80946de5f02f53',
- 'setuptools-0.6c7-py2.3.egg': '209fdf9adc3a615e5115b725658e13e2',
- 'setuptools-0.6c7-py2.4.egg': '5a8f954807d46a0fb67cf1f26c55a82e',
- 'setuptools-0.6c7-py2.5.egg': '45d2ad28f9750e7434111fde831e8372',
- 'setuptools-0.6c8-py2.3.egg': '50759d29b349db8cfd807ba8303f1902',
- 'setuptools-0.6c8-py2.4.egg': 'cba38d74f7d483c06e9daa6070cce6de',
- 'setuptools-0.6c8-py2.5.egg': '1721747ee329dc150590a58b3e1ac95b',
- 'setuptools-0.6c9-py2.3.egg': 'a83c4020414807b496e4cfbe08507c03',
- 'setuptools-0.6c9-py2.4.egg': '260a2be2e5388d66bdaee06abec6342a',
- 'setuptools-0.6c9-py2.5.egg': 'fe67c3e5a17b12c0e7c541b7ea43a8e6',
- 'setuptools-0.6c9-py2.6.egg': 'ca37b1ff16fa2ede6e19383e7b59245a',
-}
-
-import sys, os
-try: from hashlib import md5
-except ImportError: from md5 import md5
-
-def _validate_md5(egg_name, data):
- if egg_name in md5_data:
- digest = md5(data).hexdigest()
- if digest != md5_data[egg_name]:
- print >>sys.stderr, (
- "md5 validation of %s failed! (Possible download problem?)"
- % egg_name
- )
- sys.exit(2)
- return data
-
-def use_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- download_delay=15
-):
- """Automatically find/download setuptools and make it available on sys.path
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end with
- a '/'). `to_dir` is the directory where setuptools will be downloaded, if
- it is not already available. If `download_delay` is specified, it should
- be the number of seconds that will be paused before initiating a download,
- should one be required. If an older version of setuptools is installed,
- this routine will print a message to ``sys.stderr`` and raise SystemExit in
- an attempt to abort the calling script.
- """
- was_imported = 'pkg_resources' in sys.modules or 'setuptools' in sys.modules
- def do_download():
- egg = download_setuptools(version, download_base, to_dir, download_delay)
- sys.path.insert(0, egg)
- import setuptools; setuptools.bootstrap_install_from = egg
- try:
- import pkg_resources
- except ImportError:
- return do_download()
- try:
- pkg_resources.require("setuptools>="+version); return
- except pkg_resources.VersionConflict, e:
- if was_imported:
- print >>sys.stderr, (
- "The required version of setuptools (>=%s) is not available, and\n"
- "can't be installed while this script is running. Please install\n"
- " a more recent version first, using 'easy_install -U setuptools'."
- "\n\n(Currently using %r)"
- ) % (version, e.args[0])
- sys.exit(2)
- except pkg_resources.DistributionNotFound:
- pass
-
- del pkg_resources, sys.modules['pkg_resources'] # reload ok
- return do_download()
-
-def download_setuptools(
- version=DEFAULT_VERSION, download_base=DEFAULT_URL, to_dir=os.curdir,
- delay = 15
-):
- """Download setuptools from a specified location and return its filename
-
- `version` should be a valid setuptools version number that is available
- as an egg for download under the `download_base` URL (which should end
- with a '/'). `to_dir` is the directory where the egg will be downloaded.
- `delay` is the number of seconds to pause before an actual download attempt.
- """
- import urllib2, shutil
- egg_name = "setuptools-%s-py%s.egg" % (version,sys.version[:3])
- url = download_base + egg_name
- saveto = os.path.join(to_dir, egg_name)
- src = dst = None
- if not os.path.exists(saveto): # Avoid repeated downloads
- try:
- from distutils import log
- if delay:
- log.warn("""
----------------------------------------------------------------------------
-This script requires setuptools version %s to run (even to display
-help). I will attempt to download it for you (from
-%s), but
-you may need to enable firewall access for this script first.
-I will start the download in %d seconds.
-
-(Note: if this machine does not have network access, please obtain the file
-
- %s
-
-and place it in this directory before rerunning this script.)
----------------------------------------------------------------------------""",
- version, download_base, delay, url
- ); from time import sleep; sleep(delay)
- log.warn("Downloading %s", url)
- src = urllib2.urlopen(url)
- # Read/write all in one block, so we don't create a corrupt file
- # if the download is interrupted.
- data = _validate_md5(egg_name, src.read())
- dst = open(saveto,"wb"); dst.write(data)
- finally:
- if src: src.close()
- if dst: dst.close()
- return os.path.realpath(saveto)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-def main(argv, version=DEFAULT_VERSION):
- """Install or upgrade setuptools and EasyInstall"""
- try:
- import setuptools
- except ImportError:
- egg = None
- try:
- egg = download_setuptools(version, delay=0)
- sys.path.insert(0,egg)
- from setuptools.command.easy_install import main
- return main(list(argv)+[egg]) # we're done here
- finally:
- if egg and os.path.exists(egg):
- os.unlink(egg)
- else:
- if setuptools.__version__ == '0.0.1':
- print >>sys.stderr, (
- "You have an obsolete version of setuptools installed. Please\n"
- "remove it from your system entirely before rerunning this script."
- )
- sys.exit(2)
-
- req = "setuptools>="+version
- import pkg_resources
- try:
- pkg_resources.require(req)
- except pkg_resources.VersionConflict:
- try:
- from setuptools.command.easy_install import main
- except ImportError:
- from easy_install import main
- main(list(argv)+[download_setuptools(delay=0)])
- sys.exit(0) # try to force an exit
- else:
- if argv:
- from setuptools.command.easy_install import main
- main(argv)
- else:
- print "Setuptools version",version,"or greater has been installed."
- print '(Run "ez_setup.py -U setuptools" to reinstall or upgrade.)'
-
-def update_md5(filenames):
- """Update our built-in md5 registry"""
-
- import re
-
- for name in filenames:
- base = os.path.basename(name)
- f = open(name,'rb')
- md5_data[base] = md5(f.read()).hexdigest()
- f.close()
-
- data = [" %r: %r,\n" % it for it in md5_data.items()]
- data.sort()
- repl = "".join(data)
-
- import inspect
- srcfile = inspect.getsourcefile(sys.modules[__name__])
- f = open(srcfile, 'rb'); src = f.read(); f.close()
-
- match = re.search("\nmd5_data = {\n([^}]+)}", src)
- if not match:
- print >>sys.stderr, "Internal error!"
- sys.exit(2)
-
- src = src[:match.start(1)] + repl + src[match.end(1):]
- f = open(srcfile,'w')
- f.write(src)
- f.close()
-
-
-if __name__=='__main__':
- if len(sys.argv)>2 and sys.argv[1]=='--md5update':
- update_md5(sys.argv[2:])
- else:
- main(sys.argv[1:])
-
-
-
-
-
-
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..08c056b
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+cffi>=0.4
diff --git a/setup.py b/setup.py
index 2dea527..d33c099 100644
--- a/setup.py
+++ b/setup.py
@@ -1,11 +1,22 @@
#!/usr/bin/env python
-import ez_setup
-ez_setup.use_setuptools()
+import os
+import sys
-from setuptools import setup, Extension
+from setuptools import setup
+from distutils.command.build import build
-VERSION = '0.6.4'
+class cffi_build(build):
+ """This is a shameful hack to ensure that cffi is present when
+ we specify ext_modules. We can't do this eagerly because
+ setup_requires hasn't run yet.
+ """
+ def finalize_options(self):
+ from xattr.lib import ffi
+ self.distribution.ext_modules = [ffi.verifier.get_extension()]
+ build.finalize_options(self)
+
+VERSION = '0.7.0'
DESCRIPTION = "Python wrapper for extended filesystem attributes"
LONG_DESCRIPTION = """
Extended attributes extend the basic attributes of files and directories
@@ -16,7 +27,7 @@ Extended attributes are currently only available on Darwin 8.0+ (Mac OS X 10.4)
and Linux 2.6+. Experimental support is included for Solaris and FreeBSD.
"""
-CLASSIFIERS = filter(None, map(str.strip,
+CLASSIFIERS = filter(bool, map(str.strip,
"""
Environment :: Console
Intended Audience :: Developers
@@ -40,15 +51,16 @@ setup(
url="http://github.com/xattr/xattr",
license="MIT License",
packages=['xattr'],
+ ext_package='xattr',
platforms=['MacOS X', 'Linux', 'FreeBSD', 'Solaris'],
- ext_modules=[
- Extension("xattr._xattr", ["xattr/_xattr.c"]),
- ],
entry_points={
'console_scripts': [
"xattr = xattr.tool:main",
],
},
+ install_requires=["cffi>=0.4"],
+ setup_requires=["cffi>=0.4"],
test_suite="xattr.tests.all_tests_suite",
zip_safe=False,
+ cmdclass={'build': cffi_build},
)
diff --git a/xattr/__init__.py b/xattr/__init__.py
index d9431f9..a452f66 100644
--- a/xattr/__init__.py
+++ b/xattr/__init__.py
@@ -7,26 +7,20 @@ The xattr type wraps a path or file descriptor with a dict-like interface
that exposes these extended attributes.
"""
-__version__ = '0.6.4'
-from constants import XATTR_NOFOLLOW, XATTR_CREATE, XATTR_REPLACE, \
- XATTR_NOSECURITY, XATTR_MAXNAMELEN, XATTR_FINDERINFO_NAME, \
- XATTR_RESOURCEFORK_NAME
+__version__ = '0.7.0'
-import _xattr
+from .lib import (XATTR_NOFOLLOW, XATTR_CREATE, XATTR_REPLACE,
+ XATTR_NOSECURITY, XATTR_MAXNAMELEN, XATTR_FINDERINFO_NAME,
+ XATTR_RESOURCEFORK_NAME, _getxattr, _fgetxattr, _setxattr, _fsetxattr,
+ _removexattr, _fremovexattr, _listxattr, _flistxattr)
-def _pyflakes_api():
- # trick pyflakes into thinking these are used.
- return [
- XATTR_NOFOLLOW, XATTR_CREATE, XATTR_REPLACE,
- XATTR_NOSECURITY, XATTR_MAXNAMELEN, XATTR_FINDERINFO_NAME,
- XATTR_RESOURCEFORK_NAME,
- ]
+__all__ = [
+ "XATTR_NOFOLLOW", "XATTR_CREATE", "XATTR_REPLACE", "XATTR_NOSECURITY",
+ "XATTR_MAXNAMELEN", "XATTR_FINDERINFO_NAME", "XATTR_RESOURCEFORK_NAME",
+ "xattr", "listxattr", "getxattr", "setxattr", "removexattr"
+]
-def _boundfunc(func, first):
- def _func(*args):
- return func(first, *args)
- return _func
class xattr(object):
"""
@@ -45,34 +39,24 @@ class xattr(object):
"""
self.obj = obj
self.options = options
- self.flavor = None
fileno = getattr(obj, 'fileno', None)
if fileno is not None:
- obj = fileno()
- if isinstance(obj, int):
- self.flavor = 'fd'
- self._bind_any('f%sxattr', obj, options)
+ self.value = fileno()
else:
- self.flavor = 'file'
- self._bind_any('%sxattr', obj, options)
+ self.value = obj
def __repr__(self):
- if self.flavor:
- return '<%s %s=%r>' % (type(self).__name__, self.flavor, self.obj)
+ if isinstance(self.value, int):
+ flavor = "fd"
else:
- return object.__repr__(self)
-
- def _bind_any(self, fmt, obj, options):
- options = self.options
- for method in ("get", "set", "remove", "list"):
- name = '_' + method
- func = getattr(_xattr, fmt % (method,))
- meth = _boundfunc(func, obj)
- try:
- meth.__name__ = name
- except TypeError:
- pass
- setattr(self, name, meth)
+ flavor = "file"
+ return "<%s %s=%r>" % (type(self).__name__, flavor, self.value)
+
+ def _call(self, name_func, fd_func, *args):
+ if isinstance(self.value, int):
+ return fd_func(self.value, *args)
+ else:
+ return name_func(self.value, *args)
def get(self, name, options=0):
"""
@@ -81,7 +65,7 @@ class xattr(object):
See x-man-page://2/getxattr for options and possible errors.
"""
- return self._get(name, 0, 0, options | self.options)
+ return self._call(_getxattr, _fgetxattr, name, 0, 0, options | self.options)
def set(self, name, value, options=0):
"""
@@ -90,7 +74,7 @@ class xattr(object):
See x-man-page://2/setxattr for options and possible errors.
"""
- self._set(name, value, 0, options | self.options)
+ return self._call(_setxattr, _fsetxattr, name, value, 0, options | self.options)
def remove(self, name, options=0):
"""
@@ -99,6 +83,7 @@ class xattr(object):
See x-man-page://2/removexattr for options and possible errors.
"""
+ return self._call(_removexattr, _fremovexattr, name, options | self.options)
self._remove(name, options | self.options)
def list(self, options=0):
@@ -108,7 +93,7 @@ class xattr(object):
See x-man-page://2/listxattr for options and possible errors.
"""
- res = self._list(options | self.options).split('\x00')
+ res = self._call(_listxattr, _flistxattr, options | self.options).split('\x00')
res.pop()
return [unicode(s, 'utf-8') for s in res]
@@ -186,20 +171,19 @@ class xattr(object):
def listxattr(f, symlink=False):
- __doc__ = xattr.list.__doc__
return tuple(xattr(f).list(options=symlink and XATTR_NOFOLLOW or 0))
+
def getxattr(f, attr, symlink=False):
- __doc__ = xattr.get.__doc__
return xattr(f).get(attr, options=symlink and XATTR_NOFOLLOW or 0)
+
def setxattr(f, attr, value, options=0, symlink=False):
- __doc__ = xattr.set.__doc__
if symlink:
options |= XATTR_NOFOLLOW
return xattr(f).set(attr, value, options=options)
+
def removexattr(f, attr, symlink=False):
- __doc__ = xattr.remove.__doc__
options = symlink and XATTR_NOFOLLOW or 0
return xattr(f).remove(attr, options=options)
diff --git a/xattr/constants.py b/xattr/constants.py
deleted file mode 100644
index 8ee1c3b..0000000
--- a/xattr/constants.py
+++ /dev/null
@@ -1,16 +0,0 @@
-
-# Options for pathname based xattr calls
-XATTR_NOFOLLOW = 0x0001 # Don't follow symbolic links
-
-# Options for setxattr calls
-XATTR_CREATE = 0x0002 # set the value, fail if attr already exists
-XATTR_REPLACE = 0x0004 # set the value, fail if attr does not exist
-
-# Set this to bypass authorization checking (eg. if doing auth-related work)
-XATTR_NOSECURITY = 0x0008
-
-XATTR_MAXNAMELEN = 127
-
-XATTR_FINDERINFO_NAME = "com.apple.FinderInfo"
-
-XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork"
diff --git a/xattr/_xattr.c b/xattr/lib.py
index 606450d..b72a1ff 100644
--- a/xattr/_xattr.c
+++ b/xattr/lib.py
@@ -1,3 +1,31 @@
+import os
+import sys
+
+import cffi
+
+ffi = cffi.FFI()
+ffi.cdef("""
+#define XATTR_NOFOLLOW ...
+#define XATTR_CREATE ...
+#define XATTR_REPLACE ...
+#define XATTR_NOSECURITY ...
+#define XATTR_MAXNAMELEN ...
+
+ssize_t xattr_getxattr(const char *, const char *, void *, ssize_t, uint32_t, int);
+ssize_t xattr_fgetxattr(int, const char *, void *, ssize_t, uint32_t, int);
+
+ssize_t xattr_setxattr(const char *, const char *, void *, ssize_t, uint32_t, int);
+ssize_t xattr_fsetxattr(int, const char *, void *, ssize_t, uint32_t, int);
+
+ssize_t xattr_removexattr(const char *, const char *, int);
+ssize_t xattr_fremovexattr(int, const char *, int);
+
+ssize_t xattr_listxattr(const char *, char *, size_t, int);
+ssize_t xattr_flistxattr(int, char *, size_t, int);
+
+""")
+
+lib = ffi.verify("""
#include "Python.h"
#ifdef __FreeBSD__
#include <sys/extattr.h>
@@ -21,7 +49,7 @@
/* Converts a freebsd format attribute list into a NULL terminated list.
- * While the man page on extattr_list_file says it is NULL terminated,
+ * While the man page on extattr_list_file says it is NULL terminated,
* it is actually the first byte that is the length of the
* following attribute.
*/
@@ -37,10 +65,10 @@ static void convert_bsd_list(char *namebuf, size_t size)
}
static ssize_t xattr_getxattr(const char *path, const char *name,
- void *value, ssize_t size, u_int32_t position,
+ void *value, ssize_t size, u_int32_t position,
int options)
{
- if (position != 0 ||
+ if (position != 0 ||
!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
return -1;
}
@@ -73,7 +101,7 @@ static ssize_t xattr_setxattr(const char *path, const char *name,
options == XATTR_XATTR_REPLACE) {
/* meh. FreeBSD doesn't really have this in it's
- * API... Oh well.
+ * API... Oh well.
*/
}
else if (options != 0) {
@@ -179,7 +207,7 @@ static ssize_t xattr_fsetxattr(int fd, const char *name, void *value,
return -1;
}
else {
- rv = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER,
+ rv = extattr_set_fd(fd, EXTATTR_NAMESPACE_USER,
name, value, size);
}
@@ -254,19 +282,19 @@ static ssize_t xattr_fgetxattr(int fd, const char *name, void *value,
/* XXX should check that name does not have / characters in it */
xfd = openat(fd, name, O_RDONLY | O_XATTR);
if (xfd == -1) {
- return -1;
+ return -1;
}
if (lseek(xfd, position, SEEK_SET) == -1) {
- close(xfd);
- return -1;
+ close(xfd);
+ return -1;
}
if (value == NULL) {
if (fstat(xfd, &statbuf) == -1) {
- close(xfd);
- return -1;
+ close(xfd);
+ return -1;
}
- close(xfd);
- return statbuf.st_size;
+ close(xfd);
+ return statbuf.st_size;
}
/* XXX should keep reading until the buffer is exhausted or EOF */
bytes = read(xfd, value, size);
@@ -275,22 +303,22 @@ static ssize_t xattr_fgetxattr(int fd, const char *name, void *value,
}
static ssize_t xattr_getxattr(const char *path, const char *name,
- void *value, ssize_t size, u_int32_t position,
+ void *value, ssize_t size, u_int32_t position,
int options)
{
int fd;
ssize_t bytes;
- if (position != 0 ||
+ if (position != 0 ||
!(options == 0 || options == XATTR_XATTR_NOFOLLOW)) {
return -1;
}
fd = open(path,
- O_RDONLY |
- ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
+ O_RDONLY |
+ ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
if (fd == -1) {
- return -1;
+ return -1;
}
bytes = xattr_fgetxattr(fd, name, value, size, position, options);
close(fd);
@@ -305,21 +333,21 @@ static ssize_t xattr_fsetxattr(int fd, const char *name, void *value,
/* XXX should check that name does not have / characters in it */
xfd = openat(fd, name, O_XATTR | O_TRUNC |
- ((options & XATTR_XATTR_CREATE) ? O_EXCL : 0) |
- ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0) |
- ((options & XATTR_XATTR_REPLACE) ? O_RDWR : O_WRONLY|O_CREAT),
- 0644);
+ ((options & XATTR_XATTR_CREATE) ? O_EXCL : 0) |
+ ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0) |
+ ((options & XATTR_XATTR_REPLACE) ? O_RDWR : O_WRONLY|O_CREAT),
+ 0644);
if (xfd == -1) {
- return -1;
+ return -1;
}
while (size > 0) {
- bytes = write(xfd, value, size);
- if (bytes == -1) {
- close(xfd);
- return -1;
- }
- size -= bytes;
- value += bytes;
+ bytes = write(xfd, value, size);
+ if (bytes == -1) {
+ close(xfd);
+ return -1;
+ }
+ size -= bytes;
+ value += bytes;
}
close(xfd);
return 0;
@@ -337,9 +365,9 @@ static ssize_t xattr_setxattr(const char *path, const char *name,
}
fd = open(path,
- O_RDONLY | (options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0);
+ O_RDONLY | (options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0);
if (fd == -1) {
- return -1;
+ return -1;
}
bytes = xattr_fsetxattr(fd, name, value, size, position, options);
close(fd);
@@ -358,7 +386,7 @@ static ssize_t xattr_fremovexattr(int fd, const char *name, int options)
}
xfd = openat(fd, ".", O_XATTR, 0644);
if (xfd == -1) {
- return -1;
+ return -1;
}
status = unlinkat(xfd, name, 0);
close(xfd);
@@ -372,9 +400,9 @@ static ssize_t xattr_removexattr(const char *path, const char *name,
ssize_t status;
fd = open(path,
- O_RDONLY | ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
+ O_RDONLY | ((options & XATTR_XATTR_NOFOLLOW) ? O_NOFOLLOW : 0));
if (fd == -1) {
- return -1;
+ return -1;
}
status = xattr_fremovexattr(fd, name, options);
close(fd);
@@ -396,20 +424,20 @@ static ssize_t xattr_xflistxattr(int xfd, char *namebuf, size_t size, int option
if (strcmp(entry->d_name, ".") == 0 ||
strcmp(entry->d_name, "..") == 0)
continue;
- esize = strlen(entry->d_name);
- if (nsize + esize + 1 <= size) {
+ esize = strlen(entry->d_name);
+ if (nsize + esize + 1 <= size) {
snprintf((char *)(namebuf + nsize), esize + 1,
entry->d_name);
- }
- nsize += esize + 1; /* +1 for \0 */
+ }
+ nsize += esize + 1; /* +1 for \0 */
}
closedir(dirp);
return nsize;
-}
+}
static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options)
{
int xfd;
-
+
xfd = openat(fd, ".", O_RDONLY);
return xattr_xflistxattr(xfd, namebuf, size, options);
}
@@ -549,400 +577,137 @@ static ssize_t xattr_flistxattr(int fd, char *namebuf, size_t size, int options)
#define xattr_listxattr listxattr
#define xattr_flistxattr flistxattr
#endif
-
-static PyObject *xattr_error(void);
-static PyObject *xattr_error_with_filename(char *name);
-
-static PyObject *
-xattr_error(void)
-{
- return PyErr_SetFromErrno(PyExc_IOError);
-}
-
-static PyObject *
-xattr_error_with_filename(char *name)
-{
- return PyErr_SetFromErrnoWithFilename(PyExc_IOError, name);
-}
-
-PyDoc_STRVAR(pydoc_getxattr,
- "getxattr(path, name, size=0, position=0, options=0) -> str\n"
- "\n"
- "..."
-);
-static PyObject*
-py_getxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "path", "name", "size", "position", "options", NULL }; */
- char *path;
- char *name;
- PyObject *buffer;
- int options = 0;
- size_t size = 0;
- u_int32_t position = 0;
- ssize_t res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "etet|IIi:getxattr", /* keywords, */
- Py_FileSystemDefaultEncoding, &path,
- Py_FileSystemDefaultEncoding, &name,
- &size,
- &position,
- &options)) {
- return NULL;
- }
- if (size == 0) {
- Py_BEGIN_ALLOW_THREADS
- res = xattr_getxattr((const char *)path, (const char *)name, NULL, 0, position, options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- PyObject *tmp = xattr_error_with_filename(path);
- PyMem_Free(path);
- PyMem_Free(name);
- return tmp;
- }
- size = res;
- }
- buffer = PyString_FromStringAndSize((char *)NULL, size);
- if (buffer == NULL) {
- PyMem_Free(path);
- PyMem_Free(name);
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_getxattr((const char *)path, (const char *)name, (void *)PyString_AS_STRING(buffer), size, position, options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- PyObject *tmp = xattr_error_with_filename(path);
- Py_DECREF(buffer);
- PyMem_Free(path);
- PyMem_Free(name);
- return tmp;
- }
- PyMem_Free(path);
- PyMem_Free(name);
- if (res != size) {
- _PyString_Resize(&buffer, (int)res);
- }
- return buffer;
-}
-
-PyDoc_STRVAR(pydoc_fgetxattr,
- "fgetxattr(fd, name, size=0, position=0, options=0) -> str\n"
- "\n"
- "..."
-);
-static PyObject*
-py_fgetxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "fd", "name", "size", "position", "options", NULL }; */
- int fd;
- char *name;
- PyObject *buffer;
- int options = 0;
- size_t size = 0;
- u_int32_t position = 0;
- ssize_t res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "iet|IIi:fgetxattr", /* keywords, */
- &fd,
- Py_FileSystemDefaultEncoding, &name,
- &size,
- &position,
- &options)) {
- return NULL;
- }
- if (size == 0) {
- Py_BEGIN_ALLOW_THREADS
- res = xattr_fgetxattr(fd, (const char *)name, NULL, 0, position, options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- PyMem_Free(name);
- return xattr_error();
- }
- size = res;
- }
- buffer = PyString_FromStringAndSize((char *)NULL, size);
- if (buffer == NULL) {
- PyMem_Free(name);
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_fgetxattr(fd, (const char *)name, (void *)PyString_AS_STRING(buffer), size, position, options);
- Py_END_ALLOW_THREADS
- PyMem_Free(name);
- if (res == -1) {
- Py_DECREF(buffer);
- return xattr_error();
- }
- if (res != size) {
- _PyString_Resize(&buffer, (int)res);
- }
- return buffer;
-}
-
-PyDoc_STRVAR(pydoc_setxattr,
- "setxattr(path, name, value, position=0, options=0) -> None\n"
- "\n"
- "..."
-);
-static PyObject*
-py_setxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "path", "name", "value", "position", "options", NULL }; */
- PyObject *result;
- char *path;
- char *name;
- int options = 0;
- char *value;
- int size;
- u_int32_t position = 0;
- int res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "etets#|Ii:setxattr", /* keywords, */
- Py_FileSystemDefaultEncoding, &path,
- Py_FileSystemDefaultEncoding, &name,
- &value, &size,
- &position,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_setxattr((const char *)path, (const char *)name, (void *)value, size, position, options);
- Py_END_ALLOW_THREADS
- if (res) {
- result = xattr_error_with_filename(path);
- } else {
- Py_INCREF(Py_None);
- result = Py_None;
- }
- PyMem_Free(path);
- PyMem_Free(name);
- return result;
-}
-
-PyDoc_STRVAR(pydoc_fsetxattr,
- "fsetxattr(fd, name, value, position=0, options=0) -> None\n"
- "\n"
- "..."
-);
-static PyObject*
-py_fsetxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "fd", "name", "value", "position", "options", NULL }; */
- int fd;
- char *name;
- int options = 0;
- char *value;
- int size;
- u_int32_t position = 0;
- int res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "iets#|Ii:fsetxattr", /* keywords, */
- &fd,
- Py_FileSystemDefaultEncoding, &name,
- &value, &size,
- &position,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_fsetxattr(fd, (const char *)name, (void *)value, size, position, options);
- Py_END_ALLOW_THREADS
- PyMem_Free(name);
- if (res) {
- return xattr_error();
- }
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-PyDoc_STRVAR(pydoc_removexattr,
- "removexattr(path, name, options=0) -> None\n"
- "\n"
- "..."
-);
-static PyObject*
-py_removexattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "path", "name", "options", NULL }; */
- char *path;
- char *name;
- int options = 0;
- int res;
- PyObject *result;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "etet|i:removexattr", /* keywords, */
- Py_FileSystemDefaultEncoding, &path,
- Py_FileSystemDefaultEncoding, &name,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_removexattr((const char *)path, (const char *)name, options);
- Py_END_ALLOW_THREADS
- if (res) {
- result = xattr_error_with_filename(path);
- } else {
- Py_INCREF(Py_None);
- result = Py_None;
- }
- PyMem_Free(path);
- PyMem_Free(name);
- return result;
-}
-
-PyDoc_STRVAR(pydoc_fremovexattr,
- "fremovexattr(fd, name, options=0) -> None\n"
- "\n"
- "..."
-);
-static PyObject*
-py_fremovexattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "fd", "name", "options", NULL }; */
- int fd;
- char *name;
- int options = 0;
- int res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "iet|i:fremovexattr", /* keywords, */
- &fd,
- Py_FileSystemDefaultEncoding, &name,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_fremovexattr(fd, (const char *)name, options);
- Py_END_ALLOW_THREADS
- PyMem_Free(name);
- if (res) {
- return xattr_error();
- }
- Py_INCREF(Py_None);
- return Py_None;
-}
-
-PyDoc_STRVAR(pydoc_listxattr,
- "listxattr(path, options=0) -> str\n"
- "\n"
- "..."
-);
-static PyObject*
-py_listxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "path", "options", NULL }; */
- PyObject *buffer;
- char *path;
- int options = 0;
- ssize_t res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "et|i:listxattr", /* keywords, */
- Py_FileSystemDefaultEncoding, &path,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_listxattr((const char *)path, NULL, 0, options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- PyObject *tmp = xattr_error_with_filename(path);
- PyMem_Free(path);
- return tmp;
- }
- buffer = PyString_FromStringAndSize((char *)NULL, (int)res);
- if (buffer == NULL) {
- PyMem_Free(path);
- return NULL;
- }
- /* avoid 2nd listxattr call if the first one returns 0 */
- if (res == 0) {
- PyMem_Free(path);
- return buffer;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_listxattr((const char *)path, (void *)PyString_AS_STRING(buffer), (size_t)PyString_GET_SIZE(buffer), options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- PyObject *tmp = xattr_error_with_filename(path);
- Py_DECREF(buffer);
- PyMem_Free(path);
- return tmp;
- }
- PyMem_Free(path);
- if (res != (ssize_t)PyString_GET_SIZE(buffer)) {
- _PyString_Resize(&buffer, (int)res);
- }
- return buffer;
-}
-
-PyDoc_STRVAR(pydoc_flistxattr,
- "flistxattr(fd, options=0) -> str\n"
- "\n"
- "..."
-);
-static PyObject*
-py_flistxattr(PyObject* self __attribute__((__unused__)), PyObject *args) /* , PyObject *kwds) */
-{
- /* static char *keywords[] = { "fd", "options", NULL }; */
- PyObject *buffer;
- int fd;
- int options = 0;
- ssize_t res;
- if (!PyArg_ParseTuple(args, /* AndKeywords(args, kwds, */
- "i|i:flistxattr", /* keywords, */
- &fd,
- &options)) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_flistxattr(fd, NULL, 0, options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- return xattr_error();
- }
- buffer = PyString_FromStringAndSize((char *)NULL, (int)res);
- if (buffer == NULL) {
- return NULL;
- }
- Py_BEGIN_ALLOW_THREADS
- res = xattr_flistxattr(fd, (void *)PyString_AS_STRING(buffer), (size_t)PyString_GET_SIZE(buffer), options);
- Py_END_ALLOW_THREADS
- if (res == -1) {
- Py_DECREF(buffer);
- return xattr_error();
- }
- if (res != (ssize_t)PyString_GET_SIZE(buffer)) {
- _PyString_Resize(&buffer, (int)res);
- }
- return buffer;
-}
-
-
-#define DEFN(n) \
- { \
- #n, \
- (PyCFunction)py_ ##n, \
- METH_VARARGS /* | METH_KEYWORDS */, \
- pydoc_ ##n \
- }
-static PyMethodDef xattr_methods[] = {
- DEFN(getxattr),
- DEFN(fgetxattr),
- DEFN(setxattr),
- DEFN(fsetxattr),
- DEFN(removexattr),
- DEFN(fremovexattr),
- DEFN(listxattr),
- DEFN(flistxattr),
- {}
-};
-#undef DEFN
-
-void init_xattr(void);
-
-void
-init_xattr(void)
-{
- PyObject *m;
- m = Py_InitModule4("_xattr", xattr_methods, NULL, NULL, PYTHON_API_VERSION);
-}
+""", ext_package='xattr')
+
+XATTR_NOFOLLOW = lib.XATTR_NOFOLLOW
+XATTR_CREATE = lib.XATTR_CREATE
+XATTR_REPLACE = lib.XATTR_REPLACE
+XATTR_NOSECURITY = lib.XATTR_NOSECURITY
+XATTR_MAXNAMELEN = lib.XATTR_MAXNAMELEN
+
+XATTR_FINDERINFO_NAME = "com.apple.FinderInfo"
+XATTR_RESOURCEFORK_NAME = "com.apple.ResourceFork"
+
+
+def fs_encode(val):
+ if isinstance(val, bytes):
+ return val.encode(sys.getfilesystemencoding())
+ else:
+ return val
+
+
+def error(path=None):
+ errno = ffi.errno
+ strerror = os.strerror(ffi.errno)
+ if path:
+ raise IOError(errno, strerror, path)
+ else:
+ raise IOError(errno, strerror)
+
+
+def _getxattr(path, name, size=0, position=0, options=0):
+ """
+ getxattr(path, name, size=0, position=0, options=0) -> str
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ if size == 0:
+ res = lib.xattr_getxattr(path, name, ffi.NULL, 0, position, options)
+ if res == -1:
+ raise error(path)
+ size = res
+ buf = ffi.new("char[]", size)
+ res = lib.xattr_getxattr(path, name, buf, size, position, options)
+ if res == -1:
+ raise error(path)
+ return ffi.buffer(buf)[:res]
+
+
+def _fgetxattr(fd, name, size=0, position=0, options=0):
+ """
+ fgetxattr(fd, name, size=0, position=0, options=0) -> str
+ """
+ name = fs_encode(name)
+ if size == 0:
+ res = lib.xattr_fgetxattr(fd, name, ffi.NULL, 0, position, options)
+ if res == -1:
+ raise error()
+ size = res
+ buf = ffi.new("char[]", size)
+ res = lib.xattr_fgetxattr(fd, name, buf, size, position, options)
+ if res == -1:
+ raise error()
+ return ffi.buffer(buf)[:res]
+
+
+def _setxattr(path, name, value, position=0, options=0):
+ """
+ setxattr(path, name, value, position=0, options=0) -> None
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ res = lib.xattr_setxattr(path, name, value, len(value), position, options)
+ if res:
+ raise error(path)
+
+
+def _fsetxattr(fd, name, value, position=0, options=0):
+ """
+ fsetxattr(fd, name, value, position=0, options=0) -> None
+ """
+ name = fs_encode(name)
+ res = lib.xattr_fsetxattr(fd, name, value, len(value), position, options)
+ if res:
+ raise error()
+
+
+def _removexattr(path, name, options=0):
+ """
+ removexattr(path, name, options=0) -> None
+ """
+ path = fs_encode(path)
+ name = fs_encode(name)
+ res = lib.xattr_removexattr(path, name, options)
+ if res:
+ raise error(path)
+
+
+def _fremovexattr(fd, name, options=0):
+ """
+ fremovexattr(fd, name, options=0) -> None
+ """
+ name = fs_encode(name)
+ res = lib.xattr_fremovexattr(fd, name, options)
+ if res:
+ raise error()
+
+
+def _listxattr(path, options=0):
+ """
+ listxattr(path, options=0) -> str
+ """
+ path = fs_encode(path)
+ res = lib.xattr_listxattr(path, ffi.NULL, 0, options)
+ if res == -1:
+ raise error(path)
+ elif res == 0:
+ return b""
+ buf = ffi.new("char[]", res)
+ res = lib.xattr_listxattr(path, buf, res, options)
+ if res == -1:
+ raise error(path)
+ return ffi.buffer(buf)[:res]
+
+
+def _flistxattr(fd, options=0):
+ """
+ flistxattr(fd, options=0) -> str
+ """
+ res = lib.xattr_flistxattr(fd, ffi.NULL, 0, options)
+ if res == 1:
+ raise error()
+ buf = ffi.new("char[]", res)
+ res = lib.xattr_flistxattr(fd, buf, res, options)
+ if res == -1:
+ raise error()
+ return ffi.buffer(buf)[:res]
diff --git a/xattr/tests/__init__.py b/xattr/tests/__init__.py
index 2391ae6..739dd58 100644
--- a/xattr/tests/__init__.py
+++ b/xattr/tests/__init__.py
@@ -1,3 +1,5 @@
+import os
+import sys
import unittest
@@ -15,7 +17,5 @@ def main():
if __name__ == '__main__':
- import os
- import sys
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
- main() \ No newline at end of file
+ main()
diff --git a/xattr/tests/test_xattr.py b/xattr/tests/test_xattr.py
index 3a920ec..d75d7ee 100644
--- a/xattr/tests/test_xattr.py
+++ b/xattr/tests/test_xattr.py
@@ -4,15 +4,9 @@ from tempfile import mkdtemp, NamedTemporaryFile
import xattr
-class TestFile(TestCase):
- def setUp(self):
- self.tempfile = NamedTemporaryFile()
- self.tempfilename = self.tempfile.name
-
- def tearDown(self):
- self.tempfile.close()
- def testAttr(self):
+class BaseTestXattr(object):
+ def test_attr(self):
x = xattr.xattr(self.tempfile)
self.assertEqual(x.keys(), [])
self.assertEqual(dict(x), {})
@@ -33,7 +27,7 @@ class TestFile(TestCase):
x = xattr.xattr(self.tempfile)
self.assertTrue('user.sop.foo' not in x)
- def testSymlinkAttrs(self):
+ def test_symlink_attrs(self):
symlinkPath = self.tempfilename + '.link'
os.symlink(self.tempfilename, symlinkPath)
try:
@@ -45,7 +39,17 @@ class TestFile(TestCase):
finally:
os.remove(symlinkPath)
-class TestDir(TestFile):
+
+class TestFile(TestCase, BaseTestXattr):
+ def setUp(self):
+ self.tempfile = NamedTemporaryFile()
+ self.tempfilename = self.tempfile.name
+
+ def tearDown(self):
+ self.tempfile.close()
+
+
+class TestDir(TestCase, BaseTestXattr):
def setUp(self):
self.tempfile = mkdtemp()
self.tempfilename = self.tempfile
diff --git a/xattr/tool.py b/xattr/tool.py
index 3b1121e..41ea7e1 100755
--- a/xattr/tool.py
+++ b/xattr/tool.py
@@ -28,9 +28,11 @@
import sys
import os
import getopt
-import xattr
import zlib
+import xattr
+
+
def usage(e=None):
if e:
print e
@@ -57,34 +59,38 @@ def usage(e=None):
else:
sys.exit(0)
+
class NullsInString(Exception):
"""Nulls in string."""
-_FILTER=''.join([(len(repr(chr(x)))==3) and chr(x) or '.' for x in range(256)])
+
+_FILTER = ''.join([(len(repr(chr(x))) == 3) and chr(x) or '.' for x in range(256)])
+
def _dump(src, length=16):
- result=[]
+ result = []
for i in xrange(0, len(src), length):
s = src[i:i+length]
- hexa = ' '.join(["%02X"%ord(x) for x in s])
+ hexa = ' '.join(["%02X" % ord(x) for x in s])
printable = s.translate(_FILTER)
result.append("%04X %-*s %s\n" % (i, length*3, hexa, printable))
return ''.join(result)
+
def main():
try:
(optargs, args) = getopt.getopt(sys.argv[1:], "hlpwdz", ["help"])
except getopt.GetoptError, e:
usage(e)
- attr_name = None
+ attr_name = None
long_format = False
- read = False
- write = False
- delete = False
- compress = lambda x: x
- decompress = compress
- status = 0
+ read = False
+ write = False
+ delete = False
+ compress = lambda x: x
+ decompress = compress
+ status = 0
for opt, arg in optargs:
if opt in ("-h", "--help"):
@@ -104,7 +110,7 @@ def main():
if read or write:
usage("-d not allowed with -p or -w")
elif opt == "-z":
- compress = zlib.compress
+ compress = zlib.compress
decompress = zlib.decompress
if write or delete:
@@ -132,7 +138,6 @@ def main():
sys.stderr.write("No such file: %s\n" % (filename,))
else:
sys.stderr.write(str(e) + "\n")
- status = 1
try:
attrs = xattr.xattr(filename)
@@ -185,7 +190,7 @@ def main():
if long_format:
try:
if attr_value.find('\0') >= 0:
- raise NullsInString;
+ raise NullsInString
print "".join((file_prefix, "%s: " % (attr_name,), attr_value))
except (UnicodeDecodeError, NullsInString):
print "".join((file_prefix, "%s:" % (attr_name,)))