diff --git a/docs/source/conf.py b/docs/source/conf.py index a0cc668d..f3b85c68 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -38,12 +38,19 @@ # ones. extensions = [ 'sphinx.ext.autodoc', - 'sphinx.ext.viewcode' + 'sphinx.ext.viewcode', + 'sphinx.ext.napoleon', + 'sphinx.ext.intersphinx' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] +# intersphinx domain mapping +intersphinx_mapping = { + 'python': ('https://docs.python.org/3.6', None) +} + # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # source_suffix = ['.rst', '.md'] @@ -89,7 +96,7 @@ # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +default_role = 'py:obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True diff --git a/docs/source/reference.rst b/docs/source/reference.rst index 752c5988..634263f7 100644 --- a/docs/source/reference.rst +++ b/docs/source/reference.rst @@ -10,6 +10,7 @@ Reference reference/enums.rst reference/errors.rst reference/info_objects.rst + reference/filesize.rst reference/mirror.rst reference/move.rst reference/mode.rst diff --git a/docs/source/reference/appfs.rst b/docs/source/reference/appfs.rst index 3a9e6387..a0bc953b 100644 --- a/docs/source/reference/appfs.rst +++ b/docs/source/reference/appfs.rst @@ -1,7 +1,5 @@ App Filesystems =============== -Filesystems for platform-specific application directories. - .. automodule:: fs.appfs - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/base.rst b/docs/source/reference/base.rst index 34387902..92358a86 100644 --- a/docs/source/reference/base.rst +++ b/docs/source/reference/base.rst @@ -1,9 +1,5 @@ -fs.base.FS -========== +fs.base +======= -The filesystem base class is common to all filesystems. If you -familiarize yourself with this (rather straightforward) API, you can -work with any of the supported filesystems. - -.. autoclass:: fs.base.FS - :members: +.. automodule:: fs.base + :members: FS diff --git a/docs/source/reference/copy.rst b/docs/source/reference/copy.rst index f1adef16..ab1e9246 100644 --- a/docs/source/reference/copy.rst +++ b/docs/source/reference/copy.rst @@ -1,7 +1,5 @@ fs.copy ======= -Copying files from one filesystem to another. - .. automodule:: fs.copy - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/enums.rst b/docs/source/reference/enums.rst index c1256817..9756bc20 100644 --- a/docs/source/reference/enums.rst +++ b/docs/source/reference/enums.rst @@ -1,9 +1,6 @@ fs.enums ======== -.. autoclass:: fs.ResourceType - :members: - - -.. autoclass:: fs.Seek - :members: \ No newline at end of file +.. automodule:: fs.enums + :members: ResourceType, Seek + :member-order: bysource diff --git a/docs/source/reference/errors.rst b/docs/source/reference/errors.rst index f142a5dc..5e463282 100644 --- a/docs/source/reference/errors.rst +++ b/docs/source/reference/errors.rst @@ -2,4 +2,5 @@ fs.errors ========= .. automodule:: fs.errors - :members: \ No newline at end of file + :members: + :show-inheritance: diff --git a/docs/source/reference/filesize.rst b/docs/source/reference/filesize.rst new file mode 100644 index 00000000..996d4210 --- /dev/null +++ b/docs/source/reference/filesize.rst @@ -0,0 +1,5 @@ +fs.filesize +=========== + +.. automodule:: fs.filesize + :members: diff --git a/docs/source/reference/ftpfs.rst b/docs/source/reference/ftpfs.rst index 44691518..53f6478d 100644 --- a/docs/source/reference/ftpfs.rst +++ b/docs/source/reference/ftpfs.rst @@ -1,7 +1,5 @@ FTP Filesystem ============== -Manage resources on a FTP server. - .. automodule:: fs.ftpfs - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/memoryfs.rst b/docs/source/reference/memoryfs.rst index b4e6e5aa..d858e82a 100644 --- a/docs/source/reference/memoryfs.rst +++ b/docs/source/reference/memoryfs.rst @@ -1,7 +1,5 @@ Memory Filesystem ================= -Create and manage an in-memory filesystems. - .. automodule:: fs.memoryfs - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/mirror.rst b/docs/source/reference/mirror.rst index db03cd38..942e3693 100644 --- a/docs/source/reference/mirror.rst +++ b/docs/source/reference/mirror.rst @@ -1,20 +1,5 @@ fs.mirror ========= -Create a duplicate of a filesystem. - -Mirroring will create a copy of a source filesystem on a destination -filesystem. If there are no files on the destination, then mirroring -is simply a straight copy. If there are any files or directories on the -destination they may be deleted or modified to match the source. - -In order to avoid redundant copying of files, ``mirror`` can compare -timestamps, and only copy files with a newer modified date. This -timestamp comparison is only done if the file sizes are different. - -This scheme will work if you have mirrored a directory previously, and -you would like to copy any changes. Otherwise you should set the -``copy_if_newer`` parameter to ``False`` to guarantee an exact copy, at -the expense of potentially copying extra files. - -.. autofunction:: fs.mirror.mirror +.. automodule:: fs.mirror + :members: mirror diff --git a/docs/source/reference/mountfs.rst b/docs/source/reference/mountfs.rst index 890e652d..3f153964 100644 --- a/docs/source/reference/mountfs.rst +++ b/docs/source/reference/mountfs.rst @@ -43,5 +43,8 @@ Now both filesystems may be accessed with the same path structure:: print(combined_fs.gettext('/config/defaults.cfg')) read_jpg(combined_fs.open('/resources/images/logo.jpg', 'rb') -.. automodule:: fs.mountfs - :members: \ No newline at end of file +.. autoclass:: fs.mountfs.MountFS + :members: + +.. autoexception:: fs.mountfs.MountError + :show-inheritance: diff --git a/docs/source/reference/move.rst b/docs/source/reference/move.rst index 940afc35..e2e07ce5 100644 --- a/docs/source/reference/move.rst +++ b/docs/source/reference/move.rst @@ -1,7 +1,5 @@ fs.move ======= -Moving files from one filesystem to another. - .. automodule:: fs.move - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/multifs.rst b/docs/source/reference/multifs.rst index d255d397..e262861a 100644 --- a/docs/source/reference/multifs.rst +++ b/docs/source/reference/multifs.rst @@ -51,5 +51,5 @@ directories:: `-- theme.html -.. automodule:: fs.multifs - :members: \ No newline at end of file +.. autoclass:: fs.multifs.MultiFS + :members: diff --git a/docs/source/reference/opener.rst b/docs/source/reference/opener.rst index 6c7c7de4..d932a231 100644 --- a/docs/source/reference/opener.rst +++ b/docs/source/reference/opener.rst @@ -3,14 +3,23 @@ fs.opener Open filesystems from a URL. +fs.opener.base +-------------- .. automodule:: fs.opener.base :members: +fs.opener.parse +--------------- .. automodule:: fs.opener.parse :members: +fs.opener.registry +------------------ .. automodule:: fs.opener.registry :members: +fs.opener.errors +---------------- .. automodule:: fs.opener.errors :members: + :show-inheritance: diff --git a/docs/source/reference/osfs.rst b/docs/source/reference/osfs.rst index 54efab23..7d64c00d 100644 --- a/docs/source/reference/osfs.rst +++ b/docs/source/reference/osfs.rst @@ -1,9 +1,5 @@ OS Filesystem ============= -Manage the filesystem provided by your OS. - -In essence an ``OSFS`` is a thin layer over the ``io`` and ``os`` modules in the Python library. - .. automodule:: fs.osfs :members: diff --git a/docs/source/reference/subfs.rst b/docs/source/reference/subfs.rst index 15b54c51..8e5bf83c 100644 --- a/docs/source/reference/subfs.rst +++ b/docs/source/reference/subfs.rst @@ -2,4 +2,5 @@ Sub Filesystem ============== .. automodule:: fs.subfs - :members: \ No newline at end of file + :members: + :member-order: bysource diff --git a/docs/source/reference/tarfs.rst b/docs/source/reference/tarfs.rst index c26328ac..9d1efb7f 100644 --- a/docs/source/reference/tarfs.rst +++ b/docs/source/reference/tarfs.rst @@ -1,7 +1,6 @@ Tar Filesystem ============== -A filesystem implementation for .tar files. - .. automodule:: fs.tarfs :members: + :member-order: bysource diff --git a/docs/source/reference/tempfs.rst b/docs/source/reference/tempfs.rst index 1e1f5076..33f72c79 100644 --- a/docs/source/reference/tempfs.rst +++ b/docs/source/reference/tempfs.rst @@ -1,9 +1,5 @@ Temporary Filesystem ==================== -A temporary filesytem is stored in a location defined by your OS (``/tmp`` on linux). The contents are deleted when the filesystem is closed. - -A ``TempFS`` is a good way of preparing a directory structure in advance, that you can later copy. It can also be used as a temporary data store. - .. automodule:: fs.tempfs - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/tree.rst b/docs/source/reference/tree.rst index f725d2a2..66821d1a 100644 --- a/docs/source/reference/tree.rst +++ b/docs/source/reference/tree.rst @@ -1,7 +1,5 @@ fs.tree ======= -Render a text tree view, with optional color in terminals. - .. automodule:: fs.tree - :members: \ No newline at end of file + :members: diff --git a/docs/source/reference/zipfs.rst b/docs/source/reference/zipfs.rst index 8c6a0c6c..0a9af4b0 100644 --- a/docs/source/reference/zipfs.rst +++ b/docs/source/reference/zipfs.rst @@ -1,7 +1,6 @@ Zip Filesystem ============== -A filesystem implementation for .zip files. - .. automodule:: fs.zipfs :members: + :member-order: bysource diff --git a/fs/__init__.py b/fs/__init__.py index 404b8a55..680a519d 100644 --- a/fs/__init__.py +++ b/fs/__init__.py @@ -1,3 +1,6 @@ +"""Python filesystem abstraction layer. +""" + __import__('pkg_resources').declare_namespace(__name__) from ._version import __version__ diff --git a/fs/_repr.py b/fs/_repr.py index 3a853713..fa329538 100644 --- a/fs/_repr.py +++ b/fs/_repr.py @@ -1,26 +1,26 @@ -""" -Tools to generate __repr__ strings - +"""Tools to generate __repr__ strings. """ from __future__ import unicode_literals def make_repr(class_name, *args, **kwargs): - """ - Generate a repr string. + """Generate a repr string. Positional arguments should be the positional arguments used to construct the class. Keyword arguments should consist of tuples of the attribute value and default. If the value is the default, then it won't be rendered in the output. - Here's an example:: - - def __repr__(self): - return make_repr('MyClass', 'foo', name=(self.name, None)) - - The output of this would be something line ``MyClass('foo', - name='Will')``. + Example: + >>> class MyClass(object): + ... def __init__(self, name=None): + ... self.name = name + ... def __repr__(self): + ... return make_repr('MyClass', 'foo', name=(self.name, None)) + >>> MyClass('Will') + MyClass('foo', name='Will') + >>> MyClass(None) + MyClass() """ arguments = [repr(arg) for arg in args] diff --git a/fs/_version.py b/fs/_version.py index 4194d1e6..d7a06076 100644 --- a/fs/_version.py +++ b/fs/_version.py @@ -1,2 +1,3 @@ -"""Version, used in module and setup.py.""" +"""Version, used in module and setup.py. +""" __version__ = "2.0.12a0" diff --git a/fs/appfs.py b/fs/appfs.py index a5ed12bd..84df406d 100644 --- a/fs/appfs.py +++ b/fs/appfs.py @@ -1,10 +1,8 @@ -""" -A collection of filesystems that map to application specific locations -defined by the OS. +"""Manage filesystems in platform-specific application directories. These classes abstract away the different requirements for user data across platforms, which vary in their conventions. They are all -subclasses of :class:`~fs.osfs.OSFS`. +subclasses of `~fs.osfs.OSFS`. """ # Thanks to authors of https://pypi.python.org/pypi/appdirs @@ -24,10 +22,9 @@ class _AppFS(OSFS): + """Abstract base class for an app FS. """ - Abstract base class for an app FS. - """ app_dir = None def __init__(self, @@ -60,120 +57,126 @@ def __str__(self): ) class UserDataFS(_AppFS): - """ - A filesystem for per-user application data. + """A filesystem for per-user application data. May also be opened with ``open_fs('userdata://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'user_data_dir' class UserConfigFS(_AppFS): - """ - A filesystem for per-user config data. + """A filesystem for per-user config data. May also be opened with ``open_fs('userconf://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'user_config_dir' class UserCacheFS(_AppFS): - """ - A filesystem for per-user application cache data. + """A filesystem for per-user application cache data. May also be opened with ``open_fs('usercache://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'user_cache_dir' class SiteDataFS(_AppFS): - """ - A filesystem for application site data. + """A filesystem for application site data. May also be opened with ``open_fs('sitedata://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'site_data_dir' class SiteConfigFS(_AppFS): - """ - A filesystem for application config data. + """A filesystem for application config data. May also be opened with ``open_fs('siteconf://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'site_config_dir' class UserLogFS(_AppFS): - """ - A filesystem for per-user application log data. + """A filesystem for per-user application log data. May also be opened with ``open_fs('userlog://appname:author:version')``. - :param str appname: The name of the application. - :param str author: The name of the author (used on Windows). - :param str version: Optional version string, if a unique location - per version of the application is required. - :param bool roaming: If ``True``, use a *roaming* profile on - Windows. - :param bool create: If ``True`` (the default) the directory will - be created if it does not exist. + Arguments: + appname (str): The name of the application. + author (str): The name of the author (used on Windows). + version (str): Optional version string, if a unique location + per version of the application is required. + roaming (bool): If `True`, use a *roaming* profile on + Windows. + create (bool): If `True` (the default) the directory + will be created if it does not exist. """ + app_dir = 'user_log_dir' diff --git a/fs/base.py b/fs/base.py index 852b4e95..8c534c34 100644 --- a/fs/base.py +++ b/fs/base.py @@ -1,9 +1,8 @@ -""" -fs.base -======= - -PyFilesystem base class +"""PyFilesystem base class. +The filesystem base class is common to all filesystems. If you +familiarize yourself with this (rather straightforward) API, you +can work with any of the supported filesystems. """ @@ -39,29 +38,32 @@ @six.add_metaclass(abc.ABCMeta) class FS(object): - """Base class for FS objects.""" + """Base class for FS objects. + """ # This is the "standard" meta namespace. _meta = {} def __init__(self): + """Create a filesystem. See help(type(self)) for accurate signature. + """ self._closed = False self._lock = threading.RLock() super(FS, self).__init__() def __enter__(self): - """Allow use of filesystem as a context manager.""" + """Allow use of filesystem as a context manager. + """ return self def __exit__(self, exc_type, exc_value, traceback): - """Close filesystem on exit.""" + """Close filesystem on exit. + """ self.close() @property def walk(self): - """ - Get a :class:`~fs.walk.BoundWalker` object for this filesystem. - + """`~fs.walk.BoundWalker`: a walker bound to this filesystem. """ return Walker.bind(self) @@ -72,139 +74,142 @@ def walk(self): @abc.abstractmethod def getinfo(self, path, namespaces=None): - """ - Get information regarding a resource (file or directory) on a - filesystem. + """Get information about a resource on a filesystem. + + Arguments: + path (str): A path to a resource on the filesystem. + namespaces (list, optional): Info namespaces to query + (defaults to *basic*). - :param str path: A path to a resource on the filesystem. - :param namespaces: Info namespaces to query (defaults to - 'basic'). - :type namespaces: list or None - :returns: Resource information object. - :rtype: :class:`~fs.info.Info` + Returns: + ~fs.info.Info: resource information object. - For more information regarding resource information see - :ref:`info`. + For more information regarding resource information, see :ref:`info`. """ @abc.abstractmethod def listdir(self, path): - """ - Get a list of the resource names in a directory. + """Get a list of the resource names in a directory. + + This method will return a list of the resources in a directory. + A *resource* is a file, directory, or one of the other types + defined in `~fs.ResourceType`. - :param str path: A path to a directory on the filesystem. - :return: list of names, relative to ``path``. - :rtype: list + Arguments: + path (str): A path to a directory on the filesystem - :raises fs.errors.DirectoryExpected: If `path` is not a - directory. - :raises fs.errors.ResourceNotFound: If `path` does not exist. + Returns: + list: list of names, relative to ``path``. - This method will return a list of the resources in a directory. - A 'resource' is a file, directory, or one of the other types - defined in :class:`~fs.ResourceType`. + Raises: + fs.errors.DirectoryExpected: If ``path`` is not a directory. + fs.errors.ResourceNotFound: If ``path`` does not exist. """ @abc.abstractmethod def makedir(self, path, permissions=None, recreate=False): - """ - Make a directory, and return a :class:`~fs.subfs.SubFS` for - the new directory. + """Make a directory. + + Arguments: + path (str): Path to directory from root. + permissions (~fs.permissions.Permissions, optional): a + `Permissions` instance, or `None` to use default. + recreate (bool, optional): Set to `True` to avoid raising an + error if the directory already exists (defaults to `False`). - :param str path: Path to directory from root. - :param permissions: :class:`~fs.permissions.Permissions` - instance. - :type permissions: Permissions - :param bool recreate: Do not raise an error if the directory - exists. - :rtype: :class:`~fs.subfs.SubFS` + Returns: + ~fs.subfs.SubFS: a filesystem whose root is the new directory. - :raises fs.errors.DirectoryExists: if the path already exists. - :raises fs.errors.ResourceNotFound: if the path is not found. + Raises: + fs.errors.DirectoryExists: If the path already exists. + fs.errors.ResourceNotFound: If the path is not found. """ @abc.abstractmethod def openbin(self, path, mode="r", buffering=-1, **options): - """ - Open a binary file-like object. - - :param str path: A path on the filesystem. - :param str mode: Mode to open file (must be a valid non-text - mode). Since this method only opens binary files, the `b` in - the mode string is implied. - :param buffering: Buffering policy (-1 to use default - buffering, 0 to disable buffering, or positive integer to - indicate buffer size). - :type buffering: int - :param options: Keyword parameters for any additional - information required by the filesystem (if any). - :rtype: file object - - :raises fs.errors.FileExpected: If the path is not a file. - :raises fs.errors.FileExists: If the file exists, and - *exclusive mode* is specified (`x` in the mode). - :raises fs.errors.ResourceNotFound: If `path` does not exist. + """Open a binary file-like object. + + Arguments: + path (str): A path on the filesystem. + mode (str, optional): Mode to open file (must be a valid + non-text mode, defaults to *r*). Since this method only + opens binary files, the ``b`` in the mode string is implied.s + buffering (int, optional): Buffering policy (-1 to use + default buffering, 0 to disable buffering, or any + positive integer to indicate a buffer size). + **options: keyword arguments for any additional information + required by the filesystem (if any). + + Returns: + io.IOBase: a *file-like* object. + + Raises: + fs.errors.FileExpected: If the path is not a file. + fs.errors.FileExists: If the file exists, and *exclusive mode* + is specified (``x`` in the mode). + fs.errors.ResourceNotFound: If the path does not exist. """ @abc.abstractmethod def remove(self, path): - """ - Remove a file. + """Remove a file from the filesystem. - :param str path: Path to the file you want to remove. + Arguments: + path (str): Path of the file to remove. - :raises fs.errors.FileExpected: if the path is a directory. - :raises fs.errors.ResourceNotFound: if the path does not - exist. + Raises: + fs.errors.FileExpected: If the path is a directory. + fs.errors.ResourceNotFound: If the path does not exist. """ @abc.abstractmethod def removedir(self, path): - """ - Remove a directory from the filesystem. - - :param str path: Path of the directory to remove. - - :raises fs.errors.DirectoryNotEmpty: If the directory is not - empty (see :meth:`~fs.base.removetree` if you want to - remove the directory contents). - :raises fs.errors.DirectoryExpected: If the path is not a - directory. - :raises fs.errors.ResourceNotFound: If the path does not - exist. - :raises fs.errors.RemoveRootError: If an attempt is made to - remove the root directory (i.e. `'/'`). + """Remove a directory from the filesystem. + + Arguments: + path (str): Path of the directory to remove. + + Raises: + fs.errors.DirectoryNotEmpty: If the directory is not empty ( + see `~fs.base.FS.removetree` for a way to remove the + directory contents.). + fs.errors.DirectoryExpected: If the path does not refer to + a directory. + fs.errors.ResourceNotFound: If no resource exists at the + given path. + fs.errors.RemoveRootError: If an attempt is made to remove + the root directory (i.e. ``'/'``) + """ @abc.abstractmethod def setinfo(self, path, info): - """ - Set info on a resource. + """Set info on a resource. - :param str path: Path to a resource on the filesystem. - :param dict info: Dict of resource info. + This method is the compliment to `~fs.base.FS.getinfo` + and is used to set info values on a resource. - :raises fs.errors.ResourceNotFound: If `path` does not exist - on the filesystem. + Arguments: + path (str): Path to a resource on the filesystem. + info (dict): Dictionary of resource info. - This method is the compliment to :class:`~fs.base.getinfo` and - is used to set info values on a resource. + Raises: + fs.errors.ResourceNotFound: If ``path`` does not exist + on the filesystem The ``info`` dict should be in the same format as the raw - info returned by ``getinfo(file).raw``. Here's an example:: + info returned by ``getinfo(file).raw``. - details_info = { - "details": - { - "modified_time": time.time() - } - } - my_fs.setinfo('file.txt', details_info) + Example: + >>> details_info = {"details": { + ... "modified_time": time.time() + ... }} + >>> my_fs.setinfo('file.txt', details_info) """ @@ -214,15 +219,16 @@ def setinfo(self, path, info): # ---------------------------------------------------------------- # def appendbytes(self, path, data): - """ - Append bytes to the end of a file. Creating the file if it - doesn't already exists. + """Append bytes to the end of a file, creating it if needed. + + Arguments: + path (str): Path to a file. + data (bytes): Bytes to append. - :param str path: Path to a file. - :param bytes data: Bytes to append. - :raises TypeError: if ``data`` is not bytes. - :raises fs.errors.ResourceNotFound: if a parent directory of - ``path`` does not exist. + Raises: + TypeError: If ``data`` is not a `bytes` instance. + fs.errors.ResourceNotFound: If a parent directory of + ``path`` does not exist. """ if not isinstance(data, bytes): @@ -237,15 +243,21 @@ def appendtext(self, encoding='utf-8', errors=None, newline=''): - """ - Append text to a file. Creating the file if it doesn't already - exists. + """Append text to the end of a file, creating it if needed. + + Arguments: + path (str): Path to a file. + text (str): Text to append. + encoding (str, optional): Encoding for text files + (defaults to ``utf-8``). + errors (str, optional): What to do with unicode decode errors + (see `codecs` module for more information). + newline (str, optional): Newline parameter. - :param str path: Path to a file. - :param str text: Text to append. - :raises TypeError: if ``text`` is not bytes. - :raises fs.errors.ResourceNotFound: if a parent directory of - ``path`` does not exist. + Raises: + TypeError: if ``text`` is not an unicode string. + fs.errors.ResourceNotFound: if a parent directory of + ``path`` does not exist. """ if not isinstance(text, six.text_type): @@ -259,8 +271,7 @@ def appendtext(self, append_file.write(text) def close(self): - """ - Close the filesystem and release any resources. + """Close the filesystem and release any resources. It is important to call this method when you have finished working with the filesystem. Some filesystems may not finalize @@ -269,32 +280,33 @@ def close(self): times), or you can use the filesystem as a context manager to automatically close. - Here's an example of automatically closing a filesystem:: - - with OSFS('~/Desktop') as desktop_fs: - desktop_fs.settext( - 'note.txt', - "Don't forget to tape Game of Thrones" - ) + Example: + >>> with OSFS('~/Desktop') as desktop_fs: + ... desktop_fs.settext( + ... 'note.txt', + ... "Don't forget to tape Game of Thrones" + ... ) If you attempt to use a filesystem that has been closed, a - :class:`~fs.errors.FilesystemClosed` exception will be thrown. + `~fs.errors.FilesystemClosed` exception will be thrown. """ self._closed = True def copy(self, src_path, dst_path, overwrite=False): - """ - Copy file contents from ``src_path`` to ``dst_path``. + """Copy file contents from ``src_path`` to ``dst_path``. + + Arguments: + src_path (str): Path of source file. + dst_path (str): Path to destination file. + overwrite (bool, Optional): If `True`, overwrite the + destination file if it exists (defaults to `False`). - :param src_path: Path of source file. - :type src_path: str - :param dst_path: Path to destination file. - :type dst_path: str - :raises fs.errors.DestinationExists: If ``dst_path`` exists, - and overwrite == ``False``. - :raises fs.errors.ResourceNotFound: If a parent directory of - ``dst_path`` does not exist. + Raises: + fs.errors.DestinationExists: If ``dst_path`` exists, + and ``overwrite`` is `False`. + fs.errors.ResourceNotFound: If a parent directory of + ``dst_path`` does not exist. """ with self._lock: @@ -304,15 +316,18 @@ def copy(self, src_path, dst_path, overwrite=False): self.setbinfile(dst_path, read_file) def copydir(self, src_path, dst_path, create=False): - """ - Copy the contents of ``src_path`` to ``dst_path``. + """Copy the contents of ``src_path`` to ``dst_path``. + + Arguments: + src_path (str): Path of source directory. + dst_path (str): Path to destination directory. + create (bool, optional): If `True`, then ``dst_path`` + will be created if it doesn't exist alreadys + (defaults to `False`). - :param str src_path: Source directory. - :param str dst_path: Destination directory. - :param bool create: If ``True`` then ``src_path`` will be - created if it doesn't already exist. - :raises fs.errors.ResourceNotFound: If the destination - directory does not exist, and ``create`` is not True. + Raises: + fs.errors.ResourceNotFound: If the ``dst_path`` + does not exist, and ``create`` is not `True`. """ with self._lock: @@ -328,18 +343,19 @@ def copydir(self, src_path, dst_path, create=False): ) def create(self, path, wipe=False): - """ - Create an empty file. - - :param str path: Path to new file in filesystem. - :param bool wipe: Truncate any existing file to 0 bytes. - :returns: ``True`` if file was created, ``False`` if it already - existed. - :rtype: bool + """Create an empty file. The default behavior is to create a new file if one doesn't - already exist. If ``wipe == True`` an existing file will be - truncated. + already exist. If ``wipe`` is `True`, any existing file will + be truncated. + + Arguments: + path (str): Path to a new file in the filesystem. + wipe (bool, optional): If `True`, truncate any existing + file to 0 bytes (defaults to `False`). + + Returns: + bool: `True` if a new file had to be created. """ with self._lock: @@ -350,11 +366,13 @@ def create(self, path, wipe=False): return True def desc(self, path): - """ - Return a short descriptive text regarding a path. + """Return a short descriptive text regarding a path. + + Arguments: + path (str): A path to a resource on the filesystem. - :param str path: A path to a resource on the filesystem. - :rtype: str + Returns: + str: a short description of the path. """ if not self.exists(path): @@ -367,14 +385,13 @@ def desc(self, path): return syspath def exists(self, path): - """ - Check if a path maps to a resource. + """Check if a path maps to a resource. - :param str path: Path to a resource - :rtype: bool + Arguments: + path (str): Path to a resource. - A ``path`` exists if it maps to any resource (including - a directory). + Returns: + bool: `True` if a resource exists at the given path. """ try: @@ -392,50 +409,54 @@ def filterdir(self, exclude_files=None, namespaces=None, page=None): - """ - Get an iterator of resource info, filtered by file patterns. - - :param str path: A path to a directory on the filesystem. - :param list files: A list of unix shell-style patterns to filter - file names, e.g. ``['*.py']``. - :param list dirs: A list of unix shell-style wildcards to - filter directory names. - :param list exclude_dirs: An optional list of patterns used to - exclude directories - :param list exclude_files: An optional list of patterns used to - exclude files. - :param list namespaces: A list of namespaces to include in - the resource information. - :param page: May be a tuple of ``(, )`` indexes to - return an iterator of a subset of the resource info, or - ``None`` to iterate over the entire directory. Paging a - directory scan may be necessary for very large directories. - :type page: tuple or None - :return: An iterator of :class:`~fs.info.Info` objects. - :rtype: iterator - - This method enhances :meth:`~fs.base.FS.scandir` with additional + """Get an iterator of resource info, filtered by patterns. + + This method enhances `~fs.base.FS.scandir` with additional filtering functionality. - """ + Arguments: + path (str): A path to a directory on the filesystem. + files (list, optional): A list of UNIX shell-style patterns + to filter file names, e.g. ``['*.py']``. + dirs (list, optional): A list of UNIX shell-style patterns + to filter directory names. + exclude_dirs (list, optional): An optional list of patterns + used to exclude directories. + exclude_files (list, optional): An optional list of patterns + used to exclude files. + namespaces (list, optional): A list of namespaces to include + in the resource information, e.g. ``['basic', 'access']``. + page (tuple, optional): May be a tuple of ``(, )`` + indexes to return an iterator of a subset of the resource + info, or `None` to iterate over the entire directory. + Paging a directory scan may be necessary for very large + directories. + + Returns: + ~collections.abc.Iterator: an iterator of `Info` objects. + """ resources = self.scandir(path, namespaces=namespaces) filters = [] def match_dir(patterns, info): - """Pattern match info.name""" + """Pattern match info.name. + """ return info.is_file or self.match(patterns, info.name) def match_file(patterns, info): - """Pattern match info.name""" + """Pattern match info.name. + """ return info.is_dir or self.match(patterns, info.name) def exclude_dir(patterns, info): - """Pattern match info.name""" + """Pattern match info.name. + """ return info.is_file or not self.match(patterns, info.name) def exclude_file(patterns, info): - """Pattern match info.name""" + """Pattern match info.name. + """ return info.is_dir or not self.match(patterns, info.name) if files: @@ -461,15 +482,16 @@ def exclude_file(patterns, info): return iter_info def getbytes(self, path): - """ - Get the contents of a file as bytes. + """Get the contents of a file as bytes. - :param str path: A path to a readable file on the filesystem. - :returns: file contents - :rtype: bytes + Arguments: + path (str): A path to a readable file on the filesystem. - :raises fs.errors.ResourceNotFound: If ``path`` does not - exist. + Returns: + bytes: the file contents. + + Raises: + fs.errors.ResourceNotFound: if ``path`` does not exist. """ with closing(self.open(path, mode='rb')) as read_file: @@ -477,17 +499,20 @@ def getbytes(self, path): return contents def gettext(self, path, encoding=None, errors=None, newline=''): - """ - Get the contents of a file as a string. + """Get the contents of a file as a string. - :param str path: A path to a readable file on the filesystem. - :param str encoding: Encoding to use when reading contents in - text mode. - :param str errors: Unicode errors parameter. - :param str newline: Newlines parameter. - :returns: file contents. - :raises fs.errors.ResourceNotFound: If ``path`` does not - exist. + Arguments: + path (str): A path to a readable file on the filesystem. + encoding (str, optional): Encoding to use when reading contents + in text mode (defaults to `None`, reading in binary mode). + errors (str, optional): Unicode errors parameter. + newline (str, optional): Newlines parameter. + + Returns: + str: file contents. + + Raises: + fs.errors.ResourceNotFound: If ``path`` does not exist. """ with closing( @@ -502,17 +527,17 @@ def gettext(self, path, encoding=None, errors=None, newline=''): return contents def getmeta(self, namespace="standard"): - """ - Get meta information regarding a filesystem. + """Get meta information regarding a filesystem. - :param keys: A list of keys to retrieve, or None for all keys. - :type keys: list or None - :param str namespace: The meta namespace (default is - `"standard"`). - :rtype: dict + Arguments: + namespace (str, optional): The meta namespace + (defaults to ``"standard"``). + + Returns: + dict: the meta information. Meta information is associated with a *namespace* which may be - specified with the `namespace` parameter. The default namespace, + specified with the ``namespace`` parameter. The default namespace, ``"standard"``, contains common information regarding the filesystem's capabilities. Some filesystems may provide other namespaces which expose less common or implementation specific @@ -524,28 +549,29 @@ def getmeta(self, namespace="standard"): =================== ============================================ key Description ------------------- -------------------------------------------- - case_insensitive True if this filesystem is case insensitive. - invalid_path_chars A string containing the characters that may + case_insensitive `True` if this filesystem is case + insensitive. + invalid_path_chars A string containing the characters that may not be used on this filesystem. - max_path_length Maximum number of characters permitted in a - path, or None for no limit. + max_path_length Maximum number of characters permitted in + a path, or `None` for no limit. max_sys_path_length Maximum number of characters permitted in - a sys path, or None for no limit. - network True if this filesystem requires a network. - read_only True if this filesystem is read only. - supports_rename True if this filesystem supports an - os.rename operation. + a sys path, or `None` for no limit. + network `True` if this filesystem requires a + network. + read_only `True` if this filesystem is read only. + supports_rename `True` if this filesystem supports an + `os.rename` operation. =================== ============================================ Most builtin filesystems will provide all these keys, and third- party filesystems should do so whenever possible, but a key may not be present if there is no way to know the value. - .. note:: + Note: Meta information is constant for the lifetime of the filesystem, and may be cached. - """ if namespace == 'standard': meta = self._meta.copy() @@ -554,11 +580,13 @@ def getmeta(self, namespace="standard"): return meta def getsize(self, path): - """ - Get the size (in bytes) of a resource. + """Get the size (in bytes) of a resource. + + Arguments: + path (str): A path to a resource. - :param str path: A path to a resource. - :rtype: int + Returns: + int: the *size* of the resource. The *size* of a file is the total number of readable bytes, which may not reflect the exact number of bytes of reserved @@ -572,12 +600,16 @@ def getsize(self, path): return size def getsyspath(self, path): - """ - Get an *system path* to a resource. + """Get the *system path* of a resource. + + Parameters: + path (str): A path on the filesystem. - :param str path: A path on the filesystem. - :rtype: str - :raises fs.errors.NoSysPath: If there is no corresponding system path. + Returns: + str: the *system path* of the resource, if any. + + Raises: + fs.errors.NoSysPath: If there is no corresponding system path. A system path is one recognized by the OS, that may be used outside of PyFilesystem (in an application or a shell for @@ -589,11 +621,10 @@ def getsyspath(self, path): data anywhere the OS knows about. It is also possible for some paths to have a system path, whereas others don't. - If ``path`` doesn't have a system path, - a :class:`~fs.errors.NoSysPath` exception will be thrown. - - .. note:: + If ``path`` doesn't have a system path, a `~fs.errors.NoSysPath` + exception will be thrown. + Note: A filesystem may return a system path even if no resource is referenced by that path -- as long as it can be certain what that system path would be. @@ -602,15 +633,17 @@ def getsyspath(self, path): raise errors.NoSysPath(path=path) def gettype(self, path): - """ - Get the type of a resource. + """Get the type of a resource. + + Parameters: + path (str): A path on the filesystem. - :param path: A path in the filesystem. - :returns: :class:`~fs.ResourceType` + Returns: + ~fs.ResourceType: the type of the resource. A type of a resource is an integer that identifies the what the resource references. The standard type integers may be one - of the values in the :class:`~fs.ResourceType` enumerations. + of the values in the `~fs.ResourceType` enumerations. The most common resource types, supported by virtually all filesystems are ``directory`` (1) and ``file`` (2), but the @@ -637,28 +670,33 @@ def gettype(self, path): return resource_type def geturl(self, path, purpose='download'): - """ - Get a URL to the given resource. - - :param str path: A path on the filesystem - :param str purpose: A short string that indicates which URL to - retrieve for the given path (if there is more than one). The - default is `'download'`, which should return a URL that - serves the file. Other filesystems may support other values - for ``purpose``. - :returns: A URL. - :rtype: str - :raises fs.errors.NoURL: If the path does not map to a URL. + """Get the URL to a given resource. + + Parameters: + path (str): A path on the filesystem + purpose (str, optional): A short string that indicates which + URL to retrieve for the given path (if there is more + than one). The default is ``'download'``, which should + return a URL that serves the file. Other filesystems may + support other values for ``purpose``. + + Returns: + str: a URL. + + Raises: + fs.errors.NoURL: If the path does not map to a URL. """ raise errors.NoURL(path, purpose) def hassyspath(self, path): - """ - Check if a path maps to a system path. + """Check if a path maps to a system path. + + Parameters: + path (str): A path on the filesystem. - :param str path: A path on the filesystem - :rtype: bool + Returns: + bool: `True` if the resource at ``path`` has a *syspath*. """ has_sys_path = True @@ -669,13 +707,15 @@ def hassyspath(self, path): return has_sys_path def hasurl(self, path, purpose='download'): - """ - Check if a path has a corresponding URL. + """Check if a path has a corresponding URL. - :param str path: A path on the filesystem - :param str purpose: A purpose parameter, as given in - :meth:`~fs.base.FS.geturl`. - :rtype: bool + Parameters: + path (str): A path on the filesystem. + purpose (str, optional): A purpose parameter, as given in + `~fs.base.FS.geturl`. + + Returns: + bool: `True` if an URL for the given purpose exists. """ has_url = True @@ -686,55 +726,85 @@ def hasurl(self, path, purpose='download'): return has_url def isclosed(self): - """Check if the filesystem is closed.""" + """Check if the filesystem is closed. + """ return getattr(self, '_closed', False) def isdir(self, path): - """Check a path exists and is a directory.""" + """Check if a path maps to an existing directory. + + Parameters: + path (str): A path on the filesystem. + + Returns: + bool: `True` if ``path`` maps to a directory. + + """ try: return self.getinfo(path).is_dir except errors.ResourceNotFound: return False def isempty(self, path): - """ - Check if a directory is empty (contains no files or - directories). + """Check if a directory is empty. + + A directory is considered empty when it does not contain + any file or any directory. + + Parameters: + path (str): A path to a directory on the filesystem. + + Returns: + bool: `True` if the directory is empty. - :param str path: A directory path. - :rtype: bool + Raises: + errors.DirectoryExpected: If ``path`` is not a directory. + errors.ResourceNotFound: If ``path`` does not exist. """ return next(iter(self.scandir(path)), None) is None def isfile(self, path): - """Check a path exists and is a file.""" + """Check if a path maps to an existing file. + + Parameters: + path (str): A path on the filesystem. + + Returns: + bool: `True` if ``path`` maps to a file. + + """ try: return not self.getinfo(path).is_dir except errors.ResourceNotFound: return False def islink(self, path): - """ - Check if a path is a symlink. + """Check if a path maps to a symlink. + + Parameters: + path (str): A path on the filesystem. - :param str path: A path on the filesystem. - :rtype: bool + Returns: + bool: `True` if ``path`` maps to a symlink. """ self.getinfo(path) return False def lock(self): - """ - Get a context manager that *locks* the filesystem. + """Get a context manager that *locks* the filesystem. Locking a filesystem gives a thread exclusive access to it. Other threads will block until the threads with the lock has - left the context manager. Here's how you would use it:: + left the context manager. - with my_fs.lock(): # May block - # code here has exclusive access to the filesystem + Returns: + threading.RLock: a lock specific to the filesystem instance. + + Example: + >>> with my_fs.lock(): # May block + ... # code here has exclusive access to the filesystem It is a good idea to put a lock around any operations that you would like to be *atomic*. For instance if you are copying @@ -745,7 +815,7 @@ def lock(self): multiple filesystem methods. Individual methods are thread safe already, and don't need to be locked. - .. note:: + Note: This only locks at the Python level. There is nothing to prevent other processes from modifying the filesystem outside of the filesystem instance. @@ -754,14 +824,18 @@ def lock(self): return self._lock def movedir(self, src_path, dst_path, create=False): - """ - Move contents of directory ``src_path`` to ``dst_path``. + """Move contents of directory ``src_path`` to ``dst_path``. - :param str src_path: Path to source directory on the filesystem. - :param str dst_path: Path to destination directory. - :param create: If ``True``, then ``dst_path`` will be created if - it doesn't already exist. - :type create: bool + Parameters: + src_path (str): Path of source directory on the filesystem. + dst_path (str): Path to destination directory. + create (bool, optional): If `True`, then ``dst_path`` will + be created if it doesn't exist already (defaults + to `False`). + + Raises: + fs.errors.ResourceNotFound: if ``dst_path`` does not exist, + and ``create`` is `False`. """ with self._lock: @@ -775,21 +849,24 @@ def movedir(self, src_path, dst_path, create=False): ) def makedirs(self, path, permissions=None, recreate=False): - """ - Make a directory, and any missing intermediate directories. + """Make a directory, and any missing intermediate directories. - :param str path: Path to directory from root. - :param bool recreate: If ``False`` (default), it is an error to - attempt to create a directory that already exists. Set to - `True` to allow directories to be re-created without errors. - :param permissions: Initial permissions. - :returns: A sub-directory filesystem. - :rtype: :class:`~fs.subfs.SubFS` + Arguments: + path (str): Path to directory from root. + permissions (~fs.permissions.Permissions, optional): Initial + permissions, or `None` to use defaults. + recreate (bool, optional): If `False` (the default), + attempting to create an existing directory will raise an + error. Set to `True` to ignore existing directories. - :raises fs.errors.DirectoryExists: if the path is already - a directory, and ``recreate`` is False. - :raises fs.errors.DirectoryExpected: if one of the ancestors - in the path isn't a directory. + Returns: + ~fs.subfs.SubFS: A sub-directory filesystem. + + Raises: + fs.errors.DirectoryExists: if the path is already + a directory, and ``recreate`` is `False`. + fs.errors.DirectoryExpected: if one of the ancestors + in the path is not a directory. """ self.check() @@ -809,21 +886,24 @@ def move(self, src_path, dst_path, overwrite=False): - """ - Move a file from `src_path` to `dst_path`. - - :param str src_path: A path on the filesystem to move. - :param str dst_path: A path on the filesystem where the source - file will be written to. - :param bool overwrite: If `True` destination path will be - overwritten if it exists. - :raises fs.errors.DestinationExists: If ``dst_path`` exists, - and overwrite == ``False``. - :raises fs.errors.ResourceNotFound: If a parent directory of - ``dst_path`` does not exist. + """Move a file from ``src_path`` to ``dst_path``. - """ + Arguments: + src_path (str): A path on the filesystem to move. + dst_path (str): A path on the filesystem where the source + file will be written to. + overwrite (bool, optional): If `True`, destination path + will be overwritten if it exists. + + Raises: + fs.errors.FileExpected: If ``src_path`` maps to a + directory instead of a file. + fs.errors.DestinationExists: If ``dst_path`` exists, + and ``overwrite`` is `False`. + fs.errors.ResourceNotFound: If a parent directory of + ``dst_path`` does not exist. + """ if not overwrite and self.exists(dst_path): raise errors.DestinationExists(dst_path) if self.getinfo(src_path).is_dir: @@ -854,24 +934,33 @@ def open(self, errors=None, newline='', **options): - """ - Open a file. - - :param str path: A path to a file on the filesystem. - :param str mode: Mode to open file object. - :param int buffering: Buffering policy: ``0`` to switch - buffering off, ``1`` to select line buffering, ``>1`` to - select a fixed-size buffer, ``-1`` to auto-detect. - :param str encoding: Encoding for text files (defaults to - ``utf-8``) - :param str errors: What to do with unicode decode errors (see - `stdlib docs `_) - :param str newline: New line parameter (See stdlib docs). - :param options: Additional keyword parameters to set - implementation specific options (if required). See - implementation docs for details. - - :rtype: file object + """Open a file. + + Arguments: + path (str): A path to a file on the filesystem. + mode (str, optional): Mode to open the file object with + (defaults to *r*). + buffering (int, optional): Buffering policy (-1 to use + default buffering, 0 to disable buffering, 1 to select + line buffering, of any positive integer to indicate + a buffer size). + encoding (str, optional): Encoding for text files + (defaults to ``utf-8``) + errors (str, optional): What to do with unicode decode errors + (see `codecs` module for more information). + newline (str, optional): Newline parameter. + **options: keyword arguments for any additional information + required by the filesystem (if any). + + Returns: + io.IOBase: a *file-like* object. + + Raises: + fs.errors.FileExpected: If the path is not a file. + fs.errors.FileExists: If the file exists, and *exclusive mode* + is specified (``x`` in the mode). + fs.errors.ResourceNotFound: If the path does not exist. + """ validate_open_mode(mode) bin_mode = mode.replace('t', '') @@ -891,15 +980,19 @@ def open(self, def opendir(self, path, factory=None): """Get a filesystem object for a sub-directory. - :param str path: Path to a directory on the filesystem. - :param factory: A callable that when invoked with an FS instance - and `path` will return a new FS object representing the sub- - directory contents. If no `factory` is supplied then - :meth:`~fs.subfs.SubFS` will be used. - :returns: A filesystem object representing a sub-directory. - :rtype: :class:`~fs.subfs.SubFS` - :raises fs.errors.DirectoryExpected: If ``dst_path`` does not - exist or is not a directory. + Arguments: + path (str): Path to a directory on the filesystem. + factory (callable, optional): A callable that when invoked + with an FS instance and ``path`` will return a new FS object + representing the sub-directory contents. If no ``factory`` + is supplied then `~fs.subfs.SubFS` will be used. + + Returns: + ~fs.subfs.SubFS: A filesystem representing a sub-directory. + + Raises: + fs.errors.DirectoryExpected: If ``dst_path`` does not + exist or is not a directory. """ from .subfs import SubFS @@ -912,16 +1005,15 @@ def opendir(self, path, factory=None): return factory(self, path) def removetree(self, dir_path): - """ - Recursively remove the contents of a directory. + """Recursively remove the contents of a directory. - This method is similar to :meth:`~fs.base.removedir`, but will + This method is similar to `~fs.base.removedir`, but will remove the contents of the directory if it is not empty. - :param str dir_path: Path to a directory on the filesystem. + Arguments: + dir_path (str): Path to a directory on the filesystem. """ - _dir_path = abspath(normpath(dir_path)) with self._lock: walker = walk.Walker(search="depth") @@ -935,17 +1027,24 @@ def removetree(self, dir_path): self.removedir(dir_path) def scandir(self, path, namespaces=None, page=None): - """ - Get an iterator of resource info. + """Get an iterator of resource info. + + Arguments: + path (str): A path to a directory on the filesystem. + namespaces (list, optional): A list of namespaces to include + in the resource information, e.g. ``['basic', 'access']``. + page (tuple, optional): May be a tuple of ``(, )`` + indexes to return an iterator of a subset of the resource + info, or `None` to iterate over the entire directory. + Paging a directory scan may be necessary for very large + directories. - :param str path: A path on the filesystem - :param list namespaces: A sequence of info namespaces. - :param page: May be a tuple of ``(, )`` indexes to - return an iterator of a subset of the resource info, or - ``None`` to iterator the entire directory. Paging a - directory scan may be necessary for very large directories. - :type page: tuple or None - :rtype: iterator + Returns: + ~collections.abc.Iterator: an iterator of `Info` objects. + + Raises: + fs.errors.DirectoryExpected: If ``path`` is not a directory. + fs.errors.ResourceNotFound: If ``path`` does not exist. """ namespaces = namespaces or () @@ -965,11 +1064,14 @@ def scandir(self, path, namespaces=None, page=None): return iter_info def setbytes(self, path, contents): - """ - Copy (bytes) data to a file. + """Copy binary data to a file. - :param str path: Destination path on the filesystem. - :param bytes contents: A bytes object with data to be written + Arguments: + path (str): Destination path on the filesystem. + contents (bytes): Data to be written. + + Raises: + TypeError: if contents is not bytes. """ if not isinstance(contents, bytes): @@ -978,26 +1080,26 @@ def setbytes(self, path, contents): write_file.write(contents) def setbinfile(self, path, file): - """ - Set a file to the contents of a binary file object. - - :param str path: A path on the filesystem. - :param file: A file object open for reading in binary mode. - :type file: file object + """Set a file to the contents of a binary file object. This method copies bytes from an open binary file to a file on the filesystem. If the destination exists, it will first be truncated. + Arguments: + path (str): A path on the filesystem. + file (io.IOBase): a file object open for reading in + binary mode. + Note that the file object ``file`` will *not* be closed by this method. Take care to close it after this method completes - (ideally with a context manager). For example:: + (ideally with a context manager). - with open('myfile.bin') as read_file: - my_fs.setbinfile('myfile.bin', read_file) + Example: + >>> with open('myfile.bin') as read_file: + ... my_fs.setbinfile('myfile.bin', read_file) """ - with self._lock: with self.open(path, 'wb') as dst_file: tools.copy_file_data(file, dst_file) @@ -1008,31 +1110,32 @@ def setfile(self, encoding=None, errors=None, newline=None): - """ - Set a file to the contents of a file object. - - :param str path: A path on the filesystem. - :param file: A file object open for reading. - :type file: file object - :param str encoding: Encoding of destination file, or ``None`` - for binary. - :param str errors: How encoding errors should be treated (same - as ``io.open``). - :param str newline: Newline parameter (same is ``io.open``). + """Set a file to the contents of a file object. + + Arguments: + path (str): A path on the filesystem. + file (io.IOBase): A file object open for reading. + encoding (str, optional): Encoding of destination file, + defaults to `None` for binary. + errors (str, optional): How encoding errors should be treated + (same as `io.open`). + newline (str, optional): Newline parameter (same + as `io.open`). This method will read the contents of a supplied file object, and write to a file on the filesystem. If the destination exists, it will first be truncated. - If `encoding` is supplied, the destination will be opened in + If ``encoding`` is supplied, the destination will be opened in text mode. Note that the file object ``file`` will *not* be closed by this method. Take care to close it after this method completes - (ideally with a context manager). For example:: + (ideally with a context manager). - with open('myfile.bin') as read_file: - my_fs.setfile('myfile.bin', read_file) + Example: + >>> with open('myfile.bin') as read_file: + ... my_fs.setfile('myfile.bin', read_file) """ mode = 'wb' if encoding is None else 'wt' @@ -1046,16 +1149,17 @@ def setfile(self, tools.copy_file_data(file, dst_file) def settimes(self, path, accessed=None, modified=None): - """ - Set the accessed and modified time on a resource. + """Set the accessed and modified time on a resource. - :param accessed: The accessed time, as a datetime, or None - to use the current rime. - :param modified: The modified time, or ``None`` (the default) to - use the same time as ``accessed`` parameter. + Arguments: + path: A path to a resource on the filesystem. + accessed (datetime, optional): The accessed time, or + `None` (the default) to use the current time. + modified (datetime, optional): The modified time, or + `None` (the default) to use the same time as the + ``accessed`` parameter. """ - details = {} raw_info = { "details": details @@ -1081,16 +1185,19 @@ def settext(self, encoding='utf-8', errors=None, newline=''): - """ - Create or replace a file with text. + """Create or replace a file with text. - :param str contents: Path on the filesystem. - :param str encoding: Encoding of destination file (default - 'UTF-8). - :param str errors: Error parameter for encoding (same as - ``io.open``). - :param str newline: Newline parameter for encoding (same as - ``io.open``). + Arguments: + contents (str): A path on the filesystem. + encoding (str, optional): Encoding of destination file + (defaults to ``'ut-8'``). + errors (str, optional): How encoding errors should be treated + (same as `io.open`). + newline (str, optional): Newline parameter (same + as `io.open`). + + Raises: + TypeError: if ``contents`` is not a unicode string. """ if not isinstance(contents, six.text_type): @@ -1103,13 +1210,15 @@ def settext(self, write_file.write(contents) def touch(self, path): - """ - Create a new file if ``path`` doesn't exist, or update accessed - and modified times if the path does exist. + """Touch a file on the filesystem. - This method is similar to the linux command of the same name. + Touching a file means creating a new file if ``path`` doesn't + exist, or update accessed and modified times if the path does + exist. This method is similar to the linux command of the same + name. - :param str path: A path to a file on the filesystem. + Arguments: + path (str): A path to a file on the filesystem. """ with self._lock: @@ -1124,21 +1233,25 @@ def touch(self, path): self.setinfo(path, raw_info) def validatepath(self, path): - """ - Check if a path is valid on this filesystem, and return a - normalized absolute path. + """Check if a path is valid, returning a normalized absolute path. Many filesystems have restrictions on the format of paths they support. This method will check that ``path`` is valid on the underlaying storage mechanism and throw a - :class:`~fs.errors.InvalidPath` exception if it is not. + `~fs.errors.InvalidPath` exception if it is not. + + Arguments: + path (str): A path. + + Returns: + str: A normalized, absolute path. - :param str path: A path - :returns: A normalized, absolute path. - :rtype: str - :raises fs.errors.InvalidPath: If the path is invalid. - :raises fs.errors.FilesystemClosed: if the filesystem - is closed. + Raises: + fs.errors.InvalidCharsInPath: If the path contains + invalid characters. + fs.errors.InvalidPath: If the path is invalid. + fs.errors.FilesystemClosed: if the filesystem + is closed. """ self.check() @@ -1178,57 +1291,56 @@ def validatepath(self, path): # ---------------------------------------------------------------- # def getbasic(self, path): - """ - Get the *basic* resource info. - - :param str path: A path on the filesystem. - :returns: Resource information object for ``path``. - :rtype: :class:`~fs.info.Info` + """Get the *basic* resource info. This method is shorthand for the following:: fs.getinfo(path, namespaces=['basic']) + Arguments: + path (str): A path on the filesystem. + + Returns: + ~fs.info.Info: Resource information object for ``path``. + """ return self.getinfo(path, namespaces=['basic']) def getdetails(self, path): - """ - Get the *details* resource info. - - :param str path: A path on the filesystem. - :returns: Resource information object for ``path``. - :rtype: :class:`~fs.info.Info` + """Get the *details* resource info. This method is shorthand for the following:: fs.getinfo(path, namespaces=['details']) + Arguments: + path (str): A path on the filesystem. + + Returns: + ~fs.info.Info: Resource information object for ``path``. + """ return self.getinfo(path, namespaces=['details']) def check(self): - """ - Check a filesystem may be used. - - Will throw a :class:`~fs.errors.FilesystemClosed` if the - filesystem is closed. + """Check if a filesystem may be used. - :returns: None - :raises fs.errors.FilesystemClosed: if the filesystem - is closed. + Raises: + fs.errors.FilesystemClosed: if the filesystem is closed. """ if self.isclosed(): raise errors.FilesystemClosed() def match(self, patterns, name): - """ - Check if a name matches any of a list of wildcards. + """Check if a name matches any of a list of wildcards. + + Arguments: + patterns (list): A list of patterns, e.g. ``['*.py']`` + name (str): A file or directory name (not a path) - :param list patterns: A list of patterns, e.g. ``['*.py']`` - :param str name: A file or directory name (not a path) - :rtype: bool + Returns: + bool: `True` if ``name`` matches any of the patterns. If a filesystem is case *insensitive* (such as Windows) then this method will perform a case insensitive match (i.e. ``*.py`` @@ -1236,13 +1348,15 @@ def match(self, patterns, name): be case sensitive (``*.py`` and ``*.PY`` will match different names). + Example: >>> home_fs.match(['*.py'], '__init__.py') True >>> home_fs.match(['*.jpg', '*.png'], 'foo.gif') False - If ``patterns`` is ``None``, or (``['*']``), then this method - will always return True. + Note: + If ``patterns`` is `None` (or ``['*']``), then this + method will always return `True`. """ if patterns is None: @@ -1254,11 +1368,27 @@ def match(self, patterns, name): return matcher(name) def tree(self, **kwargs): - """ - Render a tree view of the filesystem to stdout or a file. + """Render a tree view of the filesystem to stdout or a file. The parameters are passed to :func:`~fs.tree.render`. + Keyword Arguments: + path (str): The path of the directory to start rendering + from (defaults to root folder, i.e. ``'/'``). + file (io.IOBase): An open file-like object to render the + tree, or `None` for stdout. + encoding (str): Unicode encoding, or `None` to + auto-detect. + max_levels (int): Maximum number of levels to + display, or `None` for no maximum. + with_color (bool): Enable terminal color output, + or `None` to auto-detect terminal. + dirs_first (bool): Show directories first. + exclude (list): Option list of directory patterns + to exclude from the tree render. + filter (list): Optional list of files patterns to + match in the tree render. + """ from .tree import render render(self, **kwargs) diff --git a/fs/compress.py b/fs/compress.py index e1b367a1..c9585814 100644 --- a/fs/compress.py +++ b/fs/compress.py @@ -1,7 +1,7 @@ -""" -This module can compress the contents of a filesystem. +"""Functions to compress the contents of a filesystem. -Currently zip and tar are supported. +Currently zip and tar are supported, using the `zipfile` and +`tarfile` modules from the standard library. """ from __future__ import absolute_import @@ -27,23 +27,20 @@ def write_zip(src_fs, compression=zipfile.ZIP_DEFLATED, encoding="utf-8", walker=None): - """ - Write the contents of a filesystem to a zip file. - - :param file: Destination file, may be a file name or an open file - object. - :type file: str or file-like. - :param compression: Compression to use (one of the constants defined - in the zipfile module in the stdlib). - :type compression: str - :param encoding: The encoding to use for filenames. The default is - ``"utf-8"``, use ``"CP437"`` if compatibility with WinZip is - desired. - :type encoding: str - :param walker: A :class:`~fs.walk.Walker` instance, or None to use - default walker. You can use this to specify which files you - want to compress. - :type walker: Walker or None + """Write the contents of a filesystem to a zip file. + + Arguments: + file (str or io.IOBase): Destination file, may be a file name + or an open file object. + compression (str, optional): Compression to use (one of the constants + defined in the `zipfile` module in the stdlib). Defaults + to `zipfile.ZIP_DEFLATED`. + encoding (str, optional): + The encoding to use for filenames. The default is ``"utf-8"``, + use ``"CP437"`` if compatibility with WinZip is desired. + walker (~fs.walk.Walker, optional): A `Walker` instance, or `None` + to use default walker. You can use this to specify which files + you want to compress. """ _zip = zipfile.ZipFile( @@ -104,24 +101,20 @@ def write_tar(src_fs, compression=None, encoding="utf-8", walker=None): - """ - Write the contents of a filesystem to a tar file. - - :param file: Destination file, may be a file name or an open file - object. - :type file: str or file-like. - :param compression: Compression to use. - :type compression: str - :param encoding: The encoding to use for filenames. The default is - ``"utf-8"``. - :type encoding: str - :param walker: A :class:`~fs.walk.Walker` instance, or None to use - default walker. You can use this to specify which files you - want to compress. - :type walker: Walker or None + """Write the contents of a filesystem to a tar file. + + Arguments: + file (str or io.IOBase): Destination file, may be a file name or + an open file object. + compression (str, optional): Compression to use, or `None` + for a plain Tar archive without compression. + encoding(str, optional): The encoding to use for filenames. The + default is ``"utf-8"``. + walker (~fs.walk.Walker, optional): A `Walker` instance, or `None` + to use default walker. You can use this to specify which files + you want to compress. """ - type_map = { ResourceType.block_special_file: tarfile.BLKTYPE, ResourceType.character: tarfile.CHRTYPE, diff --git a/fs/constants.py b/fs/constants.py index bcdc9e72..b9902a37 100644 --- a/fs/constants.py +++ b/fs/constants.py @@ -1,6 +1,9 @@ -"""Constants used by PyFilesystem.""" +"""Constants used by PyFilesystem. +""" import io DEFAULT_CHUNK_SIZE = io.DEFAULT_BUFFER_SIZE * 16 +"""`int`: the size of a single chunk read from or written to a file. +""" diff --git a/fs/copy.py b/fs/copy.py index 5d11f964..81c0a107 100644 --- a/fs/copy.py +++ b/fs/copy.py @@ -1,7 +1,4 @@ -""" - -Functions for copying resources *between* filesystem. - +"""Functions for copying resources *between* filesystem. """ from __future__ import print_function @@ -17,65 +14,60 @@ def copy_fs(src_fs, dst_fs, walker=None, on_copy=None): - """ - Copy the contents of one filesystem to another. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param walker: A walker object that will be used to scan for files - in ``src_fs``. Set this if you only want to consider a sub-set - of the resources in ``src_fs``. - :type walker: :class:`~fs.walk.Walker` - :param on_copy: A function callback called after a single file copy - is executed. - :type on_copy: Function, with signature ``(src_fs, src_path, dst_fs, - dst_path)``. + """Copy the contents of one filesystem to another. + + Arguments: + src_fs (FS or str): Source filesystem (URL or instance). + dst_fs (FS or str): Destination filesystem (URL or instance). + walker (~fs.walk.Walker, optional): A walker object that will be + used to scan for files in ``src_fs``. Set this if you only want + to consider a sub-set of the resources in ``src_fs``. + on_copy (callable):A function callback called after a single file copy + is executed. Expected signature is ``(src_fs, src_path, dst_fs, + dst_path)``. + """ return copy_dir(src_fs, '/', dst_fs, '/', walker=walker, on_copy=on_copy) def copy_fs_if_newer(src_fs, dst_fs, walker=None, on_copy=None): - """ - Copy the contents of one filesystem to another. If both source and - destination files exist, the copy is executed only if the source - file is newer than the destination file. In case modification times - of source or destination files are not available, copy file is - always executed. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param walker: A walker object that will be used to scan for files - in ``src_fs``. Set this if you only want to consider a sub-set - of the resources in ``src_fs``. - :type walker: :class:`~fs.walk.Walker` - :param on_copy: A function callback called after a single file copy - is executed. - :type on_copy: Function, with signature ``(src_fs, src_path, dst_fs, - dst_path)``. + """Copy the contents of one filesystem to another, checking times. + + If both source and destination files exist, the copy is executed + only if the source file is newer than the destination file. In case + modification times of source or destination files are not available, + copy file is always executed. + + Arguments: + src_fs (FS or str): Source filesystem (URL or instance). + dst_fs (FS or str): Destination filesystem (URL or instance). + walker (~fs.walk.Walker, optional): A walker object that will be + used to scan for files in ``src_fs``. Set this if you only want + to consider a sub-set of the resources in ``src_fs``. + on_copy (callable):A function callback called after a single file copy + is executed. Expected signature is ``(src_fs, src_path, dst_fs, + dst_path)``. + """ return copy_dir_if_newer(src_fs, '/', dst_fs, '/', walker=walker, on_copy=on_copy) def _source_is_newer(src_fs, src_path, dst_fs, dst_path): - """ - Determine if source file is newer than destination file. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: Path to a file on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param dst_path: Path to a file on ``dst_fs``. - :type dst_path: str - :returns: True if source file is newer than destination file or - file modification time cannot be determined. False otherwise. + """Determine if source file is newer than destination file. + + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a file on the source filesystem. + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a file on the destination filesystem. + + Returns: + bool: `True` if the source file is newer than the destination + file or file modification time cannot be determined, `False` + otherwise. + """ try: if dst_fs.exists(dst_path): @@ -91,18 +83,15 @@ def _source_is_newer(src_fs, src_path, dst_fs, dst_path): def copy_file(src_fs, src_path, dst_fs, dst_path): - """ - Copy a file from one filesystem to another. If the destination - exists, and is a file, it will be first truncated. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: Path to a file on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param dst_path: Path to a file on ``dst_fs``. - :type dst_path: str + """Copy a file from one filesystem to another. + + If the destination exists, and is a file, it will be first truncated. + + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a file on the source filesystem. + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a file on the destination filesystem. """ with manage_fs(src_fs, writeable=False) as src_fs: @@ -121,23 +110,22 @@ def copy_file(src_fs, src_path, dst_fs, dst_path): def copy_file_if_newer(src_fs, src_path, dst_fs, dst_path): - """ - Copy a file from one filesystem to another. If the destination - exists, and is a file, it will be first truncated. If both source - and destination files exist, the copy is executed only if the source - file is newer than the destination file. In case modification times - of source or destination files are not available, copy is always - executed. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: Path to a file on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param dst_path: Path to a file on ``dst_fs``. - :type dst_path: str - :returns: True if the file copy was executed, False otherwise. + """Copy a file from one filesystem to another, checking times. + + If the destination exists, and is a file, it will be first truncated. + If both source and destination files exist, the copy is executed only + if the source file is newer than the destination file. In case + modification times of source or destination files are not available, + copy is always executed. + + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a file on the source filesystem. + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a file on the destination filesystem. + + Returns: + bool: `True` if the file copy was executed, `False` otherwise. """ with manage_fs(src_fs, writeable=False) as src_fs: @@ -166,17 +154,14 @@ def copy_file_if_newer(src_fs, src_path, dst_fs, dst_path): def copy_structure(src_fs, dst_fs, walker=None): - """ - Copy directories (but not files) from ``src_fs`` to ``dst_fs``. + """Copy directories (but not files) from ``src_fs`` to ``dst_fs``. - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param walker: A walker object that will be used to scan for files - in ``src_fs``. Set this if you only want to consider a sub-set - of the resources in ``src_fs``. - :type walker: :class:`~fs.walk.Walker` + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + dst_fs (FS or str): Destination filesystem (instance or URL). + walker (~fs.walk.Walker, optional): A walker object that will be + used to scan for files in ``src_fs``. Set this if you only + want to consider a sub-set of the resources in ``src_fs``. """ walker = walker or Walker() @@ -189,24 +174,19 @@ def copy_structure(src_fs, dst_fs, walker=None): def copy_dir(src_fs, src_path, dst_fs, dst_path, walker=None, on_copy=None): - """ - Copy a directory from one filesystem to another. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: A path to a directory on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param str dst_path: A path to a directory on ``dst_fs``. - :param walker: A walker object that will be used to scan for files - in ``src_fs``. Set this if you only want to consider a sub-set - of the resources in ``src_fs``. - :type walker: :class:`~fs.walk.Walker` - :param on_copy: A function callback called after a single file copy - is executed. - :type on_copy: Function, with signature ``(src_fs, src_path, dst_fs, - dst_path)``. + """Copy a directory from one filesystem to another. + + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a directory on the source filesystem. + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a directory on the destination filesystem. + walker (~fs.walk.Walker, optional): A walker object that will be + used to scan for files in ``src_fs``. Set this if you only + want to consider a sub-set of the resources in ``src_fs``. + on_copy (callable, optional): A function callback called after + a single file copy is executed. Expected signature is + ``(src_fs, src_path, dst_fs, dst_path)``. """ on_copy = on_copy or (lambda *args: None) @@ -241,28 +221,24 @@ def copy_dir(src_fs, src_path, dst_fs, dst_path, def copy_dir_if_newer(src_fs, src_path, dst_fs, dst_path, walker=None, on_copy=None): - """ - Copy a directory from one filesystem to another. If both source and - destination files exist, the copy is executed only if the source - file is newer than the destination file. In case modification times - of source or destination files are not available, copy is always - executed. - - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: A path to a directory on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param str dst_path: A path to a directory on ``dst_fs``. - :param walker: A walker object that will be used to scan for files - in ``src_fs``. Set this if you only want to consider a sub-set - of the resources in ``src_fs``. - :type walker: :class:`~fs.walk.Walker` - :param on_copy: A function callback called after a single file copy - is executed. - :type on_copy: Function, with signature ``(src_fs, src_path, dst_fs, - dst_path)``. + """Copy a directory from one filesystem to another, checking times. + + If both source and destination files exist, the copy is executed only + if the source file is newer than the destination file. In case + modification times of source or destination files are not available, + copy is always executed. + + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a directory on the source filesystem. + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a directory on the destination filesystem. + walker (~fs.walk.Walker, optional): A walker object that will be + used to scan for files in ``src_fs``. Set this if you only + want to consider a sub-set of the resources in ``src_fs``. + on_copy (callable, optional): A function callback called after + a single file copy is executed. Expected signature is + ``(src_fs, src_path, dst_fs, dst_path)``. """ on_copy = on_copy or (lambda *args: None) @@ -303,4 +279,3 @@ def copy_dir_if_newer(src_fs, src_path, dst_fs, dst_path, if do_copy: copy_file(src_fs, dir_path, dst_fs, copy_path) on_copy(src_fs, dir_path, dst_fs, copy_path) - diff --git a/fs/enums.py b/fs/enums.py index ef92b3b1..3c7d3ed0 100644 --- a/fs/enums.py +++ b/fs/enums.py @@ -1,7 +1,4 @@ -""" -Filesystem Constants -==================== - +"""Enums used by PyFilesystem. """ from __future__ import absolute_import @@ -13,8 +10,7 @@ @unique class ResourceType(IntEnum): - """ - Resource Types. + """Resource Types. Positive values are reserved, negative values are implementation dependent. @@ -46,10 +42,9 @@ class ResourceType(IntEnum): @unique class Seek(IntEnum): - """ - Constants used by ``file.seek``. + """Constants used by `io.IOBase.seek`. - These match ``os.SEEK_CUR``, ``os.SEEK_END``, and ``os.SEEK_SET`` + These match `os.SEEK_CUR`, `os.SEEK_END`, and `os.SEEK_SET` from the standard library. """ diff --git a/fs/error_tools.py b/fs/error_tools.py index 2096ae3b..0abb4de3 100644 --- a/fs/error_tools.py +++ b/fs/error_tools.py @@ -1,3 +1,6 @@ +"""Tools for managing OS errors. +""" + from __future__ import print_function from __future__ import unicode_literals @@ -15,7 +18,8 @@ class _ConvertOSErrors(object): - """Context manager to convert OSErrors in to FS Errors.""" + """Context manager to convert OSErrors in to FS Errors. + """ FILE_ERRORS = { 64: errors.RemoteConnectionError, # ENONET @@ -84,8 +88,9 @@ def __exit__(self, exc_type, exc_value, traceback): @contextmanager def unwrap_errors(path_replace): - """ - A context manager to re-write the paths in resource exceptions to be + """Get a context to map OS errors to their `fs.errors` counterpart. + + The context will re-write the paths in resource exceptions to be in the same context as the wrapped filesystem. The only parameter may be the path from the parent, if only one path diff --git a/fs/errors.py b/fs/errors.py index 78b28e8d..3ed4fe46 100644 --- a/fs/errors.py +++ b/fs/errors.py @@ -1,12 +1,10 @@ -""" - -Defines the Exception classes thrown by PyFilesystem objects. +"""Exception classes thrown by filesystem operations. -Errors relating to the underlying filesystem are translated in to one of -the following exceptions. +Errors relating to the underlying filesystem are translated in +to one of the following exceptions. -All Exception classes are derived from :class:`~fs.errors.FSError` which -may be used as a catch-all filesystem exception. +All Exception classes are derived from `~fs.errors.FSError` +which may be used as a catch-all filesystem exception. """ @@ -49,7 +47,8 @@ class MissingInfoNamespace(AttributeError): - """Raised when an expected namespace was missing.""" + """An expected namespace is missing. + """ def __init__(self, namespace): msg = "namespace '{}' is required for this attribute" @@ -60,7 +59,8 @@ def __init__(self, namespace): @six.python_2_unicode_compatible class FSError(Exception): - """Base exception class for the FS module.""" + """Base exception for the `fs` module. + """ default_message = "Unspecified error" @@ -69,7 +69,8 @@ def __init__(self, msg=None): super(FSError, self).__init__() def __str__(self): - """The error message.""" + """Return the error message. + """ msg = self._msg.format(**self.__dict__) return msg @@ -79,19 +80,22 @@ def __repr__(self): class FilesystemClosed(FSError): - """An exception thrown when attempting to use a closed filesystem.""" + """Attempt to use a closed filesystem. + """ default_message = "attempt to use closed filesystem" class CreateFailed(FSError): - """An exception thrown when a FS could not be created.""" + """Filesystem could not be created. + """ default_message = "unable to create filesystem" class PathError(FSError): - """Exception for errors to do with a path string.""" + """Base exception for errors to do with a path string. + """ default_message = "path '{path}' is invalid" @@ -101,13 +105,15 @@ def __init__(self, path, msg=None): class NoSysPath(PathError): - """Exception raised when there is no sys path.""" + """The filesystem does not provide *sys paths* to the resource. + """ default_message = "path '{path}' does not map to the local filesystem" class NoURL(PathError): - """Raised when there is no URL for a given path.""" + """The filesystem does not provide an URL for the resource. + """ default_message = "path '{path}' has no '{purpose}' URL" @@ -117,20 +123,22 @@ def __init__(self, path, purpose, msg=None): class InvalidPath(PathError): - """Base exception for fs paths that can't be mapped on to the - underlaying filesystem.""" + """Path can't be mapped on to the underlaying filesystem. + """ default_message = "path '{path}' is invalid on this filesystem " class InvalidCharsInPath(InvalidPath): - """The path contains characters that are invalid on this filesystem.""" + """Path contains characters that are invalid on this filesystem. + """ default_message = "path '{path}' contains invalid characters" class OperationFailed(FSError): - """Base exception class for errors associated with a specific operation.""" + """A specific operation failed. + """ default_message = "operation failed, {details}" @@ -143,43 +151,50 @@ def __init__(self, path=None, exc=None, msg=None): class Unsupported(OperationFailed): - """Exception raised for operations that are not supported by the FS.""" + """Operation not supported by the filesystem. + """ default_message = "not supported" class RemoteConnectionError(OperationFailed): - """Exception raised when operations encounter remote connection trouble.""" + """Operations encountered remote connection trouble. + """ default_message = "remote connection error" class InsufficientStorage(OperationFailed): - """Exception raised when operations encounter storage space trouble.""" + """Storage is insufficient for requested operation. + """ default_message = "insufficient storage space" class PermissionDenied(OperationFailed): - """Permissions error.""" + """Not enough permissions. + """ default_message = "permission denied" class OperationTimeout(OperationFailed): - """Filesystem took too long.""" + """Filesystem took too long. + """ default_message = "operation timed out" class RemoveRootError(OperationFailed): - """Attempt to remove the root directory.""" + """Attempt to remove the root directory. + """ default_message = "root directory may not be removed" class ResourceError(FSError): - """Base exception class for error associated with a specific resource.""" + """Base exception class for error associated with a specific resource. + """ default_message = "failed on path {path}" @@ -190,77 +205,84 @@ def __init__(self, path, exc=None, msg=None): class ResourceNotFound(ResourceError): - """Exception raised when a required resource is not found.""" + """Required resource not found. + """ default_message = "resource '{path}' not found" class ResourceInvalid(ResourceError): - """Exception raised when a resource is the wrong type.""" + """Resource has the wrong type. + """ default_message = "resource '{path}' is invalid for this operation" class FileExists(ResourceError): - """Exception raises when opening a file in exclusive mode.""" + """File already exists. + """ default_message = "resource '{path}' exists" class FileExpected(ResourceInvalid): - """Exception raises when a file was expected.""" + """Operation only works on files. + """ default_message = "path '{path}' should be a file" class DirectoryExpected(ResourceInvalid): - """Exception raises when a directory was expected.""" + """Operation only works on directories. + """ default_message = "path '{path}' should be a directory" class DestinationExists(ResourceError): - """Exception raised when a target destination already exists.""" + """Target destination already exists. + """ default_message = "destination '{path}' exists" class DirectoryExists(ResourceError): - """Exception raised when trying to make a directory that already - exists.""" + """Directory already exists. + """ default_message = "directory '{path}' exists" class DirectoryNotEmpty(ResourceError): - """Exception raised when a directory to be removed is not empty.""" + """Attempt to remove a non-empty directory. + """ default_message = "directory '{path}' is not empty" class ResourceLocked(ResourceError): - """Exception raised when a resource can't be used because it is locked.""" + """Attempt to use a locked resource. + """ default_message = "resource '{path}' is locked" class ResourceReadOnly(ResourceError): - """Raised when attempting to modify a read only resource.""" + """Attempting to modify a read-only resource. + """ default_message = "resource '{path}' is read only" class IllegalBackReference(ValueError): - """ - Exception raised when too many backrefs exist in a path. + """Too many backrefs exist in a path. This error will occur if the back references in a path would be outside of the root. For example, ``"/foo/../../"``, contains two back references which would reference a directory above the root. - .. note:: - - This exception is a subclass of ``ValueError`` as it is not + Note: + This exception is a subclass of `ValueError` as it is not strictly speaking an issue with a filesystem or resource. """ diff --git a/fs/filesize.py b/fs/filesize.py index 85c608dc..8e53c130 100644 --- a/fs/filesize.py +++ b/fs/filesize.py @@ -1,8 +1,13 @@ -""" -fs.filesize -=========== +# coding: utf-8 +"""Functions for reporting filesizes. + +The functions declared in this module should cover the different +usecases needed to generate a string representation of a file size +using several different units. Since there are many standards regarding +file size units, three different functions have been implemented. -Functions for reporting filesizes +See Also: + * `Wikipedia: Binary prefix `_ """ @@ -33,13 +38,25 @@ def _to_str(size, suffixes, base): def traditional(size): - """ - Convert a filesize in to a string representation with traditional - (base 2) units and JDEC prefixes. + """Convert a filesize in to a string (powers of 1024, JDEC prefixes). + + In this convention, ``1024 B = 1 KB``. + + This is the format that was used to display the size of DVDs + (*700 MB* meaning actually about *734 003 200 bytes*) before + standardisation of IEC units among manufacturers, and still + used by **Windows** to report the storage capacity of hard + drives (*279.4 GB* meaning *279.4 × 1024³ bytes*). - :param int size: A file size. - :returns: A string containing a abbreviated file size and units. - :rtype str: + Arguments: + size (int): A file size. + + Returns: + `str`: A string containing an abbreviated file size and units. + + Example: + >>> filesize.traditional(30000) + '29.3 KB' """ return _to_str( @@ -50,14 +67,25 @@ def traditional(size): def binary(size): - """ - Convert a filesize in to a string representation with binary units - and SI binary prefixes. + """Convert a filesize in to a string (powers of 1024, IEC prefixes). + + In this convention, ``1024 B = 1 KiB``. - :param int size: A file size. - :param bool si: True to use SI prefixes, False to use JDEC prefixes. - :returns: A string containing a abbreviated file size and units. - :rtype str: + This is the format that has gained adoption among manufacturers + to avoid ambiguity regarding size units, since it explicitly states + using a binary base (*KiB = kibi bytes = kilo binary bytes*). + This format is notably being used by the **Linux** kernel (see + ``man 7 units``). + + Arguments: + int (size): A file size. + + Returns: + `str`: A string containing a abbreviated file size and units. + + Example: + >>> filesize.binary(30000) + '29.3 KiB' """ return _to_str( @@ -68,13 +96,25 @@ def binary(size): def decimal(size): - """ - Convert a filesize in to a string representation with decimal - units and SI decimal prefixes. + """Convert a filesize in to a string (powers of 1000, SI prefixes). + + In this convention, ``1000 B = 1 kB``. + + This is typically the format used to advertise the storage + capacity of USB flash drives and the like (*256 MB* meaning + actually a storage capacity of more than *256 000 000 B*), + or used by **Mac OS X** since v10.6 to report file sizes. + + Arguments: + int (size): A file size. + + Returns: + `str`: A string containing a abbreviated file size and units. + + Example: + >>> filesize.decimal(30000) + '30.0 kB' - :param int size: A file size. - :returns: A string containing a abbreviated file size and units. - :rtype str: """ return _to_str( size, diff --git a/fs/ftpfs.py b/fs/ftpfs.py index dd25cc78..4180d927 100644 --- a/fs/ftpfs.py +++ b/fs/ftpfs.py @@ -1,3 +1,6 @@ +"""Manage filesystems on remote FTP servers. +""" + from __future__ import print_function from __future__ import unicode_literals @@ -271,15 +274,16 @@ def seek(self, pos, whence=Seek.set): class FTPFS(FS): - """ - A FTP (File Transport Protocol) Filesystem. + """A FTP (File Transport Protocol) Filesystem. - :param str host: A FTP host, e.g. ``'ftp.mirror.nl'``. - :param str user: A username (default is ``'anonymous'``) - :param passwd: Password for the server, or ``None`` for anon. - :param acct: FTP account. - :param int timeout: Timeout for contacting server (in seconds). - :param int port: Port number (default 21). + Arguments: + host (str): A FTP host, e.g. ``'ftp.mirror.nl'``. + user (str, optional): A username (default is ``'anonymous'``). + passwd (str, optional): Password for the server, or `None` for anon. + acct (str, optional): FTP account. + timeout (int, optional): Timeout for contacting server (in seconds, + defaults to 10). + port (int, optional): FTP port number (default 21). """ @@ -378,7 +382,8 @@ def ftp_url(self): @property def ftp(self): - """Get a FTP (ftplib) object.""" + """`~ftplib.FTP`: the underlying FTP client. + """ return self._get_ftp() def _get_ftp(self): @@ -388,7 +393,8 @@ def _get_ftp(self): @property def features(self): - """Get features dict from FTP server.""" + """`dict`: features of the remote FTP server. + """ self._get_ftp() return self._features diff --git a/fs/info.py b/fs/info.py index 42149d7f..ebd1b1cc 100644 --- a/fs/info.py +++ b/fs/info.py @@ -1,3 +1,6 @@ +"""Container for filesystem resource informations. +""" + from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals @@ -12,26 +15,26 @@ class Info(object): - """ - Container for :ref:`info`, returned by the following methods: + """Container for :ref:`info`. + + Resource informations are returned by the following methods: - * :meth:`~fs.base.FS.getinfo` - * :meth:`~fs.base.FS.scandir` - * :meth:`~fs.base.FS.filterfir` + * `~fs.base.FS.getinfo` + * `~fs.base.FS.scandir` + * `~fs.base.FS.filterfir` - :param dict raw_info: A dict containing resource info. - :param to_datetime: A callable that converts an epoch time to a - datetime object. The default uses - :func:`~fs.time.epoch_to_datetime`. + Arguments: + raw_info (dict): A dict containing resource info. + to_datetime (callable, optional): A callable that converts an + epoch time to a datetime object. The default uses + :func:`~fs.time.epoch_to_datetime`. """ def __init__(self, raw_info, to_datetime=epoch_to_datetime): - """ - Create a resource info object from a raw info dict. - + """Create a resource info object from a raw info dict. """ self.raw = raw_info self._to_datetime = to_datetime @@ -53,16 +56,19 @@ def _make_datetime(self, t): return None def get(self, namespace, key, default=None): - """ - Get a raw info value. + """Get a raw info value. - >>> info.get('access', 'permissions') - ['u_r', 'u_w', '_wx'] + Arguments: + namespace (str): A namespace identifier. + key (str): A key within the namespace. + default (object, optional): A default value to return + if either the namespace or the key within the namespace + is not found. + + Example: + >>> info.get('access', 'permissions') + ['u_r', 'u_w', '_wx'] - :param str namespace: A namespace identifier. - :param str key: A key within the namespace. - :param default: A default value to return if either the - namespace or namespace + key is not found. """ try: return self.raw[namespace].get(key, default) @@ -70,110 +76,98 @@ def get(self, namespace, key, default=None): return default def _require_namespace(self, namespace): - """ - Raise a MissingInfoNamespace if the given namespace is not - present in the info. + """Check if the given namespace is present in the info. + + Raises: + ~fs.errors.MissingInfoNamespace: if the given namespace is not + present in the info. """ if namespace not in self.raw: raise MissingInfoNamespace(namespace) def is_writeable(self, namespace, key): - """ - Check if a given key in a namespace is writable (with - :meth:`~fs.base.FS.setinfo`). + """Check if a given key in a namespace is writable. + + Uses `~fs.base.FS.setinfo`. - :param namespace: A namespace identifier. - :type namespace: str - :param key: A key within the namespace. - :type key: str - :rtype: bool + Arguments: + namespace (str): A namespace identifier. + key (str): A key within the namespace. + + Returns: + bool: `True` if the key can be modified, `False` otherwise. """ _writeable = self.get(namespace, '_write', ()) return key in _writeable def has_namespace(self, namespace): - """ - Check if the resource info contains a given namespace. + """Check if the resource info contains a given namespace. + + Arguments: + namespace (str): A namespace identifier. - :param namespace: A namespace name. - :type namespace: str - :rtype: bool + Returns: + bool: `True` if the namespace was found, `False` otherwise. """ return namespace in self.raw def copy(self, to_datetime=None): - """Create a copy of this resource info object.""" + """Create a copy of this resource info object. + """ return Info( deepcopy(self.raw), to_datetime=to_datetime or self._to_datetime ) def make_path(self, dir_path): - """ - Make a path by joining ``dir_path`` with the resource name. + """Make a path by joining ``dir_path`` with the resource name. - :param dir_path: A path to a directory. - :type dir_path: str - :returns: A path. - :rtype: str + Arguments: + dir_path (str): A path to a directory. + + Returns: + str: A path to the resource. """ return join(dir_path, self.name) @property def name(self): - """ - Get the resource name. - - :rtype: str - + """`str`: the resource name. """ return self.get('basic', 'name') @property def is_dir(self): - """ - Check if the resource references a directory. - - :rtype: bool - + """`bool`: `True` if the resource references a directory. """ return self.get('basic', 'is_dir') @property def is_file(self): - """ - Check if a resource references a file. - - :rtype: bool - + """`bool`: `True` if the resource references a file. """ return not self.get('basic', 'is_dir') @property def is_link(self): - """ - Check if a resource is a symlink. - - :rtype: bool - + """`bool`: `True` if the resource is a symlink. """ self._require_namespace('link') return self.get('link', 'target') is not None @property def type(self): - """ - Get the resource type enumeration. + """`~fs.ResourceType`: the type of the resource. Requires the ``"details"`` namespace. - :type: :class:`~fs.ResourceType` - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the 'details' + namespace is not in the Info. """ self._require_namespace('details') @@ -181,15 +175,13 @@ def type(self): @property def accessed(self): - """ - Get the time this resource was last accessed, or ``None`` if not - available. + """`~datetime.datetime`: the resource last access time, or `None`. Requires the ``"details"`` namespace. - :rtype: datetime - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"details"`` + namespace is not in the Info. """ self._require_namespace('details') @@ -200,15 +192,13 @@ def accessed(self): @property def modified(self): - """ - Get the time the resource was modified, or ``None`` if not - available. + """`~datetime.datetime`: the resource last modification time, or `None`. Requires the ``"details"`` namespace. - :rtype: datetime - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"details"`` + namespace is not in the Info. """ self._require_namespace('details') @@ -219,15 +209,13 @@ def modified(self): @property def created(self): - """ - Get the time this resource was created, or ``None`` if not - available. + """`~datetime.datetime`: the resource creation time, or `None`. Requires the ``"details"`` namespace. - :rtype: datetime - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"details"`` + namespace is not in the Info. """ self._require_namespace('details') @@ -238,15 +226,13 @@ def created(self): @property def metadata_changed(self): - """ - Get the time the metadata changed, or ``None`` if not - available. + """`~datetime.datetime`: the resource metadata change time, or `None`. Requires the ``"details"`` namespace. - :rtype: datetime - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"details"`` + namespace is not in the Info. """ self._require_namespace('details') @@ -257,14 +243,13 @@ def metadata_changed(self): @property def permissions(self): - """ - Get a permissions object, or ``None`` if not available. + """`Permissions`: the permissions of the resource, or `None`. Requires the ``"access"`` namespace. - :rtype: :class:`fs.permissions.Permissions` - :raises ~fs.errors.MissingInfoNamespace: if the 'access' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"access"`` + namespace is not in the Info. """ self._require_namespace('access') @@ -276,14 +261,13 @@ def permissions(self): @property def size(self): - """ - Get the size of the resource, in bytes. + """`int`: the size of the resource, in bytes. Requires the ``"details"`` namespace. - :rtype: int - :raises ~fs.errors.MissingInfoNamespace: if the 'details' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"details"`` + namespace is not in the Info. """ self._require_namespace('details') @@ -291,14 +275,13 @@ def size(self): @property def user(self): - """ - Get the owner of a resource, or ``None`` if not available. + """`str`: the owner of the resource, or `None`. Requires the ``"access"`` namespace. - :rtype: str - :raises ~fs.errors.MissingInfoNamespace: if the 'access' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"access"`` + namespace is not in the Info. """ self._require_namespace('access') @@ -306,14 +289,13 @@ def user(self): @property def uid(self): - """ - Get the user id of a resource, or ``None`` if not available. + """`int`: the user id of the resource, or `None`. Requires the ``"access"`` namespace. - :rtype: int - :raises ~fs.errors.MissingInfoNamespace: if the 'access' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"access"`` + namespace is not in the Info. """ self._require_namespace('access') @@ -321,15 +303,13 @@ def uid(self): @property def group(self): - """ - Get the group of the resource owner, or ``None`` if not - available. + """`str`: the group of the resource owner, or `None`. Requires the ``"access"`` namespace. - :rtype: str - :raises ~fs.errors.MissingInfoNamespace: if the 'access' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"access"`` + namespace is not in the Info. """ self._require_namespace('access') @@ -337,30 +317,27 @@ def group(self): @property def gid(self): - """ - Get the group id of a resource, or ``None`` if not available. + """`int`: the group id of the resource, or `None`. Requires the ``"access"`` namespace. - :rtype: int - :raises ~fs.errors.MissingInfoNamespace: if the 'access' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"access"`` + namespace is not in the Info. """ self._require_namespace('access') return self.get('access', 'gid') @property - def target(self): - """ - Get the link target, if this is a symlink, or ``None`` if this - is not a symlink. + def target(self): # noqa: D402 + """`str`: the link target (if resource is a symlink), or `None`. Requires the ``"link"`` namespace. - :rtype: bool - :raises ~fs.errors.MissingInfoNamespace: if the 'link' - namespace is not in the Info. + Raises: + ~fs.errors.MissingInfoNamespace: if the ``"link"`` + namespace is not in the Info. """ self._require_namespace('link') diff --git a/fs/iotools.py b/fs/iotools.py index 7ff5c943..93aefa55 100644 --- a/fs/iotools.py +++ b/fs/iotools.py @@ -1,3 +1,6 @@ +"""Compatibility tools between Python 2 and Python 3 I/O interfaces. +""" + from __future__ import print_function from __future__ import unicode_literals @@ -8,7 +11,8 @@ class RawWrapper(io.IOBase): - """Convert a Python 2 style file-like object in to a IO object.""" + """Convert a Python 2 style file-like object in to a IO object. + """ def __init__(self, f, mode=None, name=None): self._f = f @@ -124,7 +128,8 @@ def make_stream(name, newline='', line_buffering=False, **kwargs): - """Take a Python 2.x binary file and return an IO Stream.""" + """Take a Python 2.x binary file and return an IO Stream. + """ reading = 'r' in mode writing = 'w' in mode appending = 'a' in mode @@ -166,7 +171,15 @@ def make_stream(name, def line_iterator(readable_file, size=None): - """A not terribly efficient char by char line iterator.""" + """Iterate over the lines of a file. + + Implementation reads each char individually, which is not very + efficient. + + Yields: + str: a single line in the file. + + """ read = readable_file.read line = [] byte = b'1' diff --git a/fs/lrucache.py b/fs/lrucache.py index 09edf034..392c9632 100644 --- a/fs/lrucache.py +++ b/fs/lrucache.py @@ -1,3 +1,6 @@ +"""Least Recently Used cache mapping. +""" + from __future__ import absolute_import from __future__ import unicode_literals @@ -17,14 +20,16 @@ def __init__(self, cache_size): super(LRUCache, self).__init__() def __setitem__(self, key, value): - """Store a new views, potentially discarding an old value.""" + """Store a new views, potentially discarding an old value. + """ if key not in self: if len(self) >= self.cache_size: self.popitem(last=False) OrderedDict.__setitem__(self, key, value) def __getitem__(self, key): - """Gets the item, but also makes it most recent.""" + """Get the item, but also makes it most recent. + """ _super = super(LRUCache, self) value = _super.__getitem__(key) _super.__delitem__(key) diff --git a/fs/memoryfs.py b/fs/memoryfs.py index f023ab94..a7b8c943 100644 --- a/fs/memoryfs.py +++ b/fs/memoryfs.py @@ -1,4 +1,5 @@ - +"""Manage a volatile in-memory filesystem. +""" from __future__ import absolute_import from __future__ import unicode_literals @@ -57,12 +58,14 @@ def _seek_lock(self): yield self.pos = self._bytes_io.tell() - def on_modify(self): - """Called when file data is modified.""" + def on_modify(self): # noqa: D401 + """Called when file data is modified. + """ self._dir_entry.modified_time = self.modified_time = time.time() - def on_access(self): - """Called when file is accessed.""" + def on_access(self): # noqa: D401 + """Called when file is accessed. + """ self._dir_entry.accessed_time = self.accessed_time = time.time() def flush(self): @@ -206,17 +209,14 @@ def remove_open_file(self, memory_file): @six.python_2_unicode_compatible class MemoryFS(FS): - """ - A filesystem that stores all file and directory information in - memory. This makes them very fast, but non-permanent. + """A filesystem that stored in memory. Memory filesystems are useful for caches, temporary data stores, - unit testing, etc. + unit testing, etc. They do require no parameters to their constructor. + They are very fast, but non-permanent. - Memory filesystems require no parameters to their constructor. The - following is how you would create a ``MemoryFS`` instance:: - - mem_fs = MemoryFS() + Example: + >>> mem_fs = MemoryFS() """ @@ -231,9 +231,7 @@ class MemoryFS(FS): } def __init__(self): - """ - Create an in-memory filesystem. - + """Create an in-memory filesystem. """ self._meta = self._meta.copy() self.root = self._make_dir_entry(ResourceType.directory, '') @@ -249,7 +247,8 @@ def _make_dir_entry(self, *args, **kwargs): return _DirEntry(*args, **kwargs) def _get_dir_entry(self, dir_path): - """Get a directory entry, or None if one doesn't exist.""" + """Get a directory entry, or `None` if one doesn't exist. + """ with self._lock: dir_path = normpath(dir_path) current_entry = self.root diff --git a/fs/mirror.py b/fs/mirror.py index 95940295..80d1528c 100644 --- a/fs/mirror.py +++ b/fs/mirror.py @@ -1,6 +1,18 @@ -""" +"""Function for *mirroring* a filesystem. + +Mirroring will create a copy of a source filesystem on a destination +filesystem. If there are no files on the destination, then mirroring +is simply a straight copy. If there are any files or directories on the +destination they may be deleted or modified to match the source. -Create a 'mirror' of a filesystem. +In order to avoid redundant copying of files, `mirror` can compare +timestamps, and only copy files with a newer modified date. This +timestamp comparison is only done if the file sizes are different. + +This scheme will work if you have mirrored a directory previously, and +you would like to copy any changes. Otherwise you should set the +``copy_if_newer`` parameter to `False` to guarantee an exact copy, at +the expense of potentially copying extra files. """ @@ -15,9 +27,10 @@ def _compare(info1, info2): - """ - Compare two (file) info objects and return True if the file should - be copied, or False if they should not. + """Compare two `Info` objects to see if they should be copied. + + Returns: + bool: `True` if the `Info` are different in size or mtime. """ # Check filesize has changed @@ -30,21 +43,18 @@ def _compare(info1, info2): def mirror(src_fs, dst_fs, walker=None, copy_if_newer=True): - """ - Mirror files / directories from one filesystem to another. - - :param src_fs: A source filesystem. - :type src_fs: FS URL or instance. - :param dst_fs: A destination filesystem. - :type dst_fs: FS URL or instance. - :param walker: An optional waler instance. - :type walker: :class:`~fs.walk.Walker` - :param bool copy_if_newer: Only copy newer files. + """Mirror files / directories from one filesystem to another. Mirroring a filesystem will create an exact copy of ``src_fs`` on ``dst_fs``, by removing any files / directories on the destination that aren't on the source, and copying files that aren't. + Arguments: + src_fs (FS or str): Source filesystem (URL or instance). + dst_fs (FS or str): Destination filesystem (URL or instance). + walker (~fs.walk.Walker, optional): An optional walker instance. + copy_if_newer (bool, optional): Only copy newer files (the default). + """ with manage_fs(src_fs, writeable=False) as _src_fs: with manage_fs(dst_fs, create=True) as _dst_fs: diff --git a/fs/mode.py b/fs/mode.py index ded64d2c..8dce3b8d 100644 --- a/fs/mode.py +++ b/fs/mode.py @@ -1,6 +1,7 @@ -""" -Tools for managing mode strings (as used in :meth:`~fs.base.FS.open` and -:meth:`~fs.base.FS.openbin`). +"""Abstract I/O mode container. + +Mode strings are used in in `~fs.base.FS.open` and +`~fs.base.FS.openbin`. """ @@ -13,17 +14,19 @@ # https://docs.python.org/3/library/functions.html#open @six.python_2_unicode_compatible class Mode(object): - """ - A mode object provides properties that can be used to interrogate - the `mode - strings `_ used - when opening files. + """An abstraction for I/O modes. + + A mode object provides properties that can be used to interrogate the + `mode strings `_ + used when opening files. - :param str mode: A *mode* string, as used by ``io.open``. - :raises ValueError: If the mode string is invalid. + Arguments: + mode (str): A *mode* string, as used by `io.open`. - Here's an example of typical use:: + Raises: + ValueError: If the mode string is invalid. + Example: >>> mode = Mode('rb') >>> mode.reading True @@ -34,7 +37,6 @@ class Mode(object): >>> mode.text False - """ def __init__(self, mode): @@ -48,12 +50,12 @@ def __str__(self): return self._mode def __contains__(self, character): - """Check if a mode contains a given character.""" + """Check if a mode contains a given character. + """ return character in self._mode def to_platform(self): - """ - Get a mode string for the current platform. + """Get a mode string for the current platform. Currently, this just removes the 'x' on PY2 because PY2 doesn't support exclusive mode. @@ -62,8 +64,7 @@ def to_platform(self): return self._mode.replace('x', 'w') if six.PY2 else self._mode def to_platform_bin(self): - """ - Get a *binary* mode string for the current platform. + """Get a *binary* mode string for the current platform. Currently, this just removes the 'x' on PY2 because PY2 doesn't support exclusive mode. @@ -73,13 +74,12 @@ def to_platform_bin(self): return _mode if 'b' in _mode else _mode + 'b' def validate(self, _valid_chars=frozenset('rwxtab+')): - """ - Validate the mode string. + """Validate the mode string. - :raises ValueError: if the mode contains invalid chars. + Raises: + ValueError: if the mode contains invalid chars. """ - mode = self._mode if not mode: raise ValueError('mode must not be empty') @@ -97,10 +97,10 @@ def validate(self, _valid_chars=frozenset('rwxtab+')): ) def validate_bin(self): - """ - Validate a mode for opening a binary file. + """Validate a mode for opening a binary file. - :raises ValueError: if the mode contains invalid chars. + Raises: + ValueError: if the mode contains invalid chars. """ self.validate() @@ -109,90 +109,106 @@ def validate_bin(self): @property def create(self): - """Check if the mode would create a file.""" + """`bool`: `True` if the mode would create a file. + """ return 'a' in self or 'w' in self or 'x' in self @property def reading(self): - """Check if the mode permits reading.""" + """`bool`: `True` if the mode permits reading. + """ return 'r' in self or '+' in self @property def writing(self): - """Check if a mode permits writing.""" + """`bool`: `True` if the mode permits writing. + """ return 'w' in self or 'a' in self or '+' in self or 'x' in self @property def appending(self): - """Check if a mode permits appending.""" + """`bool`: `True` if the mode permits appending. + """ return 'a' in self @property def updating(self): - """Check if a mode permits updating (reading and writing).""" + """`bool`: `True` if the mode permits both reading and writing. + """ return '+' in self @property def truncate(self): - """Check if a mode would truncate an existing file.""" + """`bool`: `True` if the mode would truncate an existing file. + """ return 'w' in self or 'x' in self @property def exclusive(self): - """Check if the mode require exclusive creation.""" + """`bool`: `True` if the mode require exclusive creation. + """ return 'x' in self @property def binary(self): - """Check if a mode specifies binary.""" + """`bool`: `True` if a mode specifies binary. + """ return 'b' in self @property def text(self): - """Check if a mode specifies text.""" + """`bool`: `True` if a mode specifies text. + """ return 't' in self or 'b' not in self def check_readable(mode): - """ - Check a mode string allows reading. + """Check a mode string allows reading. - :param str mode: A mode string, e.g. ``"rt"`` - :rtype: bool + Arguments: + mode (str): A mode string, e.g. ``"rt"`` + + Returns: + bool: `True` if the mode allows reading. """ return Mode(mode).reading def check_writable(mode): - """ - Check a mode string allows writing. + """Check a mode string allows writing. - :param str mode: A mode string, e.g. ``"wt"`` - :rtype: bool + Arguments: + mode (str): A mode string, e.g. ``"wt"`` + + Returns: + bool: `True` if the mode allows writing. """ return Mode(mode).writing def validate_open_mode(mode): - """ - Check ``mode`` parameter of :meth:`~fs.base.FS.open` is valid. + """Check ``mode`` parameter of `~fs.base.FS.open` is valid. - :param str mode: Mode parameter. - :raises: `ValueError` if mode is not valid. + Arguments: + mode (str): Mode parameter. + + Raises: + `ValueError` if mode is not valid. """ Mode(mode) def validate_openbin_mode(mode, _valid_chars=frozenset('rwxab+')): - """ - Check ``mode`` parameter of :meth:`~fs.base.FS.openbin` is valid. + """Check ``mode`` parameter of `~fs.base.FS.openbin` is valid. + + Arguments: + mode (str): Mode parameter. - :param mode: Mode parameter. - :type mode: str - :raises: `ValueError` if mode is not valid. + Raises: + `ValueError` if mode is not valid. """ if 't' in mode: diff --git a/fs/mountfs.py b/fs/mountfs.py index ecf62287..67e78050 100644 --- a/fs/mountfs.py +++ b/fs/mountfs.py @@ -1,3 +1,6 @@ +"""Manage other filesystems as a folder hierarchy. +""" + from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals @@ -15,16 +18,16 @@ class MountError(Exception): - """Thrown when mounts conflict.""" + """Thrown when mounts conflict. + """ class MountFS(FS): - """ - A virtual filesystem that maps directories on to other file-systems. + """A virtual filesystem that maps directories on to other file-systems. - :param auto_close: If True, the child filesystems will be closed - when the ``MountFS`` is closed. - :type auto_close: bool + Arguments: + auto_close (bool, optional): If `True` (the default), the child + filesystems will be closed when `MountFS` is closed. """ @@ -49,9 +52,15 @@ def __str__(self): return "" def _delegate(self, path): - """ - Get a tuple of (fs, path) for a mounted filesystem, or (None, - None) if no filesystem is mounted on the given path. + """Get the delegate FS for a given path. + + Arguments: + path (str): A path. + + Returns: + (FS, str): a tuple of ``(, )`` for a mounted filesystem, + or ``(None, None)`` if no filesystem is mounted on the + given ``path``. """ _path = forcedir(abspath(normpath(path))) @@ -64,13 +73,11 @@ def _delegate(self, path): return self.default_fs, path def mount(self, path, fs): - """ - Mounts a host FS object on a given path. + """Mounts a host FS object on a given path. - :param path: A path within the MountFS. - :type path: str - :param fs: A filesystem object or FS URL to mount. - :type fs: :class:`~fs.base.FS` + Arguments: + path (str): A path within the MountFS. + fs (FS or str): A filesystem (instance or URL) to mount. """ if isinstance(fs, text_type): diff --git a/fs/move.py b/fs/move.py index 250ffa20..cb30ada5 100644 --- a/fs/move.py +++ b/fs/move.py @@ -1,7 +1,4 @@ -""" - -Functions for moving files between filesystems. - +"""Functions for moving files between filesystems. """ from __future__ import print_function @@ -13,30 +10,24 @@ def move_fs(src_fs, dst_fs): - """ - Move the contents of a filesystem to another filesystem. + """Move the contents of a filesystem to another filesystem. - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + dst_fs (FS or str): Destination filesystem (instance or URL). """ move_dir(src_fs, '/', dst_fs, '/') def move_file(src_fs, src_path, dst_fs, dst_path): - """ - Move a file from one filesystem to another. + """Move a file from one filesystem to another. - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: Path to a file on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param dst_fs: Path to a file on ``dst_fs``. - :type dst_fs: str + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a file on ``src_fs``. + dst_fs (FS or str); Destination filesystem (instance or URL). + dst_path (str): Path to a file on ``dst_fs``. """ with manage_fs(src_fs) as src_fs: @@ -52,16 +43,13 @@ def move_file(src_fs, src_path, dst_fs, dst_path): def move_dir(src_fs, src_path, dst_fs, dst_path): - """ - Move a directory from one filesystem to another. + """Move a directory from one filesystem to another. - :param src_fs: Source filesystem. - :type src_fs: FS URL or instance - :param src_path: A path to a directory on ``src_fs``. - :type src_path: str - :param dst_fs: Destination filesystem. - :type dst_fs: FS URL or instance - :param str dst_path: A path to a directory on ``dst_fs``. + Arguments: + src_fs (FS or str): Source filesystem (instance or URL). + src_path (str): Path to a directory on ``src_fs`` + dst_fs (FS or str): Destination filesystem (instance or URL). + dst_path (str): Path to a directory on ``dst_fs`` """ with manage_fs(src_fs) as src_fs: diff --git a/fs/multifs.py b/fs/multifs.py index 08076910..96ed44d3 100644 --- a/fs/multifs.py +++ b/fs/multifs.py @@ -1,3 +1,6 @@ +"""Manage several filesystems through a single view. +""" + from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function @@ -24,8 +27,7 @@ class MultiFS(FS): - """ - A filesystem that delegates to a sequence of other filesystems. + """A filesystem that delegates to a sequence of other filesystems. Operations on the MultiFS will try each 'child' filesystem in order, until it succeeds. In effect, creating a filesystem that combines @@ -61,26 +63,22 @@ def __str__(self): return "" def add_fs(self, name, fs, write=False, priority=0): - """ - Adds a filesystem to the MultiFS. - - :param name: A unique name to refer to the filesystem being - added. - :type name: str - :param fs: The filesystem to add. - :type fs: Filesystem or FS URL. - :param write: If this value is True, then the ``fs`` will be - used as the writeable FS. - :type write: bool - :param priority: An integer that denotes the priority of the - filesystem being added. Filesystems will be searched in - descending priority order and then by the reverse order they - were added. So by default, the most recently added - filesystem will be looked at first. - :type priority: int + """Add a filesystem to the MultiFS. + + Arguments: + name (str): A unique name to refer to the filesystem being + added. + fs (FS or str): The filesystem (instance or URL) to add. + write (bool, optional): If this value is True, + then the ``fs`` will be used as the writeable FS (defaults + to False). + priority (int, optional): An integer that denotes the priority + of the filesystem being added. Filesystems will be searched + in descending priority order and then by the reverse order + they were added. So by default, the most recently added + filesystem will be looked at first. """ - if isinstance(fs, text_type): fs = open_fs(fs) @@ -101,21 +99,28 @@ def add_fs(self, name, fs, write=False, priority=0): self._write_fs_name = name def get_fs(self, name): - """ - Get a filesystem from its name. + """Get a filesystem from its name. + + Arguments: + name (str): The name of a filesystem previously added. + + Returns: + FS: the filesystem added as ``name`` previously. - :param name: The name of a filesystem previously added. - :type name: str + Raises: + KeyError: If no filesystem with given ``name`` could be found. """ return self._filesystems[name].fs def _resort(self): - """Force iterate_fs to re-sort on next reference.""" + """Force `iterate_fs` to re-sort on next reference. + """ self._fs_sequence = None def iterate_fs(self): - """Get iterator that returns (name, fs) in priority order.""" + """Get iterator that returns (name, fs) in priority order. + """ if self._fs_sequence is None: self._fs_sequence = [ (name, fs) @@ -129,34 +134,35 @@ def iterate_fs(self): return iter(self._fs_sequence) def _delegate(self, path): - """Get a filesystem which has a given path.""" + """Get a filesystem which has a given path. + """ for _name, fs in self.iterate_fs(): if fs.exists(path): return fs return None def _delegate_required(self, path): + """Check that there is a filesystem with the given ``path``. + """ fs = self._delegate(path) if fs is None: raise errors.ResourceNotFound(path) return fs def _require_writable(self, path): + """Check that ``path`` is writeable. + """ if self.write_fs is None: raise errors.ResourceReadOnly(path) def which(self, path, mode="r"): - """ - Get a tuple of (name, filesystem) that the given path would map - to. + """Get a tuple of (name, fs) that the given path would map to. - :param path: A path on the filesystem. - :type path: str - :param mode: A open mode. - :type mode: str + Arguments: + path (str): A path on the filesystem. + mode (str): An `io.open` mode. """ - if check_writable(mode): return self._write_fs_name, self.write_fs for name, fs in self.iterate_fs(): diff --git a/fs/opener/__init__.py b/fs/opener/__init__.py index 749e42cf..67c4ecdd 100644 --- a/fs/opener/__init__.py +++ b/fs/opener/__init__.py @@ -1,10 +1,5 @@ # coding: utf-8 -""" -fs.opener -======== - -Imported at the same time as PyFilesystem2, contains -various objects and functions to open and manage FS. +"""Open filesystems from a URL. """ # Declare fs.opener as a namespace package diff --git a/fs/opener/appfs.py b/fs/opener/appfs.py index 95716ab5..079d837b 100644 --- a/fs/opener/appfs.py +++ b/fs/opener/appfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the MemOpener.""" +"""``AppFS`` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -12,6 +13,8 @@ class AppFSOpener(Opener): + """``AppFS`` opener. + """ protocols = [ 'userdata', @@ -61,4 +64,3 @@ def open_fs(self, fs_url, parse_result, writeable, create, cwd): ) return app_fs - diff --git a/fs/opener/base.py b/fs/opener/base.py index 7ff5afcc..6db368fd 100644 --- a/fs/opener/base.py +++ b/fs/opener/base.py @@ -1,9 +1,5 @@ # coding: utf-8 -""" -fs.opener.base -============== - -Defines the Opener abstract base class. +"""`Opener` abstract base class. """ import six @@ -12,8 +8,7 @@ @six.add_metaclass(abc.ABCMeta) class Opener(object): - """ - The opener base class. + """The base class for filesystem openers. An opener is responsible for opening a filesystem for a given protocol. @@ -27,17 +22,19 @@ def __repr__(self): @abc.abstractmethod def open_fs(self, fs_url, parse_result, writeable, create, cwd): - """ - Open a filesystem object from a FS URL. - - :param str fs_url: A filesystem URL - :param parse_result: A parsed filesystem URL. - :type parse_result: :class:`~fs.opener.parse.ParseResult` - :param bool writeable: True if the filesystem must be writeable. - :param bool create: True if the filesystem should be created if - it does not exist. - :param str cwd: The current working directory (generally only - relevant for OS filesystems). - :returns: :class:`~fs.base.FS` object + """Open a filesystem object from a FS URL. + + Arguments: + fs_url (str): A filesystem URL. + parse_result (~fs.opener.parse.ParseResult): A parsed + filesystem URL. + writeable (bool): `True` if the filesystem must be writable. + create (bool): `True` if the filesystem should be created + if it does not exist. + cwd (str): The current working directory (generally only + relevant for OS filesystems). + + Returns: + `~fs.base.FS`: A filesystem instance. """ diff --git a/fs/opener/errors.py b/fs/opener/errors.py index d7b97ab5..fa432a51 100644 --- a/fs/opener/errors.py +++ b/fs/opener/errors.py @@ -1,24 +1,22 @@ # coding: utf-8 -""" -fs.opener.errors -================ - -Errors raised when attempting to open a filesystem. - +"""Errors raised when attempting to open a filesystem. """ class ParseError(ValueError): - """Raised when attempting to parse an invalid FS URL.""" + """Attempt to parse an invalid FS URL. + """ class OpenerError(Exception): - """Base class for opener related errors.""" + """Base exception for opener related errors. + """ class UnsupportedProtocol(OpenerError): - """May be raised if no opener could be found for a given - protocol.""" + """No opener found for the given protocol. + """ class EntryPointError(OpenerError): - """Raised by the registry when an entry point cannot be loaded.""" + """An entry point could not be loaded. + """ diff --git a/fs/opener/ftpfs.py b/fs/opener/ftpfs.py index 57a14b86..b070e604 100644 --- a/fs/opener/ftpfs.py +++ b/fs/opener/ftpfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the FTPOpener.""" +"""`FTPFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class FTPOpener(Opener): + """`FTPFS` opener. + """ + protocols = ['ftp'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/opener/memoryfs.py b/fs/opener/memoryfs.py index 90b8a30a..4e72caa7 100644 --- a/fs/opener/memoryfs.py +++ b/fs/opener/memoryfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the MemOpener.""" +"""`MemoryFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class MemOpener(Opener): + """`MemoryFS` opener. + """ + protocols = ['mem'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/opener/osfs.py b/fs/opener/osfs.py index c01d4a7d..967e26fd 100644 --- a/fs/opener/osfs.py +++ b/fs/opener/osfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the OSFSOpener.""" +"""`OSFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class OSFSOpener(Opener): + """`OSFS` opener. + """ + protocols = ['file', 'osfs'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/opener/parse.py b/fs/opener/parse.py index e39c48fe..822da477 100644 --- a/fs/opener/parse.py +++ b/fs/opener/parse.py @@ -1,9 +1,4 @@ -""" -fs.opener.parse -=============== - -Parses FS URLs in to their constituent parts. - +"""Function to parse FS URLs in to their constituent parts. """ from __future__ import absolute_import @@ -33,15 +28,16 @@ class ParseResult(_ParseResult): """A named tuple containing fields of a parsed FS URL. - * ``protocol`` The protocol part of the url, e.g. ``osfs`` or - ``ftp``. - * ``username`` A username, or ``None`` . - * ``password`` An password, or ``None``. - * ``resource`` A *resource*, typically a domain and path, e.g. - ``ftp.example.org/dir`` - * ``params`` An dictionary of parameters extracted from the query - string. - * ``path`` An optional path within the filesystem. + Attributes: + protocol (str): The protocol part of the url, e.g. ``osfs`` + or ``ftp``. + username (str, optional): A username, or `None`. + password (str, optional): A password, or `None`. + resource (str): A *resource*, typically a domain and path, e.g. + ``ftp.example.org/dir``. + params (dict): A dictionary of parameters extracted from the + query string. + path (str, optional): A path within the filesystem, or `None`. """ @@ -63,17 +59,18 @@ class ParseResult(_ParseResult): def parse_fs_url(fs_url): - """ - Parse a Filesystem URL and return a - :class:`~fs.opener.parse.ParseResult`, or raise - :class:`~fs.errors.ParseError` (subclass of ValueError) if the FS URL is - not value. + """Parse a Filesystem URL and return a `ParseResult`. - :param str fs_url: A filesystem URL - :rtype: :class:`~fs.opener.parse.ParseResult` + Arguments: + fs_url (str): A filesystem URL. - """ + Returns: + ~fs.opener.parse.ParseResult: a parse result instance. + + Raises: + ~fs.errors.ParseError: if the FS URL is not valid. + """ match = _RE_FS_URL.match(fs_url) if match is None: raise ParseError('{!r} is not a fs2 url'.format(fs_url)) diff --git a/fs/opener/registry.py b/fs/opener/registry.py index 4bd91613..1e3f43ac 100644 --- a/fs/opener/registry.py +++ b/fs/opener/registry.py @@ -1,11 +1,5 @@ # coding: utf-8 -""" -fs.opener.registry -=================== - -Defines the Registry, which maps protocols and FS URLs to their -respective Opener. - +"""`Registry` class mapping protocols and FS URLs to their `Opener`. """ from __future__ import absolute_import @@ -22,19 +16,16 @@ class Registry(object): - """ - A registry for `Opener` instances. - + """A registry for `Opener` instances. """ - def __init__(self, default_opener='osfs'): - """ - Create a registry object. + """Create a registry object. - :param default_opener: The protocol to use, if one is not - supplied. The default is to use 'osfs', so that the FS URL - is treated as a system path if no protocol is given. + Arguments: + default_opener (str, optional): The protocol to use, if one + is not supplied. The default is to use 'osfs', so that the + FS URL is treated as a system path if no protocol is given. """ self.default_opener = default_opener @@ -46,7 +37,8 @@ def __repr__(self): @property def protocols(self): - """A list of supported protocols.""" + """`list`: the list of supported protocols. + """ if self._protocols is None: self._protocols = [ entry_point.name @@ -56,16 +48,20 @@ def protocols(self): return self._protocols def get_opener(self, protocol): - """ - Get the opener class associated to a given protocol. + """Get the opener class associated to a given protocol. + + Arguments: + protocol (str): A filesystem protocol. - :param str protocol: A filesystem protocol. - :rtype: ``Opener``. - :raises `~fs.opener.errors.UnsupportedProtocol`: If no opener - could be found. - :raises `EntryPointLoadingError`: If the returned entry - point is not an ``Opener`` subclass or could not be loaded - successfully. + Returns: + Opener: an opener instance. + + Raises: + ~fs.opener.errors.UnsupportedProtocol: If no opener + could be found for the given protocol. + EntryPointLoadingError: If the returned entry point + is not an `Opener` subclass or could not be loaded + successfully. """ protocol = protocol or self.default_opener @@ -112,21 +108,23 @@ def open(self, create=False, cwd=".", default_protocol='osfs'): - """ - Open a filesystem from a FS URL. Returns a tuple of a filesystem - object and a path. If there is no path in the FS URL, the path - value will be ``None``. - - :param str fs_url: A filesystem URL - :param bool writeable: True if the filesystem must be writeable. - :param bool create: True if the filesystem should be created if - it does not exist. - :param cwd: The current working directory. - :type cwd: str or None - :rtype: Tuple of ``(, )`` + """Open a filesystem from a FS URL. - """ + Returns a tuple of a filesystem object and a path. If there is + no path in the FS URL, the path value will be `None`. + + Arguments: + fs_url (str): A filesystem URL. + writeable (bool, optional): `True` if the filesystem must be + writeable. + create (bool, optional): `True` if the filesystem should be + created if it does not exist. + cwd (str, optional): The current working directory, or `None`. + + Returns: + (FS, str): a tuple of ``(, )`` + """ if '://' not in fs_url: # URL may just be a path fs_url = "{}://{}".format(default_protocol, fs_url) @@ -152,19 +150,21 @@ def open_fs(self, create=False, cwd=".", default_protocol='osfs'): - """ - Open a filesystem object from a FS URL (ignoring the path - component). - - :param str fs_url: A filesystem URL. - :param bool writeable: True if the filesystem must be writeable. - :param bool create: True if the filesystem should be created if - it does not exist. - :param str cwd: The current working directory (generally only - relevant for OS filesystems). - :param str default_protocol: The protocol to use if one is not - supplied in the FS URL (defaults to ``"osfs"``). - :returns: :class:`~fs.base.FS` object + """Open a filesystem from a FS URL (ignoring the path component). + + Arguments: + fs_url (str): A filesystem URL. + writeable (bool, optional): `True` if the filesystem must + be writeable. + create (bool, optional): `True` if the filesystem should be + created if it does not exist. + cwd (str, optional): The current working directory (generally + only relevant for OS filesystems). + default_protocol (str): The protocol to use if one is not + supplied in the FS URL (defaults to ``"osfs"``). + + Returns: + ~fs.base.FS: A filesystem instance. """ from ..base import FS @@ -181,42 +181,40 @@ def open_fs(self, return _fs @contextlib.contextmanager - def manage_fs(self, fs_url, create=False, writeable=True, cwd='.'): - ''' - A context manager opens / closes a filesystem. - - :param fs_url: A FS instance or a FS URL. - :type fs_url: str or FS - :param bool create: If ``True``, then create the filesystem if - it doesn't already exist. - :param bool writeable: If ``True``, then the filesystem should - be writeable. - :param str cwd: The current working directory, if opening a - :class:`~fs.osfs.OSFS`. + def manage_fs(self, fs_url, create=False, writeable=True, cwd='.'): # noqa: D300,D301 + """Get a context manager to open and close a filesystem. + + Arguments: + fs_url (FS or str): A filesystem instance or a FS URL. + create (bool, optional): If `True`, then create the filesystem if + it doesn't already exist. + writeable (bool, optional): If `True`, then the filesystem should + be writeable. + cwd (str): The current working directory, if opening a + `~fs.osfs.OSFS`. Sometimes it is convenient to be able to pass either a FS object *or* an FS URL to a function. This context manager handles the required logic for that. - Here's an example:: + Example: + >>> def print_ls(list_fs): + ... \"\"\"List a directory.\"\"\" + ... with manage_fs(list_fs) as fs: + ... print(' '.join(fs.listdir())) - def print_ls(list_fs): - """List a directory.""" - with manage_fs(list_fs) as fs: - print(" ".join(fs.listdir())) + This function may be used in two ways. You may either pass + a ``str``, as follows:: - This function may be used in two ways. You may either pass - either a ``str``, as follows:: + >>> print_list('zip://projects.zip') - print_list('zip://projects.zip') + Or, an filesystem instance:: - Or, an FS instance:: + >>> from fs.osfs import OSFS + >>> projects_fs = OSFS('~/') + >>> print_list(projects_fs) - from fs.osfs import OSFS - projects_fs = OSFS('~/') - print_list(projects_fs) - - ''' + """ from ..base import FS if isinstance(fs_url, FS): yield fs_url diff --git a/fs/opener/tarfs.py b/fs/opener/tarfs.py index 471c0469..5986d9ed 100644 --- a/fs/opener/tarfs.py +++ b/fs/opener/tarfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the TarOpener.""" +"""`TarFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class TarOpener(Opener): + """`TarFS` opener. + """ + protocols = ['tar'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/opener/tempfs.py b/fs/opener/tempfs.py index 5a98c97a..6cde0b7f 100644 --- a/fs/opener/tempfs.py +++ b/fs/opener/tempfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the TempOpener.""" +"""`TempFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class TempOpener(Opener): + """`TempFS` opener. + """ + protocols = ['temp'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/opener/zipfs.py b/fs/opener/zipfs.py index ff62f5d4..a751c473 100644 --- a/fs/opener/zipfs.py +++ b/fs/opener/zipfs.py @@ -1,5 +1,6 @@ # coding: utf-8 -"""Defines the ZipOpener.""" +"""`ZipFS` opener definition. +""" from __future__ import absolute_import from __future__ import print_function @@ -8,6 +9,9 @@ from .base import Opener class ZipOpener(Opener): + """`ZipFS` opener. + """ + protocols = ['zip'] def open_fs(self, fs_url, parse_result, writeable, create, cwd): diff --git a/fs/osfs.py b/fs/osfs.py index 8f2633f8..dc7fdf58 100644 --- a/fs/osfs.py +++ b/fs/osfs.py @@ -1,3 +1,9 @@ +"""Manage the filesystem provided by your OS. + +In essence, an `OSFS` is a thin layer over the `io` and `os` modules +of the Python standard library. +""" + from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals @@ -41,28 +47,26 @@ @six.python_2_unicode_compatible class OSFS(FS): - """ - Create an OSFS. - - :param root_path: An OS path or path-like object to the location on - your HD you wish to manage. - :type root_path: str or path-like - :param create: Set to ``True`` to create the root directory if it - does not already exist, otherwise the directory should exist - prior to creating the ``OSFS`` instance. - :type create: bool - :param int create_mode: The permissions that will be used to create - the directory if ``create`` is True and the path doesn't exist, - defaults to ``0o777``. - :raises `fs.errors.CreateFailed`: If ``root_path`` does not - exists, or could not be created. - - - Here are some examples of creating ``OSFS`` objects:: - - current_directory_fs = OSFS('.') - home_fs = OSFS('~/') - windows_system32_fs = OSFS('c://system32') + """Create an OSFS. + + Arguments: + root_path (str or ~os.PathLike): An OS path or path-like object to + the location on your HD you wish to manage. + create (bool, optional): Set to `True` to create the root directory + if it does not already exist, otherwise the directory should exist + prior to creating the ``OSFS`` instance (default `False`). + create_mode (int, optional): The permissions that will be used to + create the directory if ``create`` is `True` and the path doesn't + exist, defaults to ``0o777``. + + Raises: + `fs.errors.CreateFailed`: If ``root_path`` does not + exists, or could not be created. + + Examples: + >>> current_directory_fs = OSFS('.') + >>> home_fs = OSFS('~/') + >>> windows_system32_fs = OSFS('c://system32') """ @@ -70,8 +74,8 @@ def __init__(self, root_path, create=False, create_mode=0o777): - """Create an OSFS instance.""" - + """Create an OSFS instance. + """ super(OSFS, self).__init__() root_path = fsdecode(fspath(root_path)) _root_path = os.path.expanduser(os.path.expandvars(root_path)) @@ -127,7 +131,8 @@ def __str__(self): self.root_path) def _to_sys_path(self, path): - """Convert a FS path to a path on the OS.""" + """Convert a FS path to a path on the OS. + """ sys_path = os.path.join( self.root_path, path.lstrip('/').replace('/', os.sep) @@ -136,7 +141,8 @@ def _to_sys_path(self, path): @classmethod def _make_details_from_stat(cls, stat_result): - """Make an info dict from a stat_result object.""" + """Make a *details* info dict from an `os.stat_result` object. + """ details = { '_write': ['accessed', 'modified'], 'accessed': stat_result.st_atime, @@ -158,6 +164,8 @@ def _make_details_from_stat(cls, stat_result): @classmethod def _make_access_from_stat(cls, stat_result): + """Make an *access* info dict from an `os.stat_result` object. + """ access = {} access['permissions'] = Permissions( mode=stat_result.st_mode @@ -190,7 +198,8 @@ def _make_access_from_stat(cls, stat_result): @classmethod def _get_type_from_stat(cls, _stat): - """Get the resource type from a stat_result object.""" + """Get the resource type from an `os.stat_result` object. + """ st_mode = _stat.st_mode st_type = stat.S_IFMT(st_mode) return cls.STAT_TO_RESOURCE_TYPE.get(st_type, ResourceType.unknown) diff --git a/fs/path.py b/fs/path.py index e9171fa3..c67c180b 100644 --- a/fs/path.py +++ b/fs/path.py @@ -1,6 +1,6 @@ -""" -Useful functions for working with PyFilesystem paths. -This is broadly similar to the standard ``os.path`` module but works +"""Useful functions for working with PyFilesystem paths. + +This is broadly similar to the standard `os.path` module but works with paths in the canonical format expected by all FS objects (that is, separated by forward slashes and with an optional leading slash). @@ -46,22 +46,24 @@ def normpath(path): - """ - Normalize a path. + """Normalize a path. This function simplifies a path by collapsing back-references and removing duplicated separators. - :param str path: Path to normalize. - :returns: A valid FS path. - :type: str + Arguments: + path (str): Path to normalize. - >>> normpath("/foo//bar/frob/../baz") - '/foo/bar/baz' - >>> normpath("foo/../../bar") - Traceback (most recent call last) - ... - IllegalBackReference: Too many backrefs in 'foo/../../bar' + Returns: + str: A valid FS path. + + Example: + >>> normpath("/foo//bar/frob/../baz") + '/foo/bar/baz' + >>> normpath("foo/../../bar") + Traceback (most recent call last) + ... + IllegalBackReference: Too many backrefs in 'foo/../../bar' """ if path in '/': @@ -88,15 +90,17 @@ def normpath(path): def iteratepath(path): - """ - Iterate over the individual components of a path. + """Iterate over the individual components of a path. + + Arguments: + path (str): Path to iterate over. - >>> iteratepath('/foo/bar/baz') - ['foo', 'bar', 'baz'] + Returns: + list: A list of path components. - :param str path: Path to iterate over. - :returns: A list of path components. - :rtype: list + Example: + >>> iteratepath('/foo/bar/baz') + ['foo', 'bar', 'baz'] """ path = relpath(normpath(path)) @@ -106,16 +110,19 @@ def iteratepath(path): def recursepath(path, reverse=False): - """ - Get intermediate paths from the root to the given path. + """Get intermediate paths from the root to the given path. + + Arguments: + path (str): A PyFilesystem path + reverse (bool, optional): Reverses the order of the paths + (default `False`). - :param str path: A PyFilesystem path - :param bool reverse: Reverses the order of the paths. - :returns: A list of paths. - :rtype: list + Returns: + list: A list of paths. - >>> recursepath('a/b/c') - ['/', '/a', '/a/b', '/a/b/c'] + Example: + >>> recursepath('a/b/c') + ['/', '/a', '/a/b', '/a/b/c'] """ if path in '/': @@ -140,11 +147,13 @@ def recursepath(path, reverse=False): def isabs(path): - """ - Check if a path is an absolute path. + """Check if a path is an absolute path. - :param str path: A PyFilesytem path. - :rtype: bool + Arguments: + path (str): A PyFilesytem path. + + Returns: + bool: `True` if the path is absolute (starts with a ``'/'``). """ # Somewhat trivial, but helps to make code self-documenting @@ -152,16 +161,17 @@ def isabs(path): def abspath(path): - """ - Convert the given path to an absolute path. + """Convert the given path to an absolute path. Since FS objects have no concept of a *current directory*, this simply adds a leading ``/`` character if the path doesn't already have one. - :param str path: A PyFilesytem path. - :returns: An absolute path. - :rtype: str + Arguments: + path (str): A PyFilesytem path. + + Returns: + str: An absolute path. """ if not path.startswith('/'): @@ -170,35 +180,41 @@ def abspath(path): def relpath(path): - """ - Convert the given path to a relative path. + """Convert the given path to a relative path. - This is the inverse of abspath(), stripping a leading ``'/'`` from + This is the inverse of `abspath`, stripping a leading ``'/'`` from the path if it is present. - :param str path: Path to adjust - :rtype: str + Arguments: + path (str): A path to adjust. + + Returns: + str: A relative path. - >>> relpath('/a/b') - 'a/b' + Example: + >>> relpath('/a/b') + 'a/b' """ return path.lstrip('/') def join(*paths): - """ - Join any number of paths together. + """Join any number of paths together. - :param paths: Paths to join are given in positional arguments. - :rtype: str + Arguments: + *paths (str): Paths to join, given as positional arguments. - >>> join('foo', 'bar', 'baz') - 'foo/bar/baz' - >>> join('foo/bar', '../baz') - 'foo/baz' - >>> join('foo/bar', '/baz') - '/baz' + Returns: + str: The joined path. + + Example: + >>> join('foo', 'bar', 'baz') + 'foo/bar/baz' + >>> join('foo/bar', '../baz') + 'foo/baz' + >>> join('foo/bar', '/baz') + '/baz' """ absolute = False @@ -217,19 +233,22 @@ def join(*paths): def combine(path1, path2): - """ - Join two paths together. - - :param str path1: A PyFilesytem path. - :param str path2: A PyFilesytem path. - :rtype: str + """Join two paths together. This is faster than :func:`~fs.path.join`, but only works when the second path is relative, and there are no back references in either path. - >>> combine("foo/bar", "baz") - 'foo/bar/baz' + Arguments: + path1 (str): A PyFilesytem path. + path2 (str): A PyFilesytem path. + + Returns: + str: The joint path. + + Example: + >>> combine("foo/bar", "baz") + 'foo/bar/baz' """ if not path1: @@ -238,22 +257,24 @@ def combine(path1, path2): def split(path): - """ - Split a path into (head, tail) pair. + """Split a path into (head, tail) pair. This function splits a path into a pair (head, tail) where 'tail' is the last pathname component and 'head' is all preceding components. - :param str path: Path to split - :returns: tuple of ``(head, tail)`` - :rtype: tuple + Arguments: + path (str): Path to split - >>> split("foo/bar") - ('foo', 'bar') - >>> split("foo/bar/baz") - ('foo/bar', 'baz') - >>> split("/foo/bar/baz") - ('/foo/bar', 'baz') + Returns: + (str, str): a tuple containing the head and the tail of the path. + + Example: + >>> split("foo/bar") + ('foo', 'bar') + >>> split("foo/bar/baz") + ('foo/bar', 'baz') + >>> split("/foo/bar/baz") + ('/foo/bar', 'baz') """ if '/' not in path: @@ -263,21 +284,21 @@ def split(path): def splitext(path): - """ - Split the extension from the path, and returns the path (up to the - last '.' and the extension). + """Split the extension from the path. - :param path: A path to split - :returns: tuple of ``(path, extension)`` - :rtype: tuple + Arguments: + path (str): A path to split. - >>> splitext('baz.txt') - ('baz', '.txt') - >>> splitext('foo/bar/baz.txt') - ('foo/bar/baz', '.txt') + Returns: + (str, str): A tuple containing the path and the extension. - """ + Example: + >>> splitext('baz.txt') + ('baz', '.txt') + >>> splitext('foo/bar/baz.txt') + ('foo/bar/baz', '.txt') + """ parent_path, pathname = split(path) if '.' not in pathname: return path, '' @@ -287,91 +308,107 @@ def splitext(path): def isdotfile(path): - """ - Detect if a path references a dot file, i.e. a resource who's name - starts with a '.' + """Detect if a path references a dot file. + + Arguments: + path (str): Path to check. - :param path: Path to check. - :type path: str - :rtype: bool + Returns: + bool: `True` if the resource name starts with a ``'.'``. - >>> isdotfile('.baz') - True - >>> isdotfile('foo/bar/.baz') - True - >>> isdotfile('foo/bar.baz') - False + Example: + >>> isdotfile('.baz') + True + >>> isdotfile('foo/bar/.baz') + True + >>> isdotfile('foo/bar.baz') + False """ return basename(path).startswith('.') def dirname(path): - """ - Return the parent directory of a path. + """Return the parent directory of a path. This is always equivalent to the 'head' component of the value returned by ``split(path)``. - :param str path: A PyFilesytem path. - :rtype: str + Arguments: + path (str): A PyFilesytem path. + + Returns: + str: the parent directory of the given path. - >>> dirname('foo/bar/baz') - 'foo/bar' - >>> dirname('/foo/bar') - '/foo' - >>> dirname('/foo') - '/' + Example: + >>> dirname('foo/bar/baz') + 'foo/bar' + >>> dirname('/foo/bar') + '/foo' + >>> dirname('/foo') + '/' """ return split(path)[0] def basename(path): - """ - Return the basename of the resource referenced by a path. + """Return the basename of the resource referenced by a path. This is always equivalent to the 'tail' component of the value returned by split(path). - :param str path: A PyFilesytem path. - :rtype: str + Arguments: + path (str): A PyFilesytem path. - >>> basename('foo/bar/baz') - 'baz' - >>> basename('foo/bar') - 'bar' - >>> basename('foo/bar/') - '' + Returns: + str: the name of the resource at the given path. + + Example: + >>> basename('foo/bar/baz') + 'baz' + >>> basename('foo/bar') + 'bar' + >>> basename('foo/bar/') + '' """ return split(path)[1] def issamedir(path1, path2): - """ - Check if two paths reference a resource in the same directory. + """Check if two paths reference a resource in the same directory. + + Arguments: + path1 (str): A PyFilesytem path. + path2 (str): A PyFilesytem path. - :param str path1: A PyFilesytem path. - :param str path2: A PyFilesytem path. - :rtype: bool + Returns: + bool: `True` if the two resources are in the same directory. - >>> issamedir("foo/bar/baz.txt", "foo/bar/spam.txt") - True - >>> issamedir("foo/bar/baz/txt", "spam/eggs/spam.txt") - False + Example: + >>> issamedir("foo/bar/baz.txt", "foo/bar/spam.txt") + True + >>> issamedir("foo/bar/baz/txt", "spam/eggs/spam.txt") + False """ return dirname(normpath(path1)) == dirname(normpath(path2)) def isbase(path1, path2): - """ - Check if path1 is a base of path2. + """Check if ``path1`` is a base of ``path2``. + + Arguments: + path1 (str): A PyFilesytem path. + path2 (str): A PyFilesytem path. + + Returns: + bool: `True` if ``path2`` starts with ``path1`` - :param str path1: A PyFilesytem path. - :param str path2: A PyFilesytem path. - :rtype: bool + Example: + >>> isbase('foo/bar', 'foo/bar/baz/egg.txt') + True """ _path1 = forcedir(abspath(path1)) @@ -380,21 +417,24 @@ def isbase(path1, path2): def isparent(path1, path2): - """ - Check if ``path1`` is a parent directory of ``path2``. + """Check if ``path1`` is a parent directory of ``path2``. + + Arguments: + path1 (str): A PyFilesytem path. + path2 (str): A PyFilesytem path. - :param str path1: A PyFilesytem path. - :param str path2: A PyFilesytem path. - :rtype: bool + Returns: + bool: `True` if ``path1`` is a parent directory of ``path2`` - >>> isparent("foo/bar", "foo/bar/spam.txt") - True - >>> isparent("foo/bar/", "foo/bar") - True - >>> isparent("foo/barry", "foo/baz/bar") - False - >>> isparent("foo/bar/baz/", "foo/baz/bar") - False + Example: + >>> isparent("foo/bar", "foo/bar/spam.txt") + True + >>> isparent("foo/bar/", "foo/bar") + True + >>> isparent("foo/barry", "foo/baz/bar") + False + >>> isparent("foo/bar/baz/", "foo/baz/bar") + False """ bits1 = path1.split("/") @@ -410,34 +450,41 @@ def isparent(path1, path2): def forcedir(path): - """ - Ensure the path ends with a trailing forward slash + """Ensure the path ends with a trailing forward slash. - :param path: A PyFilesytem path. - :rtype: bool + Arguments: + path (str): A PyFilesytem path. - >>> forcedir("foo/bar") - 'foo/bar/' - >>> forcedir("foo/bar/") - 'foo/bar/' + Returns: + str: The path, ending with a slash. - """ + Example: + >>> forcedir("foo/bar") + 'foo/bar/' + >>> forcedir("foo/bar/") + 'foo/bar/' + >>> forcedir("foo/spam.txt") + 'foo/spam.txt' + """ if not path.endswith('/'): return path + '/' return path def frombase(path1, path2): - """ - Get the final path of ``path2`` that isn't in ``path1``. + """Get the final path of ``path2`` that isn't in ``path1``. - :param str path1: A PyFilesytem path. - :param str path2: A PyFilesytem path. - :rtype: str + Arguments: + path1 (str): A PyFilesytem path. + path2 (str): A PyFilesytem path. - >>> frombase('foo/bar/', 'foo/bar/baz/egg') - 'baz/egg' + Returns: + str: the final part of ``path2``. + + Example: + >>> frombase('foo/bar/', 'foo/bar/baz/egg') + 'baz/egg' """ if not isparent(path1, path2): @@ -446,12 +493,16 @@ def frombase(path1, path2): def relativefrom(base, path): - """ - Return a path relative from a given base path, i.e. insert backrefs - as appropriate to reach the path from the base. + """Return a path relative from a given base path. + + Insert backrefs as appropriate to reach the path from the base. - :param str base: Path to a directory. - :param atr path: Path you wish to make relative. + Arguments: + base (str): Path to a directory. + path (str): Path to make relative. + + Returns: + str: the path to ``base`` from ``path``. >>> relativefrom("foo/bar", "baz/index.html") '../../baz/index.html' @@ -473,16 +524,19 @@ def relativefrom(base, path): def iswildcard(path): - """ - Check if a path ends with a wildcard. + """Check if a path ends with a wildcard. + + Arguments: + path (str): A PyFilesystem path. - :param int path: An FS path. - :rtype: bool + Returns: + bool: `True` if path ends with a wildcard. - >>> iswildcard('foo/bar/baz.*') - True - >>> iswildcard('foo/bar') - False + Example: + >>> iswildcard('foo/bar/baz.*') + True + >>> iswildcard('foo/bar') + False """ assert path is not None diff --git a/fs/permissions.py b/fs/permissions.py index 7ee252ea..9aae047b 100644 --- a/fs/permissions.py +++ b/fs/permissions.py @@ -1,6 +1,4 @@ -""" -An abstract permissions container. - +"""Abstract permissions container. """ from __future__ import print_function @@ -10,15 +8,15 @@ def make_mode(init): - """ - Make a mode integer from an initial value. - + """Make a mode integer from an initial value. """ return Permissions.get_mode(init) class _PermProperty(object): - """Creates simple properties to get/set permissions.""" + """Creates simple properties to get/set permissions. + """ + def __init__(self, name): self._name = name self.__doc__ = "Boolean for '{}' permission.".format(name) @@ -35,35 +33,34 @@ def __set__(self, obj, value): @six.python_2_unicode_compatible class Permissions(object): - """ - An abstraction for file system permissions. - - :param list names: A list of permissions. - :param int mode: A mode integer. - :param str user: A triplet of *user* permissions, e.g. ``"rwx"`` or - ``"r--"`` - :param str group: A triplet of *group* permissions, e.g. ``"rwx"`` - or ``"r--"`` - :param str other: A triplet of *other* permissions, e.g. ``"rwx"`` - or ``"r--"`` - :param bool sticky: A boolean for the *sticky* bit. - :param bool setuid: A boolean for the *setuid* bit. - :param bool setguid: A boolean for the *setuid* bit. + """An abstraction for file system permissions. Permissions objects store information regarding the permissions on a resource. It supports Linux permissions, but is generic enough to manage permission information from almost any filesystem. - >>> from fs.permissions import Permissions - >>> p = Permissions(user='rwx', group='rw-', other='r--') - >>> print(p) - rwxrw-r-- - >>> p.mode - 500 - >>> oct(p.mode) - '0764' - - + Arguments: + names (list, optional): A list of permissions. + mode (int, optional): A mode integer. + user (str, optional): A triplet of *user* permissions, e.g. + ``"rwx"`` or ``"r--"`` + group (str, optional): A triplet of *group* permissions, e.g. + ``"rwx"`` or ``"r--"`` + other (str, optional): A triplet of *other* permissions, e.g. + ``"rwx"`` or ``"r--"`` + sticky (bool, optional): A boolean for the *sticky* bit. + setuid (bool, optional): A boolean for the *setuid* bit. + setguid (bool, optional): A boolean for the *setguid* bit. + + Example: + >>> from fs.permissions import Permissions + >>> p = Permissions(user='rwx', group='rw-', other='r--') + >>> print(p) + rwxrw-r-- + >>> p.mode + 500 + >>> oct(p.mode) + '0764' """ @@ -171,7 +168,8 @@ def __ne__(self, other): @classmethod def parse(cls, ls): - """Parse permissions in linux notation.""" + """Parse permissions in Linux notation. + """ user = ls[:3] group = ls[3:6] other = ls[6:9] @@ -179,25 +177,28 @@ def parse(cls, ls): @classmethod def load(cls, permissions): - """Load a serialized permissions object.""" + """Load a serialized permissions object. + """ return cls(names=permissions) @classmethod def create(cls, init=None): - """ - Create a permissions object from an initial value. + """Create a permissions object from an initial value. - :param init: May be None for equivalent for 0o777 permissions, - a mode integer, or a list of permission names. - :returns: mode integer, that may be used by `os.makedir` - (amongst others). + Arguments: + init (int or list or None): May be None to use 0o777 permissions, + a mode integer, or a list of permission names. - >>> Permissions.create(None) - Permissions(user='rwx', group='rwx', other='rwx') - >>> Permissions.create(0o700) - Permissions(user='rwx', group='', other='') - >>> Permissions.create(['u_r', 'u_w', 'u_x']) - Permissions(user='rwx', group='', other='') + Returns: + int: mode integer that may be used for instance by `os.makedir`. + + Example: + >>> Permissions.create(None) + Permissions(user='rwx', group='rwx', other='rwx') + >>> Permissions.create(0o700) + Permissions(user='rwx', group='', other='') + >>> Permissions.create(['u_r', 'u_w', 'u_x']) + Permissions(user='rwx', group='', other='') """ if init is None: @@ -212,22 +213,23 @@ def create(cls, init=None): @classmethod def get_mode(cls, init): - """ - Convert an initial value to a mode integer. - + """Convert an initial value to a mode integer. """ return cls.create(init).mode def copy(self): - """Make a copy of this permissions object.""" + """Make a copy of this permissions object. + """ return Permissions(names=list(self._perms)) def dump(self): - """Get a list suitable for serialization.""" + """Get a list suitable for serialization. + """ return sorted(self._perms) def as_str(self): - """Get a linux-style string representation of permissions.""" + """Get a Linux-style string representation of permissions. + """ perms = [ c if name in self._perms else '-' for name, c in zip( @@ -247,7 +249,8 @@ def as_str(self): @property def mode(self): - """Mode integer.""" + """`int`: mode integer. + """ mode = 0 for name, mask in self._LINUX_PERMS: if name in self._perms: @@ -256,7 +259,6 @@ def mode(self): @mode.setter def mode(self, mode): - """Set mode integer.""" self._perms = { name for name, mask in self._LINUX_PERMS @@ -280,30 +282,34 @@ def mode(self, mode): setguid = _PermProperty('setguid') def add(self, *permissions): - """ - Add permission(s). + """Add permission(s). - :param permissions: Permission name(s). + Arguments: + *permissions (str): Permission name(s), such as ``'u_w'`` + or ``'u_x'``. """ self._perms.update(permissions) def remove(self, *permissions): - """ - Remove permission(s). + """Remove permission(s). - :param permissions: Permission name(s). + Arguments: + *permissions (str): Permission name(s), such as ``'u_w'`` + or ``'u_x'``.s """ self._perms.difference_update(permissions) def check(self, *permissions): - """ - Check if one or more permissions are enabled. + """Check if one or more permissions are enabled. + + Arguments: + *permissions (str): Permission name(s), such as ``'u_w'`` + or ``'u_x'``. - :param permissions: Permission name(s). - :returns: True if all given permissions are set. - :rtype bool: + Returns: + bool: `True` if all given permissions are set. """ return self._perms.issuperset(permissions) diff --git a/fs/subfs.py b/fs/subfs.py index e08e9286..6d2fd2a5 100644 --- a/fs/subfs.py +++ b/fs/subfs.py @@ -1,7 +1,4 @@ -""" - -A SubFS represents a directory in a 'parent' filesystem. - +"""Manage a directory in a *parent* filesystem. """ from __future__ import print_function @@ -15,12 +12,11 @@ @six.python_2_unicode_compatible class SubFS(WrapFS): - """ - A sub-directory on another filesystem. + """A sub-directory on another filesystem. A SubFS is a filesystem object that maps to a sub-directory of another filesystem. This is the object that is returned by - :meth:`~fs.base.FS.opendir`. + `~fs.base.FS.opendir`. """ @@ -50,9 +46,7 @@ def delegate_path(self, path): class ClosingSubFS(SubFS): - """ - A version of SubFS which will close its parent automatically. - + """A version of `SubFS` which closes its parent when closed. """ def close(self): diff --git a/fs/tarfs.py b/fs/tarfs.py index 40516c88..95b1b42f 100644 --- a/fs/tarfs.py +++ b/fs/tarfs.py @@ -1,3 +1,6 @@ +"""Manage the filesystem in a Tar archive. +""" + from __future__ import print_function from __future__ import unicode_literals @@ -17,13 +20,12 @@ class TarFS(WrapFS): - """ - Read and write tar files. + """Read and write tar files. There are two ways to open a TarFS for the use cases of reading a tar file, and creating a new one. - If you open the TarFS with ``write`` set to ``False`` (the + If you open the TarFS with ``write`` set to `False` (the default), then the filesystem will be a read only filesystem which maps to the files and directories within the tar file. Files are decompressed on the fly when you open them. @@ -33,7 +35,7 @@ class TarFS(WrapFS): with TarFS('foo.tar.gz') as tar_fs: readme = tar_fs.gettext('readme.txt') - If you open the TarFS with ``write`` set to ``True``, then the TarFS + If you open the TarFS with ``write`` set to `True`, then the TarFS will be a empty temporary filesystem. Any files / directories you create in the TarFS will be written in to a tar file when the TarFS is closed. The compression is set from the new file name but may be @@ -48,17 +50,14 @@ class TarFS(WrapFS): 'This tar file was written by PyFilesystem' ) - :param file: An OS filename, or a open file object. - :type file: str or file - :param write: Set to ``True`` to write a new tar file, or ``False`` - to read an existing tar file. - :type write: bool - :param compression: Compression to use (one of the formats - supported by ``tarfile``: ``xz``, ``gz``, ``bz2``, or None). - :type compression: str - :param temp_fs: An opener string for the temporary filesystem - used to store data prior to tarring. - :type temp_fs: str + Arguments: + file (str or io.IOBase): An OS filename, or an open file handle. + write (bool, optional): Set to `True` to write a new tar file, + or use default (`False`) to read an existing tar file. + compression (str, optional): Compression to use (one of the formats + supported by `tarfile`: ``xz``, ``gz``, ``bz2``, or `None`). + temp_fs (str, optional): An FS URL for the temporary + filesystem used to store data prior to tarring. """ @@ -96,7 +95,8 @@ def __new__(cls, @six.python_2_unicode_compatible class WriteTarFS(WrapFS): - """A writable tar file.""" + """A writable tar file. + """ def __init__(self, file, @@ -138,18 +138,19 @@ def close(self): super(WriteTarFS, self).close() def write_tar(self, file=None, compression=None, encoding=None): - """ - Write tar to a file. - - .. note:: + """Write tar to a file. + + Arguments: + file (str or io.IOBase, optional): Destination file, may be + a file name or an open file object. + compression (str, optional): Compression to use (one of + the constants defined in `tarfile` in the stdlib). + encoding (str, optional): The character encoding to use + (default uses the encoding defined in + `~WriteTarFS.__init__`). + + Note: This is called automatically when the TarFS is closed. - - :param file: Destination file, may be a file name or an open - file object. - :type file: str or file-like - :param compression: Compression to use (one of the constants - defined in the tarfile module in the stdlib). - """ if not self.isclosed(): write_tar( @@ -162,7 +163,8 @@ def write_tar(self, file=None, compression=None, encoding=None): @six.python_2_unicode_compatible class ReadTarFS(FS): - """A readable tar file.""" + """A readable tar file. + """ _meta = { 'case_insensitive': True, diff --git a/fs/tempfs.py b/fs/tempfs.py index 855aa223..c1258db5 100644 --- a/fs/tempfs.py +++ b/fs/tempfs.py @@ -1,3 +1,14 @@ +"""Manage filesystems in temporary locations. + +A temporary filesytem is stored in a location defined by your OS +(``/tmp`` on linux). The contents are deleted when the filesystem +is closed. + +A `TempFS` is a good way of preparing a directory structure in advance, +that you can later copy. It can also be used as a temporary data store. + +""" + from __future__ import print_function from __future__ import unicode_literals @@ -12,17 +23,18 @@ @six.python_2_unicode_compatible class TempFS(OSFS): - """ - Create a temporary filesystem. + """A temporary filesystem on the OS. - :param str identifier: A string to distinguish the directory within - the OS temp location, used as part of the directory name. - :param str temp_dir: An OS path to your temp directory (leave as - ``None`` to auto-detect) - :param bool auto_clean: If True, the directory contents will be - wiped on close. - :param bool ignore_clean_errors: If True, any errors in the clean - process will be raised. If False, they will be suppressed. + Arguments: + identifier (str): A string to distinguish the directory within + the OS temp location, used as part of the directory name. + temp_dir (str, optional): An OS path to your temp directory (leave + as `None` to auto-detect) + auto_clean (bool, optional): If `True` (the default), the directory + contents will be wiped on close. + ignore_clean_errors (bool, optional): If `True` (the default), any + errors in the clean process will be raised. If `False`, they + will be suppressed. """ @@ -56,7 +68,8 @@ def close(self): super(TempFS, self).close() def clean(self): - """Clean (delete) temporary files created by this filesystem.""" + """Clean (delete) temporary files created by this filesystem. + """ if self._cleaned: return diff --git a/fs/test.py b/fs/test.py index 0bb3c07b..14a14637 100644 --- a/fs/test.py +++ b/fs/test.py @@ -1,6 +1,5 @@ # coding: utf-8 -""" -Base class for tests. +"""Base class for tests. All Filesystems should be able to pass these. @@ -240,21 +239,21 @@ class FSTestCases(object): - """Basic FS tests.""" + """Basic FS tests. + """ def make_fs(self): - """ - Return an FS instance. + """Return an FS instance. """ raise NotImplementedError('implement me') def destroy_fs(self, fs): - """ - Destroy a FS object. + """Destroy a FS instance. - :param fs: A FS instance previously opened by - `~fs.test.FSTestCases.make_fs`. + Arguments: + fs (FS): A filesystem instance previously opened + by `~fs.test.FSTestCases.make_fs`. """ fs.close() @@ -267,47 +266,47 @@ def tearDown(self): del self.fs def assert_exists(self, path): - """ - Assert a path exists. + """Assert a path exists. - :param str path: A path on the filesystem. + Arguments: + path (str): A path on the filesystem. """ self.assertTrue(self.fs.exists(path)) def assert_not_exists(self, path): - """ - Assert a path does not exist. + """Assert a path does not exist. - :param str path: A path on the filesystem. + Arguments: + path (str): A path on the filesystem. """ self.assertFalse(self.fs.exists(path)) def assert_isfile(self, path): - """ - Assert a path is a file. + """Assert a path is a file. - :param str path: A path on the filesystem. + Arguments: + path (str): A path on the filesystem. """ self.assertTrue(self.fs.isfile(path)) def assert_isdir(self, path): - """ - Assert a path is a directory. + """Assert a path is a directory. - :param str path: A path on the filesystem. + Arguments: + path (str): A path on the filesystem. """ self.assertTrue(self.fs.isdir(path)) def assert_bytes(self, path, contents): - """ - Assert a file contains the given bytes. + """Assert a file contains the given bytes. - :param str path: A path on the filesystem. - :param bytes contents: Bytes to compare. + Arguments: + path (str): A path on the filesystem. + contents (bytes): Bytes to compare. """ assert isinstance(contents, bytes) @@ -316,11 +315,11 @@ def assert_bytes(self, path, contents): self.assertIsInstance(data, bytes) def assert_text(self, path, contents): - """ - Assert a file contains the given text. + """Assert a file contains the given text. - :param str path: A path on the filesystem. - :param str contents: Text to compare. + Arguments: + path (str): A path on the filesystem. + contents (str): Text to compare. """ assert isinstance(contents, text_type) @@ -413,7 +412,8 @@ def test_geturl(self): self.assertTrue(self.fs.hasurl('foo')) def test_geturl_purpose(self): - """Check an unknown purpose raises a NoURL error""" + """Check an unknown purpose raises a NoURL error. + """ self.fs.create('foo') with self.assertRaises(errors.NoURL): self.fs.geturl('foo', purpose='__nosuchpurpose__') diff --git a/fs/time.py b/fs/time.py index 34d87b23..48016649 100644 --- a/fs/time.py +++ b/fs/time.py @@ -1,4 +1,5 @@ -"""Time related tools.""" +"""Time related tools. +""" from __future__ import print_function from __future__ import unicode_literals @@ -14,10 +15,12 @@ def datetime_to_epoch(d): - """Convert datetime to epoch.""" + """Convert datetime to epoch. + """ return timegm(d.utctimetuple()) def epoch_to_datetime(t): - """Convert epoch time to a UTC datetime.""" + """Convert epoch time to a UTC datetime. + """ return utclocalize(utcfromtimestamp(t)) if t is not None else None diff --git a/fs/tools.py b/fs/tools.py index 74aa02b5..6f4bbf08 100644 --- a/fs/tools.py +++ b/fs/tools.py @@ -1,6 +1,4 @@ -""" -A collection of functions that operate on filesystems and tools. - +"""Miscellaneous tools for operating on filesystems. """ from __future__ import print_function @@ -18,11 +16,11 @@ def remove_empty(fs, path): - """ - Remove all empty parents. + """Remove all empty parents. - :param fs: A FS object. - :param str path: Path to a directory on the filesystem. + Arguments: + fs (FS): A filesystem instance. + path (str): Path to a directory on the filesystem. """ path = abspath(normpath(path)) @@ -35,13 +33,13 @@ def remove_empty(fs, path): def copy_file_data(src_file, dst_file, chunk_size=None): - """ - Copy data from one file object to another. + """Copy data from one file object to another. - :param file-like src_file: File open for reading. - :param file-like dst_file: File open for writing. - :param int chunk_size: Number of bytes to copy at a time (or - ``None`` to use sensible default). + Arguments: + src_file (io.IOBase): File open for reading. + dst_file (io.IOBase): File open for writing. + chunk_size (int, optional): Number of bytes to copy at + a time (or `None` to use sensible default). """ chunk_size = chunk_size or io.DEFAULT_BUFFER_SIZE @@ -53,17 +51,18 @@ def copy_file_data(src_file, dst_file, chunk_size=None): def get_intermediate_dirs(fs, dir_path): - """ - Get paths of intermediate directories required to create a new - directory. + """Get a list of non-existing intermediate directories. + + Arguments: + fs (FS): A filesystem instance. + dir_path (str): A path to a new directory on the filesystem. - :param fs: A FS object. - :param str dir_path: A path to a new directory on the filesystem. - :returns: A list of paths. - :rtype: list + Returns: + list: A list of non-existing paths. - :raises `fs.errors.DirectoryExpected`: If a path component - references a file and not a directory. + Raises: + `fs.errors.DirectoryExpected`: If a path component + references a file and not a directory. """ intermediates = [] diff --git a/fs/tree.py b/fs/tree.py index 40a27d2d..b9832100 100644 --- a/fs/tree.py +++ b/fs/tree.py @@ -1,8 +1,7 @@ -# -*- coding: utf-8 -*- - -""" -Render a FS object as text tree views. +# coding: utf-8 +"""Render a FS object as text tree views. +Color is supported on UNIX terminals. """ from __future__ import print_function @@ -22,27 +21,28 @@ def render(fs, dirs_first=True, exclude=None, filter=None): - """ - Render a directory structure in to a pretty tree. - - :param fs: A filesystem. - :type fs: A :class:`~fs.base.FS` instance - :param file: An open file-like object to render the tree, or - ``None`` for stdout. - :type file: file or None - :type encoding: Unicode encoding, or None to auto-detect. - :type encoding: str or None - :param int max_levels: Maximum number of levels to display, or None - for no maximum. - :param bool with_color: Enable terminal color output, or None to - auto-detect terminal. - :param bool dirs_first: Show directories first. - :param list exclude: Option list of directory patterns to exclude - from the tree render. - :param filter: Optional list of files patterns to match in the tree - render. - :rtype: tuple - :returns: A tuple of ``(, )``. + """Render a directory structure in to a pretty tree. + + Arguments: + fs (~fs.base.FS): A filesystem instance. + path (str): The path of the directory to start rendering + from (defaults to root folder, i.e. ``'/'``). + file (io.IOBase): An open file-like object to render the + tree, or `None` for stdout. + encoding (str or None): Unicode encoding, or `None` to + auto-detect. + max_levels (int or None): Maximum number of levels to + display, or `None` for no maximum. + with_color (bool or None): Enable terminal color output, + or `None` to auto-detect terminal. + dirs_first (bool): Show directories first. + exclude (list or None): Option list of directory patterns + to exclude from the tree render. + filter (list or None): Optional list of files patterns to + match in the tree render. + + Returns: + (int, int): A tuple of ``(, )``. """ file = file or sys.stdout @@ -69,29 +69,34 @@ def render(fs, line_indent = char_vertline + ' ' * 3 def write(line): - """Write a line to the output.""" + """Write a line to the output. + """ print(line, file=file) def format_prefix(prefix): - """Format the prefix lines.""" + """Format the prefix lines. + """ if not with_color: return prefix return '\x1b[32m%s\x1b[0m' % prefix def format_dirname(dirname): - """Format a directory name.""" + """Format a directory name. + """ if not with_color: return dirname return '\x1b[1;34m%s\x1b[0m' % dirname def format_error(msg): - """Format an error.""" + """Format an error. + """ if not with_color: return msg return '\x1b[31m%s\x1b[0m' % msg def format_filename(fname): - """Format a filename.""" + """Format a filename. + """ if not with_color: return fname if fname.startswith('.'): @@ -99,17 +104,20 @@ def format_filename(fname): return fname def sort_key_dirs_first(info): - """Sort key func with directories first.""" + """Get the info sort function with directories first. + """ return (not info.is_dir, info.name.lower()) def sort_key(info): - """Default key for info.""" + """Get the default info sort function using resource name. + """ return info.name.lower() counts = {"dirs": 0, "files": 0} def format_directory(path, levels): - """Recursive directory function.""" + """Recursive directory function. + """ try: directory = sorted( fs.filterdir(path, exclude_dirs=exclude, files=filter), diff --git a/fs/walk.py b/fs/walk.py index ec62e8fc..ca8e3a5e 100644 --- a/fs/walk.py +++ b/fs/walk.py @@ -1,7 +1,8 @@ -""" - -The machinery for walking a filesystem. See :ref:`walking` for details. +"""Machinery for walking a filesystem. +*Walking* a filesystem means recursively visiting a directory and +any sub-directories. It is a fairly common requirement for copying, +searching etc. See :ref:`walking` for details. """ from __future__ import unicode_literals @@ -20,43 +21,48 @@ from .path import normpath -"""A 'step' in a directory walk.""" Step = namedtuple('Step', 'path, dirs, files') +"""type: a *step* in a directory walk. +""" class WalkerBase(object): - """ - The base class for a Walker. + """The base class for a Walker. - To create a custom walker, implement - :meth:`~fs.walk.WalkerBase.walk` in a sub-class. + To create a custom walker, implement `~fs.walk.WalkerBase.walk` + in a sub-class. - See :meth:`~fs.walk.Walker` for a fully featured walker object that + See `~fs.walk.Walker` for a fully featured walker object that should be adequate for all but your most exotic directory walking needs. """ def walk(self, fs, path='/', namespaces=None): - """ - Walk the directory structure of a filesystem. + """Walk the directory structure of a filesystem. - :param fs: A FS object. - :param str path: a path to a directory. - :param list namespaces: A list of additional namespaces to add - to the Info objects. - :returns: Iterator of :class:`~fs.walk.Step` named tuples. + Arguments: + fs (FS): A filesystem instance. + path (str, optional): A path to a directory on the filesystem. + namespaces(list, optional): A list of additional namespaces + to add to the `~fs.info.Info` objects. + + Returns: + ~collections.Iterator: iterator of `~fs.walk.Step` named tuples. """ raise NotImplementedError def files(self, fs, path='/'): - """ - Walk a filesystem, yielding absolute paths to files. + """Walk a filesystem, yielding absolute paths to files. - :param fs: A FS object. - :param str path: A path to a directory. - :returns: An iterable of file paths. + Arguments: + fs (FS): A filesystem instance. + path (str, optional): A path to a directory on the filesystem. + + Yields: + str: absolute path to files on the filesystem found + recursively within the given directory. """ iter_walk = iter(self.walk(fs, path=path)) @@ -65,11 +71,15 @@ def files(self, fs, path='/'): yield join(_path, info.name) def dirs(self, fs, path='/'): - """ - Walk a filesystem, yielding absolute paths to directories. + """Walk a filesystem, yielding absolute paths to directories. - :param str fs: A FS object. - :param str path: A path to a directory. + Arguments: + fs (FS): A filesystem instance. + path (str, optional): A path to a directory on the filesystem. + + Yields: + str: absolute path to directories on the filesystem found + recursively within the given directory. """ for path, dirs, _ in self.walk(fs, path=path): @@ -77,15 +87,16 @@ def dirs(self, fs, path='/'): yield join(path, info.name) def info(self, fs, path='/', namespaces=None): - """ - Walk a filesystem, yielding tuples of ``(, - )``. + """Walk a filesystem, yielding tuples of ``(, )``. + + Arguments: + fs (FS): A filesystem instance. + path (str, optional): A path to a directory on the filesystem. + namespaces (list, optional): A list of additional namespaces + to add to the `Info` objects. - :param str fs: A FS object. - :param str path: A path to a directory. - :param list namespaces: A list of additional namespaces to add - to the Info objects. - :returns: An iterable of :class:`~fs.info.Info` objects. + Yields: + (str, Info): a couple of ``(, )``. """ _walk = self.walk(fs, path=path, namespaces=namespaces) @@ -95,24 +106,23 @@ def info(self, fs, path='/', namespaces=None): class Walker(WalkerBase): - """ - A walker object recursively lists directories in a filesystem. - - :param bool ignore_errors: If true, any errors reading a - directory will be ignored, otherwise exceptions will be - raised. - :param callable on_error: If ``ignore_errors`` is false, then - this callable will be invoked with a path and the exception - object. It should return True to ignore the error, or False - to re-raise it. - :param str search: If ``'breadth'`` then the directory will be - walked *top down*. Set to ``'depth'`` to walk *bottom up*. - :param list filter: If supplied, this parameter should be a list of - filename patterns, e.g. ``['*.py']``. Files will only be - returned if the final component matches one of the patterns. - :param list exclude_dirs: A list of patterns that will be used - to filter out directories from the walk. e.g. ``['*.svn', - '*.git']``. + """A walker object recursively lists directories in a filesystem. + + Arguments: + ignore_errors (bool, optional): If `True`, any errors reading + a directory will be ignored, otherwise exceptions will be + raised. + on_error (callable, optional): If ``ignore_errors`` is `False`, + then this callable will be invoked for a path and the exception + object. It should return `True` to ignore the error, or `False` + to re-raise it. + search (str, optional): If ``'breadth'`` then the directory will be + walked *top down*. Set to ``'depth'`` to walk *bottom up*. + filter (list, optional): If supplied, this parameter should be a + list of filename patterns, e.g. ``['*.py']``. Files will only + be returned if the final component matches one of the patterns. + exclude_dirs (list, optional): A list of patterns that will be used + to filter out directories from the walk. e.g. ``['*.svn', '*.git']``. """ @@ -137,11 +147,19 @@ def __init__(self, @classmethod def bind(cls, fs): - """ + """Bind a `Walker` instance to a given filesystem. + This *binds* in instance of the Walker to a given filesystem, so that you won't need to explicitly provide the filesystem as a - parameter. Here's an example of binding:: + parameter. + + Arguments: + fs (FS): A filesystem object. + + Returns: + ~fs.walk.BoundWalker: a bound walker. + Example: >>> from fs import open_fs >>> from fs.walk import Walker >>> home_fs = open_fs('~/') @@ -152,16 +170,14 @@ def bind(cls, fs): Unless you have written a customized walker class, you will be unlikely to need to call this explicitly, as filesystem objects already have a ``walk`` attribute which is a bound walker - object. Here's how you might use it:: + object. + Example: >>> from fs import open_fs >>> home_fs = open_fs('~/') >>> for path in home_fs.walk.files(filter=['*.py']): ... print(path) - :param fs: A filesystem object. - :returns: a :class:`~fs.walk.BoundWalker` - """ return BoundWalker(fs) @@ -176,17 +192,18 @@ def __repr__(self): ) def filter_files(self, fs, infos): - """ - Filters a sequence of resource Info objects. + """Filter a sequence of resource `Info` objects. The default implementation filters those files for which - :meth:`~fs.walk.Walker.check_file` returns True. + `~fs.walk.Walker.check_file` returns `True`. + + Arguments: + fs (FS): a filesystem instance. + infos (list): A list of `~fs.info.Info` instances. - :param fs: A FS object. - :type fs: :class:`~fs.base.FS` - :param infos: A list of :class:`~fs.info.Info` instances. - :type infos: list - :rtype: list + Returns: + list: a list of `Info` objects passing the ``check_file`` + validation. """ _check_file = self.check_file @@ -197,14 +214,16 @@ def filter_files(self, fs, infos): def check_open_dir(self, fs, info): - """ - Check if a directory should be opened. Override to exclude - directories from the walk. + """Check if a directory should be opened. + + Override to exclude directories from the walk. - :param fs: A FS object. - :type fs: :class:`~fs.base.FS` - :param info: A :class:`~fs.info.Info` object. - :rtype: bool + Arguments: + fs (FS): A filesystem instance. + info (Info): A resource info object. + + Returns: + bool: `True` if the directory should be opened. """ if self.exclude_dirs is None: @@ -212,26 +231,32 @@ def check_open_dir(self, fs, info): return not fs.match(self.exclude_dirs, info.name) def check_file(self, fs, info): - """ - Check if a filename should be included in the walk. Override to - exclude files from the walk. + """Check if a filename should be included. + + Override to exclude files from the walk. - :param fs: A FS object. - :type fs: :class:`~fs.base.FS` - :param info: A :class:`~fs.info.Info` object. - :rtype: bool + Arguments: + fs (FS): A filesystem instance. + info (Info): A resource info object. + + Returns: + bool: `True` if the file should be included. """ return fs.match(self.filter, info.name) def _scan(self, fs, dir_path, namespaces): - """ - Get an iterator of :class:`~fs.info.Info` objects for a - directory path. + """Get an iterator of `Info` objects for a directory path. - :param fs: A FS object. - :type fs: :class:`~fs.base.FS` - :param str dir_path: A path to a directory. + Arguments: + fs (FS): A filesystem instance. + dir_path (str): A path to a directory on the filesystem. + namespaces (list): A list of additional namespaces to + include in the `Info` objects. + + Returns: + ~collections.Iterator: iterator of `Info` objects for + resources within the given path. """ try: @@ -242,30 +267,31 @@ def _scan(self, fs, dir_path, namespaces): six.reraise(type(error), error) def walk(self, fs, path='/', namespaces=None): - """ - Walk the directory structure of a filesystem. + """Walk the directory structure of a filesystem. - :param fs: A FS object. - :param str path: a path to a directory. - :param list namespaces: A list of additional namespaces to add - to the Info objects. - :returns: :class:`~fs.walk.Step` iterator. + Arguments: + fs (FS): A filesystem instance. + path (str, optional): A path to a directory on the filesystem. + namespaces (list, optional): A list of additional namespaces + to add to the `Info` objects. + + Returns: + collections.Iterator: an iterator of `~fs.walk.Step` instances. The return value is an iterator of ``(, , )`` named tuples, where ```` is an absolute path to a directory, and ```` and ```` are a list of - :class:`~fs.info.Info` objects for directories and files - in ````. - - Here's an example:: + `~fs.info.Info` objects for directories and files in ````. - home_fs = open_fs('~/') - walker = Walker(filter=['*.py']) - for path, dirs, files in walker.walk(home_fs, namespaces=['details']): - print("[{}]".format(path)) - print("{} directories".format(len(dirs))) - total = sum(info.size for info in files) - print("{} bytes {}".format(total)) + Example: + >>> home_fs = open_fs('~/') + >>> walker = Walker(filter=['*.py']) + >>> namespaces = ['details'] + >>> for path, dirs, files in walker.walk(home_fs, namespaces) + ... print("[{}]".format(path)) + ... print("{} directories".format(len(dirs))) + ... total = sum(info.size for info in files) + ... print("{} bytes {}".format(total)) """ _path = abspath(normpath(path)) @@ -278,9 +304,7 @@ def walk(self, fs, path='/', namespaces=None): return do_walk(fs, _path, namespaces=namespaces) def _walk_breadth(self, fs, path, namespaces=None): - """ - Walk files using a breadth first search. - + """Walk files using a *breadth first* search. """ queue = deque([path]) push = queue.appendleft @@ -304,9 +328,7 @@ def _walk_breadth(self, fs, path, namespaces=None): ) def _walk_depth(self, fs, path, namespaces=None): - """ - Walk files using a depth first search. - + """Walk files using a *depth first* search. """ # No recursion! @@ -342,25 +364,25 @@ def scan(path): class BoundWalker(object): - """ - A class that binds a :class:`~fs.walk.Walker` instance to a FS - object. + """A class that binds a `Walker` instance to a `FS` instance. - :param fs: A FS object. - :param walker_class: A :class:`~fs.walk.WalkerBase` sub-class. The - default uses :class:`~fs.walk.Walker`. + Arguments: + fs (FS): A filesystem instance. + walker_class (type, optional): A `~fs.walk.WalkerBase` + sub-class. The default uses `~fs.walk.Walker`. You will typically not need to create instances of this class - explicitly. Filesystems have a ``walk`` property which returns a - ``BoundWalker`` object. + explicitly. Filesystems have a `~FS.walk` property which returns a + `BoundWalker` object. + Example: >>> import fs >>> home_fs = fs.open_fs('~/') >>> home_fs.walk BoundWalker(OSFS('/Users/will', encoding='utf-8')) - A BoundWalker is callable. Calling it is an alias for - :meth:`~fs.walk.BoundWalker.walk`. + A `BoundWalker` is callable. Calling it is an alias for + `~fs.walk.BoundWalker.walk`. """ @@ -372,7 +394,8 @@ def __repr__(self): return "BoundWalker({!r})".format(self.fs) def _make_walker(self, *args, **kwargs): - """Create a walker instance.""" + """Create a walker instance. + """ walker = self.walker_class(*args, **kwargs) return walker @@ -380,46 +403,48 @@ def walk(self, path='/', namespaces=None, **kwargs): - """ - Walk the directory structure of a filesystem. - - :param str path: A path to a directory. - :param bool ignore_errors: If true, any errors reading a - directory will be ignored, otherwise exceptions will be - raised. - :param callable on_error: If ``ignore_errors`` is false, then - this callable will be invoked with a path and the exception - object. It should return True to ignore the error, or False - to re-raise it. - :param str search: If ``'breadth'`` then the directory will be - walked *top down*. Set to ``'depth'`` to walk *bottom up*. - :param list filter: If supplied, this parameter should be a list - of file name patterns, e.g. ``['*.py']``. Files will only be - returned if the final component matches one of the - patterns. - :param list exclude_dirs: A list of patterns that will be used - to filter out directories from the walk, e.g. ``['*.svn', - '*.git']``. - :returns: :class:`~fs.walk.Step` iterator. - - The return value is an iterator of ``(, , )`` - named tuples, where ```` is an absolute path to a - directory, and ```` and ```` are a list of - :class:`~fs.info.Info` objects for directories and files - in ````. - - Here's an example:: - - home_fs = open_fs('~/') - walker = Walker(filter=['*.py']) - for path, dirs, files in walker.walk(home_fs, namespaces=['details']): - print("[{}]".format(path)) - print("{} directories".format(len(dirs))) - total = sum(info.size for info in files) - print("{} bytes {}".format(total)) + """Walk the directory structure of a filesystem. + + Arguments: + path (str, optional): + namespaces (list, optional): A list of namespaces to include + in the resource information, e.g. ``['basic', 'access']`` + (defaults to ``['basic']``). + + Keyword Arguments: + ignore_errors (bool): If `True`, any errors reading a + directory will be ignored, otherwise exceptions will be + raised. + on_error (callable): If ``ignore_errors`` is `False`, then + this callable will be invoked with a path and the exception + object. It should return `True` to ignore the error, or + `False` to re-raise it. + search (str): If ``'breadth'`` then the directory will be + walked *top down*. Set to ``'depth'`` to walk *bottom up*. + filter (list): If supplied, this parameter should be a list + of file name patterns, e.g. ``['*.py']``. Files will only be + returned if the final component matches one of the + patterns. + exclude_dirs (list): A list of patterns that will be used + to filter out directories from the walk, e.g. ``['*.svn', + '*.git']``. + + Returns: + ~collections.Iterator: an iterator of ``(, , )`` + named tuples, where ```` is an absolute path to a + directory, and ```` and ```` are a list of + `~fs.info.Info` objects for directories and files in ````. + + Example: + >>> home_fs = open_fs('~/') + >>> walker = Walker(filter=['*.py']) + >>> for path, dirs, files in walker.walk(home_fs, ['details']): + ... print("[{}]".format(path)) + ... print("{} directories".format(len(dirs))) + ... total = sum(info.size for info in files) + ... print("{} bytes {}".format(total)) - This method invokes :meth:`~fs.walk.Walker.walk` with bound FS - object. + This method invokes `Walker.walk` with bound `FS` object. """ walker = self._make_walker(**kwargs) @@ -428,89 +453,101 @@ def walk(self, __call__ = walk def files(self, path='/', **kwargs): - """ - Walk a filesystem, yielding absolute paths to files. - - :param str path: A path to a directory. - :param bool ignore_errors: If true, any errors reading a - directory will be ignored, otherwise exceptions will be - raised. - :param callable on_error: If ``ignore_errors`` is false, then - this callable will be invoked with a path and the exception - object. It should return True to ignore the error, or False - to re-raise it. - :param str search: If ``'breadth'`` then the directory will be - walked *top down*. Set to ``'depth'`` to walk *bottom up*. - :param list filter: If supplied, this parameter should be a list - of file name patterns, e.g. ``['*.py']``. Files will only be - returned if the final component matches one of the - patterns. - :param list exclude_dirs: A list of patterns that will be used - to filter out directories from the walk, e.g. ``['*.svn', - '*.git']``. - :returns: An iterable of file paths (absolute from the - filesystem root). - - This method invokes :meth:`~fs.walk.Walker.files` with the bound - FS object. + """Walk a filesystem, yielding absolute paths to files. + + Arguments: + path (str): A path to a directory. + + Keyword Arguments: + ignore_errors (bool): If `True`, any errors reading a + directory will be ignored, otherwise exceptions will be + raised. + on_error (callable): If ``ignore_errors`` is `False`, then + this callable will be invoked with a path and the exception + object. It should return `True` to ignore the error, or + `False` to re-raise it. + search (str): If ``'breadth'`` then the directory will be + walked *top down*. Set to ``'depth'`` to walk *bottom up*. + filter (list): If supplied, this parameter should be a list + of file name patterns, e.g. ``['*.py']``. Files will only be + returned if the final component matches one of the + patterns. + exclude_dirs (list): A list of patterns that will be used + to filter out directories from the walk, e.g. ``['*.svn', + '*.git']``. + + Returns: + ~collections.Iterable: An iterable of file paths (absolute + from the filesystem root). + + This method invokes `Walker.files` with the bound `FS` object. """ walker = self._make_walker(**kwargs) return walker.files(self.fs, path=path) def dirs(self, path='/', **kwargs): - """ - Walk a filesystem, yielding absolute paths to directories. + """Walk a filesystem, yielding absolute paths to directories. - :param str path: A path to a directory. - :param bool ignore_errors: If true, any errors reading a - directory will be ignored, otherwise exceptions will be - raised. - :param callable on_error: If ``ignore_errors`` is false, then - this callable will be invoked with a path and the exception - object. It should return True to ignore the error, or False - to re-raise it. - :param str search: If ``'breadth'`` then the directory will be - walked *top down*. Set to ``'depth'`` to walk *bottom up*. - :param list exclude_dirs: A list of patterns that will be used - to filter out directories from the walk, e.g. ``['*.svn', - '*.git']``. - :returns: An iterable of directory paths (absolute from the FS - root). + Arguments: + path (str): A path to a directory. + + Keyword Arguments: + ignore_errors (bool): If `True`, any errors reading a + directory will be ignored, otherwise exceptions will be + raised. + on_error (callable): If ``ignore_errors`` is `False`, then + this callable will be invoked with a path and the exception + object. It should return `True` to ignore the error, or + `False` to re-raise it. + search (str): If ``'breadth'`` then the directory will be + walked *top down*. Set to ``'depth'`` to walk *bottom up*. + exclude_dirs (list): A list of patterns that will be used + to filter out directories from the walk, e.g. ``['*.svn', + '*.git']``. - This method invokes :meth:`~fs.walk.Walker.dirs` with the bound - FS object. + Returns: + ~collections.iterable: an iterable of directory paths + (absolute from the filesystem root). + + This method invokes `Walker.dirs` with the bound `FS` object. """ walker = self._make_walker(**kwargs) return walker.dirs(self.fs, path=path) def info(self, path='/', namespaces=None, **kwargs): - """ - Walk a filesystem, yielding tuples of ``(, - )``. - - :param str path: A path to a directory. - :param bool ignore_errors: If true, any errors reading a - directory will be ignored, otherwise exceptions will be - raised. - :param callable on_error: If ``ignore_errors`` is false, then - this callable will be invoked with a path and the exception - object. It should return True to ignore the error, or False - to re-raise it. - :param str search: If ``'breadth'`` then the directory will be - walked *top down*. Set to ``'depth'`` to walk *bottom up*. - :param list filter: If supplied, this parameter should be a list - of file name patterns, e.g. ``['*.py']``. Files will only be - returned if the final component matches one of the - patterns. - :param list exclude_dirs: A list of patterns that will be used - to filter out directories from the walk, e.g. ``['*.svn', - '*.git']``. - :returns: An iterable :class:`~fs.info.Info` objects. - - This method invokes :meth:`~fs.walk.Walker.info` with the bound - FS object. + """Walk a filesystem, yielding path and `Info` of resources. + + Arguments: + path (str, optional): + namespaces (list, optional): A list of namespaces to include + in the resource information, e.g. ``['basic', 'access']`` + (defaults to ``['basic']``). + + Keyword Arguments: + ignore_errors (bool): If `True`, any errors reading a + directory will be ignored, otherwise exceptions will be + raised. + on_error (callable): If ``ignore_errors`` is `False`, then + this callable will be invoked with a path and the exception + object. It should return `True` to ignore the error, or + `False` to re-raise it. + search (str): If ``'breadth'`` then the directory will be + walked *top down*. Set to ``'depth'`` to walk *bottom up*. + filter (list): If supplied, this parameter should be a list + of file name patterns, e.g. ``['*.py']``. Files will only be + returned if the final component matches one of the + patterns. + exclude_dirs (list): A list of patterns that will be used + to filter out directories from the walk, e.g. ``['*.svn', + '*.git']``. + + Returns: + ~collections.Iterable: an iterable yielding tuples of + ``(, )``. + + This method invokes `Walker.info` with the bound `FS` object. """ walker = self._make_walker(**kwargs) diff --git a/fs/wildcard.py b/fs/wildcard.py index a32da1e1..adf4dc3a 100644 --- a/fs/wildcard.py +++ b/fs/wildcard.py @@ -1,4 +1,5 @@ -"""Match wildcard filenames.""" +"""Match wildcard filenames. +""" # Adapted from https://hg.python.org/cpython/file/2.7/Lib/fnmatch.py from __future__ import unicode_literals @@ -13,12 +14,14 @@ def match(pattern, name): - """ - Test whether ``name`` matches ``pattern``. + """Test whether a name matches a wildcard pattern. + + Arguments: + pattern (str): A wildcard pattern, e.g. ``"*.py"``. + name (bool): A filename. - :param str pattern: A wildcard pattern. e.g. ``"*.py"`` - :param str name: A filename - :rtype: bool + Returns: + bool: `True` if the filename matches the pattern. """ try: @@ -30,13 +33,14 @@ def match(pattern, name): def imatch(pattern, name): - """ - Test whether ``name`` matches ``pattern``, ignoring - case differences. + """Test whether a name matches a wildcard pattern (case insensitive). - :param str pattern: A wildcard pattern. e.g. ``"*.py"`` - :param str name: A filename - :rtype: bool + Arguments: + pattern (str): A wildcard pattern, e.g. ``"*.py"``. + name (bool): A filename. + + Returns: + bool: `True` if the filename matches the pattern. """ try: @@ -49,14 +53,17 @@ def imatch(pattern, name): def match_any(patterns, name): - """ - Test if a name matches at least one of a list of patterns. Will - return ``True`` if ``patterns`` is an empty list. + """Test if a name matches any of a list of patterns. - :param list patterns: A list of wildcard pattern. e.g. ``["*.py", - "*.pyc"]`` - :param str name: A filename. - :rtype: bool + Will return `True` if ``patterns`` is an empty list. + + Arguments: + patterns (list): A list of wildcard pattern, e.g ``["*.py", + "*.pyc"]`` + name (str): A filename. + + Returns: + bool: `True` if the name matches at least one of the patterns. """ if not patterns: @@ -65,15 +72,17 @@ def match_any(patterns, name): def imatch_any(patterns, name): - """ - Test if a name matches at least one of a list of patterns, ignoring - case differences. Will return ``True`` if ``patterns`` is an empty - list. + """Test if a name matches any of a list of patterns (case insensitive). + + Will return `True` if ``patterns`` is an empty list. - :param list patterns: A list of wildcard pattern. e.g. ``["*.py", - "*.pyc"]`` - :param str name: A filename. - :rtype: bool + Arguments: + patterns (list): A list of wildcard pattern, e.g ``["*.py", + "*.pyc"]`` + name (str): A filename. + + Returns: + bool: `True` if the name matches at least one of the patterns. """ if not patterns: @@ -82,24 +91,25 @@ def imatch_any(patterns, name): def get_matcher(patterns, case_sensitive): - """ - Get a callable that checks a list of names matches the given - wildcard patterns. - - :param list patterns: A list of wildcard pattern. e.g. ``["*.py", - "*.pyc"]`` - :param bool case_sensitive: If True, then the callable will be case - sensitive, otherwise it will be case insensitive. - :rtype: callable - - Here's an example:: - - >>> import wildcard - >>> is_python = wildcard.get_macher(['*.py']) - >>> is_python('__init__.py') - >>> True - >>> is_python('foo.txt') - >>> False + """Get a callable that matches names against the given patterns. + + Arguments: + patterns (list): A list of wildcard pattern. e.g. ``["*.py", + "*.pyc"]`` + case_sensitive (bool): If `True`, then the callable will be case + sensitive, otherwise it will be case insensitive. + + Returns: + callable: a matcher that will return `True` if the name given as + an argument matches any of the given patterns. + + Example: + >>> from fs import wildcard + >>> is_python = wildcard.get_matcher(['*.py'], True) + >>> is_python('__init__.py') + True + >>> is_python('foo.txt') + False """ if not patterns: @@ -111,11 +121,18 @@ def get_matcher(patterns, case_sensitive): def _translate(pattern, case_sensitive=True): - """ - Translate a shell PATTERN to a regular expression. + """Translate a wildcard pattern to a regular expression. There is no way to quote meta-characters. + Arguments: + pattern (str): A wildcard pattern. + case_sensitive (bool, optional): Set to `False` to use a + case insensitive regex (default `True`). + + Returns: + str: A regex equivalent to the given pattern. + """ if not case_sensitive: pattern = pattern.lower() diff --git a/fs/wrap.py b/fs/wrap.py index ab01ca21..620fa67f 100644 --- a/fs/wrap.py +++ b/fs/wrap.py @@ -1,21 +1,15 @@ -""" -fs.wrap -======= - -A collection of :class:`~fs.wrapfs.WrapFS` objects that modify the -behavior of another filesystem. +"""Collection of useful `~fs.wrapfs.WrapFS` subclasses. Here's an example that opens a filesystem then makes it *read only*:: - from fs import open_fs - from fs.wrap import read_only - - projects_fs = open_fs('~/projects') - read_only_projects_fs = read_only(projects_fs) - - # Will raise ResourceReadOnly exception - read_only_projects_fs.remove('__init__.py') - + >>> from fs import open_fs + >>> from fs.wrap import read_only + >>> projects_fs = open_fs('~/projects') + >>> read_only_projects_fs = read_only(projects_fs) + >>> read_only_projects_fs.remove('__init__.py') + Traceback (most recent call last): + ... + fs.errors.ResourceReadOnly: resource '__init__.py' is read only """ @@ -30,22 +24,26 @@ def read_only(fs): - """ - Make a read-only filesystem. + """Make a read-only filesystem. - :param fs: A FS object. - :returns: A read only version of ``fs``. + Arguments: + fs (FS): A filesystem instance. + + Returns: + FS: A read only version of ``fs`` """ return WrapReadOnly(fs) def cache_directory(fs): - """ - Make a filesystem that caches directory information. + """Make a filesystem that caches directory information. + + Arguments: + fs (FS): A filesystem instance. - :param fs: A FS object. - :returns: A filesystem that caches results of ``scandir``, ``isdir`` + Returns: + FS: A filesystem that caches results of `~FS.scandir``, `~FS.isdir` and other methods which read directory information. """ @@ -53,14 +51,13 @@ def cache_directory(fs): class WrapCachedDir(WrapFS): - """ - Caches filesystem directory information. + """Caches filesystem directory information. This filesystem caches directory information retrieved from a - scandir call. This *may* speed up code that calls ``isdir``, - ``isfile``, or ``gettype`` too frequently. + scandir call. This *may* speed up code that calls `~FS.isdir`, + `~FS.isfile`, or `~FS.gettype` too frequently. - .. note:: + Note: Using this wrap will prevent changes to directory information being visible to the filesystem object. Consequently it is best used only in a fairly limited scope where you don't expected @@ -118,10 +115,10 @@ def isfile(self, path): class WrapReadOnly(WrapFS): - """ - Makes a Filesystem read-only. Any call that would would write data - or modify the filesystem in any way will raise a - :class:`~fs.errors.ResourceReadOnly` exception. + """Makes a Filesystem read-only. + + Any call that would would write data or modify the filesystem in any way + will raise a `~fs.errors.ResourceReadOnly` exception. """ diff --git a/fs/wrapfs.py b/fs/wrapfs.py index eececfab..f2d34d05 100644 --- a/fs/wrapfs.py +++ b/fs/wrapfs.py @@ -1,3 +1,6 @@ +"""Base class for filesystem wrappers. +""" + from __future__ import unicode_literals import six @@ -13,12 +16,11 @@ @six.python_2_unicode_compatible class WrapFS(FS): - """" - A proxy for a filesystem object. + """A proxy for a filesystem object. This class exposes an filesystem interface, where the data is stored on another filesystem(s), and is the basis for - :class:`~fs.subfs.SubFS` and other *virtual* filesystems. + `~fs.subfs.SubFS` and other *virtual* filesystems. """ @@ -49,23 +51,22 @@ def __str__(self): return _str def delegate_path(self, path): - """ - Encode a path for proxied filesystem. + """Encode a path for proxied filesystem. - :param path: A path on the fileystem. - :type path: str - :returns: a tuple of , - :rtype: tuple + Arguments: + path (str): A path on the filesystem. + + Returns: + (FS, str): a tuple of ``(, )`` """ return self._wrap_fs, path def delegate_fs(self): - """ - Get the filesystem. + """Get the proxied filesystem. This method should return a filesystem for methods not - associated with a path, e.g. :meth:`~fs.base.FS.getmeta`. + associated with a path, e.g. `~fs.base.FS.getmeta`. """ return self._wrap_fs diff --git a/fs/zipfs.py b/fs/zipfs.py index a2eb38ff..b1d122d5 100644 --- a/fs/zipfs.py +++ b/fs/zipfs.py @@ -1,3 +1,6 @@ +"""Manage the filesystem in a Zip archive. +""" + from __future__ import print_function from __future__ import unicode_literals @@ -20,15 +23,14 @@ class ZipFS(WrapFS): - """ - Read and write zip files. + """Read and write zip files. There are two ways to open a ZipFS for the use cases of reading a zip file, and creating a new one. - If you open the ZipFS with ``write`` set to ``False`` (the - default), then the filesystem will be a read only filesystem which - maps to the files and directories within the zip file. Files are + If you open the ZipFS with ``write`` set to `False` (the default) + then the filesystem will be a read only filesystem which maps to + the files and directories within the zip file. Files are decompressed on the fly when you open them. Here's how you might extract and print a readme from a zip file:: @@ -36,7 +38,7 @@ class ZipFS(WrapFS): with ZipFS('foo.zip') as zip_fs: readme = zip_fs.gettext('readme.txt') - If you open the ZipFS with ``write`` set to ``True``, then the ZipFS + If you open the ZipFS with ``write`` set to `True`, then the ZipFS will be a empty temporary filesystem. Any files / directories you create in the ZipFS will be written in to a zip file when the ZipFS is closed. @@ -51,17 +53,14 @@ class ZipFS(WrapFS): ) - :param file: An OS filename, or a open file object. - :type file: str or file - :param write: Set to ``True`` to write a new zip file, or ``False`` - to read an existing zip file. - :type write: bool - :param compression: Compression to use (one of the constants - defined in the zipfile module in the stdlib). - :type compression: int - :param temp_fs: An opener string for the temporary filesystem - used to store data prior to zipping. - :type temp_fs: str + Arguments: + file (str or io.IOBase): An OS filename, or an open file object. + write (bool, optional): Set to `True` to write a new zip file, or + `False` (default) to read an existing zip file. + compression (str, optional): Compression to use (one of the constants + defined in the `zipfile` module in the stdlib). + temp_fs (str, optional): An FS URL for the temporary + filesystem used to store data prior to zipping. """ @@ -84,7 +83,8 @@ def __new__(cls, @six.python_2_unicode_compatible class WriteZipFS(WrapFS): - """A writable zip file.""" + """A writable zip file. + """ def __init__(self, file, @@ -126,18 +126,20 @@ def close(self): super(WriteZipFS, self).close() def write_zip(self, file=None, compression=None, encoding=None): - """ - Write zip to a file. - - .. note :: + """Write zip to a file. + + Arguments: + file (str or io.IOBase, optional): Destination file, may be + a file name or an open file handle. + compression (str, optional): Compression to use (one of the + constants defined in the `zipfile` module in the stdlib). + encoding (str, optional): The character encoding to use + (default uses the encoding defined in + `~WriteZipFS.__init__`). + + Note: This is called automatically when the ZipFS is closed. - :param file: Destination file, may be a file name or an open - file object. - :type file: str or file-like - :param compression: Compression to use (one of the constants - defined in the zipfile module in the stdlib). - """ if not self.isclosed(): write_zip( @@ -150,7 +152,8 @@ def write_zip(self, file=None, compression=None, encoding=None): @six.python_2_unicode_compatible class ReadZipFS(FS): - """A readable zip file.""" + """A readable zip file. + """ _meta = { 'case_insensitive': True, @@ -176,7 +179,8 @@ def __str__(self): return "".format(self._file) def _path_to_zip_name(self, path): - """Convert a path to a zip file name.""" + """Convert a path to a zip file name. + """ if self._directory.isdir(path): return relpath(normpath(path)) + '/' else: @@ -184,10 +188,7 @@ def _path_to_zip_name(self, path): @property def _directory(self): - """ - Make a memory filesystem with the same directory structure - as the zip. - + """`MemoryFS`: a filesystem with the same folder hierarchy as the zip. """ self.check() with self._lock: diff --git a/setup.cfg b/setup.cfg index 2c038eed..1730eb2a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,8 @@ [bdist_wheel] universal = 1 -[coverage:run] +[pydocstyle] +inherit = false +ignore = D102,D105,D200,D203,D213,D406,D407 +match-dir = (?!tests)(?!docs)[^\.].* +match = (?!test)(?!setup)[^\._].*\.py