Anonymous avatar Anonymous committed 927c34d

- Fixed spelling mistake in package name.

Comments (0)

Files changed (14)

Add a comment to this file

src/invenio_devscripts/__init__.py

Empty file removed.

src/invenio_devscripts/config.py

-"""
-Default Values
-
-INSTALL_PATH will be set to current active virtualenv or CFG_INVENIO_PREFIX if set.
-SRC_PATH will be set to CFG_INVENIO_SRC if set otherwise ~/src/invenio/
-
-All config values can be overwritten by puttinh a config_local.py in your
-site-packages.
-"""
-import os
-
-# Check if CFG_INVENIO_SRC is set otherwise use default. 
-if 'CFG_INVENIO_SRC' in os.environ:
-    SRC_PATH = [os.environ['CFG_INVENIO_SRC']]
-else:
-    SRC_PATH = [ os.path.expanduser("~/src/invenio"), ]
-
-
-# Try if we're in a virtualenv or CFG_INVENIO_PREFIX is set.
-INSTALL_PATH = "/opt/invenio/"
-for var in ['VIRTUAL_ENV', 'CFG_INVENIO_PREFIX']:
-    if var in os.environ:
-        INSTALL_PATH = os.environ[var] 
-        break
-
-# All the extensions specified here will be copied to their destination
-# directories when they are changed
-DIRS = {
-    'py': 'lib/python/invenio',
-    'js': 'var/www/js',
-    'css': 'var/www/css',
-}
-
-STATIC_FILES = {
-    '/img': '/var/www/img',
-    '/js': '/var/www/js',
-    '/flash': '/var/www/flash',
-    '/css': '/var/www/css',
-    '/export': '/var/www/export',
-    '/MathJax': '/var/www/MathJax',
-    '/jsCalendar': '/var/www/jsCalendar',
-    '/ckeditor': '/var/www/ckeditor',
-    '/mediaelement': '/var/www/mediaelement',
-    '/ckeditor': '/var/www/ckeditor',
-    '/robots.txt': '/var/www/robots.txt',
-    '/favicon.ico': '/var/www/favicon.ico',
-}
-
-try:
-    import config_local
-    for setting in dir(config_local):
-        if setting == setting.upper():
-            globals()[setting] = getattr(config_local, setting)
-except ImportError:
-    pass
-

src/invenio_devscripts/mailserve.py

-# -*- coding: utf-8 -*-
-
-import smtpd
-import asyncore
-import optparse
-
-DESCRIPTION = "Invenio mail server for development"
-USAGE_MESSAGE = "mailserve [-bp]"
-
-def printing_smtpd(addr, port):
-    """
-    Start a mail debug server
-    
-    Remember to update your invenio-local.cfg
-    """
-    addr = addr
-    try:
-        port = int(port)
-    except Exception:
-        print("%s is not a valid port number." % port)
-        return
-    
-    try: 
-        print """Remember to set the two following settings in invenio-local.conf:
-
-CFG_MISCUTIL_SMTP_HOST=%s
-CFG_MISCUTIL_SMTP_PORT=%s
-"""  % (addr, port)
-
-        print "Now accepting mail at %s:%s (hit CONTROL-C to stop)" % (addr, port)
-        smtpd.DebuggingServer((addr, port), None)
-        asyncore.loop()
-    except KeyboardInterrupt:
-        print 'Exiting'
-
-
-def parse_cli_options():
-    """Parse command line options"""
-    parser = optparse.OptionParser(description=DESCRIPTION,
-                                   usage=USAGE_MESSAGE)
-    # Display help and exit
-    parser.add_option('-b', dest='bind_address', default='127.0.0.1',
-                                                    help='Address to bind to')
-    parser.add_option('-p', dest='bind_port', type='int', default=1025,
-                                    help='Port to bind to')
-    return parser.parse_args()
-
-
-def main():
-    """Script entrance"""
-    (options, args) = parse_cli_options()
-    
-    printing_smtpd(options.bind_address, options.bind_port)
-
-
-if __name__ == '__main__':
-    main()
-        

src/invenio_devscripts/serve.py

