Commit c56e8ae6 by willmcgugan

Fixes and documentation

parent 462723b6
Concepts
========
It is generally quite easy to get in to the mind-set of using PyFilesystem interface over lower level interfaces (since the code tends to be simpler) but there are a few concepts which you will need to keep in mind.
Working with PyFilesystem is generally easier than working with lower level interfaces, as long as you are aware these simple concepts.
Sandboxing
----------
......@@ -25,7 +25,9 @@ We can open the `foo` directory with the following code::
from fs.osfs import OSFS
foo_fs = OSFS('foo')
The `foo_fs` object can work with any of the contents of `bar` and `baz`, which may not be desirable, especially if we are passing `foo_fs` to an untrusted function or one that could potentially delete files. Fortunately we can isolate a single sub-directory with then :meth:`~fs.base.FS.opendir` method::
The `foo_fs` object can work with any of the contents of `bar` and `baz`, which may not be desirable,
especially if we are passing `foo_fs` to an untrusted function or to a function that has the potential to delete files.
Fortunately we can isolate a single sub-directory with then :meth:`~fs.base.FS.opendir` method::
bar_fs = foo_fs.opendir('bar')
......@@ -54,7 +56,7 @@ When working with paths in FS objects, keep in mind the following:
* A double dot means 'previous directory'
Note that paths used by the FS interface will use this format, but the constructor or additional methods may not.
Notably the :mod:`~fs.osfs.OSFS` constructor which requires an OS path -- the format of which can be platform-dependent.
Notably the :mod:`~fs.osfs.OSFS` constructor which requires an OS path -- the format of which is platform-dependent.
There are many helpful functions for working with paths in the :mod:`fs.path` module.
......
......@@ -14,13 +14,21 @@ Add the -U switch if you want to upgrade a previous installation::
easy_install -U fs
This will install the latest stable release. If you would prefer to install the cutting edge release then you can get the latest copy of the source via SVN::
If you prefer to use Pip (http://pypi.python.org/pypi/pip) to install Python packages, the procedure is much the same::
pip install fs
Or to upgrade::
pip install fs --upgrade
You can also install the cutting edge release by checking out the source via SVN::
svn checkout http://pyfilesystem.googlecode.com/svn/trunk/ pyfilesystem-read-only
cd pyfilesystem-read-only
python setup.py install
You should now have the `fs` module on your path:
Whichever method you use, you should now have the `fs` module on your path (version number may vary)::
>>> import fs
>>> fs.__version__
......
......@@ -205,15 +205,15 @@ class FS(object):
# type of lock that should be there. None == no lock,
# True == a proper lock, False == a dummy lock.
state = self.__dict__.copy()
lock = state.get("_lock",None)
lock = state.get("_lock", None)
if lock is not None:
if isinstance(lock,threading._RLock):
if isinstance(lock, threading._RLock):
state["_lock"] = True
else:
state["_lock"] = False
return state
def __setstate__(self,state):
def __setstate__(self, state):
self.__dict__.update(state)
lock = state.get("_lock")
if lock is not None:
......@@ -336,26 +336,39 @@ class FS(object):
"""Open a the given path as a file-like object.
:param path: a path to file that should be opened
:type path: string
:param mode: mode of file to open, identical to the mode string used
in 'file' and 'open' builtins
:type mode: string
:param kwargs: additional (optional) keyword parameters that may
be required to open the file
:type kwargs: dict
:rtype: a file-like object
:raises `fs.errors.ParentDirectoryMissingError`: if an intermediate directory is missing
:raises `fs.errors.ResourceInvalidError`: if an intermediate directory is an file
:raises `fs.errors.ResourceNotFoundError`: if the path is not found
"""
raise UnsupportedError("open file")
def safeopen(self, path, mode="r", **kwargs):
"""Like :py:meth:`~fs.base.FS.open`, but returns a :py:class:`~fs.base.NullFile` if the file could not be opened.
"""Like :py:meth:`~fs.base.FS.open`, but returns a
:py:class:`~fs.base.NullFile` if the file could not be opened.
A ``NullFile`` is a dummy file which has all the methods of a file-like object,
but contains no data.
:param path: a path to file that should be opened
:type path: string
:param mode: mode of file to open, identical to the mode string used
in 'file' and 'open' builtins
:type mode: string
:param kwargs: additional (optional) keyword parameters that may
be required to open the file
:type kwargs: dict
:rtype: a file-like object
"""
......@@ -369,6 +382,8 @@ class FS(object):
"""Check if a path references a valid resource.
:param path: A path in the filesystem
:type path: string
:rtype: bool
"""
......@@ -378,6 +393,8 @@ class FS(object):
"""Check if a path references a directory.
:param path: a path in the filesystem
:type path: string
:rtype: bool
"""
......@@ -387,6 +404,8 @@ class FS(object):
"""Check if a path references a file.
:param path: a path in the filesystem
:type path: string
:rtype: bool
"""
......@@ -419,10 +438,12 @@ class FS(object):
:type dirs_only: bool
:param files_only: if True, only return files
:type files_only: bool
:rtype: iterable of paths
:raises `fs.errors.ResourceNotFoundError`: if the path is not found
:raises `fs.errors.ParentDirectoryMissingError`: if an intermediate directory is missing
:raises `fs.errors.ResourceInvalidError`: if the path exists, but is not a directory
:raises `fs.errors.ResourceNotFoundError`: if the path is not found
"""
raise UnsupportedError("list directory")
......@@ -460,7 +481,7 @@ class FS(object):
if full or absolute:
return self.getinfo(p)
else:
return self.getinfo(pathjoin(path,p))
return self.getinfo(pathjoin(path, p))
except FSError:
return {}
......@@ -553,6 +574,7 @@ class FS(object):
"""Make a directory on the filesystem.
:param path: path of directory
:type path: string
:param recursive: if True, any intermediate directories will also be created
:type recursive: bool
:param allow_recreate: if True, re-creating a directory wont be an error
......@@ -561,6 +583,7 @@ class FS(object):
:raises `fs.errors.DestinationExistsError`: if the path is already a directory, and allow_recreate is False
:raises `fs.errors.ParentDirectoryMissingError`: if a containing directory is missing and recursive is False
:raises `fs.errors.ResourceInvalidError`: if a path is an existing file
:raises `fs.errors.ResourceNotFoundError`: if the path is not found
"""
raise UnsupportedError("make directory")
......@@ -569,9 +592,11 @@ class FS(object):
"""Remove a file from the filesystem.
:param path: Path of the resource to remove
:type path: string
:raises `fs.errors.ResourceNotFoundError`: if the path does not exist
:raises `fs.errors.ParentDirectoryMissingError`: if an intermediate directory is missing
:raises `fs.errors.ResourceInvalidError`: if the path is a directory
:raises `fs.errors.ResourceNotFoundError`: if the path does not exist
"""
raise UnsupportedError("remove resource")
......@@ -580,14 +605,16 @@ class FS(object):
"""Remove a directory from the filesystem
:param path: path of the directory to remove
:type path: string
:param recursive: if True, empty parent directories will be removed
:type recursive: bool
:param force: if True, any directory contents will be removed
:type force: bool
:raises `fs.errors.ResourceNotFoundError`: if the path does not exist
:raises `fs.errors.ResourceInvalidError`: if the path is not a directory
:raises `fs.errors.DirectoryNotEmptyError`: if the directory is not empty and force is False
:raises `fs.errors.ParentDirectoryMissingError`: if an intermediate directory is missing
:raises `fs.errors.ResourceInvalidError`: if the path is not a directory
:raises `fs.errors.ResourceNotFoundError`: if the path does not exist
"""
raise UnsupportedError("remove directory")
......@@ -596,7 +623,15 @@ class FS(object):
"""Renames a file or directory
:param src: path to rename
:type src: string
:param dst: new name
:type dst: string
:raises ParentDirectoryMissingError: if a containing directory is missing
:raises ResourceInvalidError: if the path or a parent path is not a
directory or src is a parent of dst or one of src or dst is a dir
and the other don't
:raises ResourceNotFoundError: if the src path does not exist
"""
raise UnsupportedError("rename resource")
......@@ -606,8 +641,11 @@ class FS(object):
"""Set the accessed time and modified time of a file
:param path: path to a file
:param accessed_time: a datetime object the file was accessed (defaults to current time)
:param modified_time: a datetime object the file was modified (defaults to current time)
:type path: string
:param accessed_time: the datetime the file was accessed (defaults to current time)
:type accessed_time: datetime
:param modified_time: the datetime the file was modified (defaults to current time)
:type modified_time: datetime
"""
......@@ -637,8 +675,14 @@ class FS(object):
* "modified_time" - A datetime object containing the time the resource was modified
:param path: a path to retrieve information for
:type path: string
:rtype: dict
:raises `fs.errors.ParentDirectoryMissingError`: if an intermediate directory is missing
:raises `fs.errors.ResourceInvalidError`: if the path is not a directory
:raises `fs.errors.ResourceNotFoundError`: if the path does not exist
"""
raise UnsupportedError("get resource info")
......@@ -675,7 +719,7 @@ class FS(object):
if f is not None:
f.close()
def setcontents(self, path, data, chunk_size=1024*64):
def setcontents(self, path, data, chunk_size=1024 * 64):
"""A convenience method to create a new file from a string or file-like object
:param path: a path of the file to create
......@@ -708,7 +752,7 @@ class FS(object):
def setcontents_async(self,
path,
data,
chunk_size=1024*64,
chunk_size=1024 * 64,
progress_callback=None,
finished_callback=None,
error_callback=None):
......@@ -795,6 +839,9 @@ class FS(object):
"""Opens a directory and returns a FS object representing its contents.
:param path: path to directory to open
:type path: string
:return: the opened dir
:rtype: an FS object
"""
......@@ -815,6 +862,7 @@ class FS(object):
contents.
:param path: root path to start walking
:type path: string
:param wildcard: if given, only return files that match this wildcard
:type wildcard: a string containing a wildcard (e.g. `*.txt`) or a callable that takes the file path and returns a boolean
:param dir_wildcard: if given, only walk directories that match the wildcard
......@@ -825,6 +873,9 @@ class FS(object):
* ``"depth"`` yields the deepest paths first
:param ignore_errors: ignore any errors reading the directory
:type ignore_errors: bool
:rtype: iterator of (current_path, paths)
"""
......@@ -893,16 +944,24 @@ class FS(object):
wildcard=None,
dir_wildcard=None,
search="breadth",
ignore_errors=False ):
ignore_errors=False):
"""Like the 'walk' method, but just yields file paths.
:param path: root path to start walking
:type path: string
:param wildcard: if given, only return files that match this wildcard
:type wildcard: A string containing a wildcard (e.g. `*.txt`) or a callable that takes the file path and returns a boolean
:param dir_wildcard: if given, only walk directories that match the wildcard
:type dir_wildcard: A string containing a wildcard (e.g. `*.txt`) or a callable that takes the directory name and returns a boolean
:param search: same as walk method
:param search: a string identifying the method used to walk the directories. There are two such methods:
* ``"breadth"`` yields paths in the top directories first
* ``"depth"`` yields the deepest paths first
:param ignore_errors: ignore any errors reading the directory
:type ignore_errors: bool
:rtype: iterator of file paths
"""
for path, files in self.walk(path, wildcard=wildcard, dir_wildcard=dir_wildcard, search=search, ignore_errors=ignore_errors):
......@@ -917,10 +976,18 @@ class FS(object):
"""Like the 'walk' method but yields directories.
:param path: root path to start walking
:type path: string
:param wildcard: if given, only return directories that match this wildcard
:type wildcard: A string containing a wildcard (e.g. `*.txt`) or a callable that takes the directory name and returns a boolean
:param search: same as the walk method
:param search: a string identifying the method used to walk the directories. There are two such methods:
* ``"breadth"`` yields paths in the top directories first
* ``"depth"`` yields the deepest paths first
:param ignore_errors: ignore any errors reading the directory
:type ignore_errors: bool
:rtype: iterator of dir paths
"""
for p, _files in self.walk(path, dir_wildcard=wildcard, search=search, ignore_errors=ignore_errors):
......@@ -931,8 +998,10 @@ class FS(object):
"""Returns the size (in bytes) of a resource.
:param path: a path to the resource
:rtype: integer
:type path: string
:returns: the size of the file
:rtype: integer
"""
info = self.getinfo(path)
......@@ -941,22 +1010,26 @@ class FS(object):
raise OperationFailedError("get size of resource", path)
return size
def copy(self, src, dst, overwrite=False, chunk_size=1024*64):
def copy(self, src, dst, overwrite=False, chunk_size=1024 * 64):
"""Copies a file from src to dst.
:param src: the source path
:type src: string
:param dst: the destination path
:type dst: string
:param overwrite: if True, then an existing file at the destination may
be overwritten; If False then DestinationExistsError
will be raised.
:type overwrite: bool
:param chunk_size: size of chunks to use if a simple copy is required
(defaults to 64K).
:type chunk_size: bool
"""
if not self.isfile(src):
if self.isdir(src):
raise ResourceInvalidError(src,msg="Source is not a file: %(path)s")
raise ResourceInvalidError(src, msg="Source is not a file: %(path)s")
raise ResourceNotFoundError(src)
if not overwrite and self.exists(dst):
raise DestinationExistsError(dst)
......@@ -985,7 +1058,7 @@ class FS(object):
shutil.copyfile(src_syspath, dst_syspath)
except IOError, e:
# shutil reports ENOENT when a parent directory is missing
if getattr(e,"errno",None) == 2:
if getattr(e, "errno", None) == 2:
if not os.path.exists(dirname(dst_syspath)):
raise ParentDirectoryMissingError(dst_syspath)
raise
......@@ -1000,10 +1073,9 @@ class FS(object):
"""moves a file from one location to another.
:param src: source path
:type src: string
:param dst: destination path
:param overwrite: if True, then an existing file at the destination path
will be silently overwritten; if False then an exception
will be raised in this case.
:type dst: string
:param overwrite: When True the destination will be overwritten (if it exists),
otherwise a DestinationExistsError will be thrown
:type overwrite: bool
......@@ -1011,6 +1083,8 @@ class FS(object):
is required
:type chunk_size: integer
:raise `fs.errors.DestinationExistsError`: if destination exists and `overwrite` is False
"""
src_syspath = self.getsyspath(src, allow_none=True)
......@@ -1037,13 +1111,20 @@ class FS(object):
"""moves a directory from one location to another.
:param src: source directory path
:type src: string
:param dst: destination directory path
:type dst: string
:param overwrite: if True then any existing files in the destination
directory will be overwritten
:type overwrite: bool
:param ignore_errors: if True then this method will ignore FSError
exceptions when moving files
:type ignore_errors: bool
:param chunk_size: size of chunks to use when copying, if a simple copy
is required
:type chunk_size: integer
:raise `fs.errors.DestinationExistsError`: if destination exists and `overwrite` is False
"""
if not self.isdir(src):
......@@ -1058,7 +1139,7 @@ class FS(object):
if src_syspath is not None and dst_syspath is not None:
try:
os.rename(src_syspath,dst_syspath)
os.rename(src_syspath, dst_syspath)
return
except OSError:
pass
......@@ -1097,7 +1178,9 @@ class FS(object):
"""copies a directory from one location to another.
:param src: source directory path
:type src: string
:param dst: destination directory path
:type dst: string
:param overwrite: if True then any existing files in the destination
directory will be overwritten
:type overwrite: bool
......@@ -1144,6 +1227,7 @@ class FS(object):
"""Check if a directory is empty (contains no files or sub-directories)
:param path: a directory path
:rtype: bool
"""
......@@ -1162,6 +1246,9 @@ class FS(object):
:param path: path to the new directory
:param recursive: if True any intermediate directories will be created
:return: the opened dir
:rtype: an FS object
"""
self.makedir(path, allow_recreate=True, recursive=recursive)
......
......@@ -19,10 +19,28 @@ class _SocketFile(object):
def write(self, data):
self.socket.sendall(data)
class ConnectionThread(threading.Thread):
def remote_call(method_name=None):
method = method_name
def deco(f):
if not hasattr(f, '_remote_call_names'):
f._remote_call_names = []
f._remote_call_names.append(method or f.__name__)
return f
return deco
class RemoteResponse(Exception):
def __init__(self, header, payload):
self.header = header
self.payload = payload
class ConnectionHandlerBase(threading.Thread):
_methods = {}
def __init__(self, server, connection_id, socket, address):
super(ConnectionThread, self).__init__()
super(ConnectionHandlerBase, self).__init__()
self.server = server
self.connection_id = connection_id
self.socket = socket
......@@ -34,6 +52,16 @@ class ConnectionThread(threading.Thread):
self._lock = threading.RLock()
self.socket_error = None
if not self._methods:
for method_name in dir(self):
method = getattr(self, method_name)
if callable(method) and hasattr(method, '_remote_call_names'):
for name in method._remote_call_names:
self._methods[name] = method
print self._methods
self.fs = None
def run(self):
......@@ -71,16 +99,36 @@ class ConnectionThread(threading.Thread):
print '-' * 30
print repr(header)
print repr(payload)
if header['method'] == 'ping':
self.encoder.write({'client_ref':header['client_ref']}, payload)
if header['type'] == 'rpc':
method = header['method']
args = header['args']
kwargs = header['kwargs']
method_callable = self._methods[method]
remote = dict(type='rpcresult',
client_ref = header['client_ref'])
try:
response = method_callable(*args, **kwargs)
remote['response'] = response
self.encoder.write(remote, '')
except RemoteResponse, response:
self.encoder.write(response.header, response.payload)
class RemoteFSConnection(ConnectionHandlerBase):
@remote_call()
def auth(self, username, password, resource):
self.username = username
self.password = password
self.resource = resource
from fs.memoryfs import MemoryFS
self.fs = MemoryFS()
class Server(object):
def __init__(self, addr='', port=3000):
def __init__(self, addr='', port=3000, connection_factory=RemoteFSConnection):
self.addr = addr
self.port = port
self.connection_factory = connection_factory
self.socket = None
self.connection_id = 0
self.threads = {}
......@@ -124,7 +172,7 @@ class Server(object):
print "Connection from", address
with self._lock:
self.connection_id += 1
thread = ConnectionThread(self,
thread = self.connection_factory(self,
self.connection_id,
clientsocket,
address)
......
......@@ -245,7 +245,10 @@ class SFTPRequestHandler(sockserv.StreamRequestHandler):
t.start_server(server=self.server)
class BaseSFTPServer(sockserv.TCPServer,paramiko.ServerInterface):
class ThreadedTCPServer(sockserv.TCPServer, sockserv.ThreadingMixIn):
pass
class BaseSFTPServer(ThreadedTCPServer, paramiko.ServerInterface):
"""SocketServer.TCPServer subclass exposing an FS via SFTP.
BaseSFTPServer combines a simple SocketServer.TCPServer subclass with an
......@@ -318,6 +321,7 @@ if __name__ == "__main__":
from fs.tempfs import TempFS
server = BaseSFTPServer(("localhost",8022),TempFS())
try:
#import rpdb2; rpdb2.start_embedded_debugger('password')
server.serve_forever()
except (SystemExit,KeyboardInterrupt):
server.server_close()
......
......@@ -1209,7 +1209,7 @@ class FTPFS(FS):
@ftperrors
def listdir(self, path="./", wildcard=None, full=False, absolute=False, dirs_only=False, files_only=False):
path = normpath(path)
self.clear_dircache(path)
#self.clear_dircache(path)
if not self.exists(path):
raise ResourceNotFoundError(path)
if not self.isdir(path):
......
......@@ -174,7 +174,7 @@ class OpenerRegistry(object):
for name in opener.names:
self.registry[name] = index
def parse(self, fs_url, default_fs_name=None, writeable=False, create_dir=False):
def parse(self, fs_url, default_fs_name=None, writeable=False, create_dir=False, cache_hint=True):
"""Parses a FS url and returns an fs object a path within that FS object
(if indicated in the path). A tuple of (<FS instance>, <path>) is returned.
......@@ -217,6 +217,7 @@ class OpenerRegistry(object):
raise OpenerError("Unable to parse '%s'" % orig_url)
fs, fs_path = opener.get_fs(self, fs_name, fs_name_params, fs_url, writeable, create_dir)
fs.cache_hint(cache_hint)
if fs_path and iswildcard(fs_path):
pathname, resourcename = pathsplit(fs_path or '')
......
......@@ -237,6 +237,7 @@ def splitext(path):
path = pathjoin(parent_path, pathname)
return path, '.' + ext
def isdotfile(path):
"""Detects if a path references a dot file, i.e. a resource who's name
starts with a '.'
......@@ -255,6 +256,7 @@ def isdotfile(path):
"""
return basename(path).startswith('.')
def dirname(path):
"""Returns the parent directory of a path.
......@@ -266,10 +268,14 @@ def dirname(path):
>>> dirname('foo/bar/baz')
'foo/bar'
>>> dirname('/foo/bar')
'/foo'
>>> dirname('/foo')
'/'
"""
if '/' not in path:
return ''
return path.rsplit('/', 1)[0]
return pathsplit(path)[0]
def basename(path):
......@@ -290,9 +296,7 @@ def basename(path):
''
"""
if '/' not in path:
return path
return path.rsplit('/', 1)[-1]
return pathsplit(path)[1]
def issamedir(path1, path2):
......@@ -309,11 +313,13 @@ def issamedir(path1, path2):
"""
return dirname(normpath(path1)) == dirname(normpath(path2))
def isbase(path1, path2):
p1 = forcedir(abspath(path1))
p2 = forcedir(abspath(path2))
return p1 == p2 or p1.startswith(p2)
def isprefix(path1, path2):
"""Return true is path1 is a prefix of path2.
......@@ -341,6 +347,7 @@ def isprefix(path1, path2):
return False
return True
def forcedir(path):
"""Ensure the path ends with a trailing /
......
# Work in Progress - Do not use
from __future__ import with_statement
from fs.base import FS
from fs.expose.serve import packetstream
......@@ -109,17 +109,38 @@ class _SocketFile(object):
self.socket.close()
class _RemoteFile(object):
def __init__(self, path, connection):
self.path = path
self.connection = connection
class RemoteFS(FS):
def __init__(self, addr='', port=3000, transport=None):
_meta = { 'thead_safe' : True,
'network' : True,
'virtual' : False,
'read_only' : False,
'unicode_paths' : True,
}
def __init__(self, addr='', port=3000, username=None, password=None, resource=None, transport=None):
self.addr = addr
self.port = port
self.username = None
self.password = None
self.resource = None
self.transport = transport
if self.transport is None:
self.transport = self._open_connection()
self.packet_handler = PacketHandler(self.transport)
self.packet_handler.start()
self._remote_call('auth',
username=username,
password=password,
resource=resource)
def _open_connection(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((self.addr, self.port))
......@@ -127,6 +148,19 @@ class RemoteFS(FS):
socket_file.write('pyfs/0.1\n')
return socket_file
def _make_call(self, method_name, *args, **kwargs):
call = dict(type='rpc',
method=method_name,
args=args,
kwargs=kwargs)
return call
def _remote_call(self, method_name, *args, **kwargs):
call = self._make_call(method_name, *args, **kwargs)
call_id = self.packet_handler.send_packet(call)
header, payload = self.packet_handler.get_packet(call_id)
return header, payload
def ping(self, msg):
call_id = self.packet_handler.send_packet({'type':'rpc', 'method':'ping'}, msg)
header, payload = self.packet_handler.get_packet(call_id)
......@@ -138,10 +172,17 @@ class RemoteFS(FS):
self.transport.close()
self.packet_handler.join()
def open(self, path, mode="r", **kwargs):
pass
def exists(self, path):
remote = self._remote_call('exists', path)
return remote.get('response')
if __name__ == "__main__":
rfs = RemoteFS()
rfs.ping("Hello, World!")
rfs.close()
\ No newline at end of file
......@@ -222,6 +222,8 @@ class SFTPFS(FS):
@property
@synchronize
def client(self):
if self.closed:
return None
client = getattr(self._tlocal, 'client', None)
if client is None:
if self._transport is None:
......@@ -242,9 +244,10 @@ class SFTPFS(FS):
def close(self):
"""Close the connection to the remote server."""
if not self.closed:
if self.client:
self.client.close()
if self._owns_transport and self._transport:
self._tlocal = None
#if self.client:
# self.client.close()
if self._owns_transport and self._transport and self._transport.is_active:
self._transport.close()
self.closed = True
......
......@@ -29,6 +29,22 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
port = 3000
self.temp_fs = TempFS()
self.server = None
self.serve_more_requests = True
self.server_thread = threading.Thread(target=self.runServer)
self.server_thread.setDaemon(True)
self.start_event = threading.Event()
self.end_event = threading.Event()
self.server_thread.start()
self.start_event.wait()
def runServer(self):
"""Run the server, swallowing shutdown-related execptions."""
port = 3000
while not self.server:
try:
self.server = self.makeServer(self.temp_fs,("127.0.0.1",port))
......@@ -37,37 +53,48 @@ class TestRPCFS(unittest.TestCase, FSTestCases, ThreadingTestCases):
port += 1
else:
raise
self.server_addr = ("127.0.0.1",port)
self.serve_more_requests = True
self.server_thread = threading.Thread(target=self.runServer)
self.server_thread.daemon = True
self.server_thread.start()
self.server_addr = ("127.0.0.1", port)
self.server.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
# if sys.platform != "win32":
# try:
# self.server.socket.settimeout(1)
# except socket.error:
# pass
#
self.start_event.set()
def runServer(self):
"""Run the server, swallowing shutdown-related execptions."""
if sys.platform != "win32":
try:
self.server.socket.settimeout(0.1)
except socket.error:
pass
try:
#self.server.serve_forever()
while self.serve_more_requests:
self.server.handle_request()
except Exception, e:
pass
self.end_event.set()
def setUp(self):
self.startServer()
self.fs = rpcfs.RPCFS("http://%s:%d" % self.server_addr)
def tearDown(self):
self.serve_more_requests = False
#self.server.socket.close()
# self.server.socket.shutdown(socket.SHUT_RDWR)
# self.server.socket.close()
# self.temp_fs.close()
#self.server_thread.join()
#self.end_event.wait()
#return
try:
self.bump()
self.server.server_close()
except Exception:
pass
self.server_thread.join()
#self.server_thread.join()
self.temp_fs.close()
def bump(self):
......
......@@ -105,7 +105,6 @@ class TestPathFunctions(unittest.TestCase):
self.assertEquals(recursepath("hello",reverse=True),["/hello","/"])
self.assertEquals(recursepath("",reverse=True),["/"])
def test_isdotfile(self):
for path in ['.foo',
'.svn',
......@@ -125,7 +124,9 @@ class TestPathFunctions(unittest.TestCase):
tests = [('foo', ''),
('foo/bar', 'foo'),
('foo/bar/baz', 'foo/bar'),
('/', '')]
('/foo/bar', '/foo'),
('/foo', '/'),
('/', '/')]
for path, test_dirname in tests:
self.assertEqual(dirname(path), test_dirname)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment