Skip to content

Commit

Permalink
added appfs
Browse files Browse the repository at this point in the history
  • Loading branch information
willmcgugan committed Nov 20, 2016
1 parent 606483e commit c47f209
Show file tree
Hide file tree
Showing 17 changed files with 249 additions and 55 deletions.
1 change: 1 addition & 0 deletions docs/source/builtin.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ Builtin Filesystems
.. toctree::
:maxdepth: 3

reference/appfs.rst
reference/ftpfs.rst
reference/memoryfs.rst
reference/mountfs.rst
Expand Down
2 changes: 2 additions & 0 deletions docs/source/reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ Reference
reference/errors.rst
reference/info_objects.rst
reference/move.rst
reference/mode.rst
reference/opener.rst
reference/path.rst
reference/permissions.rst
reference/tools.rst
reference/tree.rst
reference/walk.rst
reference/wildcard.rst
7 changes: 7 additions & 0 deletions docs/source/reference/appfs.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
App Filesystems
===============

Filesystems for platform specific application directories.

.. automodule:: fs.appfs
:members:
5 changes: 5 additions & 0 deletions docs/source/reference/mode.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fs.mode
=======

.. automodule:: fs.mode
:members:
5 changes: 5 additions & 0 deletions docs/source/reference/tools.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fs.tools
========

.. automodule:: fs.tools
:members:
11 changes: 6 additions & 5 deletions fs/_repr.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from __future__ import unicode_literals

def make_repr(class_name, args):
def make_repr(class_name, *args, **kwargs):
"""Generate a repr string."""
arguments = [
"{!s}={!r}".format(name, value)
for name, value, default in args
arguments = [repr(arg) for arg in args]
arguments.extend([
"{}={!r}".format(name, value)
for name, (value, default) in kwargs.items()
if value != default
]
])
return "{}({})".format(class_name, ', '.join(arguments))
162 changes: 162 additions & 0 deletions fs/appfs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
"""
A collection of filesystems that map to application specific locations
defined by the OS.
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`.
"""
# Thanks to authors of https://pypi.python.org/pypi/appdirs

# see http://technet.microsoft.com/en-us/library/cc766489(WS.10).aspx

from .osfs import OSFS
from ._repr import make_repr
from appdirs import AppDirs

__all__ = ['UserDataFS',
'UserConfigFS',
'SiteDataFS',
'SiteConfigFS',
'UserCacheFS',
'UserLogFS']


class _AppFS(OSFS):
"""
Abstract base class for an app FS.
"""
app_dir = None

def __init__(self,
appname,
author=None,
version=None,
roaming=False,
create=True):
self.app_dirs = AppDirs(appname, author, version, roaming)
self.create = create
super(_AppFS, self).__init__(
getattr(self.app_dirs, self.app_dir),
create=create
)

def __repr__(self):
return make_repr(
self.__class__.__name__,
self.app_dirs.appname,
author=(self.app_dirs.appauthor, None),
version=(self.app_dirs.version, None),
roaming=(self.app_dirs.roaming, False),
create=(self.create, True)
)

def __str__(self):
return "<{} '{}'>".format(
self.__class__.__name__.lower(),
self.app_dirs.appname
)

class UserDataFS(_AppFS):
"""
A filesystem for per-user application data.
: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.
"""
app_dir = 'user_data_dir'


class UserConfigFS(_AppFS):
"""
A filesystem for per-user config data.
: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, see http://technet.microsoft.com/en-
us/library/cc766489(WS.10).aspx
:param bool create: 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.
: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.
"""
app_dir = 'user_cache_dir'


class SiteDataFS(_AppFS):
"""
A filesystem for application site data.
: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.
"""
app_dir = 'site_data_dir'


class SiteConfigFS(_AppFS):
"""
A filesystem for application config data.
: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.
"""
app_dir = 'site_config_dir'


