Package libxyz :: Package vfs :: Module local
[hide private]
[frames] | no frames]

Source Code for Module libxyz.vfs.local

  1  #-*- coding: utf8 -* 
  2  # 
  3  # Max E. Kuznecov ~syhpoon <syhpoon@syhpoon.name> 2008 
  4  # 
  5  # This file is part of XYZCommander. 
  6  # XYZCommander is free software: you can redistribute it and/or modify 
  7  # it under the terms of the GNU Lesser Public License as published by 
  8  # the Free Software Foundation, either version 3 of the License, or 
  9  # (at your option) any later version. 
 10  # XYZCommander is distributed in the hope that it will be useful, 
 11  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 
 13  # GNU Lesser Public License for more details. 
 14  # You should have received a copy of the GNU Lesser Public License 
 15  # along with XYZCommander. If not, see <http://www.gnu.org/licenses/>. 
 16   
 17  import os 
 18  import stat 
 19  import time 
 20  import shutil 
 21  import errno 
 22   
 23  from libxyz.exceptions import VFSError 
 24  from libxyz.exceptions import XYZRuntimeError 
 25  from libxyz.exceptions import XYZValueError 
 26  from libxyz.vfs import vfsobj 
 27  from libxyz.vfs import types 
 28  from libxyz.vfs import util 
 29  from libxyz.vfs import mode 
 30  from libxyz.core.utils import ustring, bstring 
 31  from libxyz.ui import BlockEntries 
