1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00

Added options to the server script

This commit is contained in:
sam 2012-05-01 19:21:21 +07:00
parent 36216fe118
commit 15b203eb11
6 changed files with 491 additions and 57 deletions

428
libs/clize.py Normal file
View File

@ -0,0 +1,428 @@
from __future__ import print_function
from functools import wraps, partial
from collections import namedtuple
import re
from textwrap import TextWrapper
from traceback import print_exc
import sys
import os
import inspect
from gettext import gettext as _, ngettext as _n
class ArgumentError(TypeError):
def __str__(self):
return str(self.args[0] + '\n'
+ help(self.args[2], self.args[1],
just_do_usage=True, do_print=False))
Option = namedtuple(
'Option',
(
'source',
'names',
'default',
'type',
'help',
'optional',
'positional',
'takes_argument',
'catchall',
)
)
def make_flag(
source,
names,
default=False,
type=bool,
help='',
takes_argument=0,
):
return Option(
source, names, default, type, help,
optional=True, positional=False,
takes_argument=takes_argument, catchall=False
)
Command = namedtuple(
'Command',
(
'description',
'footnotes',
'posargs',
'options'
)
)
argdesc = re.compile('^(\w+): (.*)$', re.DOTALL)
def read_arguments(fn, alias, force_positional, require_excess, coerce):
argspec = inspect.getargspec(fn)
doc = inspect.getdoc(fn)
description = []
footnotes = []
opts_help = {}
if doc:
for paragraph in doc.split('\n\n'):
m = argdesc.match(paragraph)
if m:
optname, desc = m.groups()
opts_help[optname] = desc
else:
if opts_help:
footnotes.append(paragraph)
else:
description.append(paragraph)
posargs = []
options = []
for i, argname in enumerate(argspec.args):
try:
default = argspec.defaults[-len(argspec.args) + i]
except (IndexError, TypeError):
default = None
optional = False
type_ = str
else:
optional = True
type_ = type(default)
type_ = coerce.get(argname, type_)
positional = not optional
if argname in force_positional:
positional = True
if positional and options and options[-1].optional:
optional = True
option = Option(
source=argname,
names=(argname.replace('_', '-'),) + alias.get(argname, ()),
default=default,
type=type_,
help=opts_help.get(argname, ''),
optional=optional,
positional=positional,
takes_argument=int(optional and type_ != bool),
catchall=False,
)
if positional:
posargs.append(option)
else:
options.append(option)
if argspec.varargs:
posargs.append(
Option(
source=argspec.varargs,
names=(argspec.varargs.replace('_', '-'),),
default=None,
type=str,
help=opts_help.get(argspec.varargs, ''),
optional=bool(not require_excess or posargs and posargs[-1].optional),
positional=True,
takes_argument=False,
catchall=True,
)
)
return Command(
description=tuple(description), footnotes=tuple(footnotes),
posargs=posargs, options=options)
def get_arg_name(arg):
name = arg.names[0] + (arg.catchall and '...' or '')
return (arg.optional and '[' + name + ']'
or name)
def get_option_names(option):
shorts = []
longs = []
for name in option.names:
if option.positional:
longs.append(name)
elif len(name) == 1:
shorts.append('-' + name)
else:
longs.append('--' + name)
if ((not option.positional and option.type != bool)
or (option.positional and option.type != str)):
longs[-1] += '=' + option.type.__name__.upper()
if option.positional and option.catchall:
longs[-1] += '...'
return ', '.join(shorts + longs)
def get_terminal_width():
return 70 #fair terminal dice roll
def print_arguments(arguments, width=None):
if width == None:
width = 0
for arg in arguments:
width = max(width, len(get_option_names(arg)))
help_wrapper = TextWrapper(
width=get_terminal_width(),
initial_indent=' ' * (width + 5),
subsequent_indent=' ' * (width + 5),
)
return ('\n'.join(
' ' * 2 + '{0:<{width}} {1}'.format(
get_option_names(arg),
arg.help and help_wrapper.fill(
arg.help +
(arg.default not in (None, False)
and _('(default: {0!r})').format(arg.default)
or '')
)[width + 4:]
or '',
width=width,
) for arg in arguments))
def help(name, command, just_do_usage=False, do_print=True, **kwargs):
ret = ""
ret += (_('Usage: {0}{1} {2}').format(
name,
command.options and _(' [OPTIONS]') or '',
' '.join(get_arg_name(arg) for arg in command.posargs),
))
if just_do_usage:
if do_print:
print(ret)
return ret
tw = TextWrapper(
width=get_terminal_width()
)
ret += '\n\n'.join(
tw.fill(p) for p in ('',) + command.description) + '\n'
if command.posargs:
ret += '\n' + _('Positional arguments:') + '\n'
ret += print_arguments(command.posargs) + '\n'
if command.options:
ret += '\n' + _('Options:') + '\n'
ret += print_arguments(command.options) + '\n'
if command.footnotes:
ret += '\n' + '\n\n'.join(tw.fill(p) for p in command.footnotes)
ret += '\n'
if do_print:
print(ret)
return ret
def get_option(name, list):
for option in list:
if name in option.names:
return option
raise KeyError
def coerce_option(val, option, key, command, name):
try:
return option.type(val)
except ValueError:
key = (len(key) == 1 and '-' + key) or ('--' + key)
raise ArgumentError(_("{0} needs an argument of type {1}")
.format(key, option.type.__name__.upper()),
name, command
)
def set_arg_value(val, option, key, params, name, command):
if callable(option.source):
return option.source(name=name, command=command,
val=val, params=params)
else:
params[option.source] = coerce_option(
val, option, key, name, command)
def get_following_arguments(i, option, input, key, command, name):
if i + option.takes_argument >= len(input):
raise ArgumentError(
_n("--{0} needs an argument.",
"--{0} needs {1} arguments.",
option.takes_argument)
.format(key, option.takes_argument),
command, name
)
if option.catchall:
val_ = input[i+1:]
else:
val_ = input[
i+1:i+option.takes_argument+1]
return len(val_), ' '.join(val_)
def clize(
fn=None,
alias={},
help_names=('help', 'h'),
force_positional=(),
coerce={},
require_excess=False,
extra=(),
):
def _wrapperer(fn):
command = read_arguments(
fn,
alias, force_positional,
require_excess, coerce,
)
if help_names:
help_option = make_flag(
source=help,
names=help_names,
help=_("Show this help"),
)
command.options.append(help_option)
command.options.extend(extra)
@wraps(fn)
def _getopts(*input):
name = input[0]
input = input[1:]
kwargs = {}
args = []
skip_next = 0
for i, arg in enumerate(input):
if skip_next:
skip_next -= 1
continue
if arg.startswith('--'):
if len(arg) == 2:
args.extend(input[i+1:])
break
keyarg = arg[2:].split('=', 1)
try:
option = get_option(keyarg[0], command.options)
except KeyError:
raise ArgumentError(
_("Unrecognized option {0}").format(arg),
command,
name
)
else:
if option.takes_argument or option.catchall:
try:
key, val = keyarg
except ValueError:
key = keyarg[0]
skip_next, val = get_following_arguments(
i, option, input, key, command, name
)
else:
key = keyarg[0]
val = True
if set_arg_value(
val, option, key,
kwargs,
name, command
):
return
elif arg.startswith('-'):
skip_next_ = 0
for j, c in enumerate(arg[1:]):
if skip_next_:
skip_next_ -= 1
continue
try:
option = get_option(c, command.options)
except KeyError:
raise ArgumentError(_("Unknown option -{0}.").format(c),
command, name)
else:
if option.takes_argument:
if len(arg) > 2+j:
if option.type == int:
val = ""
for k in range(2+j, len(arg)):
if k == 2+j and arg[k] == '-':
val += '-'
elif '0' <= arg[k] and arg[k] <= '9':
val += arg[k]
else:
break
else:
val = arg[2+j:]
skip_next_ = len(val)
else:
skip_next, val = get_following_arguments(
i, option, input, option.source, command, name
)
else:
val = True
if set_arg_value(
val, option, c,
kwargs,
name, command
):
return
else:
args.append(arg)
for i, option in enumerate(command.posargs):
if i >= len(args):
if option.optional:
if not option.catchall:
args.append(option.default)
else:
raise ArgumentError(_("Not enough arguments."), command, name)
if not option.catchall:
args[i] = option.type(args[i])
if len(args) != len(command.posargs):
if (not command.posargs
or not command.posargs[-1].catchall):
raise ArgumentError(_("Too many arguments."), command, name)
for option in command.options:
if not callable(option.source):
kwargs.setdefault(option.source, option.default)
fn_args = inspect.getargspec(fn).args
for i, key in enumerate(fn_args):
if key in kwargs:
args.insert(i, kwargs[key])
return fn(*args)
return _getopts
if fn == None:
return _wrapperer
else:
return _wrapperer(fn)
def run(fn, args=None):
if args == None:
args = sys.argv
import os.path
try:
fn(*sys.argv)
except ArgumentError as e:
print(os.path.basename(args[0]) + ': ' + str(e),
file=sys.stderr)

