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 pwd 
 21  import grp 
 22  import shutil 
 23  import errno 
 24   
 25  from libxyz.exceptions import VFSError 
 26  from libxyz.exceptions import XYZRuntimeError 
 27  from libxyz.exceptions import XYZValueError 
 28  from libxyz.vfs import vfsobj 
 29  from libxyz.vfs import types 
 30  from libxyz.vfs import util 
 31  from libxyz.vfs import mode 
 32  from libxyz.core.utils import ustring, bstring 
 33   
34 -class LocalVFSObject(vfsobj.VFSObject):
35 """ 36 Local VFS object is used to access local filesystem 37 """ 38 39 ### Public API 40
41 - def walk(self):
42 """ 43 Directory tree walker 44 @return: tuple (parent, dir, dirs, files) where: 45 parent - parent dir LocalVFSObject instance 46 dir - current LocalVFSObject instance 47 dirs - list of LocalVFSObject objects of directories 48 files - list of LocalVFSObject objects of files 49 """ 50 51 try: 52 _dir, _dirs, _files = os.walk(self.path).next() 53 except StopIteration: 54 raise XYZRuntimeError(_(u"Unable to walk on %s") % 55 ustring(self.path)) 56 57 _dirs.sort() 58 _files.sort() 59 _parent = self.xyz.vfs.get_parent(_dir, self.enc) 60 61 get_path = lambda x: os.path.abspath(os.path.join(self.path, x)) 62 63 return [ 64 _parent, 65 self, 66 [self.xyz.vfs.dispatch(get_path(x), self.enc) for x in _dirs], 67 [self.xyz.vfs.dispatch(get_path(x), self.enc) for x in _files], 68 ]
69 70 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 71
72 - def remove(self, recursive=True):
73 """ 74 [Recursively] remove object 75 """ 76 77 if self.is_dir(): 78 if recursive: 79 shutil.rmtree(self.path) 80 else: 81 os.rmdir(self.path) 82 else: 83 os.unlink(self.path)
84 85 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 86
87 - def mkdir(self, newdir):
88 """ 89 Create new dir inside object (only valid for directory object types) 90 """ 91 92 if not self.is_dir(): 93 raise XYZValueError( 94 _(u"Unable to create directory inside %s object type") % 95 self.ftype) 96 else: 97 os.mkdir(os.path.join(self.path, newdir))
98 99 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 100
101 - def copy(self, path, existcb=None, errorcb=None, save_attrs=True, 102 follow_links=False, cancel=None):
103 104 env = { 105 'override': 'abort', 106 'error': 'abort' 107 } 108 109 try: 110 if self.is_dir(): 111 f = self._copy_dir 112 else: 113 f = self._copy_file 114 115 f(self.full_path, path, existcb, errorcb, 116 save_attrs, follow_links, env, cancel) 117 except XYZRuntimeError: 118 # Aborted 119 return False 120 else: 121 return True
122 123 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 124
125 - def move(self, path, existcb=None, errorcb=None, save_attrs=True, 126 follow_links=False, cancel=None):
127 """ 128 Move object 129 """ 130 131 def _handle_error(e, obj): 132 if env['error'] != 'skip all': 133 if errorcb: 134 try: 135 env['error'] = errorcb(obj, str(e)) 136 except Exception: 137 env['error'] = 'abort' 138 139 if env['error'] == 'abort': 140 raise XYZRuntimeError()
141 142 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 143 144 def _move_file(src, dst, *args, **kwargs): 145 if os.path.exists(dst) and os.path.isdir(dst): 146 dstto = os.path.join(dst, os.path.basename(src)) 147 else: 148 dstto = dst 149 150 if os.path.exists(dstto): 151 if env['override'] not in ('override all', 'skip all'): 152 if existcb: 153 try: 154 env['override'] = existcb( 155 self.xyz.vfs.dispatch(dstto)) 156 except Exception: 157 env['override'] = 'abort' 158 159 if env['override'] == 'abort': 160 raise XYZRuntimeError() 161 elif env['override'] in ('skip', 'skip all'): 162 return False 163 164 try: 165 os.rename(src, dstto) 166 167 return True 168 except OSError, e: 169 # Cross-device link, try to copy 170 if e.errno == errno.EXDEV: 171 if self._copy_file(src, dst, *args, **kwargs): 172 # Remove after successfully copied 173 try: 174 os.unlink(src) 175 except Exception, e2: 176 _handle_error(e2, self.xyz.vfs.dispatch(src)) 177 else: 178 _handle_error(e, self.xyz.vfs.dispatch(src)) 179 except XYZRuntimeError: 180 raise 181 except Exception, e3: 182 _handle_error(e3, self.xyz.vfs.dispatch(src))
183 184 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 185 186 def _move_dir(src, dst, *args, **kwargs): 187 if os.path.exists(dst) and os.path.isdir(dst) and \ 188 os.path.basename(src) != os.path.basename(dst): 189 dst = os.path.join(dst, os.path.basename(src)) 190 191 if os.path.isdir(src) and not os.path.exists(dst): 192 os.makedirs(dst) 193 194 files = os.listdir(src) 195 196 cancel = kwargs.get('cancel', None) 197 198 for f in files: 199 if cancel is not None and cancel.isSet(): 200 raise StopIteration() 201 202 srcobj = os.path.join(src, f) 203 dstobj = os.path.join(dst, f) 204 205 if os.path.isdir(srcobj): 206 fun = _move_dir 207 else: 208 fun = _move_file 209 210 fun(srcobj, dstobj, *args, **kwargs) 211 212 try: 213 os.rmdir(src) 214 except Exception, e: 215 _handle_error(e, self.xyz.vfs.dispatch(src)) 216 217 return True 218 219 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 220 221 env = { 222 'override': 'abort', 223 'error': 'abort' 224 } 225 226 try: 227 if self.is_dir(): 228 fun = _move_dir 229 else: 230 fun = _move_file 231 232 fun(self.full_path, path, existcb, errorcb, save_attrs, 233 follow_links, env, cancel=cancel) 234 except XYZRuntimeError: 235 # Aborted 236 return False 237 238 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 239 ## Internal stuff 240
241 - def _prepare(self):
242 self.ftype = self._find_type(self.path) 243 self.vtype = self.ftype.vtype 244 245 self._set_attributes() 246 247 _time = lambda x: ustring(time.ctime(x)) 248 249 self.attributes = ( 250 (_(u"Name"), ustring(self.name)), 251 (_(u"Type"), ustring(self.ftype)), 252 (_(u"Access time"), _time(self.atime)), 253 (_(u"Modification time"), _time(self.mtime)), 254 (_(u"Change time"), _time(self.ctime)), 255 (_(u"Size in bytes"), ustring(self.size)), 256 (_(u"Owner"), ustring(self._uid(self.uid))), 257 (_(u"Group"), ustring(self._gid(self.gid))), 258 (_(u"Access mode"), ustring(self.mode)), 259 (_(u"Inode"), ustring(self.inode)), 260 (_(u"Type-specific data"), self.data), 261 )
262 263 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 264
265 - def __str__(self):
266 return "<LocalVFSObject object: %s>" % self.path
267 268 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 269
270 - def _uid(self, uid):
271 try: 272 _name = pwd.getpwuid(uid).pw_name 273 except (KeyError, TypeError): 274 _name = None 275 276 if _name is not None: 277 return "%s (%s)" % (bstring(uid), _name) 278 else: 279 return bstring(uid)
280 281 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 282
283 - def _gid(self, gid):
284 try: 285 _name = grp.getgrgid(gid).gr_name 286 except (KeyError, TypeError): 287 _name = None 288 289 if _name is not None: 290 return "%s (%s)" % (bstring(gid), _name) 291 else: 292 return bstring(gid)
293 294 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 295
296 - def _find_type(self, path):
297 """ 298 Find out file type 299 """ 300 301 try: 302 self._stat = os.lstat(path) 303 except OSError, e: 304 raise VFSError(_(u"Unable to stat file %s: %s" % (path, str(e)))) 305 306 return util.get_file_type(self._stat.st_mode)
307 308 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 309
310 - def _set_attributes(self):
311 """ 312 Set file attibutes 313 """ 314 315 def set_link_attributes(): 316 """ 317 Set appropriate soft link attibutes 318 """ 319 320 _realpath = os.readlink(self.path) 321 _fullpath = os.path.realpath(self.path) 322 323 if not os.path.exists(_fullpath): 324 self.vtype = "!" 325 else: 326 try: 327 self.data = self.xyz.vfs.dispatch(_fullpath, self.enc) 328 except VFSError, e: 329 xyzlog.error(_(u"Error creating VFS object: %s") % 330 ustring(str(e))) 331 else: 332 if isinstance(self.data.ftype, types.VFSTypeDir): 333 self.vtype = "~" 334 self.info = "" 335 self.visual = "-> %s" % _realpath
336 337 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 338 339 def set_char_attributes(): 340 """ 341 Set appropriate character device attibutes 342 """ 343 344 _dev = self._stat.st_rdev 345 self.info = "%s, %s %s" % (os.major(_dev), os.minor(_dev), 346 self.mode) 347 348 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 349 350 self.atime = self._stat.st_atime 351 self.mtime = self._stat.st_mtime 352 self.ctime = self._stat.st_ctime 353 self.size = self._stat.st_size 354 self.uid = self._stat.st_uid 355 self.gid = self._stat.st_gid 356 self.inode = self._stat.st_ino 357 self.mode = mode.Mode(self._stat.st_mode, self.ftype) 358 self.visual = "%s%s" % (self.vtype, self.name) 359 self.info = "%s %s" % (util.format_size(self.size), self.mode) 360 361 if self.is_link(): 362 set_link_attributes() 363 elif self.is_char(): 364 set_char_attributes() 365 elif self.is_file(): 366 _mode = stat.S_IMODE(self.mode.raw) 367 368 # Executable 369 if _mode & 0111: 370 self.vtype = "*" 371 self.visual = "*%s" % self.name 372 373 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 374
375 - def _copy_file(self, src, dst, existcb, errorcb, save_attrs, 376 follow_links, env, cancel=None):
377 """ 378 File-to-file copy 379 """ 380 381 if os.path.exists(dst) and os.path.isdir(dst): 382 dstto = os.path.join(dst, os.path.basename(src)) 383 else: 384 dstto = dst 385 386 if os.path.exists(dstto): 387 if env['override'] not in ('override all', 'skip all'): 388 if existcb: 389 try: 390 env['override'] = existcb( 391 self.xyz.vfs.dispatch(dstto)) 392 except Exception: 393 env['override'] = 'abort' 394 395 if env['override'] == 'abort': 396 raise XYZRuntimeError() 397 elif env['override'] in ('skip', 'skip all'): 398 return False 399 400 try: 401 if not follow_links and os.path.islink(src): 402 linkto = os.readlink(src) 403 os.symlink(linkto, dstto) 404 else: 405 if save_attrs: 406 fun = shutil.copy2 407 else: 408 fun = shutil.copyfile 409 410 fun(src, dstto) 411 412 return True 413 except Exception, e: 414 if env['error'] != 'skip all': 415 if errorcb: 416 try: 417 env['error'] = errorcb( 418 self.xyz.vfs.dispatch(src), str(e)) 419 except Exception: 420 env['error'] = 'abort' 421 422 if env['error'] == 'abort': 423 raise XYZRuntimeError() 424 425 return False
426 427 #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 428
429 - def _copy_dir(self, src, dst, existcb, errorcb, save_attrs, 430 follow_links, env, cancel=None):
431 """ 432 Dir-to-dir copy 433 """ 434 435 if os.path.exists(dst) and os.path.isdir(dst) and \ 436 os.path.basename(src) != os.path.basename(dst): 437 dst = os.path.join(dst, os.path.basename(src)) 438 439 if not follow_links and os.path.islink(src): 440 linkto = os.readlink(src) 441 os.symlink(linkto, dst) 442 443 return True 444 445 if os.path.isdir(src) and not os.path.exists(dst): 446 os.makedirs(dst) 447 448 files = os.listdir(src) 449 450 for f in files: 451 if cancel is not None and cancel.isSet(): 452 raise StopIteration() 453 454 srcobj = os.path.join(src, f) 455 dstobj = os.path.join(dst, f) 456 457 if os.path.isdir(srcobj): 458 fun = self._copy_dir 459 else: 460 fun = self._copy_file 461 462 fun(srcobj, dstobj, existcb, errorcb, save_attrs, 463 follow_links, env, cancel) 464 465 if os.path.isdir(src) and save_attrs: 466 shutil.copystat(src, dst)
467