32 33 -def ensure_opened(func):
34 def wrap(self, *args, **kwargs): 35 if self.fileobj is None: 36 raise VFSError(_(u"Object must be opened before any operation")) 37 else: 38 return func(self, *args, **kwargs)
39 40 return wrap 41
42 -class LocalVFSObject(vfsobj.VFSObject):
43 """ 44 Local VFS object is used to access local filesystem 45 """ 46 47 ### Public API 48
49 - def walk(self):
50 """ 51 Directory tree walker 52 @return: tuple (parent, dir, objects) where: 53 parent - parent dir LocalVFSObject instance 54 dir - current LocalVFSObject instance 55 objects - BlockEntries of LocalVFSObject objects 56 """ 57 58 try: 59 _dir, _dirs, _files = os.walk(self.path).next() 60 except StopIteration: 61 raise XYZRuntimeError(_(u"Unable to walk on %s") % 62 ustring(self.path)) 63 64 _dirs.sort() 65 _files.sort() 66 67 _parent = self.xyz.vfs.get_parent(_dir, self.enc) 68 69 get_path = lambda x: os.path.abspath(os.path.join(self.path, x)) 70 71 return [_parent, self, 72 BlockEntries(self.xyz, _dirs + _files, get_path)]
73 74 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 75
76 - def remove(self, recursive=True):
77 """ 78 [Recursively] remove object 79 """ 80 81 if self.is_dir(): 82 if recursive: 83 shutil.rmtree(self.path) 84 else: 85 os.rmdir(self.path) 86 else: 87 os.unlink(self.path)
88 89 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 90
91 - def mkdir(self, newdir):
92 """ 93 Create new dir inside object (only valid for directory object types) 94 """ 95 96 if not (self.is_dir() or 97 (self.is_link() and self.data.is_dir())): 98 raise XYZValueError( 99 _(u"Unable to create directory inside %s object type") % 100 self.ftype) 101 else: 102 os.mkdir(newdir)
103 104 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 105
106 - def copy(self, path, existcb=None, errorcb=None, save_attrs=True, 107 follow_links=False, cancel=None):
108 109 env = { 110 'override': 'abort', 111 'error': 'abort' 112 } 113 114 try: 115 if self.is_dir(): 116 f = self._copy_dir 117 else: 118 f = self._copy_file 119 120 f(self.full_path, path, existcb, errorcb, 121 save_attrs, follow_links, env, cancel) 122 except XYZRuntimeError: 123 # Aborted 124 return False 125 else: 126 return True
127 128 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 129
130 - def move(self, path, existcb=None, errorcb=None, save_attrs=True, 131 follow_links=False, cancel=None):
132 """ 133 Move object 134 """ 135 136 def _handle_error(e, obj): 137 if env['error'] != 'skip all': 138 if errorcb: 139 try: 140 env['error'] = errorcb(obj, str(e)) 141 except Exception: 142 env['error'] = 'abort' 143 144 if env['error'] == 'abort': 145 raise XYZRuntimeError()
146 147 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 148 149 def _move_file(src, dst, *args, **kwargs): 150 if os.path.exists(dst) and os.path.isdir(dst): 151 dstto = os.path.join(dst, os.path.basename(src)) 152 else: 153 dstto = dst 154 155 if os.path.exists(dstto): 156 if env['override'] not in ('override all', 'skip all'): 157 if existcb: 158 try: 159 env['override'] = existcb( 160 self.xyz.vfs.dispatch(dstto)) 161 except Exception: 162 env['override'] = 'abort' 163 164 if env['override'] == 'abort': 165 raise XYZRuntimeError() 166 elif env['override'] in ('skip', 'skip all'): 167 return False 168 169 try: 170 os.rename(src, dstto) 171 172 return True 173 except OSError, e: 174 # Cross-device link, try to copy 175 if e.errno == errno.EXDEV: 176 if self._copy_file(src, dst, *args, **kwargs): 177 # Remove after successfully copied 178 try: 179 os.unlink(src) 180 except Exception, e2: 181 _handle_error(e2, self.xyz.vfs.dispatch(src)) 182 else: 183 _handle_error(e, self.xyz.vfs.dispatch(src)) 184 except XYZRuntimeError: 185 raise 186 except Exception, e3: 187 _handle_error(e3, self.xyz.vfs.dispatch(src))
188 189 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 190 191 def _move_dir(src, dst, *args, **kwargs): 192 if os.path.exists(dst) and os.path.isdir(dst) and \ 193 os.path.basename(src) != os.path.basename(dst): 194 dst = os.path.join(dst, os.path.basename(src)) 195 196 if os.path.isdir(src) and not os.path.exists(dst): 197 os.makedirs(dst) 198 199 files = os.listdir(src) 200 201 cancel = kwargs.get('cancel', None) 202 203 for f in files: 204 if cancel is not None and cancel.isSet(): 205 raise StopIteration() 206 207 srcobj = os.path.join(src, f) 208 dstobj = os.path.join(dst, f) 209 210 if os.path.isdir(srcobj): 211 fun = _move_dir 212 else: 213 fun = _move_file 214 215 fun(srcobj, dstobj, *args, **kwargs) 216 217 try: 218 os.rmdir(src) 219 except Exception, e: 220 _handle_error(e, self.xyz.vfs.dispatch(src)) 221 222 return True 223 224 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 225 226 env = { 227 'override': 'abort', 228 'error': 'abort' 229 } 230 231 try: 232 if self.is_dir(): 233 fun = _move_dir 234 else: 235 fun = _move_file 236 237 fun(self.full_path, path, existcb, errorcb, save_attrs, 238 follow_links, env, cancel=cancel) 239 except XYZRuntimeError: 240 # Aborted 241 return False 242 243 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 244
245 - def open(self, mode='r'):
246 """ 247 Open self object in provided mode 248 """ 249 250 if self.fileobj: 251 return self 252 else: 253 self.fileobj = open(self.path, mode) 254 255 return self
256 257 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 258
259 - def close(self):
260 """ 261 Close self object 262 """ 263 264 if self.fileobj is None: 265 return 266 else: 267 try: 268 self.fileobj.close() 269 finally: 270 self.fileobj = None
271 272 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 273 274 @ensure_opened
275 - def read(self, bytes=None):
276 """ 277 Read bytes from self object 278 """ 279 280 return self.fileobj.read(bytes)
281 282 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 283 284 @ensure_opened
285 - def tell(self):
286 """ 287 Tell file position 288 """ 289 290 return self.fileobj.tell()
291 292 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 293 294 @ensure_opened
295 - def seek(self, offset, whence=0):
296 """ 297 Perform seek() on object 298 """ 299 300 return self.fileobj.seek(offset, whence)
301 302 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 303 ## Internal stuff 304
305 - def _prepare(self):
306 self.ftype = self._find_type(self.path) 307 self.vtype = self.ftype.vtype 308 309 self._set_attributes() 310 311 _time = lambda x: ustring(time.ctime(x)) 312 313 self.attributes = ( 314 (_(u"Name"), ustring(self.name)), 315 (_(u"Type"), ustring(self.ftype)), 316 (_(u"Access time"), _time(self.atime)), 317 (_(u"Modification time"), _time(self.mtime)), 318 (_(u"Change time"), _time(self.ctime)), 319 (_(u"Size in bytes"), ustring(self.size)), 320 (_(u"Owner"), ustring(self._uid(self.uid))), 321 (_(u"Group"), ustring(self._gid(self.gid))), 322 (_(u"Access mode"), ustring(self.mode)), 323 (_(u"Inode"), ustring(self.inode)), 324 (_(u"Type-specific data"), self.data), 325 )
326 327 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 328
329 - def __str__(self):
330 return "<LocalVFSObject object: %s>" % self.path
331 332 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 333
334 - def _uid(self, uid):
335 _name = util.get_user(uid) 336 337 if _name is not None: 338 return "%s (%s)" % (bstring(uid), _name) 339 else: 340 return bstring(uid)
341 342 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 343
344 - def _gid(self, gid):
345 _name = util.get_group(gid) 346 347 if _name is not None: 348 return "%s (%s)" % (bstring(gid), _name) 349 else: 350 return bstring(gid)
351 352 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 353
354 - def _find_type(self, path):
355 """ 356 Find out file type 357 """ 358 359 try: 360 self._stat = os.lstat(path) 361 except OSError, e: 362 raise VFSError(_(u"Unable to stat file %s: %s") % 363 (ustring(path), unicode(e.strerror, xyzenc))) 364 365 return util.get_file_type(self._stat.st_mode)
366 367 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 368
369 - def _set_attributes(self):
370 """ 371 Set file attibutes 372 """ 373 374 def set_link_attributes(): 375 """ 376 Set appropriate soft link attibutes 377 """ 378 379 _realpath = os.readlink(self.path) 380 _fullpath = os.path.realpath(self.path) 381 382 if not os.path.exists(_fullpath): 383 self.vtype = "!" 384 else: 385 try: 386 self.data = self.xyz.vfs.dispatch(_fullpath, self.enc) 387 except VFSError, e: 388 xyzlog.error(_(u"Error creating VFS object: %s") % 389 unicode(e)) 390 else: 391 if isinstance(self.data.ftype, types.VFSTypeDir): 392 self.vtype = "~" 393 self.info = "" 394 self.visual = "-> %s" % _realpath
395 396 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 397 398 def set_char_attributes(): 399 """ 400 Set appropriate character device attibutes 401 """ 402 403 _dev = self._stat.st_rdev 404 self.info = "%s, %s %s" % (os.major(_dev), os.minor(_dev), 405 self.mode) 406 407 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 408 409 self.atime = self._stat.st_atime 410 self.mtime = self._stat.st_mtime 411 self.ctime = self._stat.st_ctime 412 self.size = self._stat.st_size 413 self.uid = self._stat.st_uid 414 self.gid = self._stat.st_gid 415 self.inode = self._stat.st_ino 416 self.mode = mode.Mode(self._stat.st_mode, self.ftype) 417 self.visual = "%s%s" % (self.vtype, self.name) 418 self.info = "%s %s" % (util.format_size(self.size), self.mode) 419 420 if self.is_link(): 421 set_link_attributes() 422 elif self.is_char(): 423 set_char_attributes() 424 elif self.is_file(): 425 _mode = stat.S_IMODE(self.mode.raw) 426 427 # Executable 428 if _mode & 0111: 429 self.vtype = "*" 430 self.visual = "*%s" % self.name 431 432 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 433
434 - def _copy_file(self, src, dst, existcb, errorcb, save_attrs, 435 follow_links, env, cancel=None):
436 """ 437 File-to-file copy 438 """ 439 440 if os.path.exists(dst) and os.path.isdir(dst): 441 dstto = os.path.join(dst, os.path.basename(src)) 442 else: 443 dstto = dst 444 445 if os.path.exists(dstto): 446 if env['override'] not in ('override all', 'skip all'): 447 if existcb: 448 try: 449 env['override'] = existcb( 450 self.xyz.vfs.dispatch(dstto)) 451 except Exception: 452 env['override'] = 'abort' 453 454 if env['override'] == 'abort': 455 raise XYZRuntimeError() 456 elif env['override'] in ('skip', 'skip all'): 457 return False 458 459 try: 460 if not follow_links and os.path.islink(src): 461 linkto = os.readlink(src) 462 os.symlink(linkto, dstto) 463 else: 464 if save_attrs: 465 fun = shutil.copy2 466 else: 467 fun = shutil.copyfile 468 469 fun(src, dstto) 470 471 return True 472 except Exception, e: 473 if env['error'] != 'skip all': 474 if errorcb: 475 try: 476 env['error'] = errorcb( 477 self.xyz.vfs.dispatch(src), str(e)) 478 except Exception: 479 env['error'] = 'abort' 480 481 if env['error'] == 'abort': 482 raise XYZRuntimeError() 483 484 return False
485 486 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 487
488 - def _copy_dir(self, src, dst, existcb, errorcb, save_attrs, 489 follow_links, env, cancel=None):
490 """ 491 Dir-to-dir copy 492 """ 493 494 if os.path.exists(dst) and os.path.isdir(dst) and \ 495 os.path.basename(src) != os.path.basename(dst): 496 dst = os.path.join(dst, os.path.basename(src)) 497 498 if not follow_links and os.path.islink(src): 499 linkto = os.readlink(src) 500 os.symlink(linkto, dst) 501 502 return True 503 504 if os.path.isdir(src) and not os.path.exists(dst): 505 os.makedirs(dst) 506 507 files = os.listdir(src) 508 509 for f in files: 510 if cancel is not None and cancel.isSet(): 511 raise StopIteration() 512 513 srcobj = os.path.join(src, f) 514 dstobj = os.path.join(dst, f) 515 516 if os.path.isdir(srcobj): 517 fun = self._copy_dir 518 else: 519 fun = self._copy_file 520 521 fun(srcobj, dstobj, existcb, errorcb, save_attrs, 522 follow_links, env, cancel) 523 524 if os.path.isdir(src) and save_attrs: 525 shutil.copystat(src, dst)
526