degal/filesystem.py
changeset 109 66a01c0806f1
parent 97 92c20f8b297f
child 117 a2e4562deaab
equal deleted inserted replaced
101:698dc68a985d 109:66a01c0806f1
   209         yield self.name if unicode else self.fsname
   209         yield self.name if unicode else self.fsname
   210     
   210     
   211     @lazy_load
   211     @lazy_load
   212     def _stat (self) :
   212     def _stat (self) :
   213         """
   213         """
   214             Cached OS-level stats.
   214             Cached low-level stat.
   215 
   215 
   216             Returns None on ENOENT (node doesn't exist).
   216             Returns None on ENOENT (node doesn't exist).
   217         """
   217         """
   218         
   218         
   219         try :
   219         try :
   298         """
   298         """
   299             Tests that this node exists. Raises an error it not, otherwise, returns the node itself
   299             Tests that this node exists. Raises an error it not, otherwise, returns the node itself
   300         """
   300         """
   301 
   301 
   302         if not self.exists() :
   302         if not self.exists() :
   303             raise Exception("Filesystem node does not exist: %s" % self)
   303             raise NodeErrno(self, errno.ENOENT)
   304 
   304 
   305         return self
   305         return self
   306     
   306     
   307     def path_to (self, node) :
   307     def path_to (self, node) :
   308         """
   308         """
   341 
   341 
   342     def __unicode__ (self) :
   342     def __unicode__ (self) :
   343         return self.unicodepath
   343         return self.unicodepath
   344     
   344     
   345     def __repr__ (self) :
   345     def __repr__ (self) :
   346         """
       
   347             Returns a str representing this dir
       
   348         """
       
   349 
       
   350         return "Node(%r, %r)" % (self.parent.path, self.fsname)
   346         return "Node(%r, %r)" % (self.parent.path, self.fsname)
   351     
   347     
   352     def __eq__ (self, other) :
   348     def __eq__ (self, other) :
   353         """
   349         """
   354             Compare for equality
   350             A Node is equal if compared to another node that shares the same name, and the parents are also equal.
   355         """
   351         """
   356 
   352 
   357         return isinstance(other, Node) and self.name == other.name and self.parent == other.parent
   353         return isinstance(other, Node) and self.name == other.name and self.parent == other.parent
   358 
   354 
   359     def __cmp__ (self, other) :
   355     def __cmp__ (self, other) :
   360         """
   356         """
   361             Arbitrary comparisons between Nodes
   357             Compare two Nodes or with None. This does not support comparisons with other kinds of objects.
   362             
   358             
   363             >>> cmp(Node(None, 'foo'), Node(None, 'foo'))
   359             >>> cmp(Node(None, 'foo'), Node(None, 'foo'))
   364             0
   360             0
   365             >>> cmp(Node(None, 'aaa'), Node(None, 'bbb'))
   361             >>> cmp(Node(None, 'aaa'), Node(None, 'bbb'))
   366             -1
   362             -1
   394         A Path is a sequence of Nodes that form a path through a Node tree rooted at some Root.
   390         A Path is a sequence of Nodes that form a path through a Node tree rooted at some Root.
   395 
   391 
   396         Each node must either be the parent or the child of the following node.
   392         Each node must either be the parent or the child of the following node.
   397 
   393 
   398         The first and last nodes may be Files, but all other objects must be Directories.
   394         The first and last nodes may be Files, but all other objects must be Directories.
       
   395 
       
   396         XXX: better to keep Paths symbolic/relative?
   399     """
   397     """
   400 
   398 
   401     def __init__ (self, *nodes) :
   399     def __init__ (self, *nodes) :
   402         """
   400         """
   403             Initialize with the given node path.
   401             Initialize with the given node path.
   552         return self
   550         return self
   553 
   551 
   554     def open (self, mode='r', encoding=None, errors=None, bufsize=None) :
   552     def open (self, mode='r', encoding=None, errors=None, bufsize=None) :
   555         """
   553         """
   556             Wrapper for open/codecs.open.
   554             Wrapper for open/codecs.open.
   557 
   555         """
   558             Raises an error if read_only mode is set and mode contains any of 'wa+'
       
   559         """
       
   560 
       
   561         if self.config.read_only and any((c in mode) for c in 'wa+') :
       
   562             raise Exception("Unable to open file for %s due to read_only mode: %s" % (mode, self))
       
   563 
   556 
   564         if encoding :
   557         if encoding :
   565             return codecs.open(self.path, mode, encoding, errors, bufsize)
   558             return codecs.open(self.path, mode, encoding, errors, bufsize)
   566 
   559 
   567         else :
   560         else :
   576 
   569 
   577     def copy_from (self, file) :
   570     def copy_from (self, file) :
   578         """
   571         """
   579             Replace this file with a copy of the given file with default permissions.
   572             Replace this file with a copy of the given file with default permissions.
   580 
   573 
   581             Raises an error if read_only mode is set.
       
   582 
       
   583             XXX: accept mode
   574             XXX: accept mode
   584         """
   575         """
   585 
   576 
   586         if self.config.read_only :
       
   587             raise Exception("Not copying file as read_only mode is set: %s -> %s" % (file, self))
       
   588         
       
   589         # perform the copy
   577         # perform the copy
   590         shutil.copyfile(file.path, self.path)
   578         shutil.copyfile(file.path, self.path)
   591 
   579 
   592     def newer_than (self, file) :
   580     def newer_than (self, file) :
   593         """
   581         """
   618    
   606    
   619     def subdir (self, name, create=False) :
   607     def subdir (self, name, create=False) :
   620         """
   608         """
   621             Returns a Directory object representing the name underneath this dir.
   609             Returns a Directory object representing the name underneath this dir.
   622 
   610 
   623             If the create option is given, the directory will be created if it does not exist. Note that this will
   611             If the create option is given, the directory will be created if it does not exist.
   624             raise an error if read_only mode is set
       
   625         """
   612         """
   626 
   613 
   627         subdir = Directory(self, name=name)
   614         subdir = Directory(self, name=name)
   628 
   615 
   629         if create and not subdir.is_dir() :
   616         if create and not subdir.is_dir() :
   651 
   638 
   652     def mkdir (self) :
   639     def mkdir (self) :
   653         """
   640         """
   654             Create this directory with default permissions.
   641             Create this directory with default permissions.
   655 
   642 
   656             This will fail if read_only mode is set
       
   657             
       
   658             XXX: mode argument
   643             XXX: mode argument
   659         """
   644         """
   660         
       
   661         if self.config.read_only :
       
   662             # forbidden
       
   663             raise Exception("Unable to create dir due to read_only mode: %s" % self)
       
   664         
   645         
   665         # do it
   646         # do it
   666         os.mkdir(self.path)
   647         os.mkdir(self.path)
   667 
   648 
   668     def listdir (self, skip_dotfiles=True) :
   649     def listdir (self, skip_dotfiles=True) :
   701         """
   682         """
   702         
   683         
   703         # build using parent root_path
   684         # build using parent root_path
   704         return os.path.join('..', self.parent.root_path)
   685         return os.path.join('..', self.parent.root_path)
   705  
   686  
   706     def children (self) :
       
   707         """
       
   708             Yield a series of Node subclasses representing the items in this dir.
       
   709             
       
   710             This uses self.NODE_TYPES to figure out what kind of sub-node object to build. This should be a list of
       
   711                 (test_func, node_type)
       
   712 
       
   713             tuples, of which the first is a function that takes a Node as it's sole argument, and returns a boolean.
       
   714             For the first test_func which returns True, a Node-subclass object is constructed using node_type.from_node.
       
   715 
       
   716             XXX: never used
       
   717         """
       
   718 
       
   719         for node in self :
       
   720             # figure out what type to use
       
   721             for test_func, node_type in self.NODE_TYPES :
       
   722                 if test_func(node) :
       
   723                     # matches, build
       
   724                     yield node_type.from_node(node)
       
   725 
       
   726             else :
       
   727                 # unknown file type!
       
   728                 raise Exception("unrecongized type of file: %s" % node);
       
   729 
       
   730 # assign default Directory.NODE_TYPES
       
   731 Directory.NODE_TYPES = [
       
   732     (Node.is_dir,   Directory),
       
   733     (Node.is_file,  File),
       
   734 ]
       
   735 
       
   736 
       
   737 class Root (Directory) :
   687 class Root (Directory) :
   738     """
   688     """
   739         A special Directory that overrides the Node methods to anchor the recursion/etc at some 'real' filesystem path.
   689         A special Directory that overrides the Node methods to anchor the recursion/etc at some 'real' filesystem path.
   740     """
   690     """
   741 
   691