Source code for apptools.io.file

# (C) Copyright 2005-2022 Enthought, Inc., Austin, TX
# All rights reserved.
#
# This software is provided without warranty under the terms of the BSD
# license included in LICENSE.txt and may be redistributed only under
# the conditions described in the aforementioned license. The license
# is also available online at http://www.enthought.com/licenses/BSD.txt
#
# Thanks for using Enthought open source!
""" A representation of files and folders in a file system. """


# Standard/built-in imports.
import mimetypes
import os
import shutil
import stat

# Enthought library imports.
from traits.api import Bool, HasPrivateTraits, Instance, List, Property
from traits.api import Str


[docs]class File(HasPrivateTraits): """ A representation of files and folders in a file system. """ #### 'File' interface ##################################################### # The absolute path name of this file/folder. absolute_path = Property(Str) # The folder's children (for files this is always None). children = Property(List("File")) # The file extension (for folders this is always the empty string). # # fixme: Currently the extension includes the '.' (ie. we have '.py' and # not 'py'). This is because things like 'os.path.splitext' leave the '.' # on, but I'm not sure that this is a good idea! ext = Property(Str) # Does the file/folder exist? exists = Property(Bool) # Is this an existing file? is_file = Property(Bool) # Is this an existing folder? is_folder = Property(Bool) # Is this a Python package (ie. a folder contaning an '__init__.py' file. is_package = Property(Bool) # Is the file/folder readonly? is_readonly = Property(Bool) # The MIME type of the file (for a folder this will always be # 'context/unknown' (is that what it should be?)). mime_type = Property(Str) # The last component of the path without the extension. name = Property(Str) # The parent of this file/folder (None if it has no parent). parent = Property(Instance("File")) # The path name of this file/folder. path = Str # A URL reference to the file. url = Property(Str) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, path, **traits): """ Creates a new representation of the specified path. """ super(File, self).__init__(path=path, **traits) def __str__(self): """ Returns an 'informal' string representation of the object. """ return "File(%s)" % self.path ########################################################################### # 'File' interface. ########################################################################### #### Properties ########################################################### def _get_absolute_path(self): """ Returns the absolute path of this file/folder. """ return os.path.abspath(self.path) def _get_children(self): """Returns the folder's children. Returns None if the path does not exist or is not a folder. """ if self.is_folder: children = [] for name in os.listdir(self.path): children.append(File(os.path.join(self.path, name))) else: children = None return children def _get_exists(self): """ Returns True if the file exists, otherwise False. """ return os.path.exists(self.path) def _get_ext(self): """ Returns the file extension. """ name, ext = os.path.splitext(self.path) return ext def _get_is_file(self): """ Returns True if the path exists and is a file. """ return self.exists and os.path.isfile(self.path) def _get_is_folder(self): """ Returns True if the path exists and is a folder. """ return self.exists and os.path.isdir(self.path) def _get_is_package(self): """ Returns True if the path exists and is a Python package. """ return self.is_folder and "__init__.py" in os.listdir(self.path) def _get_is_readonly(self): """ Returns True if the file/folder is readonly, otherwise False. """ # If the File object is a folder, os.access cannot be used because it # returns True for both read-only and writable folders on Windows # systems. if self.is_folder: # Mask for the write-permission bits on the folder. If these bits # are set to zero, the folder is read-only. WRITE_MASK = 0x92 permissions = os.stat(self.path)[0] if permissions & WRITE_MASK == 0: readonly = True else: readonly = False elif self.is_file: readonly = not os.access(self.path, os.W_OK) else: readonly = False return readonly def _get_mime_type(self): """ Returns the mime-type of this file/folder. """ mime_type, encoding = mimetypes.guess_type(self.path) if mime_type is None: mime_type = "content/unknown" return mime_type def _get_name(self): """ Returns the last component of the path without the extension. """ basename = os.path.basename(self.path) name, ext = os.path.splitext(basename) return name def _get_parent(self): """ Returns the parent of this file/folder. """ return File(os.path.dirname(self.path)) def _get_url(self): """ Returns the path as a URL. """ # Strip out the leading slash on POSIX systems. return "file:///%s" % self.absolute_path.lstrip("/") #### Methods ##############################################################
[docs] def copy(self, destination): """ Copies this file/folder. """ # Allow the destination to be a string. if not isinstance(destination, File): destination = File(destination) if self.is_folder: shutil.copytree(self.path, destination.path) elif self.is_file: shutil.copyfile(self.path, destination.path)
[docs] def create_file(self, contents=""): """ Creates a file at this path. """ if self.exists: raise ValueError("file %s already exists" % self.path) f = open(self.path, "w") f.write(contents) f.close()
[docs] def create_folder(self): """Creates a folder at this path. All intermediate folders MUST already exist. """ if self.exists: raise ValueError("folder %s already exists" % self.path) os.mkdir(self.path)
[docs] def create_folders(self): """Creates a folder at this path. This will attempt to create any missing intermediate folders. """ if self.exists: raise ValueError("folder %s already exists" % self.path) os.makedirs(self.path)
[docs] def create_package(self): """Creates a package at this path. All intermediate folders/packages MUST already exist. """ if self.exists: raise ValueError("package %s already exists" % self.path) os.mkdir(self.path) # Create the '__init__.py' file that actually turns the folder into a # package! init = File(os.path.join(self.path, "__init__.py")) init.create_file()
[docs] def delete(self): """Deletes this file/folder. Does nothing if the file/folder does not exist. """ if self.is_folder: # Try to make sure that everything in the folder is writeable. self.make_writeable() # Delete it! shutil.rmtree(self.path) elif self.is_file: # Try to make sure that the file is writeable. self.make_writeable() # Delete it! os.remove(self.path)
[docs] def make_writeable(self): """ Attempt to make the file/folder writeable. """ if self.is_folder: # Try to make sure that everything in the folder is writeable # (i.e., can be deleted!). This comes in especially handy when # deleting '.svn' directories. for path, dirnames, filenames in os.walk(self.path): for name in dirnames + filenames: filename = os.path.join(path, name) if not os.access(filename, os.W_OK): os.chmod(filename, stat.S_IWUSR) elif self.is_file: # Try to make sure that the file is writeable (i.e., can be # deleted!). if not os.access(self.path, os.W_OK): os.chmod(self.path, stat.S_IWUSR)
[docs] def move(self, destination): """ Moves this file/folder. """ # Allow the destination to be a string. if not isinstance(destination, File): destination = File(destination) # Try to make sure that everything in the directory is writeable. self.make_writeable() # Move it! shutil.move(self.path, destination.path)