View File

@ -16,11 +16,10 @@ DEBUG = True
# default in projectdirectory/static/content/ # default in projectdirectory/static/content/
# use "/" even under Windows # use "/" even under Windows
PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content') PASTE_FILES_ROOT = os.path.join(STATIC_FILES_ROOT, 'content')
# Port and host the embeded python server should be using in prod and in dev
PROD_HOST = "0.0.0.0" # Port and host the embeded python server should be using
PROD_PORT= "80" HOST = "127.0.0.1"
DEV_HOST = "127.0.0.1" PORT= "8000"
DEV_PORT= "8000"
# User and group the server should run as. Set to None if it should be the # User and group the server should run as. Set to None if it should be the
# current user # current user

View File

@ -9,14 +9,5 @@ import sys
import settings import settings
from paste import Paste from paste import Paste
from utils import drop_privileges
def setup_path():
"""
Add the project dir in the python path to the site to run with the
source code beeing just copied/pasted and not installed.
Add fallback on embeded libs to path.
"""
sys.path.insert(0, os.path.dirname(settings.ROOT_DIR))
sys.path.append(os.path.join(settings.ROOT_DIR, 'libs'))

32
src/utils.py Normal file
View File

@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import time
import sys
import os
import settings
import glob
import tempfile
try:
from privilege import drop_privileges_permanently, coerce_user, coerce_group
except (AttributeError):
pass # privilege does't work on several plateform
def drop_privileges():
time.sleep(5)
if settings.USER:
settings.GROUP = settings.GROUP or settings.USER
try:
user = coerce_user(settings.USER)
group = coerce_group(settings.GROUP)
lock_files = glob.glob(os.path.join(tempfile.gettempdir(),
'bottle.*.lock'))
for lock_file in lock_files:
os.chown(lock_file, user, group)
drop_privileges_permanently(settings.USER, settings.GROUP, ())
except Exception:
print "Failed to drop privileges. Running with current user."

