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