Gentoo Archives: gentoo-portage-dev

From: Arfrever Frehtes Taifersar Arahesis <arfrever.fta@×××××.com>
To: Gentoo Portage Development <gentoo-portage-dev@l.g.o>
Subject: Re: [gentoo-portage-dev] [PATCH] xattr: centralize the various shims in one place
Date: Thu, 17 Oct 2013 00:03:30
Message-Id: 201310170202.51105.Arfrever.FTA@gmail.com
In Reply to: [gentoo-portage-dev] [PATCH] xattr: centralize the various shims in one place by Mike Frysinger
1 2013-10-16 23:03 Mike Frysinger napisał(a):
2 > Rather than each module implementing its own shim around the various
3 > methods for accessing extended attributes, start a dedicated module
4 > that exports a consistent API.
5
6 Some things are incompatible with Python 3.
7 See other comments below.
8
9 > ---
10 > bin/xattr-helper.py | 11 +--
11 > pym/portage/util/_xattr.py | 189 +++++++++++++++++++++++++++++++++++++++++++
12 > pym/portage/util/movefile.py | 99 ++++++-----------------
13 > 3 files changed, 213 insertions(+), 86 deletions(-)
14 > create mode 100644 pym/portage/util/_xattr.py
15 >
16 > diff --git a/bin/xattr-helper.py b/bin/xattr-helper.py
17 > index 6d99521..69b83f7 100755
18 > --- a/bin/xattr-helper.py
19 > +++ b/bin/xattr-helper.py
20 > @@ -17,16 +17,7 @@ import re
21 > import sys
22 >
23 > from portage.util._argparse import ArgumentParser
24 > -
25 > -if hasattr(os, "getxattr"):
26 > -
27 > - class xattr(object):
28 > - get = os.getxattr
29 > - set = os.setxattr
30 > - list = os.listxattr
31 > -
32 > -else:
33 > - import xattr
34 > +from portage.util._xattr import xattr
35 >
36 >
37 > _UNQUOTE_RE = re.compile(br'\\[0-7]{3}')
38 > diff --git a/pym/portage/util/_xattr.py b/pym/portage/util/_xattr.py
39 > new file mode 100644
40 > index 0000000..0e594f9
41 > --- /dev/null
42 > +++ b/pym/portage/util/_xattr.py
43 > @@ -0,0 +1,189 @@
44 > +# Copyright 2010-2013 Gentoo Foundation
45 > +# Distributed under the terms of the GNU General Public License v2
46 > +
47 > +"""Portability shim for xattr support
48 > +
49 > +Exported API is the xattr object with get/get_all/set/remove/list operations.
50 > +
51 > +See the standard xattr module for more documentation.
52 > +"""
53 > +
54 > +import contextlib
55 > +import os
56 > +import subprocess
57 > +
58 > +from portage.exception import OperationNotSupported
59 > +
60 > +
61 > +class _XattrGetAll(object):
62 > + """Implement get_all() using list()/get() if there is no easy bulk method"""
63 > +
64 > + @classmethod
65 > + def get_all(cls, item, nofollow=False, namespace=None):
66 > + return [(name, cls.get(item, name, nofollow=nofollow, namespace=namespace))
67 > + for name in cls.list(item, nofollow=nofollow, namespace=namespace)]
68 > +
69 > +
70 > +class _XattrSystemCommands(_XattrGetAll):
71 > + """Implement things with getfattr/setfattr"""
72 > +
73 > + @staticmethod
74 > + def _parse_output(output):
75 > + for line in proc.stdout.readlines():
76
77 NameError: global name 'proc' is not defined
78
79 > + if line.startswith('#'):
80 > + continue
81 > + line = line.rstrip()
82 > + if not line:
83 > + continue
84 > + # The line will have the format:
85 > + # user.name0="value0"
86 > + yield line.split('=', 1)
87 > +
88 > + @classmethod
89 > + def get(item, name, nofollow=False, namespace=None):
90 > + if namespace:
91 > + name = '%s.%s' % (namespace, name)
92 > + cmd = ['getfattr', '--absolute-names', '-n', name, item]
93 > + if nofollow:
94 > + cmd += ['-h']
95 > + proc = subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
96 > + proc.wait()
97
98 AttributeError: 'int' object has no attribute 'wait'
99
100 > +
101 > + value = None
102 > + for _, value in cls._parse_output(proc.stdout):
103 > + break
104 > +
105 > + proc.stdout.close()
106 > + return value
107 > +
108 > + @staticmethod
109 > + def set(item, name, value, flags=0, namespace=None):
110 > + if namespace:
111 > + name = '%s.%s' % (namespace, name)
112 > + cmd = ['setfattr', '-n', name, '-v', value, item]
113
114 Calling setfattr once per attribute is slower than once per file (as is now).
115
116 > + subprocess.call(cmd)
117 > +
118 > + @staticmethod
119 > + def remove(item, name, nofollow=False, namespace=None):
120 > + if namespace:
121 > + name = '%s.%s' % (namespace, name)
122 > + cmd = ['setfattr', '-x', name, item]
123 > + subprocess.call(cmd)
124 > +
125 > + @classmethod
126 > + def list(cls, item, nofollow=False, namespace=None, _names_only=True):
127 > + cmd = ['getfattr', '-d', '--absolute-names', item]
128 > + if nofollow:
129 > + cmd += ['-h']
130 > + cmd += ['-m', ('^%s[.]' % namespace) if namespace else '']
131 > + proc = subprocess.call(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
132 > + proc.wait()
133
134 AttributeError: 'int' object has no attribute 'wait'
135
136 > +
137 > + ret = []
138 > + if namespace:
139 > + namespace = '%s.' % namespace
140 > + for name, value in cls._parse_output(proc.stdout):
141 > + if namespace:
142 > + if name.startswith(namespace):
143 > + name = name[len(namespace):]
144 > + else:
145 > + continue
146 > + if _names_only:
147 > + ret.append(name)
148 > + else:
149 > + ret.append((name, value))
150 > +
151 > + proc.stdout.close()
152 > + return ret
153 > +
154 > + @classmethod
155 > + def get_all(cls, item, nofollow=False, namespace=None):
156 > + cls.list(item, nofollow=nofollow, namespace=namespace, _names_only=False)
157 > +
158 > +
159 > +class _XattrStub(_XattrGetAll):
160 > + """Fake object since system doesn't support xattrs"""
161 > +
162 > + @staticmethod
163 > + def _raise():
164 > + e = OSError('stub')
165 > + e.errno = OperationNotSupported.errno
166 > + raise e
167 > +
168 > + @staticmethod
169 > + def get(item, name, nofollow=False, namespace=None):
170 > + raise OperationNotSupported('stub')
171 > +
172 > + @staticmethod
173 > + def set(item, name, value, flags=0, namespace=None):
174 > + raise OperationNotSupported('stub')
175 > +
176 > + @staticmethod
177 > + def remove(item, name, nofollow=False, namespace=None):
178 > + raise OperationNotSupported('stub')
179 > +
180 > + @staticmethod
181 > + def list(item, nofollow=False, namespace=None):
182 > + raise OperationNotSupported('stub')
183 > +
184 > +
185 > +if hasattr(os, 'getxattr'):
186 > + # Easy as pie -- active python supports it.
187 > + class xattr(_XattrGetAll):
188 > + """Python >=3.3 and GNU/Linux"""
189 > + get = os.getxattr
190 > + set = os.setxattr
191 > + remove = os.removexattr
192 > + list = os.listxattr
193 > +
194 > +else:
195 > + try:
196 > + # Maybe we have the xattr module.
197 > + import xattr
198 > +
199 > + except ImportError:
200 > + try:
201 > + # Maybe we have the attr package.
202 > + with open(os.devnull, 'wb') as f:
203 > + subprocess.call(['getfattr', '--version'], stdout=f)
204 > + subprocess.call(['setfattr', '--version'], stdout=f)
205 > + xattr = _XattrSystemCommands
206 > +
207 > + except OSError:
208 > + # Stub it out completely.
209 > + xattr = _XattrStub
210 > +
211 > +
212 > +@××××××××××.contextmanager
213 > +def preserve_xattrs(path, nofollow=False, namespace=None):
214 > + """Context manager to save/restore extended attributes on |path|
215 > +
216 > + If you want to rewrite a file (possibly replacing it with a new one), but
217 > + want to preserve the extended attributes, this will do the trick.
218 > +
219 > + # First read all the extended attributes.
220 > + with save_xattrs('/some/file'):
221 > + ... rewrite the file ...
222 > + # Now the extended attributes are restored as needed.
223 > + """
224 > + kwargs = {
225 > + 'nofollow': nofollow,
226 > + 'namespace': namespace,
227 > + }
228 > + old_attrs = dict(xattr.get_all(path, **kwargs))
229 > + try:
230 > + yield
231 > + finally:
232 > + new_attrs = dict(xattrs.get_all(path, **kwargs))
233 > + for name, value in new_attrs.iteritems():
234 > + if name not in old_attrs:
235 > + # Clear out new ones.
236 > + xattr.remove(path, name, **kwargs)
237 > + elif new_attrs[name] != old:
238 > + # Update changed ones.
239 > + xattr.set(path, name, value, **kwargs)
240 > +
241 > + for name, value in old_attrs.iteritems():
242 > + if name not in new_attr:
243 > + # Re-add missing ones.
244 > + xattr.set(path, name, value, **kwargs)
245 > diff --git a/pym/portage/util/movefile.py b/pym/portage/util/movefile.py
246 > index 4f158cd..553374a 100644
247 > --- a/pym/portage/util/movefile.py
248 > +++ b/pym/portage/util/movefile.py
249 > @@ -23,6 +23,7 @@ from portage.exception import OperationNotSupported
250 > from portage.localization import _
251 > from portage.process import spawn
252 > from portage.util import writemsg
253 > +from portage.util._xattr import xattr
254 >
255 > def _apply_stat(src_stat, dest):
256 > _os.chown(dest, src_stat.st_uid, src_stat.st_gid)
257 > @@ -68,86 +69,32 @@ class _xattr_excluder(object):
258 >
259 > return False
260 >
261 > -if hasattr(_os, "getxattr"):
262 > - # Python >=3.3 and GNU/Linux
263 > - def _copyxattr(src, dest, exclude=None):
264 > -
265 > - try:
266 > - attrs = _os.listxattr(src)
267 > - except OSError as e:
268 > - if e.errno != OperationNotSupported.errno:
269 > - raise
270 > - attrs = ()
271 > - if attrs:
272 > - if exclude is not None and isinstance(attrs[0], bytes):
273 > - exclude = exclude.encode(_encodings['fs'])
274 > - exclude = _get_xattr_excluder(exclude)
275 > -
276 > - for attr in attrs:
277 > - if exclude(attr):
278 > - continue
279 > - try:
280 > - _os.setxattr(dest, attr, _os.getxattr(src, attr))
281 > - raise_exception = False
282 > - except OSError:
283 > - raise_exception = True
284 > - if raise_exception:
285 > - raise OperationNotSupported(_("Filesystem containing file '%s' "
286 > - "does not support extended attribute '%s'") %
287 > - (_unicode_decode(dest), _unicode_decode(attr)))
288 > -else:
289 > +def _copyxattr(src, dest, exclude=None):
290 > + """Copy the extended attributes from |src| to |dest|"""
291 > try:
292 > - import xattr
293 > - except ImportError:
294 > - xattr = None
295 > - if xattr is not None:
296 > - def _copyxattr(src, dest, exclude=None):
297 > -
298 > - try:
299 > - attrs = xattr.list(src)
300 > - except IOError as e:
301 > - if e.errno != OperationNotSupported.errno:
302 > - raise
303 > - attrs = ()
304 > + attrs = xattr.list(src)
305 > + except (OSError, IOError) as e:
306 > + if e.errno != OperationNotSupported.errno:
307 > + raise
308 > + attrs = ()
309 >
310 > - if attrs:
311 > - if exclude is not None and isinstance(attrs[0], bytes):
312 > - exclude = exclude.encode(_encodings['fs'])
313 > - exclude = _get_xattr_excluder(exclude)
314 > + if attrs:
315 > + if exclude is not None and isinstance(attrs[0], bytes):
316 > + exclude = exclude.encode(_encodings['fs'])
317 > + exclude = _get_xattr_excluder(exclude)
318 >
319 > - for attr in attrs:
320 > - if exclude(attr):
321 > - continue
322 > - try:
323 > - xattr.set(dest, attr, xattr.get(src, attr))
324 > - raise_exception = False
325 > - except IOError:
326 > - raise_exception = True
327 > - if raise_exception:
328 > - raise OperationNotSupported(_("Filesystem containing file '%s' "
329 > - "does not support extended attribute '%s'") %
330 > - (_unicode_decode(dest), _unicode_decode(attr)))
331 > - else:
332 > + for attr in attrs:
333 > + if exclude(attr):
334 > + continue
335 > try:
336 > - with open(os.devnull, 'wb') as f:
337 > - subprocess.call(["getfattr", "--version"], stdout=f)
338 > - subprocess.call(["setfattr", "--version"], stdout=f)
339 > - except OSError:
340 > - def _copyxattr(src, dest, exclude=None):
341 > - # TODO: implement exclude
342 > - getfattr_process = subprocess.Popen(["getfattr", "-d", "--absolute-names", src], stdout=subprocess.PIPE)
343 > - getfattr_process.wait()
344 > - extended_attributes = getfattr_process.stdout.readlines()
345 > - getfattr_process.stdout.close()
346 > - if extended_attributes:
347 > - extended_attributes[0] = b"# file: " + _unicode_encode(dest) + b"\n"
348 > - setfattr_process = subprocess.Popen(["setfattr", "--restore=-"], stdin=subprocess.PIPE, stderr=subprocess.PIPE)
349 > - setfattr_process.communicate(input=b"".join(extended_attributes))
350 > - if setfattr_process.returncode != 0:
351 > - raise OperationNotSupported("Filesystem containing file '%s' does not support extended attributes" % dest)
352 > - else:
353 > - def _copyxattr(src, dest, exclude=None):
354 > - pass
355 > + xattr.set(dest, attr, xattr.get(src, attr))
356 > + raise_exception = False
357 > + except (OSError, IOError) as e:
358
359 Unused variable e
360
361 > + raise_exception = True
362 > + if raise_exception:
363 > + raise OperationNotSupported(_("Filesystem containing file '%s' "
364 > + "does not support extended attribute '%s'") %
365 > + (_unicode_decode(dest), _unicode_decode(attr)))
366 >
367 > def movefile(src, dest, newmtime=None, sstat=None, mysettings=None,
368 > hardlink_candidates=None, encoding=_encodings['fs']):
369 >
370
371 --
372 Arfrever Frehtes Taifersar Arahesis

Attachments

File name MIME type
signature.asc application/pgp-signature

Replies