Пример #1
0
def make_app(full=False):
    """ Returns Drink WSGI application """
    global reset_required, debug
    if not PERSISTENT_STORAGE or 'BOTTLE_CHILD' not in os.environ:
        with db as c:
            if len(c) < 3:
                reset_required = True

        if reset_required:
            init()

    # And now, http/wsgi part

    mode = config.get('server', 'backend')

    # try some (optional) asynchronous optimization

    async = False

    if dbg_in_env:
        mode = 'debug'

    if full and mode not in ('debug', 'paste', 'rocket'):
        try:
            import gevent.monkey
            gevent.monkey.patch_all()
        except ImportError:
            async = False
        else:
            log.warning('Using gevent for asynchronous processing')
            async = True

    config.async = async
Пример #2
0
def init():
    """ Load all objects.
    Reads configuration `objects_source` from [server].
    Objects to load are read at module loading time from the [objects] section
    """
    altern_source = config.get("server", "objects_source")
    sys.path.insert(0, drink.BASE_DIR)

    log.debug("Trying to load: %s (custom dir: %s)" % (", ".join(objects_to_load), altern_source or "not defined"))
    for obj in objects_to_load:
        log.info("[Loading %s]", obj)
        _imported = None
        if altern_source:
            try:
                exec("from %s import %s as _imported" % (altern_source, obj))
            except ImportError:
                pass
        if not _imported:
            try:
                exec("from . import %s as _imported" % obj)
            except ImportError:
                log.error(
                    "Unable to find %s, remove it from config file in [objects] section\n. You may also fix server.objects_sources in project's drink.ini file.",
                    obj,
                )
                continue

        for _child in dir(_imported):
            klass = getattr(_imported, _child)
            exported_name = getattr(klass, "drink_name", None)
            if exported_name:
                if exported_name in classes:
                    raise ValueError(
                        "Duplicate object: %s !! provided by %r and %r"
                        % (exported_name, classes[exported_name], _imported)
                    )
                else:
                    log.info("  - %s loaded" % exported_name)
                    classes[exported_name] = klass