-import os
-import time
-import shutil
-import signal
-import optparse
-import socket
-import traceback
-import sys
-from itertools import chain
-
-try:
-    from functools import partial
-except ImportError:
-    def partial(fn, *initialargs, **initialkwds):
-        def proxy(*finalargs, **finalkwds):
-            args = initialargs + finalargs
-            kwds = initialkwds.copy()
-            kwds.update(finalkwds)
-            return fn(*args, **kwds)
-        return proxy
-
-
-from invenio_devserver.webserver import run_simple
-from werkzeug._internal import _log
-
-from invenio_devserver import config
-
-try:
-    BaseException
-except NameError:
-    # Python 2.4 compatibility
-    BaseException = Exception
-
-DESCRIPTION = "Invenio web server for development"
-USAGE_MESSAGE = "python serve.py [-bp]"
-
-
-def get_extension(filename):
-    try:
-        return filename.rsplit('.', 1)[1]
-    except IndexError:
-        return ''
-
-
-def valid_extension(filename, dirs=config.DIRS):
-    """Checks the extension of the file to see if we should monitor it"""
-    return get_extension(filename) in dirs.keys()
-
-
-def generate_invenio_files_list(invenio_path=config.SRC_PATH):
-    """Generates the list of all the source files to be monitored"""
-    if isinstance(invenio_path, basestring):
-        invenio_path = [invenio_path]
-
-    def iter_files(dirname, filenames):
-        return (os.path.join(dirname, filename) \
-                      for filename in filenames if valid_extension(filename))
-
-    def iter_folder(folder):
-        return chain(*(
-            iter_files(dirname, filenames) \
-                                for dirname, _, filenames in os.walk(folder)
-        ))
-
-    return chain(*(iter_folder(folder) for folder in invenio_path))
-
-
-def select_destination_path(filename, install_path=config.INSTALL_PATH, dests=config.DIRS):
-    lib_dir = dests[get_extension(filename)]
-    return os.path.join(install_path,
-                        lib_dir,
-                        os.path.basename(filename))
-
-
-def reloader_loop(files, reloader=None, interval=1):
-    """Monitor files, copies files to invenio install when they change and
-    eventually restart our worker process"""
-    mtimes = {}
-    while 1:
-        has_changes = False
-
-        for filename in set(files):
-            try:
-                stats = os.stat(filename)
-            except AttributeError:
-                continue
-            except OSError:
-                continue
-
-            mtime = stats.st_mtime
-            old_time = mtimes.get(filename)
-            if old_time is None:
-                mtimes[filename] = mtime
-                continue
-            elif mtime > old_time:
-                mtimes[filename] = mtime
-                # Sleep for a while to wait for the texteditor to
-                # finish writing the file
-                time.sleep(0.1)
-                dest = select_destination_path(filename)
-                _log('info', ' * Detected change in %r, copying to %s' % \
-                    (filename, dest))
-                shutil.copyfile(filename, dest)
-                has_changes = True
-
-        if has_changes and reloader:
-            reloader()
-            time.sleep(1)
-
-        time.sleep(interval)
-
-
-class Reloader(object):
-    """Function object that reloads the worker when called"""
-
-    def __init__(self, options, server_socket):
-        """Reloader initializer
-
-        Saves:
-        * worker pid to be able to kill it
-        * server socket that should be given to the worker
-                        to accept connections
-        """
-        self.options = options
-        self.worker_pid = None
-        self.server_socket = server_socket
-
-    def __call__(self):
-        """Called to reload the worker"""
-        if self.worker_pid:
-            kill_worker(self.worker_pid)
-        try:
-            self.worker_pid = spawn_server(self.options, self.server_socket)
-        except:
-            # Set to None in case, we kill the whole process group
-            self.worker_pid = None
-            raise
-
-
-
-def start_server(options, server_socket, static_files=config.STATIC_FILES):
-    """Start a new http server
-
-    Called by the worker
-    """
-    # We only import wsgi here because this import some invenio components
-    # and we do not want anything imported from invenio in the parent
-    import wsgi
-
-    wsgi.replace_error_handler()
-    wsgi.wrap_warn()
-
-    static = dict([(k, config.INSTALL_PATH + v) for k, v in static_files.items()])
-
-    wsgi_app = partial(wsgi.application, options)
-
-    if options.use_pdb:
-        import pdb
-        def pdb_on_error(f, *args, **kwargs):
-            try:
-                f(*args, **kwargs)
-            except:
-                pdb.post_mortem()
-
-        wsgi_app = partial(pdb_on_error, wsgi_app)
-
-    run_simple(server_socket,
-               wsgi_app,
-               use_debugger=True,
-               use_evalex=True,
-               static_files=static)
-
-
-def spawn_server(options, server_socket):
-    """Create a new worker"""
-    _log('info', ' * Spawning worker')
-    pid = os.fork()
-    if pid == 0:
-        start_server(options, server_socket)
-    return pid
-
-
-def parse_cli_options():
-    """Parse command line options"""
-    parser = optparse.OptionParser(description=DESCRIPTION,
-                                   usage=USAGE_MESSAGE)
-    # Display help and exit
-    parser.add_option('-b', dest='bind_address', default='localhost',
-                                                    help='Address to bind to')
-    parser.add_option('-p', dest='bind_port', type='int', default=4000,
-                                    help='Port to bind to')
-    parser.add_option('--no-reload', action='store_false', dest='auto_reload',
-                      default=True, help='Disable automatic reloading\n'\
-                      'when a source file is changed')
-    parser.add_option('--no-http', action='store_false', dest='serve_http',
-                      default=True, help='Disable http server, only update ' \
-                      'invenio install')
-    parser.add_option('--buffer-output', dest='buffer_output', default=False,
-                      action='store_true', help='Buffer output to display\n' \
-                      'to display debug pages')
-    parser.add_option('--pdb', dest='use_pdb', default=False,
-                      action='store_true', help='Drop to python debugger\n' \
-                      'on errors')
-    parser.add_option('-s', action='append', dest='src_path', metavar='SRC_PATH',
-                      default=[], help='Source folder (one or more)')
-    parser.add_option('-o', dest='install_path', metavar='INSTALL_PATH',
-                      default=[], help='Path to Invenio installation.')
-    return parser.parse_args()
-
-
-def kill_worker(pid):
-    """Kill worker with <pid>"""
-    _log('info', ' * Killing worker %r' % pid)
-    os.kill(pid, signal.SIGTERM)
-    _log('info', ' * Waiting for worker to stop')
-    os.waitpid(pid, 0)
-
-
-def create_socket(server_address, server_port):
-    """Bind socket"""
-    s = socket.socket(socket.AF_INET,
-                           socket.SOCK_STREAM)
-    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-    s.bind((server_address, server_port))
-    return s
-
-
-def bind_socket(options, ssl_context=None):
-    server_address, server_port = options.bind_address, options.bind_port
-    server_socket = create_socket(server_address, server_port)
-    display_hostname = server_address != '*' and server_address or 'localhost'
-    if ':' in display_hostname:
-        display_hostname = '[%s]' % display_hostname
-    _log('info', ' * Running on %s://%s:%d/', ssl_context is None
-                         and 'http' or 'https', display_hostname, server_port)
-    return server_socket
-
-def prepare_server(options, ssl_context=None):
-    """Prepare http server
-
-    First binds the socket to accept connections from,
-    then create a worker that will start a http server
-    """
-    server_socket = bind_socket(options, ssl_context)
-
-    reloader = None
-    # Create a new process group
-    # Used for cleanup when quiting
-    os.setpgrp()
-    try:
-        reloader = Reloader(options, server_socket)
-        # Start first worker
-        reloader()
-        if reloader.worker_pid:
-            invenio_files = list(generate_invenio_files_list())
-            reloader_loop(invenio_files, reloader)
-    except BaseException, e:
-        if reloader and reloader.worker_pid:
-            kill_worker(reloader.worker_pid)
-        else:
-            # We are killing ourselves
-            # The python interpreter will not get a chance to print
-            # the traceback
-            if not isinstance(e, KeyboardInterrupt) \
-                                            and not isinstance(e, SystemExit):
-                print traceback.format_exc()[:-1]
-            os.killpg(0, signal.SIGKILL)
-        raise
-
-
-def main():
-    """Script entrance"""
-    (options, args) = parse_cli_options()
-    
-    # Override config SRC_PATH and INSTALL_PATH
-    if options.src_path:
-        config.SRC_PATH = [os.path.expanduser(x) for x in options.src_path]
-    if options.install_path:
-        config.INSTALL_PATH = os.path.expanduser(options.install_path)
-
-
-    if options.serve_http and options.auto_reload:
-        print 'HTTP Server mode with reload mode'
-        prepare_server(options)
-    elif options.serve_http:
-        print 'Simple HTTP Server mode'
-        start_server(options, bind_socket(options))
-    elif options.auto_reload:
-        print 'Copy-file only mode'
-        invenio_files = list(generate_invenio_files_list())
-        reloader_loop(invenio_files)
-
-
-if __name__ == '__main__':
-    try:
-        main()
-    except KeyboardInterrupt:
-        print 'Exiting'

src/invenio_devscripts/webserver.py