66
start.py Normal file → Executable file
View File

@ -9,35 +9,30 @@ import sys
import os import os
import hashlib import hashlib
import thread import thread
import time
import tempfile
import glob
import math import math
from datetime import datetime, timedelta from datetime import datetime, timedelta
from src import settings, setup_path, Paste import settings
sys.path.insert(0, os.path.dirname(settings.ROOT_DIR))
setup_path() sys.path.append(os.path.join(settings.ROOT_DIR, 'libs'))
try:
from privilege import drop_privileges_permanently, coerce_user, coerce_group
except AttributeError:
pass # privilege does't work on several plateform
import bottle
from bottle import (Bottle, route, run, abort, from bottle import (Bottle, route, run, abort,
static_file, debug, view, request) static_file, debug, view, request)
import clize
from src import settings, Paste, drop_privileges
app = Bottle() app = Bottle()
import settings
@app.route('/') @app.route('/')
@view('home') @view('home')
def index(): def index():
max_size_kb = int(math.ceil(settings.MAX_SIZE/1024.0)) max_size_kb = int(math.ceil(settings.MAX_SIZE / 1024.0))
return {'max_size': settings.MAX_SIZE, 'max_size_kb': max_size_kb} return {'max_size': settings.MAX_SIZE, 'max_size_kb': max_size_kb}
@ -97,38 +92,25 @@ def display_paste(paste_id):
return {'paste': paste, 'keep_alive': keep_alive} return {'paste': paste, 'keep_alive': keep_alive}
@app.route('/static/<filename:path>') @clize.clize
def server_static(filename): def runserver(host=settings.HOST, port=settings.PORT, debug=settings.DEBUG,
return static_file(filename, root=settings.STATIC_FILES_ROOT) serve_static=settings.DEBUG):
if serve_static:
if __name__ == "__main__": @app.route('/static/<filename:path>')
def server_static(filename):
def drop_privileges(): return static_file(filename, root=settings.STATIC_FILES_ROOT)
time.sleep(5)
if settings.USER:
settings.GROUP = settings.GROUP or settings.USER
try:
user = coerce_user(settings.USER)
group = coerce_group(settings.GROUP)
lock_files = glob.glob(os.path.join(tempfile.gettempdir(),
'bottle.*.lock'))
for lock_file in lock_files:
os.chown(lock_file, user, group)
drop_privileges_permanently(settings.USER, settings.GROUP, ())
except Exception:
print "Failed to drop privileges. Running with current user."
thread.start_new_thread(drop_privileges, ()) thread.start_new_thread(drop_privileges, ())
if settings.DEBUG: if debug:
debug(True) bottle.debug(True)
run(app, host=settings.DEV_HOST, port=settings.DEV_PORT, run(app, host=host, port=port, reloader=True, server="cherrypy")
reloader=True, server="cherrypy")
else: else:
run(app, host=settings.PROD_HOST, run(app, host=host, port=port, server="cherrypy")
port=settings.PROD_PORT, server="cherrypy")
if __name__ == "__main__":
clize.run(runserver)

View File

@ -79,10 +79,12 @@
<small>Edgar Allan Poe</small> <small>Edgar Allan Poe</small>
</blockquote> </blockquote>
<!--
<h4 id="pixels-total" > <h4 id="pixels-total" >
<p>ø</p> <p>ø</p>
<strong>41,017,923,819</strong> pastes øbinned <strong>41,017,923,819</strong> pastes øbinned
</h4> </h4>
-->
</br> </br>
<p class="greetings span12"> <p class="greetings span12">