mirror of
https://github.com/JonathanHerrewijnen/calibre-web.git
synced 2024-11-14 15:14:07 +00:00
bbf6d9b026
Bugfix for feeds - removed categories related and up - load new books now working - category random now working login page is free of non accessible elements boolean custom column is vivible in UI books with only with certain languages can be shown book shelfs can be deleted from UI Anonymous user view is more resticted Added browse of series in sidebar Dependencys in vendor folder are updated to newer versions (licencs files are now present) Bugfix editing Authors names Made upload on windows working
319 lines
11 KiB
Python
319 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
r'''
|
|
werkzeug.script
|
|
~~~~~~~~~~~~~~~
|
|
|
|
.. admonition:: Deprecated Functionality
|
|
|
|
``werkzeug.script`` is deprecated without replacement functionality.
|
|
Python's command line support improved greatly with :mod:`argparse`
|
|
and a bunch of alternative modules.
|
|
|
|
Most of the time you have recurring tasks while writing an application
|
|
such as starting up an interactive python interpreter with some prefilled
|
|
imports, starting the development server, initializing the database or
|
|
something similar.
|
|
|
|
For that purpose werkzeug provides the `werkzeug.script` module which
|
|
helps you writing such scripts.
|
|
|
|
|
|
Basic Usage
|
|
-----------
|
|
|
|
The following snippet is roughly the same in every werkzeug script::
|
|
|
|
#!/usr/bin/env python
|
|
# -*- coding: utf-8 -*-
|
|
from werkzeug import script
|
|
|
|
# actions go here
|
|
|
|
if __name__ == '__main__':
|
|
script.run()
|
|
|
|
Starting this script now does nothing because no actions are defined.
|
|
An action is a function in the same module starting with ``"action_"``
|
|
which takes a number of arguments where every argument has a default. The
|
|
type of the default value specifies the type of the argument.
|
|
|
|
Arguments can then be passed by position or using ``--name=value`` from
|
|
the shell.
|
|
|
|
Because a runserver and shell command is pretty common there are two
|
|
factory functions that create such commands::
|
|
|
|
def make_app():
|
|
from yourapplication import YourApplication
|
|
return YourApplication(...)
|
|
|
|
action_runserver = script.make_runserver(make_app, use_reloader=True)
|
|
action_shell = script.make_shell(lambda: {'app': make_app()})
|
|
|
|
|
|
Using The Scripts
|
|
-----------------
|
|
|
|
The script from above can be used like this from the shell now:
|
|
|
|
.. sourcecode:: text
|
|
|
|
$ ./manage.py --help
|
|
$ ./manage.py runserver localhost 8080 --debugger --no-reloader
|
|
$ ./manage.py runserver -p 4000
|
|
$ ./manage.py shell
|
|
|
|
As you can see it's possible to pass parameters as positional arguments
|
|
or as named parameters, pretty much like Python function calls.
|
|
|
|
|
|
:copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
|
|
:license: BSD, see LICENSE for more details.
|
|
'''
|
|
from __future__ import print_function
|
|
|
|
import sys
|
|
import inspect
|
|
import getopt
|
|
from os.path import basename
|
|
from werkzeug._compat import iteritems
|
|
|
|
|
|
argument_types = {
|
|
bool: 'boolean',
|
|
str: 'string',
|
|
int: 'integer',
|
|
float: 'float'
|
|
}
|
|
|
|
|
|
converters = {
|
|
'boolean': lambda x: x.lower() in ('1', 'true', 'yes', 'on'),
|
|
'string': str,
|
|
'integer': int,
|
|
'float': float
|
|
}
|
|
|
|
|
|
def run(namespace=None, action_prefix='action_', args=None):
|
|
"""Run the script. Participating actions are looked up in the caller's
|
|
namespace if no namespace is given, otherwise in the dict provided.
|
|
Only items that start with action_prefix are processed as actions. If
|
|
you want to use all items in the namespace provided as actions set
|
|
action_prefix to an empty string.
|
|
|
|
:param namespace: An optional dict where the functions are looked up in.
|
|
By default the local namespace of the caller is used.
|
|
:param action_prefix: The prefix for the functions. Everything else
|
|
is ignored.
|
|
:param args: the arguments for the function. If not specified
|
|
:data:`sys.argv` without the first argument is used.
|
|
"""
|
|
if namespace is None:
|
|
namespace = sys._getframe(1).f_locals
|
|
actions = find_actions(namespace, action_prefix)
|
|
|
|
if args is None:
|
|
args = sys.argv[1:]
|
|
if not args or args[0] in ('-h', '--help'):
|
|
return print_usage(actions)
|
|
elif args[0] not in actions:
|
|
fail('Unknown action \'%s\'' % args[0])
|
|
|
|
arguments = {}
|
|
types = {}
|
|
key_to_arg = {}
|
|
long_options = []
|
|
formatstring = ''
|
|
func, doc, arg_def = actions[args.pop(0)]
|
|
for idx, (arg, shortcut, default, option_type) in enumerate(arg_def):
|
|
real_arg = arg.replace('-', '_')
|
|
if shortcut:
|
|
formatstring += shortcut
|
|
if not isinstance(default, bool):
|
|
formatstring += ':'
|
|
key_to_arg['-' + shortcut] = real_arg
|
|
long_options.append(isinstance(default, bool) and arg or arg + '=')
|
|
key_to_arg['--' + arg] = real_arg
|
|
key_to_arg[idx] = real_arg
|
|
types[real_arg] = option_type
|
|
arguments[real_arg] = default
|
|
|
|
try:
|
|
optlist, posargs = getopt.gnu_getopt(args, formatstring, long_options)
|
|
except getopt.GetoptError as e:
|
|
fail(str(e))
|
|
|
|
specified_arguments = set()
|
|
for key, value in enumerate(posargs):
|
|
try:
|
|
arg = key_to_arg[key]
|
|
except IndexError:
|
|
fail('Too many parameters')
|
|
specified_arguments.add(arg)
|
|
try:
|
|
arguments[arg] = converters[types[arg]](value)
|
|
except ValueError:
|
|
fail('Invalid value for argument %s (%s): %s' % (key, arg, value))
|
|
|
|
for key, value in optlist:
|
|
arg = key_to_arg[key]
|
|
if arg in specified_arguments:
|
|
fail('Argument \'%s\' is specified twice' % arg)
|
|
if types[arg] == 'boolean':
|
|
if arg.startswith('no_'):
|
|
value = 'no'
|
|
else:
|
|
value = 'yes'
|
|
try:
|
|
arguments[arg] = converters[types[arg]](value)
|
|
except ValueError:
|
|
fail('Invalid value for \'%s\': %s' % (key, value))
|
|
|
|
newargs = {}
|
|
for k, v in iteritems(arguments):
|
|
newargs[k.startswith('no_') and k[3:] or k] = v
|
|
arguments = newargs
|
|
return func(**arguments)
|
|
|
|
|
|
def fail(message, code=-1):
|
|
"""Fail with an error."""
|
|
print('Error: %s' % message, file=sys.stderr)
|
|
sys.exit(code)
|
|
|
|
|
|
def find_actions(namespace, action_prefix):
|
|
"""Find all the actions in the namespace."""
|
|
actions = {}
|
|
for key, value in iteritems(namespace):
|
|
if key.startswith(action_prefix):
|
|
actions[key[len(action_prefix):]] = analyse_action(value)
|
|
return actions
|
|
|
|
|
|
def print_usage(actions):
|
|
"""Print the usage information. (Help screen)"""
|
|
actions = sorted(iteritems(actions))
|
|
print('usage: %s <action> [<options>]' % basename(sys.argv[0]))
|
|
print(' %s --help' % basename(sys.argv[0]))
|
|
print()
|
|
print('actions:')
|
|
for name, (func, doc, arguments) in actions:
|
|
print(' %s:' % name)
|
|
for line in doc.splitlines():
|
|
print(' %s' % line)
|
|
if arguments:
|
|
print()
|
|
for arg, shortcut, default, argtype in arguments:
|
|
if isinstance(default, bool):
|
|
print(' %s' % (
|
|
(shortcut and '-%s, ' % shortcut or '') + '--' + arg
|
|
))
|
|
else:
|
|
print(' %-30s%-10s%s' % (
|
|
(shortcut and '-%s, ' % shortcut or '') + '--' + arg,
|
|
argtype, default
|
|
))
|
|
print()
|
|
|
|
|
|
def analyse_action(func):
|
|
"""Analyse a function."""
|
|
description = inspect.getdoc(func) or 'undocumented action'
|
|
arguments = []
|
|
args, varargs, kwargs, defaults = inspect.getargspec(func)
|
|
if varargs or kwargs:
|
|
raise TypeError('variable length arguments for action not allowed.')
|
|
if len(args) != len(defaults or ()):
|
|
raise TypeError('not all arguments have proper definitions')
|
|
|
|
for idx, (arg, definition) in enumerate(zip(args, defaults or ())):
|
|
if arg.startswith('_'):
|
|
raise TypeError('arguments may not start with an underscore')
|
|
if not isinstance(definition, tuple):
|
|
shortcut = None
|
|
default = definition
|
|
else:
|
|
shortcut, default = definition
|
|
argument_type = argument_types[type(default)]
|
|
if isinstance(default, bool) and default is True:
|
|
arg = 'no-' + arg
|
|
arguments.append((arg.replace('_', '-'), shortcut,
|
|
default, argument_type))
|
|
return func, description, arguments
|
|
|
|
|
|
def make_shell(init_func=None, banner=None, use_ipython=True):
|
|
"""Returns an action callback that spawns a new interactive
|
|
python shell.
|
|
|
|
:param init_func: an optional initialization function that is
|
|
called before the shell is started. The return
|
|
value of this function is the initial namespace.
|
|
:param banner: the banner that is displayed before the shell. If
|
|
not specified a generic banner is used instead.
|
|
:param use_ipython: if set to `True` ipython is used if available.
|
|
"""
|
|
if banner is None:
|
|
banner = 'Interactive Werkzeug Shell'
|
|
if init_func is None:
|
|
init_func = dict
|
|
|
|
def action(ipython=use_ipython):
|
|
"""Start a new interactive python session."""
|
|
namespace = init_func()
|
|
if ipython:
|
|
try:
|
|
try:
|
|
from IPython.frontend.terminal.embed import InteractiveShellEmbed
|
|
sh = InteractiveShellEmbed(banner1=banner)
|
|
except ImportError:
|
|
from IPython.Shell import IPShellEmbed
|
|
sh = IPShellEmbed(banner=banner)
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
sh(global_ns={}, local_ns=namespace)
|
|
return
|
|
from code import interact
|
|
interact(banner, local=namespace)
|
|
return action
|
|
|
|
|
|
def make_runserver(app_factory, hostname='localhost', port=5000,
|
|
use_reloader=False, use_debugger=False, use_evalex=True,
|
|
threaded=False, processes=1, static_files=None,
|
|
extra_files=None, ssl_context=None):
|
|
"""Returns an action callback that spawns a new development server.
|
|
|
|
.. versionadded:: 0.5
|
|
`static_files` and `extra_files` was added.
|
|
|
|
..versionadded:: 0.6.1
|
|
`ssl_context` was added.
|
|
|
|
:param app_factory: a function that returns a new WSGI application.
|
|
:param hostname: the default hostname the server should listen on.
|
|
:param port: the default port of the server.
|
|
:param use_reloader: the default setting for the reloader.
|
|
:param use_evalex: the default setting for the evalex flag of the debugger.
|
|
:param threaded: the default threading setting.
|
|
:param processes: the default number of processes to start.
|
|
:param static_files: optional dict of static files.
|
|
:param extra_files: optional list of extra files to track for reloading.
|
|
:param ssl_context: optional SSL context for running server in HTTPS mode.
|
|
"""
|
|
def action(hostname=('h', hostname), port=('p', port),
|
|
reloader=use_reloader, debugger=use_debugger,
|
|
evalex=use_evalex, threaded=threaded, processes=processes):
|
|
"""Start a new development server."""
|
|
from werkzeug.serving import run_simple
|
|
app = app_factory()
|
|
run_simple(hostname, port, app,
|
|
use_reloader=reloader, use_debugger=debugger,
|
|
use_evalex=evalex, extra_files=extra_files,
|
|
reloader_interval=1, threaded=threaded, processes=processes,
|
|
static_files=static_files, ssl_context=ssl_context)
|
|
return action
|