-# -*- coding: utf-8 -*-
-"""
-    werkzeug.serving
-    ~~~~~~~~~~~~~~~~
-
-    There are many ways to serve a WSGI application.  While you're developing
-    it you usually don't want a full blown webserver like Apache but a simple
-    standalone one.  From Python 2.5 onwards there is the `wsgiref`_ server in
-    the standard library.  If you're using older versions of Python you can
-    download the package from the cheeseshop.
-
-    However there are some caveats. Sourcecode won't reload itself when
-    changed and each time you kill the server using ``^C`` you get an
-    `KeyboardInterrupt` error.  While the latter is easy to solve the first
-    one can be a pain in the ass in some situations.
-
-    The easiest way is creating a small ``start-myproject.py`` that runs the
-    application::
-
-        #!/usr/bin/env python
-        # -*- coding: utf-8 -*-
-        from myproject import make_app
-        from werkzeug.serving import run_simple
-
-        app = make_app(...)
-        run_simple('localhost', 8080, app, use_reloader=True)
-
-    You can also pass it a `extra_files` keyword argument with a list of
-    additional files (like configuration files) you want to observe.
-
-    For bigger applications you should consider using `werkzeug.script`
-    instead of a simple start file.
-
-
-    :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
-    :license: BSD, see LICENSE for more details.
-"""
-import __future__
-import os
-import socket
-import sys
-import signal
-from urllib import unquote
-from SocketServer import ThreadingMixIn, ForkingMixIn
-from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
-
-import werkzeug
-from werkzeug._internal import _log
-from werkzeug.exceptions import InternalServerError
-
-
-if hasattr(__future__, 'with_statement'):
-    from webserver_ssl import load_ssl_context, \
-                              is_ssl_error, \
-                              generate_adhoc_ssl_context, \
-                              _SSLConnectionFix
-
-
-class WSGIRequestHandler(BaseHTTPRequestHandler, object):
-    """A request handler that implements WSGI dispatching."""
-
-    @property
-    def server_version(self):
-        return 'Werkzeug/' + werkzeug.__version__
-
-    def make_environ(self):
-        if '?' in self.path:
-            path_info, query = self.path.split('?', 1)
-        else:
-            path_info = self.path
-            query = ''
-
-        def shutdown_server():
-            self.server.shutdown_signal = True
-
-        url_scheme = self.server.ssl_context is None and 'http' or 'https'
-        environ = {
-            'wsgi.version':         (1, 0),
-            'wsgi.url_scheme':      url_scheme,
-            'wsgi.input':           self.rfile,
-            'wsgi.errors':          sys.stderr,
-            'wsgi.multithread':     self.server.multithread,
-            'wsgi.multiprocess':    self.server.multiprocess,
-            'wsgi.run_once':        False,
-            'werkzeug.server.shutdown':
-                                    shutdown_server,
-            'SERVER_SOFTWARE':      self.server_version,
-            'REQUEST_METHOD':       self.command,
-            'SCRIPT_NAME':          '',
-            'PATH_INFO':            unquote(path_info),
-            'QUERY_STRING':         query,
-            'CONTENT_TYPE':         self.headers.get('Content-Type', ''),
-            'CONTENT_LENGTH':       self.headers.get('Content-Length', ''),
-            'REMOTE_ADDR':          self.client_address[0],
-            'REMOTE_PORT':          self.client_address[1],
-            'SERVER_NAME':          self.server.server_address[0],
-            'SERVER_PORT':          str(self.server.server_address[1]),
-            'SERVER_PROTOCOL':      self.request_version
-        }
-
-        for key, value in self.headers.items():
-            key = 'HTTP_' + key.upper().replace('-', '_')
-            if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
-                environ[key] = value
-
-        return environ
-
-    def run_wsgi(self):
-        app = self.server.app
-        environ = self.make_environ()
-        headers_set = []
-        headers_sent = []
-
-        def write(data):
-            assert headers_set, 'write() before start_response'
-            if not headers_sent:
-                status, response_headers = headers_sent[:] = headers_set
-                code, msg = status.split(None, 1)
-                self.send_response(int(code), msg)
-                header_keys = set()
-                for key, value in response_headers:
-                    self.send_header(key, value)
-                    key = key.lower()
-                    header_keys.add(key)
-                if 'content-length' not in header_keys:
-                    self.close_connection = True
-                    self.send_header('Connection', 'close')
-                if 'server' not in header_keys:
-                    self.send_header('Server', self.version_string())
-                if 'date' not in header_keys:
-                    self.send_header('Date', self.date_time_string())
-                self.end_headers()
-
-            assert type(data) is str, 'applications must write bytes'
-            self.wfile.write(data)
-            self.wfile.flush()
-
-        def start_response(status, response_headers, exc_info=None):
-            if exc_info:
-                try:
-                    if headers_sent:
-                        raise exc_info[0], exc_info[1], exc_info[2]
-                finally:
-                    exc_info = None
-            elif headers_set:
-                raise AssertionError('Headers already set')
-            headers_set[:] = [status, response_headers]
-            return write
-
-        def execute(app):
-            application_iter = app(environ, start_response)
-            try:
-                for data in application_iter:
-                    write(data)
-                # make sure the headers are sent
-                if not headers_sent:
-                    write('')
-            finally:
-                if hasattr(application_iter, 'close'):
-                    application_iter.close()
-                application_iter = None
-
-        try:
-            execute(app)
-        except (socket.error, socket.timeout), e:
-            self.connection_dropped(e, environ)
-        except Exception:
-            if self.server.passthrough_errors:
-                raise
-            from werkzeug.debug.tbtools import get_current_traceback
-            traceback = get_current_traceback(ignore_system_exceptions=True)
-            try:
-                # if we haven't yet sent the headers but they are set
-                # we roll back to be able to set them again.
-                if not headers_sent:
-                    del headers_set[:]
-                execute(InternalServerError())
-            except Exception:
-                pass
-            self.server.log('error', 'Error on request:\n%s',
-                            traceback.plaintext)
-
-    def handle(self):
-        """Handles a request ignoring dropped connections."""
-        rv = None
-        try:
-            rv = BaseHTTPRequestHandler.handle(self)
-        except (socket.error, socket.timeout), e:
-            self.connection_dropped(e)
-        except Exception:
-            if self.server.ssl_context is None or not is_ssl_error():
-                raise
-        if self.server.shutdown_signal:
-            self.initiate_shutdown()
-        return rv
-
-    def initiate_shutdown(self):
-        """A horrible, horrible way to kill the server for Python 2.6 and
-        later.  It's the best we can do.
-        """
-        # Windows does not provide SIGKILL, go with SIGTERM then.
-        sig = getattr(signal, 'SIGKILL', signal.SIGTERM)
-        # reloader active
-        if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
-            os.kill(os.getpid(), sig)
-        # python 2.7
-        self.server._BaseServer__shutdown_request = True
-        # python 2.6
-        self.server._BaseServer__serving = False
-
-    def connection_dropped(self, error, environ=None):
-        """Called if the connection was closed by the client.  By default
-        nothing happens.
-        """
-
-    def handle_one_request(self):
-        """Handle a single HTTP request."""
-        self.raw_requestline = self.rfile.readline()
-        if not self.raw_requestline:
-            self.close_connection = 1
-        elif self.parse_request():
-            return self.run_wsgi()
-
-    def send_response(self, code, message=None):
-        """Send the response header and log the response code."""
-        self.log_request(code)
-        if message is None:
-            message = code in self.responses and self.responses[code][0] or ''
-        if self.request_version != 'HTTP/0.9':
-            self.wfile.write("%s %d %s\r\n" %
-                             (self.protocol_version, code, message))
-
-    def version_string(self):
-        return BaseHTTPRequestHandler.version_string(self).strip()
-
-    def address_string(self):
-        return self.client_address[0]
-
-    def log_request(self, code='-', size='-'):
-        self.log('info', '"%s" %s %s', self.requestline, code, size)
-
-    def log_error(self, *args):
-        self.log('error', *args)
-
-    def log_message(self, format, *args):
-        self.log('info', format, *args)
-
-    def log(self, type, message, *args):
-        _log(type, '%s - - [%s] %s\n' % (self.address_string(),
-                                         self.log_date_time_string(),
-                                         message % args))
-
-
-class BaseWSGIServer(HTTPServer, object):
-    """Simple single-threaded, single-process WSGI server."""
-    multithread = False
-    multiprocess = False
-    request_queue_size = 128
-
-    def __init__(self, server_socket, app, handler=None,
-                 passthrough_errors=False, ssl_context=None):
-        if handler is None:
-            handler = WSGIRequestHandler
-
-        self.server_socket = server_socket
-        HTTPServer.__init__(self, ('localhost', 4000), handler)
-        self.app = app
-        self.passthrough_errors = passthrough_errors
-        self.shutdown_signal = False
-
-        if ssl_context is not None:
-            try:
-                from OpenSSL import tsafe
-            except ImportError:
-                raise TypeError('SSL is not available if the OpenSSL '
-                                'library is not installed.')
-            if isinstance(ssl_context, tuple):
-                ssl_context = load_ssl_context(*ssl_context)
-            if ssl_context == 'adhoc':
-                ssl_context = generate_adhoc_ssl_context()
-            self.socket = tsafe.Connection(ssl_context, self.socket)
-            self.ssl_context = ssl_context
-        else:
-            self.ssl_context = None
-
-    def server_bind(self):
-        self.socket = self.server_socket
-        self.server_address = self.socket.getsockname()
-
-    def log(self, type, message, *args):
-        _log(type, message, *args)
-
-    def serve_forever(self):
-        self.shutdown_signal = False
-        try:
-            _log('info', ' * Ready')
-            HTTPServer.serve_forever(self)
-        except KeyboardInterrupt:
-            pass
-
-    def handle_error(self, request, client_address):
-        if self.passthrough_errors:
-            raise
-        else:
-            return HTTPServer.handle_error(self, request, client_address)
-
-    def get_request(self):
-        con, info = self.socket.accept()
-        if self.ssl_context is not None:
-            con = _SSLConnectionFix(con)
-        return con, info
-
-
-class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
-    """A WSGI server that does threading."""
-    multithread = True
-
-
-class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
-    """A WSGI server that does forking."""
-    multiprocess = True
-
-    def __init__(self, server_socket, app, processes=40, handler=None,
-                 passthrough_errors=False, ssl_context=None):
-        BaseWSGIServer.__init__(self, server_socket, app, handler,
-                                passthrough_errors, ssl_context)
-        self.max_children = processes
-
-
-def make_server(server_socket, app=None, threaded=False, processes=1,
-                request_handler=None, passthrough_errors=False,
-                ssl_context=None):
-    """Create a new server instance that is either threaded, or forks
-    or just processes one request after another.
-    """
-    if threaded and processes > 1:
-        raise ValueError("cannot have a multithreaded and "
-                         "multi process server.")
-    elif threaded:
-        return ThreadedWSGIServer(server_socket, app, request_handler,
-                                  passthrough_errors, ssl_context)
-    elif processes > 1:
-        return ForkingWSGIServer(server_socket, app, processes, request_handler,
-                                 passthrough_errors, ssl_context)
-    else:
-        return BaseWSGIServer(server_socket, app, request_handler,
-                              passthrough_errors, ssl_context)
-
-
-def run_simple(server_socket, application,
-               use_debugger=False, use_evalex=True,
-               extra_files=None, threaded=False,
-               processes=1, request_handler=None, static_files=None,
-               passthrough_errors=False, ssl_context=None):
-    """Start an application using wsgiref and with an optional reloader.  This
-    wraps `wsgiref` to fix the wrong default reporting of the multithreaded
-    WSGI variable and adds optional multithreading and fork support.
-
-    .. versionadded:: 0.5
-       `static_files` was added to simplify serving of static files as well
-       as `passthrough_errors`.
-
-    .. versionadded:: 0.6
-       support for SSL was added.
-
-    .. versionadded:: 0.8
-       Added support for automatically loading a SSL context from certificate
-       file and private key.
-
-    :param socket: The socket for accpeting connections
-    :param application: the WSGI application to execute
-    :param use_debugger: should the werkzeug debugging system be used?
-    :param use_evalex: should the exception evaluation feature be enabled?
-    :param extra_files: a list of files the reloader should watch
-                        additionally to the modules.  For example configuration
-                        files.
-    :param threaded: should the process handle each request in a separate
-                     thread?
-    :param processes: number of processes to spawn.
-    :param request_handler: optional parameter that can be used to replace
-                            the default one.  You can use this to replace it
-                            with a different
-                            :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
-                            subclass.
-    :param static_files: a dict of paths for static files.  This works exactly
-                         like :class:`SharedDataMiddleware`, it's actually
-                         just wrapping the application in that middleware before
-                         serving.
-    :param passthrough_errors: set this to `True` to disable the error catching.
-                               This means that the server will die on errors but
-                               it can be useful to hook debuggers in (pdb etc.)
-    :param ssl_context: an SSL context for the connection. Either an OpenSSL
-                        context, a tuple in the form ``(cert_file, pkey_file)``,
-                        the string ``'adhoc'`` if the server should
-                        automatically create one, or `None` to disable SSL
-                        (which is the default).
-    """
-    if use_debugger:
-        from werkzeug.debug import DebuggedApplication
-        application = DebuggedApplication(application, use_evalex)
-    if static_files:
-        from werkzeug.wsgi import SharedDataMiddleware
-        application = SharedDataMiddleware(application, static_files)
-
-    make_server(server_socket, application, threaded,
-                processes, request_handler,
-                passthrough_errors, ssl_context).serve_forever()

