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/
# use "/" even under Windows
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"
PROD_PORT= "80"
DEV_HOST = "127.0.0.1"
DEV_PORT= "8000"
# Port and host the embeded python server should be using
HOST = "127.0.0.1"
PORT= "8000"
# User and group the server should run as. Set to None if it should be the
# current user

View File

@ -9,14 +9,5 @@ import sys
import settings
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 hashlib
import thread
import time
import tempfile
import glob
import math
from datetime import datetime, timedelta
from src import settings, setup_path, Paste
setup_path()
try:
from privilege import drop_privileges_permanently, coerce_user, coerce_group
except AttributeError:
pass # privilege does't work on several plateform
import settings
sys.path.insert(0, os.path.dirname(settings.ROOT_DIR))
sys.path.append(os.path.join(settings.ROOT_DIR, 'libs'))
import bottle
from bottle import (Bottle, route, run, abort,
static_file, debug, view, request)
import clize
from src import settings, Paste, drop_privileges
app = Bottle()
import settings
@app.route('/')
@view('home')
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}
@ -97,38 +92,25 @@ def display_paste(paste_id):
return {'paste': paste, 'keep_alive': keep_alive}
@app.route('/static/<filename:path>')
def server_static(filename):
return static_file(filename, root=settings.STATIC_FILES_ROOT)
@clize.clize
def runserver(host=settings.HOST, port=settings.PORT, debug=settings.DEBUG,
serve_static=settings.DEBUG):
if __name__ == "__main__":
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."
if serve_static:
@app.route('/static/<filename:path>')
def server_static(filename):
return static_file(filename, root=settings.STATIC_FILES_ROOT)
thread.start_new_thread(drop_privileges, ())
if settings.DEBUG:
debug(True)
run(app, host=settings.DEV_HOST, port=settings.DEV_PORT,
reloader=True, server="cherrypy")
if debug:
bottle.debug(True)
run(app, host=host, port=port, reloader=True, server="cherrypy")
else:
run(app, host=settings.PROD_HOST,
port=settings.PROD_PORT, server="cherrypy")
run(app, host=host, port=port, server="cherrypy")
if __name__ == "__main__":
clize.run(runserver)

View File

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