With a little care, you can implement a PyFilesystem interface for any filesystem, which will allow it to work interchangeably with any of the built-in FS classes and tools.
To create a PyFilesystem interface, derive a class from :class:`~fs.base.FS` and implement the :ref:`essential-methods`. This should give you a working FS class.
Take care to copy the method signatures exactly, including default values. It is also essential that you follow the same logic with regards to exceptions, and only raise exceptions in :mod:`~fs.errors`.
There are no particular requirements regarding how a PyFilesystem class is constructed, but be sure to call the base class __init__
method with no parameters.
All Filesystems should be thread-safe. The simplest way to achieve that is by using the _lock
attribute supplied by the :class:`~fs.base.FS` constructor. This is a RLock
object from the standard library, which you can use as a context manager, so methods you implement will start something like this:
with self._lock: do_something()
You aren't required to use _lock
. Just as long as calling methods on the FS object from multiple threads doesn't break anything.
PyFilesystem supports Python2.7 and Python3.X. The differences between the two major Python versions are largely managed by the six
library.
You aren't obligated to support the same versions of Python that PyFilesystem itself supports, but it is recommended if your project is for general use.
To test your implementation, you can borrow the test suite used to test the built in filesystems. If your code passes these tests, then you can be confident your implementation will work seamlessly.
Here's the simplest possible example to test a filesystem class called MyFS
:
from fs.test import FSTestCases class TestMyFS(FSTestCases): def make_fs(self): # Return an instance of your FS object here return MyFS()
You may also want to override some of the methods in the test suite for more targeted testing:
.. autoclass:: fs.test.FSTestCases :members:
The following methods MUST be implemented in a PyFilesystem interface.
- :meth:`~fs.base.FS.getinfo` Get info regarding a file or directory.
- :meth:`~fs.base.FS.listdir` Get a list of resources in a directory.
- :meth:`~fs.base.FS.makedir` Make a directory.
- :meth:`~fs.base.FS.openbin` Open a binary file.
- :meth:`~fs.base.FS.remove` Remove a file.
- :meth:`~fs.base.FS.removedir` Remove a directory.
- :meth:`~fs.base.FS.setinfo` Set resource information.
The following methods MAY be implemented in a PyFilesystem interface.
These methods have a default implementation in the base class, but may be overridden if you can supply a more optimal version.
Exactly which methods you should implement depends on how and where the data is stored. For network filesystems, a good candidate to implement, is the scandir
method which would otherwise call a combination of listdir
and getinfo
for each file.
In the general case, it is a good idea to look at how these methods are implemented in :class:`~fs.base.FS`, and only write a custom version if it would be more efficient than the default.
- :meth:`~fs.base.FS.appendbytes`
- :meth:`~fs.base.FS.appendtext`
- :meth:`~fs.base.FS.close`
- :meth:`~fs.base.FS.copy`
- :meth:`~fs.base.FS.copydir`
- :meth:`~fs.base.FS.create`
- :meth:`~fs.base.FS.desc`
- :meth:`~fs.base.FS.download`
- :meth:`~fs.base.FS.exists`
- :meth:`~fs.base.FS.filterdir`
- :meth:`~fs.base.FS.getmeta`
- :meth:`~fs.base.FS.getsize`
- :meth:`~fs.base.FS.getsyspath`
- :meth:`~fs.base.FS.gettype`
- :meth:`~fs.base.FS.geturl`
- :meth:`~fs.base.FS.hassyspath`
- :meth:`~fs.base.FS.hasurl`
- :meth:`~fs.base.FS.isclosed`
- :meth:`~fs.base.FS.isempty`
- :meth:`~fs.base.FS.isfile`
- :meth:`~fs.base.FS.lock`
- :meth:`~fs.base.FS.makedirs`
- :meth:`~fs.base.FS.move`
- :meth:`~fs.base.FS.movedir`
- :meth:`~fs.base.FS.open`
- :meth:`~fs.base.FS.opendir`
- :meth:`~fs.base.FS.readbytes`
- :meth:`~fs.base.FS.readtext`
- :meth:`~fs.base.FS.removetree`
- :meth:`~fs.base.FS.scandir`
- :meth:`~fs.base.FS.settimes`
- :meth:`~fs.base.FS.touch`
- :meth:`~fs.base.FS.upload`
- :meth:`~fs.base.FS.validatepath`
- :meth:`~fs.base.FS.writebytes`
- :meth:`~fs.base.FS.writefile`
- :meth:`~fs.base.FS.writetext`
These methods SHOULD NOT be implemented.
Implementing these is highly unlikely to be worthwhile.