src/invenio_devscripts/webserver_ssl.py

-def generate_adhoc_ssl_pair(cn=None):
-    from random import random
-    from OpenSSL import crypto
-
-    # pretty damn sure that this is not actually accepted by anyone
-    if cn is None:
-        cn = '*'
-
-    cert = crypto.X509()
-    cert.set_serial_number(int(random() * sys.maxint))
-    cert.gmtime_adj_notBefore(0)
-    cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
-
-    subject = cert.get_subject()
-    subject.CN = cn
-    subject.O = 'Dummy Certificate'
-
-    issuer = cert.get_issuer()
-    issuer.CN = 'Untrusted Authority'
-    issuer.O = 'Self-Signed'
-
-    pkey = crypto.PKey()
-    pkey.generate_key(crypto.TYPE_RSA, 768)
-    cert.set_pubkey(pkey)
-    cert.sign(pkey, 'md5')
-
-    return cert, pkey
-
-
-def make_ssl_devcert(base_path, host=None, cn=None):
-    """Creates an SSL key for development.  This should be used instead of
-    the ``'adhoc'`` key which generates a new cert on each server start.
-    It accepts a path for where it should store the key and cert and
-    either a host or CN.  If a host is given it will use the CN
-    ``*.host/CN=host``.
-
-    For more information see :func:`run_simple`.
-
-    .. versionadded:: 0.9
-
-    :param base_path: the path to the certificate and key.  The extension
-                      ``.crt`` is added for the certificate, ``.key`` is
-                      added for the key.
-    :param host: the name of the host.  This can be used as an alternative
-                 for the `cn`.
-    :param cn: the `CN` to use.
-    """
-    from OpenSSL import crypto
-    if host is not None:
-        cn = '*.%s/CN=%s' % (host, host)
-    cert, pkey = generate_adhoc_ssl_pair(cn=cn)
-
-    cert_file = base_path + '.crt'
-    pkey_file = base_path + '.key'
-
-    with open(cert_file, 'w') as f:
-        f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
-    with open(pkey_file, 'w') as f:
-        f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
-
-    return cert_file, pkey_file
-
-
-def generate_adhoc_ssl_context():
-    """Generates an adhoc SSL context for the development server."""
-    from OpenSSL import SSL
-    pkey, cert = generate_adhoc_ssl_pair()
-    ctx = SSL.Context(SSL.SSLv23_METHOD)
-    ctx.use_privatekey(pkey)
-    ctx.use_certificate(cert)
-    return ctx
-
-
-def load_ssl_context(cert_file, pkey_file):
-    """Loads an SSL context from a certificate and private key file."""
-    from OpenSSL import SSL
-    ctx = SSL.Context(SSL.SSLv23_METHOD)
-    ctx.use_certificate_file(cert_file)
-    ctx.use_privatekey_file(pkey_file)
-    return ctx
-
-
-def is_ssl_error(error=None):
-    """Checks if the given error (or the current one) is an SSL error."""
-    if error is None:
-        error = sys.exc_info()[1]
-    from OpenSSL import SSL
-    return isinstance(error, SSL.Error)
-
-
-class _SSLConnectionFix(object):
-    """Wrapper around SSL connection to provide a working makefile()."""
-
-    def __init__(self, con):
-        self._con = con
-
-    def makefile(self, mode, bufsize):
-        return socket._fileobject(self._con, mode, bufsize)
-
-    def __getattr__(self, attrib):
-        return getattr(self._con, attrib)
-
-    def shutdown(self, arg=None):
-        self._con.shutdown()

src/invenio_devscripts/wsgi.py

-import traceback
-import sys
-from StringIO import StringIO
-
-
-def replace_error_handler():
-    from invenio import errorlib
-
-    def error_handler(stream='error',
-                      req=None,
-                      prefix='',
-                      suffix='',
-                      alert_admin=False,
-                      subject=''):
-        print traceback.format_exc()[:-1]
-
-    errorlib.register_exception = error_handler
-
-
-def wrap_warn():
-    import warnings
-
-    def wrapper(warn_fun):
-        def fun(*args, **kwargs):
-            traceback.print_stack()
-            return warn_fun(*args, **kwargs)
-        return fun
-
-    warnings.warn = wrapper(warnings.warn)
-
-from invenio.webinterface_handler_wsgi import SimulatedModPythonRequest, \
-                                              SERVER_RETURN, \
-                                              register_exception, \
-                                              is_mp_legacy_publisher_path, \
-                                              mp_legacy_publisher, \
-                                              CFG_WSGI_SERVE_STATIC_FILES, \
-                                              invenio_handler, \
-                                              OK, \
-                                              DONE, \
-                                              alert_admin_for_server_status_p, \
-                                              generate_error_page, \
-                                              is_static_path
-
-
-class BufferedWSGIRequest(SimulatedModPythonRequest):
-    _response_buffer = None
-
-    def start_response_wrapper(self, status, headers):
-        self._response_status = status
-        self._response_headers = headers
-        self._response_buffer = StringIO()
-
-        def write(bytes):
-            self._response_buffer.write(bytes)
-        return write
-
-    def __init__(self, environ, start_response):
-        self.__start_response_orig = start_response
-        super(BufferedWSGIRequest, self).__init__(environ,
-                                                  self.start_response_wrapper)
-
-    def final_flush(self):
-        if self._response_buffer:
-            writer = self.__start_response_orig(self._response_status,
-                                                  self._response_headers)
-            writer(self._response_buffer.getvalue())
-
-
-class WSGIRequest(SimulatedModPythonRequest):
-    def final_flush(self):
-        pass
-
-
-def application(options, environ, start_response):
-    """
-    Entry point for wsgi.
-    """
-    if options.buffer_output:
-        request = BufferedWSGIRequest
-    else:
-        request = WSGIRequest
-    req = request(environ, start_response)
-
-    try:
-        try:
-            possible_module, possible_handler = \
-                            is_mp_legacy_publisher_path(environ['PATH_INFO'])
-            if possible_module is not None:
-                mp_legacy_publisher(req, possible_module, possible_handler)
-            elif CFG_WSGI_SERVE_STATIC_FILES:
-                possible_static_path = is_static_path(environ['PATH_INFO'])
-                if possible_static_path is not None:
-                    from invenio.bibdocfile import stream_file
-                    stream_file(req, possible_static_path)
-                else:
-                    invenio_handler(req)
-            else:
-                invenio_handler(req)
-            req.flush()
-            req.final_flush()
-        except SERVER_RETURN, status:
-            status = int(str(status))
-            if status not in (OK, DONE):
-                req.status = status
-                req.headers_out['content-type'] = 'text/html'
-                admin_to_be_alerted = alert_admin_for_server_status_p(status,
-                                                req.headers_in.get('referer'))
-                if admin_to_be_alerted:
-                    register_exception(req=req, alert_admin=True)
-                if not req.response_sent_p:
-                    start_response(req.get_wsgi_status(),
-                                   req.get_low_level_headers(),
-                                   sys.exc_info())
-                return generate_error_page(req, admin_to_be_alerted)
-            else:
-                req.flush()
-    finally:
-        for (callback, data) in req.get_cleanups():
-            callback(data)
-    return []
Add a comment to this file

src/invenio_devserver/__init__.py

Empty file added.

src/invenio_devserver/config.py