Пример #3
0
def startup():
    """ Starts drink application """
    import sys

    try:
        import setproctitle
    except ImportError:
        log.warning("Unable to set process' name, easy_install setproctitle, if you want it.")
    else:
        setproctitle.setproctitle('drink')

    # handle commands
    # TODO: create "update" command
    if len(sys.argv) == 2 and sys.argv[1] == "help":
        print """
Drink help

commands:
    make    : make a new drink project
    start   : start all required servers
    run     : starts the server
    db      : starts the database server
    stopdb  : stops the database server
    init    : reset database
    pack    : pack database (more compact/faster)
    rebuild : EXPERIMENTAL, will try to convert & clean an old database to newer format
    export  : EXPERIMENTAL, will try to dump all the data into the given folder
    debug   : run a debugger after loading
    help    : this help :)

Without any argument, it will just run the server.
if DEBUG environment variable is set, it will start in debug mode.
        """
    elif len(sys.argv) == 2 and sys.argv[1] == "init":
        init()
        db.pack()
    elif len(sys.argv) == 2 and sys.argv[1] in ("run", "db", "start"):
        if sys.argv[1] != "run":
            fname = os.path.join(DB_PATH, 'zeo.conf')
            log.debug('fixing %r', fname)
            new_conf = _fix_datadir(open(fname).readlines(), fname)
            open(fname, 'w').write(new_conf)
            cmd = 'zeoctl -C %s start'%(fname)
            os.system(cmd)
        if sys.argv[1] != "db":
            host = config.get('server', 'host')
            port = int(config.get('server', 'port'))
            app = make_app(full=True)
            bottle.run(app=app, host=host, port=port, reloader=debug, server='wsgiref' if debug else config.get('server', 'backend'))
    elif len(sys.argv) == 2 and sys.argv[1] == "stopdb":
        cmd = "zeoctl -C %s stop"%os.path.join(DB_PATH, 'zeo.conf')
        print(cmd)
        os.system(cmd)
    elif len(sys.argv) == 2 and sys.argv[1] == "make":
        def inp(txt):
            return raw_input(txt+': ').strip()

        import shutil
        import drink
        import drink_defaults

        fold = inp('Project folder')

        if os.path.exists(fold):
            shutil.rmtree(fold)

        # fresh copy
        os.mkdir(fold)

        fold = os.path.abspath(fold)

        for project_fold, subs in (
            [ os.path.dirname(drink_defaults.__file__), None],
            [ os.path.join(os.path.dirname(drink.__file__)), ['static', 'templates']],
            ):
            for src in subs or os.listdir(project_fold):
                f = os.path.join(project_fold, src)
                if os.path.isdir(f):
                    if fold.endswith('_'):
                        os.symlink(f, os.path.join(fold, src))
                    else:
                        shutil.copytree(f, os.path.join(fold, src))

        cust = inp('Additional python package with drink objects\n(can contain dots)')
        host = inp('Ip to use (just ENTER to allow all)') or '0.0.0.0'
        port = inp('HTTP port number (ex: "80"), by default %s:5000 will be used'%host) or '5000'
        print "Objects to activate:"
        objs = [
            ('a gtd-like tasklist', 'tasks'),
            ('a wiki-like web page in markdown format', 'markdown'),
            ('a tool to find objects in database', 'finder'),
            ('a filesystem proxy, allow sharing of arbitrary folder or static websites', 'filesystem'),
            ('a simple but powerful spreadsheet', 'spreadsheet'),
        ]
        objects = []
        for o in objs:
            #ans = inp('Activate %s, %s (y/N)'%(o[1], o[0]))
            ans = 'y'
            print("%s : %s"%(' - '.join(o), ans))
            if ans.lower() == 'y':
                objects.append(o[1]+'=')

        layout = []
        k = classes.keys()
        while True:
            name = inp('Additional root item name (just ENTER to finish)')
            if not name:
                break
            for i, n in enumerate(k):
                print "%2d - %s"%(i, n)
            idx = int(inp('Select the desired type index'))
            layout.append('%s = %s'%(name, k[idx]))
        conf = """[server]
database = %s%sdatabase
objects_source = %s
templates = %s
static_folder = %s
host = %s
port = %s


[objects]
%s

[layout]
pages = Folder index
search = Finder
%s
"""%(fold, os.path.sep, cust,
        os.path.abspath(os.path.join(fold, 'templates')),
        os.path.abspath(os.path.join(fold, 'static')),
        host, port,
        '\n'.join(objects),
        '\n'.join(layout),
        )
        open( os.path.join(fold, 'drink.ini'), 'w' ).write(conf)
        conf = """#!/bin/sh
SOCK_DIR="/tmp"
cat << EOF
Example of nginx configation:
upstream drink {
    ip_hash;
    server unix:${SOCK_DIR}/uwsgi.sock;
}
location / {
    uwsgi_pass drink;
    include uwsgi_params;
}
EOF

if [ ! -z "$VIRTUAL_ENV" ] ; then
    export PYTHONHOME=$VIRTUAL_ENV
fi

export PYTHONPATH="${PWD}:%(proj)s:${PYTHONPATH}"

cd "%(proj)s"

http='--http %(host)s:%(port)s'
sock="-s ${SOCK_DIR}/uwsgi.sock -C 666"
stats="--stats ${SOCK_DIR}/stats.socket"
stats='' # disable stats

zeoctl -C database/zeo.conf start

exec uwsgi -p 1 $sock $http $stats --module 'drink:make_app()'
"""%dict(proj=fold, host=host, port=port)
        open( os.path.join(fold, 'start_uwsgi'), 'w' ).write(conf)
        os.chmod(os.path.join(fold, 'start_uwsgi'), 0777)
        if cust.strip():
            try:
                __import__(cust)
            except ImportError:
                cust_dir = os.path.join(fold, cust.strip())
                os.mkdir(cust_dir)
                open(os.path.join(cust_dir, '__init__.py'), 'w')
        print """Project created successfuly.

You can now go into the %s folder and run

- drink db (to start the database daemon)
- drink run (to start the web server)

If you run with DEBUG=1 in environment, templates and python code should reload automatically when changed.
For static files changes, no restart is needed.
        """%fold
    elif len(sys.argv) == 2 and sys.argv[1] == "pack":
        from drink.objects import finder
        finder.init()
        finder.indexer.optimize()
        db.pack()
        print "packed."

    elif len(sys.argv) == 2 and sys.argv[1] == "rebuild":

        from .zdb import Blob

        if not 'settings' in db.db:
            # TODO: request.identity.user = admin
            fake_authentication()
            settings = Settings('settings', '/')
            settings.server_backend = config.get('server', 'backend')
            db.db['settings'] = settings

        objs = list(db.db['pages'].values())
        objs.extend(db.db['users'].itervalues())
        for o in objs:
            try:
                log.info("+%r", o.id)
            except Exception, e:
                import pdb; pdb.set_trace()

            for k, v in o.iteritems():
                k2 = omni(k)
                k3 = omni(v.id)
                try:
                    del o[k]
                except KeyError:
                    pass
                try:
                    del o[k2]
                except KeyError:
                    pass

                v.id = k3
                o[v.id] = v

            try:
                o.reset_items()
            except AttributeError:
                pass

            if not getattr(o, '_no_scan', None):
                objs.extend(o.values())

            f_set = set(('path', 'description', 'id', 'title', 'mime'))

            broken_content = getattr(o, 'content_name', '').endswith('.blob') and 'blobs' in getattr(o, 'content_name', '')

            for name, caster in o.editable_fields.iteritems():
                v = caster.get(o, name)
                if isinstance(v, DataBlob) and broken_content:
                    cur_data = v.open().read()
                    if not cur_data:
                        log.error("FIXING BROKEN BLOB")
                        inp = open(o.content_name, 'rb')
                        out = v.open('w')
                        sz = 2**20
                        while True:
                            data = inp.read(sz)
                            if not data:
                                break
                            out.write(data)
                        inp.close()
                        out.close()
                        os.unlink(o.content_name)
                        o.set_field(name, v)
                        o.content_name = o.title
                elif isinstance(v, Blob):
                    blob = DataBlob(v)
                    setattr(o, name, blob)
                else:
                    try:
                        o.set_field(name, v)
                        f_set.discard(name)
                    except AttributeError:
                        log.error("Could not refresh %r on %r", name, o.id)
                    except (UnicodeError, TypeError), e:
                        import pdb; pdb.set_trace()
            for field in f_set:
                try:
                    _d = getattr(o, field)
                    if isinstance(_d, basestring):
                        setattr(o, field, omni(_d))
                except AttributeError:
                    log.error("Could not set %r on %r", field, o.id)
                except (UnicodeError, TypeError), e:
                    import pdb; pdb.set_trace()
