Commit 742ff4a6 by willmcgugan

Improved output for fsserve

parent 9076762b
...@@ -15,7 +15,7 @@ Serves the contents of PATH with one of a number of methods""" ...@@ -15,7 +15,7 @@ Serves the contents of PATH with one of a number of methods"""
optparse = super(FSServe, self).get_optparse() optparse = super(FSServe, self).get_optparse()
optparse.add_option('-t', '--type', dest='type', type="string", default="http", optparse.add_option('-t', '--type', dest='type', type="string", default="http",
help="Server type to create (http, rpc, sftp)", metavar="TYPE") help="Server type to create (http, rpc, sftp)", metavar="TYPE")
optparse.add_option('-a', '--addr', dest='addr', type="string", default="", optparse.add_option('-a', '--addr', dest='addr', type="string", default="localhost",
help="Server address", metavar="ADDR") help="Server address", metavar="ADDR")
optparse.add_option('-p', '--port', dest='port', type="int", optparse.add_option('-p', '--port', dest='port', type="int",
help="Port number", metavar="") help="Port number", metavar="")
...@@ -33,9 +33,8 @@ Serves the contents of PATH with one of a number of methods""" ...@@ -33,9 +33,8 @@ Serves the contents of PATH with one of a number of methods"""
if fs.isdir(path): if fs.isdir(path):
fs = fs.opendir(path) fs = fs.opendir(path)
path = '/' path = '/'
if options.verbose: self.output("Opened %s\n" % fs, verbose=True)
print "Serving \"%s\" in %s" % (path, fs)
port = options.port port = options.port
...@@ -44,15 +43,17 @@ Serves the contents of PATH with one of a number of methods""" ...@@ -44,15 +43,17 @@ Serves the contents of PATH with one of a number of methods"""
if options.type == 'http': if options.type == 'http':
from fs.expose.http import serve_fs from fs.expose.http import serve_fs
if port is None: if port is None:
port = 80 port = 80
serve_fs(fs, options.addr, port) self.output("Starting http server on %s:%i\n" % (options.addr, port), verbose=True)
serve_fs(fs, options.addr, port)
elif options.type == 'rpc': elif options.type == 'rpc':
from fs.expose.xmlrpc import RPCFSServer from fs.expose.xmlrpc import RPCFSServer
if port is None: if port is None:
port = 80 port = 80
s = RPCFSServer(fs, (options.addr, port)) s = RPCFSServer(fs, (options.addr, port))
s.serve_forever() self.output("Starting rpc server on %s:%i\n" % (options.addr, port), verbose=True)
s.serve_forever()
elif options.type == 'sftp': elif options.type == 'sftp':
from fs.expose.sftp import BaseSFTPServer from fs.expose.sftp import BaseSFTPServer
...@@ -60,6 +61,7 @@ Serves the contents of PATH with one of a number of methods""" ...@@ -60,6 +61,7 @@ Serves the contents of PATH with one of a number of methods"""
port = 22 port = 22
server = BaseSFTPServer((options.addr, port), fs) server = BaseSFTPServer((options.addr, port), fs)
try: try:
self.output("Starting sftp server on %s:%i\n" % (options.addr, port), verbose=True)
server.serve_forever() server.serve_forever()
except Exception, e: except Exception, e:
pass pass
......
...@@ -147,8 +147,7 @@ class Command(object): ...@@ -147,8 +147,7 @@ class Command(object):
def get_resources(self, fs_urls, dirs_only=False, files_only=False, single=False): def get_resources(self, fs_urls, dirs_only=False, files_only=False, single=False):
fs_paths = [self.open_fs(fs_url) for fs_url in fs_urls] fs_paths = [self.open_fs(fs_url) for fs_url in fs_urls]
resources = [] resources = []
for fs, path in fs_paths: for fs, path in fs_paths:
...@@ -187,10 +186,9 @@ class Command(object): ...@@ -187,10 +186,9 @@ class Command(object):
text = text.encode(self.encoding, 'replace') text = text.encode(self.encoding, 'replace')
return text return text
def output(self, msgs, verbose=False): def output(self, msgs, verbose=False):
if verbose and not self.verbose: if verbose and not self.options.verbose:
return return
if isinstance(msgs, basestring): if isinstance(msgs, basestring):
msgs = (msgs,) msgs = (msgs,)
...@@ -281,6 +279,7 @@ class Command(object): ...@@ -281,6 +279,7 @@ class Command(object):
def run(self): def run(self):
parser = self.get_optparse() parser = self.get_optparse()
options, args = parser.parse_args() options, args = parser.parse_args()
self.options = options
if options.listopeners: if options.listopeners:
self.list_openers() self.list_openers()
......
...@@ -9,6 +9,7 @@ import urllib ...@@ -9,6 +9,7 @@ import urllib
import posixpath import posixpath
import time import time
import threading import threading
import socket
def _datetime_to_epoch(d): def _datetime_to_epoch(d):
return mktime(d.timetuple()) return mktime(d.timetuple())
...@@ -27,7 +28,10 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -27,7 +28,10 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
try: try:
f = self.send_head() f = self.send_head()
if f: if f:
self.copyfile(f, self.wfile) try:
self.copyfile(f, self.wfile)
except socket.error:
pass
finally: finally:
if f is not None: if f is not None:
f.close() f.close()
...@@ -44,8 +48,6 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -44,8 +48,6 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
""" """
path = self.translate_path(self.path) path = self.translate_path(self.path)
#path = self.translate_path(self.path)
#print path
f = None f = None
if self._fs.isdir(path): if self._fs.isdir(path):
if not self.path.endswith('/'): if not self.path.endswith('/'):
...@@ -54,7 +56,7 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ...@@ -54,7 +56,7 @@ class FSHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
self.send_header("Location", self.path + "/") self.send_header("Location", self.path + "/")
self.end_headers() self.end_headers()
return None return None
for index in "index.html", "index.htm": for index in ("index.html", "index.htm"):
index = pathjoin(path, index) index = pathjoin(path, index)
if self._fs.exists(index): if self._fs.exists(index):
path = index path = index
......
"""
fs.opener: open file systems from a FS url
"""
__all__ = ['OpenerError',
'NoOpenerError',
'OpenerRegistry',
'opener']
import sys import sys
from fs.osfs import OSFS from fs.osfs import OSFS
from fs.path import pathsplit, basename, join, iswildcard from fs.path import pathsplit, basename, join, iswildcard
...@@ -12,10 +22,6 @@ class OpenerError(Exception): ...@@ -12,10 +22,6 @@ class OpenerError(Exception):
class NoOpenerError(OpenerError): class NoOpenerError(OpenerError):
pass pass
class MissingParameterError(OpenerError):
pass
def _expand_syspath(path): def _expand_syspath(path):
if path is None: if path is None:
return path return path
...@@ -64,6 +70,7 @@ def _split_url_path(url): ...@@ -64,6 +70,7 @@ def _split_url_path(url):
class OpenerRegistry(object): class OpenerRegistry(object):
"""An opener stores a number of opener objects that are used to parse FS URLs"""
re_fs_url = re.compile(r''' re_fs_url = re.compile(r'''
^ ^
...@@ -95,18 +102,39 @@ class OpenerRegistry(object): ...@@ -95,18 +102,39 @@ class OpenerRegistry(object):
return match return match
def get_opener(self, name): def get_opener(self, name):
"""Retrieve an opener for the given protocol
:param name: name of the opener to open
:raises NoOpenerError: if no opener has been registered of that name
"""
if name not in self.registry: if name not in self.registry:
raise NoOpenerError("No opener for %s" % name) raise NoOpenerError("No opener for %s" % name)
index = self.registry[name] index = self.registry[name]
return self.openers[index] return self.openers[index]
def add(self, opener): def add(self, opener):
"""Adds an opener to the registry
:param opener: a class derived from fs.opener.Opener
"""
index = len(self.openers) index = len(self.openers)
self.openers[index] = opener self.openers[index] = opener
for name in opener.names: for name in opener.names:
self.registry[name] = index self.registry[name] = index
def parse(self, fs_url, default_fs_name=None, open_dir=True, writeable=False, create_dir=False): def parse(self, fs_url, default_fs_name=None, writeable=False, create_dir=False):
"""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.
:param fs_url: an FS url
:param default_fs_name: the default FS to use if none is indicated (defaults is OSFS)
:param writeable: if True, a writeable FS will be returned
:oaram create_dir: if True, then the directory in the FS will be created
"""
orig_url = fs_url orig_url = fs_url
match = self.split_segments(fs_url) match = self.split_segments(fs_url)
...@@ -154,7 +182,7 @@ class OpenerRegistry(object): ...@@ -154,7 +182,7 @@ class OpenerRegistry(object):
fs = fs.opendir(pathname) fs = fs.opendir(pathname)
fs_path = resourcename fs_path = resourcename
return fs, fs_path return fs, fs_path or ''
def open(self, fs_url, mode='rb'): def open(self, fs_url, mode='rb'):
"""Opens a file from a given FS url """Opens a file from a given FS url
...@@ -173,15 +201,17 @@ class OpenerRegistry(object): ...@@ -173,15 +201,17 @@ class OpenerRegistry(object):
fs, path = self.parse(fs_url, writeable=writeable) fs, path = self.parse(fs_url, writeable=writeable)
file_object = fs.open(path, mode) file_object = fs.open(path, mode)
# If we just return the file, the fs goes out of scope and closes, # If we just return the file, then fs goes out of scope and closes,
# which may make the file unusable. To get around this, we store a # which may make the file unusable. To get around this, we store a
# reference in the file object to the FS, and patch the file's # reference in the file object to the FS, and patch the file's
# close method to also close the FS. # close method to also close the FS.
close = file_object.close close = file_object.close
def replace_close(): close_fs = fs
fs.close() def replace_close():
return close() ret = close()
file_object.close = replace_close close_fs.close()
return ret
file_object.close = replace_close
return file_object return file_object
...@@ -204,23 +234,35 @@ class OpenerRegistry(object): ...@@ -204,23 +234,35 @@ class OpenerRegistry(object):
it doesn't already exist it doesn't already exist
""" """
fs, path = self.parse(fs_url, writable=writeable, create_dir=create_dir) fs, path = self.parse(fs_url, writeable=writeable, create_dir=create_dir)
if path: if path:
return fs.opendir(path) return fs.opendir(path)
return fs return fs
class Opener(object): class Opener(object):
"""The base class for openers
@classmethod Opener follow a very simple protocol. To create an opener, derive a class
def get_param(cls, params, name, default=None): from `Opener` and define a classmethod called `get_fs`, which should have the following signature::
try:
param = params.pop(0) @classmethod
except IndexError: def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir):
if default is not None:
return default The parameters of `get_fs` are as follows:
raise MissingParameterError(error_msg)
return param * `fs_name` the name of the opener, as extracted from the protocol part of the url,
* `fs_name_params` reserved for future use
* `fs_path` the path part of the url
* `writeable` if True, then `get_fs` must return an FS that can be written to
* `create_dir` if True then `get_fs` should attempt to silently create the directory references in path
In addition to `get_fs` an opener class should contain
two class attributes; names and desc. `names` is a list of protocols that
list opener will opener. `desc` is an English description of the individual opener syntax.
"""
pass
class OSFSOpener(Opener): class OSFSOpener(Opener):
...@@ -299,7 +341,7 @@ rpc://www.example.org (opens an RPC server on www.example.org, default port 80)" ...@@ -299,7 +341,7 @@ rpc://www.example.org (opens an RPC server on www.example.org, default port 80)"
def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir): def get_fs(cls, registry, fs_name, fs_name_params, fs_path, writeable, create_dir):
from fs.rpcfs import RPCFS from fs.rpcfs import RPCFS
username, password, fs_path = _parse_credentials(fs_path) username, password, fs_path = _parse_credentials(fs_path)
if not fs_path.startswith('http://'): if '://' not in fs_path:
fs_path = 'http://' + fs_path fs_path = 'http://' + fs_path
scheme, netloc, path, params, query, fragment = urlparse(fs_path) scheme, netloc, path, params, query, fragment = urlparse(fs_path)
......
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