+"""
+Default Values
+
+INSTALL_PATH will be set to current active virtualenv or CFG_INVENIO_PREFIX if set.
+SRC_PATH will be set to CFG_INVENIO_SRC if set otherwise ~/src/invenio/
+
+All config values can be overwritten by puttinh a config_local.py in your
+site-packages.
+"""
+import os
+
+# Check if CFG_INVENIO_SRC is set otherwise use default. 
+if 'CFG_INVENIO_SRC' in os.environ:
+    SRC_PATH = [os.environ['CFG_INVENIO_SRC']]
+else:
+    SRC_PATH = [ os.path.expanduser("~/src/invenio"), ]
+
+
+# Try if we're in a virtualenv or CFG_INVENIO_PREFIX is set.
+INSTALL_PATH = "/opt/invenio/"
+for var in ['VIRTUAL_ENV', 'CFG_INVENIO_PREFIX']:
+    if var in os.environ:
+        INSTALL_PATH = os.environ[var] 
+        break
+
+# All the extensions specified here will be copied to their destination
+# directories when they are changed
+DIRS = {
+    'py': 'lib/python/invenio',
+    'js': 'var/www/js',
+    'css': 'var/www/css',
+}
+
+STATIC_FILES = {
+    '/img': '/var/www/img',
+    '/js': '/var/www/js',
+    '/flash': '/var/www/flash',
+    '/css': '/var/www/css',
+    '/export': '/var/www/export',
+    '/MathJax': '/var/www/MathJax',
+    '/jsCalendar': '/var/www/jsCalendar',
+    '/ckeditor': '/var/www/ckeditor',
+    '/mediaelement': '/var/www/mediaelement',
+    '/ckeditor': '/var/www/ckeditor',
+    '/robots.txt': '/var/www/robots.txt',
+    '/favicon.ico': '/var/www/favicon.ico',
+}
+
+try:
+    import config_local
+    for setting in dir(config_local):
+        if setting == setting.upper():
+            globals()[setting] = getattr(config_local, setting)
+except ImportError:
+    pass
+

src/invenio_devserver/mailserve.py

+# -*- coding: utf-8 -*-
+
+import smtpd
+import asyncore
+import optparse
+
+DESCRIPTION = "Invenio mail server for development"
+USAGE_MESSAGE = "mailserve [-bp]"
+
+def printing_smtpd(addr, port):
+    """
+    Start a mail debug server
+    
+    Remember to update your invenio-local.cfg
+    """
+    addr = addr
+    try:
+        port = int(port)
+    except Exception:
+        print("%s is not a valid port number." % port)
+        return
+    
+    try: 
+        print """Remember to set the two following settings in invenio-local.conf:
+
+CFG_MISCUTIL_SMTP_HOST=%s
+CFG_MISCUTIL_SMTP_PORT=%s
+"""  % (addr, port)
+
+        print "Now accepting mail at %s:%s (hit CONTROL-C to stop)" % (addr, port)
+        smtpd.DebuggingServer((addr, port), None)
+        asyncore.loop()
+    except KeyboardInterrupt:
+        print 'Exiting'
+
+
+def parse_cli_options():
+    """Parse command line options"""
+    parser = optparse.OptionParser(description=DESCRIPTION,
+                                   usage=USAGE_MESSAGE)
+    # Display help and exit
+    parser.add_option('-b', dest='bind_address', default='127.0.0.1',
+                                                    help='Address to bind to')
+    parser.add_option('-p', dest='bind_port', type='int', default=1025,
+                                    help='Port to bind to')
+    return parser.parse_args()
+
+
+def main():
+    """Script entrance"""
+    (options, args) = parse_cli_options()
+    
+    printing_smtpd(options.bind_address, options.bind_port)
+
+
+if __name__ == '__main__':
+    main()
+        

src/invenio_devserver/serve.py

+import os
+import time
+import shutil
+import signal
+import optparse
+import socket
+import traceback
+import sys
+from itertools import chain
+
+try:
+    from functools import partial
+except ImportError:
+    def partial(fn, *initialargs, **initialkwds):
+        def proxy(*finalargs, **finalkwds):
+            args = initialargs + finalargs
+            kwds = initialkwds.copy()
+            kwds.update(finalkwds)
+            return fn(*args, **kwds)
+        return proxy
+
+
+from invenio_devserver.webserver import run_simple
+from werkzeug._internal import _log
+
+from invenio_devserver import config
+
+try:
+    BaseException
+except NameError:
+    # Python 2.4 compatibility
+    BaseException = Exception
+
+DESCRIPTION = "Invenio web server for development"
+USAGE_MESSAGE = "python serve.py [-bp]"
+
+
+def get_extension(filename):
+    try:
+        return filename.rsplit('.', 1)[1]
+    except IndexError:
+        return ''
+
+
+def valid_extension(filename, dirs=config.DIRS):
+    """Checks the extension of the file to see if we should monitor it"""
+    return get_extension(filename) in dirs.keys()
+
+
+def generate_invenio_files_list(invenio_path=config.SRC_PATH):
+    """Generates the list of all the source files to be monitored"""
+    if isinstance(invenio_path, basestring):
+        invenio_path = [invenio_path]
+
+    def iter_files(dirname, filenames):
+        return (os.path.join(dirname, filename) \
+                      for filename in filenames if valid_extension(filename))
+
+    def iter_folder(folder):
+        return chain(*(
+            iter_files(dirname, filenames) \
+                                for dirname, _, filenames in os.walk(folder)
+        ))
+
+    return chain(*(iter_folder(folder) for folder in invenio_path))
+
+
+def select_destination_path(filename, install_path=config.INSTALL_PATH, dests=config.DIRS):
+    lib_dir = dests[get_extension(filename)]
+    return os.path.join(install_path,
+                        lib_dir,
+                        os.path.basename(filename))
+
+
+def reloader_loop(files, reloader=None, interval=1):
+    """Monitor files, copies files to invenio install when they change and
+    eventually restart our worker process"""
+    mtimes = {}
+    while 1:
+        has_changes = False
+
+        for filename in set(files):
+            try:
+                stats = os.stat(filename)
+            except AttributeError:
+                continue
+            except OSError:
+                continue
+
+            mtime = stats.st_mtime
+            old_time = mtimes.get(filename)
+            if old_time is None:
+                mtimes[filename] = mtime
+                continue
+            elif mtime > old_time:
+                mtimes[filename] = mtime
+                # Sleep for a while to wait for the texteditor to
+                # finish writing the file
+                time.sleep(0.1)
+                dest = select_destination_path(filename)
+                _log('info', ' * Detected change in %r, copying to %s' % \
+                    (filename, dest))
+                shutil.copyfile(filename, dest)
+                has_changes = True
+
+        if has_changes and reloader:
+            reloader()
+            time.sleep(1)
+
+        time.sleep(interval)
+
+
+class Reloader(object):
+    """Function object that reloads the worker when called"""
+
+    def __init__(self, options, server_socket):
+        """Reloader initializer
+
+        Saves:
+        * worker pid to be able to kill it
+        * server socket that should be given to the worker
+                        to accept connections
+        """
+        self.options = options
+        self.worker_pid = None
+        self.server_socket = server_socket
+
+    def __call__(self):
+        """Called to reload the worker"""
+        if self.worker_pid:
+            kill_worker(self.worker_pid)
+        try:
+            self.worker_pid = spawn_server(self.options, self.server_socket)
+        except:
+            # Set to None in case, we kill the whole process group
+            self.worker_pid = None
+            raise
+
+
+
+def start_server(options, server_socket, static_files=config.STATIC_FILES):
+    """Start a new http server
+
+    Called by the worker
+    """
+    # We only import wsgi here because this import some invenio components
+    # and we do not want anything imported from invenio in the parent
+    import wsgi
+
+    wsgi.replace_error_handler()
+    wsgi.wrap_warn()
+
+    static = dict([(k, config.INSTALL_PATH + v) for k, v in static_files.items()])
+
+    wsgi_app = partial(wsgi.application, options)
+
+    if options.use_pdb:
+        import pdb
+        def pdb_on_error(f, *args, **kwargs):
+            try:
+                f(*args, **kwargs)
+            except:
+                pdb.post_mortem()
+
+        wsgi_app = partial(pdb_on_error, wsgi_app)
+
+    run_simple(server_socket,
+               wsgi_app,
+               use_debugger=True,
+               use_evalex=True,
+               static_files=static)
+
+
+def spawn_server(options, server_socket):
+    """Create a new worker"""
+    _log('info', ' * Spawning worker')
+    pid = os.fork()
+    if pid == 0:
+        start_server(options, server_socket)
+    return pid
+
+
+def parse_cli_options():
+    """Parse command line options"""
+    parser = optparse.OptionParser(description=DESCRIPTION,
+                                   usage=USAGE_MESSAGE)
+    # Display help and exit
+    parser.add_option('-b', dest='bind_address', default='localhost',
+                                                    help='Address to bind to')
+    parser.add_option('-p', dest='bind_port', type='int', default=4000,
+                                    help='Port to bind to')
+    parser.add_option('--no-reload', action='store_false', dest='auto_reload',
+                      default=True, help='Disable automatic reloading\n'\
+                      'when a source file is changed')
+    parser.add_option('--no-http', action='store_false', dest='serve_http',
+                      default=True, help='Disable http server, only update ' \
+                      'invenio install')
+    parser.add_option('--buffer-output', dest='buffer_output', default=False,
+                      action='store_true', help='Buffer output to display\n' \
+                      'to display debug pages')
+    parser.add_option('--pdb', dest='use_pdb', default=False,
+                      action='store_true', help='Drop to python debugger\n' \
+                      'on errors')
+    parser.add_option('-s', action='append', dest='src_path', metavar='SRC_PATH',
+                      default=[], help='Source folder (one or more)')
+    parser.add_option('-o', dest='install_path', metavar='INSTALL_PATH',
+                      default=[], help='Path to Invenio installation.')
+    return parser.parse_args()
+
+
+def kill_worker(pid):
+    """Kill worker with <pid>"""
+    _log('info', ' * Killing worker %r' % pid)
+    os.kill(pid, signal.SIGTERM)
+    _log('info', ' * Waiting for worker to stop')
+    os.waitpid(pid, 0)
+
+
+def create_socket(server_address, server_port):
+    """Bind socket"""
+    s = socket.socket(socket.AF_INET,
+                           socket.SOCK_STREAM)
+    s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+    s.bind((server_address, server_port))
+    return s
+
+
+def bind_socket(options, ssl_context=None):
+    server_address, server_port = options.bind_address, options.bind_port
+    server_socket = create_socket(server_address, server_port)
+    display_hostname = server_address != '*' and server_address or 'localhost'
+    if ':' in display_hostname:
+        display_hostname = '[%s]' % display_hostname
+    _log('info', ' * Running on %s://%s:%d/', ssl_context is None
+                         and 'http' or 'https', display_hostname, server_port)
+    return server_socket
+
+def prepare_server(options, ssl_context=None):
+    """Prepare http server
+
+    First binds the socket to accept connections from,
+    then create a worker that will start a http server
+    """
+    server_socket = bind_socket(options, ssl_context)
+
+    reloader = None
+    # Create a new process group
+    # Used for cleanup when quiting
+    os.setpgrp()
+    try:
+        reloader = Reloader(options, server_socket)
+        # Start first worker
+        reloader()
+        if reloader.worker_pid:
+            invenio_files = list(generate_invenio_files_list())
+            reloader_loop(invenio_files, reloader)
+    except BaseException, e:
+        if reloader and reloader.worker_pid:
+            kill_worker(reloader.worker_pid)
+        else:
+            # We are killing ourselves
+            # The python interpreter will not get a chance to print
+            # the traceback
+            if not isinstance(e, KeyboardInterrupt) \
+                                            and not isinstance(e, SystemExit):
+                print traceback.format_exc()[:-1]
+            os.killpg(0, signal.SIGKILL)
+        raise
+
+
+def main():
+    """Script entrance"""
+    (options, args) = parse_cli_options()
+    
+    # Override config SRC_PATH and INSTALL_PATH
+    if options.src_path:
+        config.SRC_PATH = [os.path.expanduser(x) for x in options.src_path]
+    if options.install_path:
+        config.INSTALL_PATH = os.path.expanduser(options.install_path)
+
+
+    if options.serve_http and options.auto_reload:
+        print 'HTTP Server mode with reload mode'
+        prepare_server(options)
+    elif options.serve_http:
+        print 'Simple HTTP Server mode'
+        start_server(options, bind_socket(options))
+    elif options.auto_reload:
+        print 'Copy-file only mode'
+        invenio_files = list(generate_invenio_files_list())
+        reloader_loop(invenio_files)
+
+
+if __name__ == '__main__':
+    try:
+        main()
+    except KeyboardInterrupt:
+        print 'Exiting'