Пример #4
0
def init():
    """ Re-initialize drink's database """
    from drink.objects.finder import reset
    reset()
    with db as root:
        root.clear()
        transaction.commit()

        from .objects import users as obj


        request.identity = FakeId()
        groups = root['groups'] = obj.GroupList('groups', '/')
        users = root['users'] = obj.UserList('users', '/')
        root['groups']['users'] = obj.Group('users', '/groups/')

        admin = obj.User('admin', '/users/')
        request.identity.user = admin

        anon = obj.User('anonymous', '/users/')

        groups.owner = admin
        groups.read_groups = set()
        groups.write_groups = set()

        users.owner = admin
        users.read_groups = set()
        users.write_groups = set()
        users.min_rights = 't'

        root['users']['anonymous'] = anon
        root['users']['admin'] = admin

        admin.set_field('password', 'admin')
        admin.surname = "BOFH"
        admin.name = "Mr Admin"
        admin.owner = admin

        anon.groups = set()
        anon.owner = admin

        users = root['groups']['users']
        users.owner = admin
        users.read_groups = set()
        users.write_groups = set()
        users.min_rights = 't'

        settings = Settings('settings', '/')
        settings.server_backend = config.get('server', 'backend')
        settings.server_port = int(config.get('server', 'port'))
        settings.server_address = config.get('server', 'host')
        settings.debug_framework = config.get('server', 'debug')
        settings.active_objects = set(config.items('objects'))

        root['settings'] = settings

        root['pages'] = Page('pages', '/')

        # deploy layout
        for pagename, name in config.items('layout'):
            try:
                root[pagename] = classes[ name ](pagename, '/')
            except Exception, e:
                log.error('Unable to create %r element with type %r (%r)\nexpect problems, try to enable this object type next time.'%(pagename, name, e))

        mdown = classes['Web page (markdown)']('help', '/pages/')
        help = os.path.abspath(os.path.join(DEFAULTS_DIR, "HELP.md"))
        mdown.content = open(help).read()
        mdown.owner = admin
        mdown.min_rights = 'r'
        root['pages']['help'] = mdown
Пример #5
0
try:
    import pyjaco
except ImportError:
    log.info("Can't find pyjaco ( http://pyjaco.org/ )")
else:
    #: compile python to javascript using `Pyjaco <http://pyjaco.org/>`_
    def js(text):
        c = pyjaco.Compiler()
        c.append_string(text)
        return str(c)


#: All kind of drink objects as a ``{"name": Page_class}`` mapping.

classes = {}
if config.get('server', 'templates'):
    bottle.TEMPLATE_PATH.append(config.get('server', 'templates').strip())
bottle.TEMPLATE_PATH.append(os.path.join(BASE_DIR, 'templates'))
STATIC_PATH = config.get('server', 'static_folder') or os.path.abspath(os.path.join(BASE_DIR, "static"))
BASE_DIR = os.path.dirname(STATIC_PATH.rstrip(os.path.sep))
import drink_defaults
DEFAULTS_DIR = os.path.dirname(drink_defaults.__file__)
ORIG_DB_PATH = os.path.abspath(os.path.join(DEFAULTS_DIR, 'database'))

DB_PATH = config.get('server', 'database') or ORIG_DB_PATH
if not DB_PATH.endswith(os.sep):
    DB_PATH += os.sep

#: update some property
def update_property(pklass, klass, name, value):
    """ Update one page property (dicts) """