class UserLogFS(_AppFS):
"""
A filesystem for per-user application log data.
: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.
"""
app_dir = 'user_log_dir'
12 changes: 6 additions & 6 deletions fs/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,13 @@ def setinfo(self, path, info):
The ``info`` dict should be in the same format as the raw
info returned by ``getinfo(file).raw``. Here's an example::
details_info = {
"details":
{
"modified_time": time.time()
details_info = {
"details":
{
"modified_time": time.time()
}
}
}
my_fs.setinfo('file.txt', details_info)
my_fs.setinfo('file.txt', details_info)
"""
raise NotImplementedError('setinfo')
Expand Down
7 changes: 4 additions & 3 deletions fs/compress.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ def write_zip(src_fs,
encoding="utf-8",
walker=None):
"""
Write a filesystem to a zip file.
Write the contents of a filesystem to a zip file.
:param file: Destination file, may be a file name or an open file
object.
Expand All @@ -38,8 +38,9 @@ def write_zip(src_fs,
desired.
:type encoding: str
:param walker: A :class:`fs.walk.Walker` instance, or None to use
default walker.
:type walker: walker or None
default walker. You can use this to specify which files you
want to compress.
:type walker: Walker or None
"""
_zip = zipfile.ZipFile(
Expand Down
22 changes: 15 additions & 7 deletions fs/mode.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,11 @@
# https://docs.python.org/3/library/functions.html#open
@six.python_2_unicode_compatible
class Mode(object):

"""
Mode objects provide
A mode object provides properties that can be used to interrogate
the mode strings used when opening files.
:param mode: A *mode* as used by ``open``.
:type mode: str
:param str mode: A *mode* as used by ``open``.
:raises ValueError: If the mode string is invalid.
"""
Expand All @@ -34,8 +32,9 @@ def __repr__(self):
def __str__(self):
return self._mode

def __contains__(self, c):
return c in self._mode
def __contains__(self, character):
"""Check if a mode contains a given character."""
return character in self._mode

def to_platform(self):
"""
Expand Down Expand Up @@ -95,38 +94,47 @@ def validate_bin(self):

@property
def create(self):
"""Check if the mode would create a file."""
return 'w' in self or 'x' in self

@property
def reading(self):
"""Check if the mode permits reading."""
return 'r' in self or '+' in self

@property
def writing(self):
"""Check if a 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."""
return 'a' in self

@property
def updating(self):
"""Check if a mode permits updating (reading and writing)."""
return '+' in self

@property
def truncate(self):
"""Check if a mode would truncate the file."""
return 'w' in self or 'x' in self

@property
def exclusive(self):
"""Check if the mode require exclusive creation."""
return 'x' in self

@property
def binary(self):
"""Check if a mode specifies binary."""
return 'b' in self

@property
def text(self):
"""Check if a mode specifies text."""
return 't' in self or 'b' not in self


Expand Down
23 changes: 13 additions & 10 deletions fs/osfs.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,15 @@ def __init__(self,
os.pathconf(_root_path, os.pathconf_names['PC_PATH_MAX'])

def __repr__(self):
return "OSFS({!r}, encoding={!r})".format(self.root_path,
self.encoding)
_fmt = "{}({!r}, encoding={!r})"
return _fmt.format(self.__class__.__name__,
self.root_path,
self.encoding)

def __str__(self):
fmt = "<osfs '{}'>"
return fmt.format(self.root_path)
fmt = "<{} '{}'>"
return fmt.format(self.__class__.__name__.lower(),
self.root_path)

def _to_sys_path(self, path):
"""Convert a FS path to a path on the OS."""
Expand All @@ -113,23 +116,23 @@ def _to_sys_path(self, path):
return sys_path

@classmethod
def _make_details_from_stat(cls, stat):
def _make_details_from_stat(cls, stat_result):
"""Make an info dict from a stat_result object."""
details = {
'_write': ['accessed', 'modified'],
'accessed': stat.st_atime,
'modified': stat.st_mtime,
'size': stat.st_size,
'accessed': stat_result.st_atime,
'modified': stat_result.st_mtime,
'size': stat_result.st_size,
'type': int(cls._get_type_from_stat(stat))
}
if hasattr(stat, 'st_birthtime'):
details['created'] = stat.st_birthtime
details['created'] = stat_result.st_birthtime
ctime_key = (
'created'
if _WINDOWS_PLATFORM
else 'metadata_changed'
)
details[ctime_key] = stat.st_ctime
details[ctime_key] = stat_result.st_ctime
return details

@classmethod
Expand Down

0 comments on commit c47f209

Please sign in to comment.