src/invenio_devserver/webserver.py

+# -*- coding: utf-8 -*-
+"""
+    werkzeug.serving
+    ~~~~~~~~~~~~~~~~
+
+    There are many ways to serve a WSGI application.  While you're developing
+    it you usually don't want a full blown webserver like Apache but a simple
+    standalone one.  From Python 2.5 onwards there is the `wsgiref`_ server in
+    the standard library.  If you're using older versions of Python you can
+    download the package from the cheeseshop.
+
+    However there are some caveats. Sourcecode won't reload itself when
+    changed and each time you kill the server using ``^C`` you get an
+    `KeyboardInterrupt` error.  While the latter is easy to solve the first
+    one can be a pain in the ass in some situations.
+
+    The easiest way is creating a small ``start-myproject.py`` that runs the
+    application::
+
+        #!/usr/bin/env python
+        # -*- coding: utf-8 -*-
+        from myproject import make_app
+        from werkzeug.serving import run_simple
+
+        app = make_app(...)
+        run_simple('localhost', 8080, app, use_reloader=True)
+
+    You can also pass it a `extra_files` keyword argument with a list of
+    additional files (like configuration files) you want to observe.
+
+    For bigger applications you should consider using `werkzeug.script`
+    instead of a simple start file.
+
+
+    :copyright: (c) 2011 by the Werkzeug Team, see AUTHORS for more details.
+    :license: BSD, see LICENSE for more details.
+"""
+import __future__
+import os
+import socket
+import sys
+import signal
+from urllib import unquote
+from SocketServer import ThreadingMixIn, ForkingMixIn
+from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
+
+import werkzeug
+from werkzeug._internal import _log
+from werkzeug.exceptions import InternalServerError
+
+
+if hasattr(__future__, 'with_statement'):
+    from webserver_ssl import load_ssl_context, \
+                              is_ssl_error, \
+                              generate_adhoc_ssl_context, \
+                              _SSLConnectionFix
+
+
+class WSGIRequestHandler(BaseHTTPRequestHandler, object):
+    """A request handler that implements WSGI dispatching."""
+
+    @property
+    def server_version(self):
+        return 'Werkzeug/' + werkzeug.__version__
+
+    def make_environ(self):
+        if '?' in self.path:
+            path_info, query = self.path.split('?', 1)
+        else:
+            path_info = self.path
+            query = ''
+
+        def shutdown_server():
+            self.server.shutdown_signal = True
+
+        url_scheme = self.server.ssl_context is None and 'http' or 'https'
+        environ = {
+            'wsgi.version':         (1, 0),
+            'wsgi.url_scheme':      url_scheme,
+            'wsgi.input':           self.rfile,
+            'wsgi.errors':          sys.stderr,
+            'wsgi.multithread':     self.server.multithread,
+            'wsgi.multiprocess':    self.server.multiprocess,
+            'wsgi.run_once':        False,
+            'werkzeug.server.shutdown':
+                                    shutdown_server,
+            'SERVER_SOFTWARE':      self.server_version,
+            'REQUEST_METHOD':       self.command,
+            'SCRIPT_NAME':          '',
+            'PATH_INFO':            unquote(path_info),
+            'QUERY_STRING':         query,
+            'CONTENT_TYPE':         self.headers.get('Content-Type', ''),
+            'CONTENT_LENGTH':       self.headers.get('Content-Length', ''),
+            'REMOTE_ADDR':          self.client_address[0],
+            'REMOTE_PORT':          self.client_address[1],
+            'SERVER_NAME':          self.server.server_address[0],
+            'SERVER_PORT':          str(self.server.server_address[1]),
+            'SERVER_PROTOCOL':      self.request_version
+        }
+
+        for key, value in self.headers.items():
+            key = 'HTTP_' + key.upper().replace('-', '_')
+            if key not in ('HTTP_CONTENT_TYPE', 'HTTP_CONTENT_LENGTH'):
+                environ[key] = value
+
+        return environ
+
+    def run_wsgi(self):
+        app = self.server.app
+        environ = self.make_environ()
+        headers_set = []
+        headers_sent = []
+
+        def write(data):
+            assert headers_set, 'write() before start_response'
+            if not headers_sent:
+                status, response_headers = headers_sent[:] = headers_set
+                code, msg = status.split(None, 1)
+                self.send_response(int(code), msg)
+                header_keys = set()
+                for key, value in response_headers:
+                    self.send_header(key, value)
+                    key = key.lower()
+                    header_keys.add(key)
+                if 'content-length' not in header_keys:
+                    self.close_connection = True
+                    self.send_header('Connection', 'close')
+                if 'server' not in header_keys:
+                    self.send_header('Server', self.version_string())
+                if 'date' not in header_keys:
+                    self.send_header('Date', self.date_time_string())
+                self.end_headers()
+
+            assert type(data) is str, 'applications must write bytes'
+            self.wfile.write(data)
+            self.wfile.flush()
+
+        def start_response(status, response_headers, exc_info=None):
+            if exc_info:
+                try:
+                    if headers_sent:
+                        raise exc_info[0], exc_info[1], exc_info[2]
+                finally:
+                    exc_info = None
+            elif headers_set:
+                raise AssertionError('Headers already set')
+            headers_set[:] = [status, response_headers]
+            return write
+
+        def execute(app):
+            application_iter = app(environ, start_response)
+            try:
+                for data in application_iter:
+                    write(data)
+                # make sure the headers are sent
+                if not headers_sent:
+                    write('')
+            finally:
+                if hasattr(application_iter, 'close'):
+                    application_iter.close()
+                application_iter = None
+
+        try:
+            execute(app)
+        except (socket.error, socket.timeout), e:
+            self.connection_dropped(e, environ)
+        except Exception:
+            if self.server.passthrough_errors:
+                raise
+            from werkzeug.debug.tbtools import get_current_traceback
+            traceback = get_current_traceback(ignore_system_exceptions=True)
+            try:
+                # if we haven't yet sent the headers but they are set
+                # we roll back to be able to set them again.
+                if not headers_sent:
+                    del headers_set[:]
+                execute(InternalServerError())
+            except Exception:
+                pass
+            self.server.log('error', 'Error on request:\n%s',
+                            traceback.plaintext)
+
+    def handle(self):
+        """Handles a request ignoring dropped connections."""
+        rv = None
+        try:
+            rv = BaseHTTPRequestHandler.handle(self)
+        except (socket.error, socket.timeout), e:
+            self.connection_dropped(e)
+        except Exception:
+            if self.server.ssl_context is None or not is_ssl_error():
+                raise
+        if self.server.shutdown_signal:
+            self.initiate_shutdown()
+        return rv
+
+    def initiate_shutdown(self):
+        """A horrible, horrible way to kill the server for Python 2.6 and
+        later.  It's the best we can do.
+        """
+        # Windows does not provide SIGKILL, go with SIGTERM then.
+        sig = getattr(signal, 'SIGKILL', signal.SIGTERM)
+        # reloader active
+        if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
+            os.kill(os.getpid(), sig)
+        # python 2.7
+        self.server._BaseServer__shutdown_request = True
+        # python 2.6
+        self.server._BaseServer__serving = False
+
+    def connection_dropped(self, error, environ=None):
+        """Called if the connection was closed by the client.  By default
+        nothing happens.
+        """
+
+    def handle_one_request(self):
+        """Handle a single HTTP request."""
+        self.raw_requestline = self.rfile.readline()
+        if not self.raw_requestline:
+            self.close_connection = 1
+        elif self.parse_request():
+            return self.run_wsgi()
+
+    def send_response(self, code, message=None):
+        """Send the response header and log the response code."""
+        self.log_request(code)
+        if message is None:
+            message = code in self.responses and self.responses[code][0] or ''
+        if self.request_version != 'HTTP/0.9':
+            self.wfile.write("%s %d %s\r\n" %
+                             (self.protocol_version, code, message))
+
+    def version_string(self):
+        return BaseHTTPRequestHandler.version_string(self).strip()
+
+    def address_string(self):
+        return self.client_address[0]
+
+    def log_request(self, code='-', size='-'):
+        self.log('info', '"%s" %s %s', self.requestline, code, size)
+
+    def log_error(self, *args):
+        self.log('error', *args)
+
+    def log_message(self, format, *args):
+        self.log('info', format, *args)
+
+    def log(self, type, message, *args):
+        _log(type, '%s - - [%s] %s\n' % (self.address_string(),
+                                         self.log_date_time_string(),
+                                         message % args))
+
+
+class BaseWSGIServer(HTTPServer, object):
+    """Simple single-threaded, single-process WSGI server."""
+    multithread = False
+    multiprocess = False
+    request_queue_size = 128
+
+    def __init__(self, server_socket, app, handler=None,
+                 passthrough_errors=False, ssl_context=None):
+        if handler is None:
+            handler = WSGIRequestHandler
+
+        self.server_socket = server_socket
+        HTTPServer.__init__(self, ('localhost', 4000), handler)
+        self.app = app
+        self.passthrough_errors = passthrough_errors
+        self.shutdown_signal = False
+
+        if ssl_context is not None:
+            try:
+                from OpenSSL import tsafe
+            except ImportError:
+                raise TypeError('SSL is not available if the OpenSSL '
+                                'library is not installed.')
+            if isinstance(ssl_context, tuple):
+                ssl_context = load_ssl_context(*ssl_context)
+            if ssl_context == 'adhoc':
+                ssl_context = generate_adhoc_ssl_context()
+            self.socket = tsafe.Connection(ssl_context, self.socket)
+            self.ssl_context = ssl_context
+        else:
+            self.ssl_context = None
+
+    def server_bind(self):
+        self.socket = self.server_socket
+        self.server_address = self.socket.getsockname()
+
+    def log(self, type, message, *args):
+        _log(type, message, *args)
+
+    def serve_forever(self):
+        self.shutdown_signal = False
+        try:
+            _log('info', ' * Ready')
+            HTTPServer.serve_forever(self)
+        except KeyboardInterrupt:
+            pass
+
+    def handle_error(self, request, client_address):
+        if self.passthrough_errors:
+            raise
+        else:
+            return HTTPServer.handle_error(self, request, client_address)
+
+    def get_request(self):
+        con, info = self.socket.accept()
+        if self.ssl_context is not None:
+            con = _SSLConnectionFix(con)
+        return con, info
+
+
+class ThreadedWSGIServer(ThreadingMixIn, BaseWSGIServer):
+    """A WSGI server that does threading."""
+    multithread = True
+
+
+class ForkingWSGIServer(ForkingMixIn, BaseWSGIServer):
+    """A WSGI server that does forking."""
+    multiprocess = True
+
+    def __init__(self, server_socket, app, processes=40, handler=None,
+                 passthrough_errors=False, ssl_context=None):
+        BaseWSGIServer.__init__(self, server_socket, app, handler,
+                                passthrough_errors, ssl_context)
+        self.max_children = processes
+
+
+def make_server(server_socket, app=None, threaded=False, processes=1,
+                request_handler=None, passthrough_errors=False,
+                ssl_context=None):
+    """Create a new server instance that is either threaded, or forks
+    or just processes one request after another.
+    """
+    if threaded and processes > 1:
+        raise ValueError("cannot have a multithreaded and "
+                         "multi process server.")
+    elif threaded:
+        return ThreadedWSGIServer(server_socket, app, request_handler,
+                                  passthrough_errors, ssl_context)
+    elif processes > 1:
+        return ForkingWSGIServer(server_socket, app, processes, request_handler,
+                                 passthrough_errors, ssl_context)
+    else:
+        return BaseWSGIServer(server_socket, app, request_handler,
+                              passthrough_errors, ssl_context)
+
+
+def run_simple(server_socket, application,
+               use_debugger=False, use_evalex=True,
+               extra_files=None, threaded=False,
+               processes=1, request_handler=None, static_files=None,
+               passthrough_errors=False, ssl_context=None):
+    """Start an application using wsgiref and with an optional reloader.  This
+    wraps `wsgiref` to fix the wrong default reporting of the multithreaded
+    WSGI variable and adds optional multithreading and fork support.
+
+    .. versionadded:: 0.5
+       `static_files` was added to simplify serving of static files as well
+       as `passthrough_errors`.
+
+    .. versionadded:: 0.6
+       support for SSL was added.
+
+    .. versionadded:: 0.8
+       Added support for automatically loading a SSL context from certificate
+       file and private key.
+
+    :param socket: The socket for accpeting connections
+    :param application: the WSGI application to execute
+    :param use_debugger: should the werkzeug debugging system be used?
+    :param use_evalex: should the exception evaluation feature be enabled?
+    :param extra_files: a list of files the reloader should watch
+                        additionally to the modules.  For example configuration
+                        files.
+    :param threaded: should the process handle each request in a separate
+                     thread?
+    :param processes: number of processes to spawn.
+    :param request_handler: optional parameter that can be used to replace
+                            the default one.  You can use this to replace it
+                            with a different
+                            :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
+                            subclass.
+    :param static_files: a dict of paths for static files.  This works exactly
+                         like :class:`SharedDataMiddleware`, it's actually
+                         just wrapping the application in that middleware before
+                         serving.
+    :param passthrough_errors: set this to `True` to disable the error catching.
+                               This means that the server will die on errors but
+                               it can be useful to hook debuggers in (pdb etc.)
+    :param ssl_context: an SSL context for the connection. Either an OpenSSL
+                        context, a tuple in the form ``(cert_file, pkey_file)``,
+                        the string ``'adhoc'`` if the server should
+                        automatically create one, or `None` to disable SSL
+                        (which is the default).
+    """
+    if use_debugger:
+        from werkzeug.debug import DebuggedApplication
+        application = DebuggedApplication(application, use_evalex)
+    if static_files:
+        from werkzeug.wsgi import SharedDataMiddleware
+        application = SharedDataMiddleware(application, static_files)
+
+    make_server(server_socket, application, threaded,
+                processes, request_handler,
+                passthrough_errors, ssl_context).serve_forever()

