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
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
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()
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
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) """