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 |