src/invenio_devserver/webserver_ssl.py

+def generate_adhoc_ssl_pair(cn=None):
+    from random import random
+    from OpenSSL import crypto
+
+    # pretty damn sure that this is not actually accepted by anyone
+    if cn is None:
+        cn = '*'
+
+    cert = crypto.X509()
+    cert.set_serial_number(int(random() * sys.maxint))
+    cert.gmtime_adj_notBefore(0)
+    cert.gmtime_adj_notAfter(60 * 60 * 24 * 365)
+
+    subject = cert.get_subject()
+    subject.CN = cn
+    subject.O = 'Dummy Certificate'
+
+    issuer = cert.get_issuer()
+    issuer.CN = 'Untrusted Authority'
+    issuer.O = 'Self-Signed'
+
+    pkey = crypto.PKey()
+    pkey.generate_key(crypto.TYPE_RSA, 768)
+    cert.set_pubkey(pkey)
+    cert.sign(pkey, 'md5')
+
+    return cert, pkey
+
+
+def make_ssl_devcert(base_path, host=None, cn=None):
+    """Creates an SSL key for development.  This should be used instead of
+    the ``'adhoc'`` key which generates a new cert on each server start.
+    It accepts a path for where it should store the key and cert and
+    either a host or CN.  If a host is given it will use the CN
+    ``*.host/CN=host``.
+
+    For more information see :func:`run_simple`.
+
+    .. versionadded:: 0.9
+
+    :param base_path: the path to the certificate and key.  The extension
+                      ``.crt`` is added for the certificate, ``.key`` is
+                      added for the key.
+    :param host: the name of the host.  This can be used as an alternative
+                 for the `cn`.
+    :param cn: the `CN` to use.
+    """
+    from OpenSSL import crypto
+    if host is not None:
+        cn = '*.%s/CN=%s' % (host, host)
+    cert, pkey = generate_adhoc_ssl_pair(cn=cn)
+
+    cert_file = base_path + '.crt'
+    pkey_file = base_path + '.key'
+
+    with open(cert_file, 'w') as f:
+        f.write(crypto.dump_certificate(crypto.FILETYPE_PEM, cert))
+    with open(pkey_file, 'w') as f:
+        f.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, pkey))
+
+    return cert_file, pkey_file
+
+
+def generate_adhoc_ssl_context():
+    """Generates an adhoc SSL context for the development server."""
+    from OpenSSL import SSL
+    pkey, cert = generate_adhoc_ssl_pair()
+    ctx = SSL.Context(SSL.SSLv23_METHOD)
+    ctx.use_privatekey(pkey)
+    ctx.use_certificate(cert)
+    return ctx
+
+
+def load_ssl_context(cert_file, pkey_file):
+    """Loads an SSL context from a certificate and private key file."""
+    from OpenSSL import SSL
+    ctx = SSL.Context(SSL.SSLv23_METHOD)
+    ctx.use_certificate_file(cert_file)
+    ctx.use_privatekey_file(pkey_file)
+    return ctx
+
+
+def is_ssl_error(error=None):
+    """Checks if the given error (or the current one) is an SSL error."""
+    if error is None:
+        error = sys.exc_info()[1]
+    from OpenSSL import SSL
+    return isinstance(error, SSL.Error)
+
+
+class _SSLConnectionFix(object):
+    """Wrapper around SSL connection to provide a working makefile()."""
+
+    def __init__(self, con):
+        self._con = con
+
+    def makefile(self, mode, bufsize):
+        return socket._fileobject(self._con, mode, bufsize)
+
+    def __getattr__(self, attrib):
+        return getattr(self._con, attrib)
+
+    def shutdown(self, arg=None):
+        self._con.shutdown()

src/invenio_devserver/wsgi.py

+import traceback
+import sys
+from StringIO import StringIO
+
+
+def replace_error_handler():
+    from invenio import errorlib
+
+    def error_handler(stream='error',
+                      req=None,
+                      prefix='',
+                      suffix='',
+                      alert_admin=False,
+                      subject=''):
+        print traceback.format_exc()[:-1]
+
+    errorlib.register_exception = error_handler
+
+
+def wrap_warn():
+    import warnings
+
+    def wrapper(warn_fun):
+        def fun(*args, **kwargs):
+            traceback.print_stack()
+            return warn_fun(*args, **kwargs)
+        return fun
+
+    warnings.warn = wrapper(warnings.warn)
+
+from invenio.webinterface_handler_wsgi import SimulatedModPythonRequest, \
+                                              SERVER_RETURN, \
+                                              register_exception, \
+                                              is_mp_legacy_publisher_path, \
+                                              mp_legacy_publisher, \
+                                              CFG_WSGI_SERVE_STATIC_FILES, \
+                                              invenio_handler, \
+                                              OK, \
+                                              DONE, \
+                                              alert_admin_for_server_status_p, \
+                                              generate_error_page, \
+                                              is_static_path
+
+
+class BufferedWSGIRequest(SimulatedModPythonRequest):
+    _response_buffer = None
+
+    def start_response_wrapper(self, status, headers):
+        self._response_status = status
+        self._response_headers = headers
+        self._response_buffer = StringIO()
+
+        def write(bytes):
+            self._response_buffer.write(bytes)
+        return write
+
+    def __init__(self, environ, start_response):
+        self.__start_response_orig = start_response
+        super(BufferedWSGIRequest, self).__init__(environ,
+                                                  self.start_response_wrapper)
+
+    def final_flush(self):
+        if self._response_buffer:
+            writer = self.__start_response_orig(self._response_status,
+                                                  self._response_headers)
+            writer(self._response_buffer.getvalue())
+
+
+class WSGIRequest(SimulatedModPythonRequest):
+    def final_flush(self):
+        pass
+
+
+def application(options, environ, start_response):
+    """
+    Entry point for wsgi.
+    """
+    if options.buffer_output:
+        request = BufferedWSGIRequest
+    else:
+        request = WSGIRequest
+    req = request(environ, start_response)
+
+    try:
+        try:
+            possible_module, possible_handler = \
+                            is_mp_legacy_publisher_path(environ['PATH_INFO'])
+            if possible_module is not None:
+                mp_legacy_publisher(req, possible_module, possible_handler)
+            elif CFG_WSGI_SERVE_STATIC_FILES:
+                possible_static_path = is_static_path(environ['PATH_INFO'])
+                if possible_static_path is not None:
+                    from invenio.bibdocfile import stream_file
+                    stream_file(req, possible_static_path)
+                else:
+                    invenio_handler(req)
+            else:
+                invenio_handler(req)
+            req.flush()
+            req.final_flush()
+        except SERVER_RETURN, status:
+            status = int(str(status))
+            if status not in (OK, DONE):
+                req.status = status
+                req.headers_out['content-type'] = 'text/html'
+                admin_to_be_alerted = alert_admin_for_server_status_p(status,
+                                                req.headers_in.get('referer'))
+                if admin_to_be_alerted:
+                    register_exception(req=req, alert_admin=True)
+                if not req.response_sent_p:
+                    start_response(req.get_wsgi_status(),
+                                   req.get_low_level_headers(),
+                                   sys.exc_info())
+                return generate_error_page(req, admin_to_be_alerted)
+            else:
+                req.flush()
+    finally:
+        for (callback, data) in req.get_cleanups():
+            callback(data)
+    return []
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.