new command line options

This commit is contained in:
mdipierro
2019-05-01 21:07:52 -07:00
parent 6f12be7e20
commit 1c08c07a0f
25 changed files with 809 additions and 409 deletions
+21
View File
@@ -1,3 +1,24 @@
## 2.19.0
- new command line options (Thanks Paolo Pastori)
OLD NAME NEW NAME
================== ==================
--debug --log_level
--nogui --no_gui
--ssl_private_key --server_key
--ssl_certificate --server_cert
--minthreads --min_threads
--maxthreads --max_threads
--profiler --profiler_dir
--run-cron --with_cron
--softcron --soft_cron
--cron --cron_run
--cronjob * --cron_job *
--test --run_doctests
--add_options
--interface
--crontab
## 2.18.1-2.18.5
- pydal 19.04
- made template its own module (Yet Another Template Language)
+1 -1
View File
@@ -19,4 +19,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
+1 -1
View File
@@ -24,4 +24,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+1 -1
View File
@@ -25,4 +25,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+1 -1
View File
@@ -25,4 +25,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+1 -1
View File
@@ -24,4 +24,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+1 -1
View File
@@ -24,4 +24,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+1 -1
View File
@@ -18,4 +18,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
+1 -1
View File
@@ -20,4 +20,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
@@ -22,4 +22,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
@@ -17,4 +17,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
@@ -17,4 +17,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
@@ -17,4 +17,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
@@ -17,4 +17,4 @@ WORKDIR /web2py
EXPOSE 443
CMD python /web2py/web2py.py --nogui --no-banner -a 'a' -c web2py.crt -k web2py.key -i 0.0.0.0 -p 443
CMD python /web2py/web2py.py --no_gui --no_banner -a 'a' -k web2py.key -c web2py.crt -i 0.0.0.0 -p 443
+1 -1
View File
@@ -24,4 +24,4 @@ WORKDIR /home/web2py/web2py
EXPOSE 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --nogui --no-banner -a 'a' -i 0.0.0.0 -p 8000
CMD . /home/web2py/bin/activate && python /home/web2py/web2py/web2py.py --no_gui --no_banner -a 'a' -i 0.0.0.0 -p 8000
+712
View File
@@ -0,0 +1,712 @@
# -*- coding: utf-8 -*-
# vim: set ts=4 sw=4 et ai:
"""
| This file is part of the web2py Web Framework
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
Command line interface
----------------------
The processing of all command line arguments is done using
the argparse library in the console function.
The basic principle is to process and check for all options
in a single place, this place is the parse_args function.
Notice that when I say all options I mean really all,
options sourced from a configuration file are included.
A brief summary of options style follows,
for the benefit of code maintainers/developers:
- use the underscore to split words in long names (as in
'--run_system_tests')
- remember to allow the '-' too as word separator (e.g.
'--run-system-tests') but do not use this form on help
- prefer short names on help messages, instead use
all options names in warning/error messages (e.g.
'-R/--run requires -S/--shell')
"""
from __future__ import print_function
__author__ = 'Paolo Pastori'
import os.path
import argparse
import logging
import socket
import sys
import re
import ast
from collections import OrderedDict
import copy
from gluon._compat import PY2
from gluon.shell import die
from gluon.utils import is_valid_ip_address
from gluon.settings import global_settings
def warn(msg):
print("%s: warning: %s" % (sys.argv[0], msg), file=sys.stderr)
def is_appdir(applications_parent, app):
return os.path.isdir(os.path.join(applications_parent, 'applications', app))
def console(version):
"""
Load command line options.
Trivial -h/--help and --version options are also processed.
Returns a namespace object (in the sense of argparse)
with all options loaded.
"""
# replacement hints for deprecated options
deprecated_opts = {
'--debug': '--log_level',
'--nogui': '--no_gui',
'--ssl_private_key': '--server_key',
'--ssl_certificate': '--server_cert',
'--interfaces': None, # dest is 'interfaces', hint is '--interface'
'-n': '--min_threads', '--numthreads': '--min_threads',
'--minthreads': '--min_threads',
'--maxthreads': '--max_threads',
'-z': None, '--shutdown_timeout': None,
'--profiler': '--profiler_dir',
'--run-cron': '--with_cron',
'--softcron': '--soft_cron',
'--cron': '--cron_run',
'--test': '--run_doctests'
}
class HelpFormatter2(argparse.HelpFormatter):
"""Hides the options listed in _hidden_options in usage help."""
# NOTE: preferred style for long options name is to use '_'
# between words (as in 'no_gui'), also accept the '-' in
# most of the options but do not show both versions on help
_omitted_opts = ('--add-options', '--errors-to-console',
'--no-banner', '--log-level', '--no-gui', '--import-models',
'--server-name', '--server-key', '--server-cert', '--ca-cert',
'--pid-filename', '--log-filename', '--min-threads',
'--max-threads', '--request-queue-size', '--socket-timeout',
'--profiler-dir', '--with-scheduler', '--with-cron',
'--soft-cron', '--cron-run',
'--run-doctests', '--run-system-tests', '--with-coverage')
_hidden_options = _omitted_opts + tuple(deprecated_opts.keys())
def _format_action_invocation(self, action):
if not action.option_strings:
return super(HelpFormatter2, self)._format_action_invocation(action)
parts = []
if action.nargs == 0:
parts.extend(filter(lambda o : o not in self._hidden_options,
action.option_strings))
else:
default = action.dest.upper()
args_string = self._format_args(action, default)
for option_string in action.option_strings:
if option_string in self._hidden_options:
continue
parts.append('%s %s' % (option_string, args_string))
return ', '.join(parts)
class ExtendAction(argparse._AppendAction):
"""Action to accumulate values in a flat list."""
def __call__(self, parser, namespace, values, option_string=None):
if isinstance(values, list):
# must copy to avoid altering the option default value
items = argparse._ensure_value(namespace, self.dest, [])[:]
# for options that allows multiple args (i.e. those declared
# with add_argument(..., nargs='+', ...)) the values are
# always placed into a list
while len(values) == 1 and isinstance(values[0], list):
values = values[0]
items.extend(values)
setattr(namespace, self.dest, items)
else:
super(ExtendAction, self).__call__(parser, namespace, values, option_string)
parser = argparse.ArgumentParser(
usage='python %(prog)s [options]',
description='web2py Web Framework startup script.',
epilog='''NOTE: unless a password is specified (-a 'passwd')
web2py will attempt to run a GUI to ask for it when starting the web server
(if not disabled with --no_gui).''',
formatter_class=HelpFormatter2,
add_help=False) # do not add -h/--help option
# global options
g = parser.add_argument_group('global options')
g.add_argument('-h', '--help', action='help',
help='show this help message and exit')
g.add_argument('--version', action='version',
version=version,
help="show program's version and exit")
folder = os.getcwd()
g.add_argument('-f', '--folder',
default=folder, metavar='WEB2PY_DIR',
help='web2py installation directory (%(default)s)')
def existing_file(v):
if not v:
raise argparse.ArgumentTypeError('empty argument')
if not os.path.exists(v):
raise argparse.ArgumentTypeError("file %r not found" % v)
return v
g.add_argument('-L', '--config',
type=existing_file,
metavar='PYTHON_FILE',
help='read all options from PYTHON_FILE')
g.add_argument('--add_options', '--add-options',
default=False,
action='store_true', help=
'add options to existing ones, useful with -L only')
g.add_argument('-a', '--password',
default='<ask>', help=
'password to be used for administration (use "<recycle>" '
'to reuse the last password), when no password is available '
'the administrative web interface will be disabled')
g.add_argument('-e', '--errors_to_console', '--errors-to-console',
default=False,
action='store_true',
help='log application errors to console')
g.add_argument('--no_banner', '--no-banner',
default=False,
action='store_true',
help='do not print header banner')
g.add_argument('-Q', '--quiet',
default=False,
action='store_true',
help='disable all output')
integer_log_level = []
def log_level(v):
# try to convert a lgging level name to its numeric value,
# could use logging.getLevelName but not with
# 3.4 <= Python < 3.4.2, see
# https://docs.python.org/3/library/logging.html#logging.getLevelName)
try:
name2level = logging._levelNames
except AttributeError:
# logging._levelNames has gone with Python 3.4, see
# https://github.com/python/cpython/commit/3b84eae03ebd8122fdbdced3d85999dd9aedfc7e
name2level = logging._nameToLevel
try:
return name2level[v.upper()]
except KeyError:
pass
try:
ill = int(v)
# value deprecated: integer in range(101)
if 0 <= ill <= 100:
integer_log_level.append(ill)
return ill
except ValueError:
pass
raise argparse.ArgumentTypeError("bad level %r" % v)
g.add_argument('-D', '--log_level', '--log-level',
'--debug', # deprecated
default='WARNING',
type=log_level,
metavar='LOG_LEVEL', help=
'set log level, allowed values are: NOTSET, DEBUG, INFO, WARN, '
'WARNING, ERROR, and CRITICAL, also lowercase (default is '
'%(default)s)')
# GUI options
g = parser.add_argument_group('GUI options')
g.add_argument('--no_gui', '--no-gui',
'--nogui', # deprecated
default=False,
action='store_true',
help='do not run GUI')
g.add_argument('-t', '--taskbar',
default=False,
action='store_true',
help='run in taskbar (system tray)')
# console options
g = parser.add_argument_group('console options')
g.add_argument('-S', '--shell',
metavar='APP_ENV', help=
'run web2py in Python interactive shell or IPython (if installed) '
'with specified application environment (if application does not '
'exist it will be created). APP_ENV like a/c/f?x=y (c, f and vars '
'optional), if APP_ENV include the action f then after the '
'action execution the interpreter is exited')
g.add_argument('-B', '--bpython',
default=False,
action='store_true', help=
'use bpython (if installed) when running in interactive shell, '
'see -S above')
g.add_argument('-P', '--plain',
default=False,
action='store_true', help=
'use plain Python shell when running in interactive shell, '
'see -S above')
g.add_argument('-M', '--import_models', '--import-models',
default=False,
action='store_true', help=
'auto import model files when running in interactive shell '
'(default is %(default)s), see -S above. NOTE: when the APP_ENV '
'argument of -S include a controller c automatic import of '
'models is always enabled')
g.add_argument('-R', '--run',
type=existing_file,
metavar='PYTHON_FILE', help=
'run PYTHON_FILE in web2py environment; require -S')
g.add_argument('-A', '--args',
default=[],
nargs=argparse.REMAINDER, help=
'use this to pass arguments to the PYTHON_FILE above; require '
'-R. NOTE: must be the last option because eat all remaining '
'arguments')
# web server options
g = parser.add_argument_group('web server options')
g.add_argument('-s', '--server_name', '--server-name',
default=socket.gethostname(),
help='web server name (%(default)s)')
def ip_addr(v):
if not is_valid_ip_address(v):
raise argparse.ArgumentTypeError("bad IP address %s" % v)
return v
g.add_argument('-i', '--ip',
default='127.0.0.1',
type=ip_addr, metavar='IP_ADDR', help=
'IP address of the server (%(default)s), accept either IPv4 or '
'IPv6 (e.g. ::1) addresses. NOTE: this option is ignored if '
'--interface is specified')
def not_negative_int(v, err_label='value'):
try:
iv = int(v)
if iv < 0: raise ValueError()
return iv
except ValueError:
pass
raise argparse.ArgumentTypeError("bad %s %s" % (err_label, v))
def port(v):
return not_negative_int(v, err_label='port')
g.add_argument('-p', '--port',
default=8000,
type=port, metavar='NUM', help=
'port of server (%(default)d). '
'NOTE: this option is ignored if --interface is specified')
g.add_argument('-k', '--server_key', '--server-key',
'--ssl_private_key', # deprecated
type=existing_file,
metavar='FILE', help='server private key')
g.add_argument('-c', '--server_cert', '--server-cert',
'--ssl_certificate', # deprecated
type=existing_file,
metavar='FILE', help='server certificate')
g.add_argument('--ca_cert', '--ca-cert',
type=existing_file,
metavar='FILE', help='CA certificate')
def iface(v, sep=','):
if not v:
raise argparse.ArgumentTypeError('empty argument')
if sep == ':':
# deprecated --interfaces ip:port:key:cert:ca_cert
# IPv6 addresses in square brackets
if v.startswith('['):
# IPv6
ip, v_remainder = v.split(']', 1)
ip = ip[1:]
ifp = v_remainder[1:].split(':')
ifp.insert(0, ip)
else:
# IPv4
ifp = v.split(':')
else:
# --interface
ifp = v.split(sep, 5)
if not len(ifp) in (2, 4, 5):
raise argparse.ArgumentTypeError("bad interface %r" % v)
try:
ip_addr(ifp[0])
ifp[1] = port(ifp[1])
for fv in ifp[2:]:
existing_file(fv)
except argparse.ArgumentTypeError as ex:
raise argparse.ArgumentTypeError("bad interface %r (%s)" % (v, ex))
return tuple(ifp)
g.add_argument('--interface', dest='interfaces',
default=[], action=ExtendAction,
type=iface, nargs='+',
metavar='IF_INFO', help=
'listen on specified interface, IF_INFO = '
'IP_ADDR,PORT[,KEY_FILE,CERT_FILE[,CA_CERT_FILE]].'
' NOTE: this option can be used multiple times to provide additional '
'interfaces to choose from but you can choose which one to listen to '
'only using the GUI otherwise the first interface specified is used')
def ifaces(v):
# deprecated --interfaces 'if1;if2;...'
if not v:
raise argparse.ArgumentTypeError('empty argument')
return [iface(i, ':') for i in v.split(';')]
g.add_argument('--interfaces', # deprecated
default=argparse.SUPPRESS, # do not set if absent
action=ExtendAction,
type=ifaces,
help=argparse.SUPPRESS) # do not show on help
g.add_argument('-d', '--pid_filename', '--pid-filename',
default='httpserver.pid',
metavar='FILE', help='server pid file (%(default)s)')
g.add_argument('-l', '--log_filename', '--log-filename',
default='httpserver.log',
metavar='FILE', help='server log file (%(default)s)')
g.add_argument('--min_threads', '--min-threads',
'--minthreads', '-n', '--numthreads', # deprecated
type=not_negative_int, metavar='NUM',
help='minimum number of server threads')
g.add_argument('--max_threads', '--max-threads',
'--maxthreads', # deprecated
type=not_negative_int, metavar='NUM',
help='maximum number of server threads')
g.add_argument('-q', '--request_queue_size', '--request-queue-size',
default=5,
type=not_negative_int, metavar='NUM', help=
'max number of queued requests when server busy (%(default)d)')
g.add_argument('-o', '--timeout',
default=10,
type=not_negative_int, metavar='SECONDS',
help='timeout for individual request (%(default)d seconds)')
g.add_argument('--socket_timeout', '--socket-timeout',
default=5,
type=not_negative_int, metavar='SECONDS',
help='timeout for socket (%(default)d seconds)')
g.add_argument('-z', '--shutdown_timeout', # deprecated
type=not_negative_int,
help=argparse.SUPPRESS) # do not show on help
g.add_argument('-F', '--profiler_dir', '--profiler-dir',
'--profiler', # deprecated
help='profiler directory')
# scheduler options
g = parser.add_argument_group('scheduler options')
g.add_argument('-X', '--with_scheduler', '--with-scheduler',
default=False,
action='store_true', help=
'run schedulers alongside web server; require --K')
def is_app(app):
return is_appdir(folder, app)
def scheduler(v):
if not v:
raise argparse.ArgumentTypeError('empty argument')
if ',' in v:
# legacy "app1,..."
vl = [n.strip() for n in v.split(',')]
return [scheduler(iv) for iv in vl]
vp = [n.strip() for n in v.split(':')]
app = vp[0]
if not app:
raise argparse.ArgumentTypeError('empty application')
if not is_app(app):
warn("argument -K/--scheduler: bad application %r, skipped" % app)
return None
return ':'.join(filter(None, vp))
g.add_argument('-K', '--scheduler', dest='schedulers',
default=[], action=ExtendAction,
type=scheduler, nargs='+',
metavar='APP_INFO', help=
'run scheduler for the specified application(s), APP_INFO = '
'APP_NAME[:GROUPS], that is an optional list of groups can follow '
'the application name (e.g. app:group1:group2); require a scheduler '
"to be defined in the application's models. NOTE: this option can "
'be used multiple times to add schedulers')
# cron options
g = parser.add_argument_group('cron options')
g.add_argument('-Y', '--with_cron', '--with-cron',
'--run-cron', # deprecated
default=False,
action='store_true', help=
'run cron service alongside web server')
def crontab(v):
if not v:
raise argparse.ArgumentTypeError('empty argument')
if not is_app(v):
warn("argument --crontab: bad application %r, skipped" % v)
return None
return v
g.add_argument('--crontab', dest='crontabs',
default=[], action=ExtendAction,
type=crontab, nargs='+',
metavar='APP_NAME', help=
'tell cron to read the crontab for the specified application(s) '
'only, the default behaviour is to read the crontab for all of the '
'installed applications. NOTE: this option can be used multiple '
'times to build the list of crontabs to be processed by cron')
g.add_argument('--soft_cron', '--soft-cron',
'--softcron', # deprecated
default=False,
action='store_true', help=
'use cron software emulation instead of separate cron process; '
'require -Y. NOTE: use of cron software emulation is strongly '
'discouraged')
g.add_argument('-C', '--cron_run', '--cron-run',
'--cron', # deprecated
default=False,
action='store_true', help=
'trigger a cron run and exit; usually used when invoked '
'from a system (external) crontab')
g.add_argument('--cron_job', # NOTE: this is intended for internal use only
default=False,
action='store_true',
help=argparse.SUPPRESS) # do not show on help
# test options
g = parser.add_argument_group('test options')
g.add_argument('-v', '--verbose',
default=False,
action='store_true', help='increase verbosity')
g.add_argument('-T', '--run_doctests', '--run-doctests',
'--test', # deprecated
metavar='APP_ENV', help=
'run doctests in application environment. APP_ENV like a/c/f (c, f '
'optional)')
g.add_argument('--run_system_tests', '--run-system-tests',
default=False,
action='store_true', help='run web2py test suite')
g.add_argument('--with_coverage', '--with-coverage',
default=False,
action='store_true', help=
'collect coverage data when used with --run_system_tests; '
'require Python 2.7+ and the coverage module installed')
# other options
g = parser.add_argument_group('other options')
g.add_argument('-G', '--GAE', dest='gae',
metavar='APP_NAME', help=
'will create app.yaml and gaehandler.py and exit')
options = parse_args(parser, sys.argv[1:],
deprecated_opts, integer_log_level)
# make a copy of all options for global_settings
copy_options = copy.deepcopy(options)
copy_options.password = '******'
global_settings.cmd_options = copy_options
return options
REGEX_PEP263 = r'^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)'
def get_pep263_encoding(source):
"""
Read python source file encoding, according to PEP 263, see
https://www.python.org/dev/peps/pep-0263/
"""
with open(source, 'r') as sf:
l12 = (sf.readline(), sf.readline())
m12 = re.match(REGEX_PEP263, l12[0]) or re.match(REGEX_PEP263, l12[1])
return m12 and m12.group(1)
IGNORE = lambda: None
def load_config(config_file, opt_map):
"""
Load options from config file (a Python script).
config_file(str): file name
opt_map(dict): mapping fom option name (key) to callable (val),
used to post-process parsed value for the option
Notice that the configuring Python script is never executed/imported,
instead the ast library is used to evaluate each option assignment,
provided that it is writen on a single line.
Returns an OrderedDict with sourced options.
"""
REGEX_ASSIGN_EXP = re.compile(r'\s*=\s*(.+)')
map_items = opt_map.items()
# preserve the order of loaded options even if this is not needed
pl = OrderedDict()
config_encoding = get_pep263_encoding(config_file)
# NOTE: assume 'ascii' encoding when not explicitly stated (Python 2),
# this is not correct for Python 3 where the default is 'utf-8'
open_kwargs = dict() if PY2 else dict(encoding=config_encoding or 'ascii')
with open(config_file, 'r', **open_kwargs) as cfil:
for linenum, clin in enumerate(cfil, start=1):
if PY2 and config_encoding:
clin = unicode(clin, config_encoding)
clin = clin.strip()
for opt, mapr in map_items:
if clin.startswith(opt):
m = REGEX_ASSIGN_EXP.match(clin[len(opt):])
if m is None: continue
try:
val = opt_map[opt](ast.literal_eval(m.group(1)))
except:
die("cannot parse config file %r at line %d" % (config_file, linenum))
if val is not IGNORE:
pl[opt] = val
return pl
def parse_args(parser, cli_args, deprecated_opts, integer_log_level,
namespace=None):
#print('PARSING ARGS:', cli_args)
del integer_log_level[:]
options = parser.parse_args(cli_args, namespace)
#print('PARSED OPTIONS:', options)
# warn for deprecated options
deprecated_args = [a for a in cli_args if a in deprecated_opts]
for da in deprecated_args:
# verify if it was a real option by looking into
# parsed values for the actual destination
hint = deprecated_opts[da]
dest = (hint or da).lstrip('-')
default = parser.get_default(dest)
if da == '--interfaces':
hint = '--interface'
if getattr(options, dest) is not default:
# the option has been specified
msg = "%s is deprecated" % da
if hint:
msg += ", use %s instead" % hint
warn(msg)
# warn for deprecated values
if integer_log_level and '--debug' not in deprecated_args:
warn('integer argument for -D/--log_level is deprecated, '
'use label instead')
# fix schedulers and die if all were skipped
if None in options.schedulers:
options.schedulers = [i for i in options.schedulers if i is not None]
if not options.schedulers:
die('no scheduler left')
# fix crontabs and die if all were skipped
if None in options.crontabs:
options.crontabs = [i for i in options.crontabs if i is not None]
if not options.crontabs:
die('no crontab left')
# taskbar
if options.taskbar and os.name != 'nt':
warn('--taskbar not supported on this platform, skipped')
options.taskbar = False
# options consistency checkings
if options.run and not options.shell:
die('-R/--run requires -S/--shell', exit_status=2)
if options.args and not options.run:
die('-A/--args requires -R/--run', exit_status=2)
if options.with_scheduler and not options.schedulers:
die('-X/--with_scheduler requires -K/--scheduler', exit_status=2)
if options.soft_cron and not options.with_cron:
die('--soft_cron requires -Y/--with_cron', exit_status=2)
if options.shell:
for o, os in dict(with_scheduler='-X/--with_scheduler',
schedulers='-K/--scheduler',
with_cron='-Y/--with_cron',
cron_run='-C/--cron_run',
run_doctests='-T/--run_doctests',
run_system_tests='--run_system_tests').items():
if getattr(options, o):
die("-S/--shell and %s are conflicting options" % os,
exit_status=2)
if options.bpython and options.plain:
die('-B/--bpython and -P/--plain are conflicting options',
exit_status=2)
if options.cron_run:
for o, os in dict(with_cron='-Y/--with_cron',
run_doctests='-T/--run_doctests',
run_system_tests='--run_system_tests').items():
if getattr(options, o):
die("-C/--cron_run and %s are conflicting options" % os,
exit_status=2)
if options.run_doctests and options.run_system_tests:
die('-T/--run_doctests and --run_system_tests are conflicting options',
exit_status=2)
if options.config:
# load options from file,
# all options sourced from file that evaluates to False
# are skipped, the special IGNORE value is used for this
store_true = lambda v: True if v else IGNORE
str_or_default = lambda v : str(v) if v else IGNORE
list_or_default = lambda v : (
[str(i) for i in v] if isinstance(v, list) else [str(v)]) if v \
else IGNORE
# NOTE: 'help', 'version', 'folder', 'cron_job' and 'GAE' are not
# sourced from file, the same applies to deprecated options
opt_map = {
# global options
'config': str_or_default,
'add_options': store_true,
'password': str_or_default,
'errors_to_console': store_true,
'no_banner': store_true,
'quiet': store_true,
'log_level': str_or_default,
# GUI options
'no_gui': store_true,
'taskbar': store_true,
# console options
'shell': str_or_default,
'bpython': store_true,
'plain': store_true,
'import_models': store_true,
'run': str_or_default,
'args': list_or_default,
# web server options
'server_name': str_or_default,
'ip': str_or_default,
'port': str_or_default,
'server_key': str_or_default,
'server_cert': str_or_default,
'ca_cert': str_or_default,
'interface': list_or_default,
'pid_filename': str_or_default,
'log_filename': str_or_default,
'min_threads': str_or_default,
'max_threads': str_or_default,
'request_queue_size': str_or_default,
'timeout': str_or_default,
'socket_timeout': str_or_default,
'profiler_dir': str_or_default,
# scheduler options
'with_scheduler': store_true,
'scheduler': list_or_default,
# cron options
'with_cron': store_true,
'crontab': list_or_default,
'soft_cron': store_true,
'cron_run': store_true,
# test options
'verbose': store_true,
'run_doctests': str_or_default,
'run_system_tests': store_true,
'with_coverage': store_true,
}
od = load_config(options.config, opt_map)
#print("LOADED FROM %s:" % options.config, od)
# convert loaded options dict as retuned by load_config
# into a list of arguments for further parsing by parse_args
file_args = []; args_args = [] # '--args' must be the last
for key, val in od.items():
if key != 'args':
file_args.append('--' + key)
if isinstance(val, list): file_args.extend(val)
elif not isinstance(val, bool): file_args.append(val)
else:
args_args = ['--args'] + val
file_args += args_args
if options.add_options:
# add options to existing ones,
# must clear config to avoid infinite recursion
options.config = options.add_options = None
return parse_args(parser, file_args,
deprecated_opts, integer_log_level, options)
return parse_args(parser, file_args,
deprecated_opts, integer_log_level)
return options
+1 -2
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@@ -374,7 +373,7 @@ def get_session(request, other_application='admin'):
if not os.path.exists(session_filename):
session_filename = generate(session_filename)
osession = storage.load_storage(session_filename)
except:
except Exception:
osession = storage.Storage()
return osession
+3 -6
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@@ -355,11 +354,9 @@ class Request(Storage):
"""
cmd_opts = global_settings.cmd_options
# checking if this is called within the scheduler or within the shell
# in addition to checking if it's a cronjob
# FIXME: cmd_opts.scheduler does not imply that
# we are running in the scheduler
if (self.is_https or cmd_opts and (
cmd_opts.shell or cmd_opts.scheduler or cmd_opts.cronjob)):
# in addition to checking if it's a cron job
if (self.is_https or self.is_scheduler or cmd_opts and (
cmd_opts.shell or cmd_opts.cron_job)):
current.session.secure()
else:
current.session.forget()
+2 -2
View File
@@ -1,4 +1,3 @@
#!/bin/env python
# -*- coding: utf-8 -*-
"""
@@ -557,7 +556,8 @@ def wsgibase(environ, responder):
return wsgibase(new_environ, responder)
if global_settings.web2py_crontype == 'soft':
cmd_opts = global_settings.cmd_options
newcron.softcron(global_settings.applications_parent).start()
newcron.softcron(global_settings.applications_parent,
apps=cmd_opts and cmd_opts.crontabs).start()
return http_response.to(responder, env=env)
+2 -5
View File
@@ -287,7 +287,7 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
('dom', now_s.tm_mday),
('dow', (now_s.tm_wday + 1) % 7))
if apps is None:
if not apps:
apps = [x for x in os.listdir(apppath)
if os.path.isdir(os.path.join(apppath, x))]
@@ -303,10 +303,7 @@ def crondance(applications_parent, ctype='soft', startup=False, apps=None):
base_commands.append(w2p_path)
if applications_parent != global_settings.gluon_parent:
base_commands.extend(('-f', applications_parent))
base_commands.extend(('--cronjob', '--no-banner', '--nogui', '--plain',
# FIXME: this should not be needed since we are
# not launching the web server
'-a', '"<recycle>"'))
base_commands.extend(('--cron_job', '--no_banner', '--no_gui', '--plain'))
for app in apps:
if _cron_stopping:
+5 -5
View File
@@ -146,7 +146,7 @@ def env(
request.is_shell = cmd_opts.shell is not None
else:
ip = '127.0.0.1'; port = 8000
# FIXME: what about request.is_shell ?
request.is_shell = False
request.is_scheduler = False
request.env.http_host = '%s:%s' % (ip, port)
request.env.remote_addr = '127.0.0.1'
@@ -213,7 +213,7 @@ def run(
startfile=None,
bpython=False,
python_code=None,
cronjob=False,
cron_job=False,
scheduler_job=False):
"""
Start interactive shell or run Python script (startfile) in web2py
@@ -233,7 +233,7 @@ def run(
adir = os.path.join('applications', a)
if not os.path.exists(adir):
if not cronjob and not scheduler_job and \
if not cron_job and not scheduler_job and \
sys.stdin and not sys.stdin.name == '/dev/null':
confirm = raw_input(
'application %s does not exist, create (y/n)?' % a)
@@ -259,7 +259,7 @@ def run(
pyfile = os.path.join('applications', a, 'controllers', c + '.py')
pycfile = os.path.join('applications', a, 'compiled',
"controllers_%s_%s.pyc" % (c, f))
if ((cronjob and os.path.isfile(pycfile))
if ((cron_job and os.path.isfile(pycfile))
or not os.path.isfile(pyfile)):
exec(read_pyc(pycfile), _env)
elif os.path.isfile(pyfile):
@@ -380,7 +380,7 @@ def test(testpath, import_models=True, verbose=False):
import doctest
if os.path.isfile(testpath):
mo = re.match(r'(|.*/)applications/(?P<a>[^/]+)', testpath)
mo = re.search('/?applications/(?P<a>[^/]+)', testpath)
if not mo:
die('test file is not in application directory: %s'
% testpath)
+1 -2
View File
@@ -1,4 +1,3 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
@@ -636,7 +635,7 @@ def termination():
def exec_sched(self):
import subprocess
call_args = [sys.executable, 'web2py.py', '--no-banner', '-D', '20','-K', 'welcome']
call_args = [sys.executable, 'web2py.py', '--no_banner', '-D', 'INFO','-K', 'welcome']
ret = subprocess.call(call_args, env=dict(os.environ))
return ret
+46 -371
View File
@@ -5,8 +5,8 @@
| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
The widget is called from web2py
----------------------------------
GUI widget and services start function
--------------------------------------
"""
from __future__ import print_function
@@ -16,15 +16,15 @@ from gluon._compat import thread, xrange, PY2
import time
import threading
import os
import copy
import socket
import signal
import math
import logging
import getpass
from gluon import main, newcron
from gluon.fileutils import read_file, write_file, create_welcome_w2p
from gluon import main, newcron
from gluon.fileutils import read_file, create_welcome_w2p
from gluon.console import console
from gluon.settings import global_settings
from gluon.shell import die, run, test
from gluon.utils import is_valid_ip_address, is_loopback_ip_address, getipaddrinfo
@@ -314,22 +314,20 @@ class web2pyDialog(object):
self.tb = None
def update_schedulers(self, start=False):
applications_folder = os.path.join(self.options.folder, 'applications')
apps = []
available_apps = [
arq for arq in os.listdir(applications_folder)
if os.path.isdir(os.path.join(applications_folder, arq))
]
if start:
# the widget takes care of starting the scheduler
if self.options.scheduler and self.options.with_scheduler:
apps = [app for app
in map(lambda ag : ag.split(':', 1)[0].strip(), self.options.scheduler.split(','))
if app in available_apps]
if start and self.options.with_scheduler and self.options.with_schedulers:
# the widget takes care of starting the schedulers
apps = [ag.split(':', 1)[0] for ag in self.options.with_schedulers]
else:
apps = []
for app in apps:
self.try_start_scheduler(app)
# reset the menu
applications_folder = os.path.join(self.options.folder, 'applications')
available_apps = [
arq for arq in os.listdir(applications_folder)
if os.path.isdir(os.path.join(applications_folder, arq))
]
self.schedmenu.delete(0, len(available_apps))
for arq in available_apps:
@@ -351,7 +349,7 @@ class web2pyDialog(object):
code = "from gluon.globals import current;current._scheduler.loop()"
print('starting scheduler from widget for "%s"...' % app)
args = (app, True, True, None, False, code, False, True)
logging.getLogger().setLevel(self.options.debuglevel)
logging.getLogger().setLevel(self.options.log_level)
p = Process(target=run, args=args)
self.scheduler_processes[app] = p
self.update_schedulers()
@@ -473,7 +471,7 @@ class web2pyDialog(object):
except:
return self.error('invalid port number')
if self.options.ssl_certificate and self.options.ssl_private_key:
if self.options.server_key and self.options.server_cert:
proto = 'https'
else:
proto = 'http'
@@ -492,11 +490,11 @@ class web2pyDialog(object):
pid_filename=options.pid_filename,
log_filename=options.log_filename,
profiler_dir=options.profiler_dir,
ssl_certificate=options.ssl_certificate,
ssl_private_key=options.ssl_private_key,
ssl_certificate=options.server_cert,
ssl_private_key=options.server_key,
ssl_ca_certificate=options.ca_cert,
min_threads=options.minthreads,
max_threads=options.maxthreads,
min_threads=options.min_threads,
max_threads=options.max_threads,
server_name=options.server_name,
request_queue_size=req_queue_size,
timeout=options.timeout,
@@ -582,321 +580,6 @@ class web2pyDialog(object):
self.canvas.after(1000, self.update_canvas)
def console():
""" Defines the behavior of the console web2py execution """
import optparse
parser = optparse.OptionParser(
usage='python %prog [options]',
version=ProgramVersion,
description='web2py Web Framework startup script.',
epilog='''NOTE: unless a password is specified (-a 'passwd')
web2py will attempt to run a GUI to ask for it when starting the web server
(if not disabled with --nogui).''')
parser.add_option('-i', '--ip',
default='127.0.0.1',
metavar='IP_ADDR', help=\
'IP address of the server (e.g., 127.0.0.1 or ::1); ' \
'Note: This value is ignored when using the --interfaces option')
parser.add_option('-p', '--port',
default=8000,
type='int', metavar='NUM', help=\
'port of server (%default); ' \
'Note: This value is ignored when using the --interfaces option')
parser.add_option('-G', '--GAE', dest='gae',
default=None,
metavar='APP_NAME', help=\
'will create app.yaml and gaehandler.py and exit')
parser.add_option('-a', '--password',
default='<ask>',
help=\
'password to be used for administration ' \
'(use "<recycle>" to reuse the last password), ' \
'when no password is available the administrative ' \
'interface will be disabled')
parser.add_option('-c', '--ssl_certificate',
default=None,
metavar='FILE', help='server certificate file')
parser.add_option('-k', '--ssl_private_key',
default=None,
metavar='FILE', help='server private key file')
parser.add_option('--ca-cert', dest='ca_cert', # not needed
default=None,
metavar='FILE', help='CA certificate file')
parser.add_option('-d', '--pid_filename',
default='httpserver.pid',
metavar='FILE', help='server pid file (%default)')
parser.add_option('-l', '--log_filename',
default='httpserver.log',
metavar='FILE', help='server log file (%default)')
parser.add_option('-n', '--numthreads',
default=None,
type='int', metavar='NUM',
help='number of threads (deprecated)')
parser.add_option('--minthreads',
default=None,
type='int', metavar='NUM',
help='minimum number of server threads')
parser.add_option('--maxthreads',
default=None,
type='int', metavar='NUM',
help='maximum number of server threads')
parser.add_option('-s', '--server_name',
default=socket.gethostname(),
help='web server name (%default)')
parser.add_option('-q', '--request_queue_size',
default=5,
type='int', metavar='NUM',
help=\
'max number of queued requests when server unavailable (%default)')
parser.add_option('-o', '--timeout',
default=10,
type='int', metavar='SECONDS',
help='timeout for individual request (%default seconds)')
parser.add_option('-z', '--shutdown_timeout',
default=None,
type='int', metavar='SECONDS',
help=\
'timeout on server shutdown; this value is not used by ' \
'Rocket web server')
parser.add_option('--socket-timeout', dest='socket_timeout', # not needed
default=5,
type='int', metavar='SECONDS',
help='timeout for socket (%default seconds)')
parser.add_option('-f', '--folder',
default=os.getcwd(), metavar='WEB2PY_DIR',
help='folder from which to run web2py')
parser.add_option('-v', '--verbose',
default=False,
action='store_true',
help='increase --test and --run_system_tests verbosity')
parser.add_option('-Q', '--quiet',
default=False,
action='store_true',
help='disable all output')
parser.add_option('-e', '--errors_to_console',
default=False,
action='store_true',
help='log all errors to console')
parser.add_option('-D', '--debug', dest='debuglevel',
default=30,
type='int',
metavar='LOG_LEVEL', help=\
'set log level (0-100, 0 means all, 100 means none; ' \
'default is %default)')
parser.add_option('-S', '--shell',
default=None,
metavar='APPNAME', help=\
'run web2py in interactive shell or IPython (if installed) with ' \
'specified appname (if app does not exist it will be created). ' \
'APPNAME like a/c/f?x=y (c, f and vars optional)')
parser.add_option('-B', '--bpython',
default=False,
action='store_true',
help=\
'run web2py in interactive shell or bpython (if installed) with ' \
'specified appname (if app does not exist it will be created). ' \
'Use combined with --shell')
parser.add_option('-P', '--plain',
default=False,
action='store_true',
help=\
'only use plain python shell; should be used with --shell option')
parser.add_option('-M', '--import_models',
default=False,
action='store_true',
help=\
'auto import model files (default is %default); should be used ' \
'with --shell option')
parser.add_option('-R', '--run',
default='', # NOTE: used for sys.argv[0] if --shell
metavar='PYTHON_FILE', help=\
'run PYTHON_FILE in web2py environment; ' \
'should be used with --shell option')
parser.add_option('-K', '--scheduler',
default=None,
metavar='APP_LIST', help=\
'run scheduled tasks for the specified apps: expects a list of ' \
'app names as app1,app2,app3 ' \
'or a list of app:groups as app1:group1:group2,app2:group1 ' \
'(only strings, no spaces allowed). NOTE: ' \
'Requires a scheduler defined in the models')
parser.add_option('-X', '--with-scheduler', dest='with_scheduler', # not needed
default=False,
action='store_true',
help=\
'run schedulers alongside webserver, needs -K')
parser.add_option('-T', '--test',
default=None,
metavar='TEST_PATH', help=\
'run doctests in web2py environment; ' \
'TEST_PATH like a/c/f (c, f optional)')
parser.add_option('-C', '--cron', dest='extcron',
default=False,
action='store_true',
help=\
'trigger a cron run and exit; usually used when invoked ' \
'from a system crontab')
parser.add_option('--softcron',
default=False,
action='store_true',
help=\
'use software cron emulation instead of separate cron process, '\
'needs -Y; NOTE: use of software cron emulation is strongly '
'discouraged')
parser.add_option('-Y', '--run-cron', dest='runcron',
default=False,
action='store_true',
help='start the background cron process')
parser.add_option('-J', '--cronjob',
default=False,
action='store_true',
# NOTE: help suppressed because this option is
# intended for internal use only
help=optparse.SUPPRESS_HELP)
parser.add_option('-L', '--config',
default='',
help='config file')
parser.add_option('-F', '--profiler', dest='profiler_dir',
default=None,
help='profiler dir')
parser.add_option('-t', '--taskbar',
default=False,
action='store_true',
help='use web2py GUI and run in taskbar (system tray)')
parser.add_option('--nogui',
default=False,
action='store_true',
help='do not run GUI')
parser.add_option('-A', '--args',
default=None,
help=\
'should be followed by a list of arguments to be passed to script, ' \
'to be used with -S; NOTE: must be the last option because eat all ' \
'remaining arguments')
parser.add_option('--no-banner', dest='no_banner', # not needed
default=False,
action='store_true',
help='do not print header banner')
parser.add_option('--interfaces',
default=None,
help=\
'listen on multiple addresses: ' \
'"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' \
'(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' \
'square [] brackets)')
parser.add_option('--run_system_tests',
default=False,
action='store_true',
help='run web2py tests')
parser.add_option('--with_coverage',
default=False,
action='store_true',
help=\
'collect coverage data when used with --run_system_tests; ' \
'require Python 2.7+ and the coverage module installed')
if '-A' in sys.argv:
k = sys.argv.index('-A')
elif '--args' in sys.argv:
k = sys.argv.index('--args')
else:
k = len(sys.argv)
sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
(options, args) = parser.parse_args()
# TODO: warn or error if args (should be no unparsed arguments)
options.args = other_args
if options.taskbar and os.name != 'nt':
# TODO: warn and disable taskbar instead of exit
die('taskbar not supported on this platform')
if options.config.endswith('.py'):
options.config = options.config[:-3]
if options.config:
# import options from options.config file
try:
# FIXME: avoid __import__
options2 = __import__(options.config)
except:
die("cannot import config file %s" % options.config)
for key in dir(options2):
if hasattr(options, key):
setattr(options, key, getattr(options2, key))
# transform options.interfaces, in the form
# "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3"
# (no spaces; optional key:cert:ca_cert indicate SSL), into
# a list of tuples
if options.interfaces:
interfaces = options.interfaces.split(';')
options.interfaces = []
for interface in interfaces:
if interface.startswith('['):
# IPv6
ip, if_remainder = interface.split(']', 1)
ip = ip[1:]
interface = if_remainder[1:].split(':')
interface.insert(0, ip)
else:
# IPv4
interface = interface.split(':')
interface[1] = int(interface[1]) # numeric port
options.interfaces.append(tuple(interface))
if options.numthreads is not None and options.minthreads is None:
options.minthreads = options.numthreads # legacy
copy_options = copy.deepcopy(options)
copy_options.password = '******'
global_settings.cmd_options = copy_options
return options, args
def check_existent_app(options, appname):
if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
return True
@@ -921,11 +604,10 @@ def start_schedulers(options):
except:
sys.stderr.write('Sorry, -K only supported for Python 2.6+\n')
return
logging.getLogger().setLevel(options.debuglevel)
logging.getLogger().setLevel(options.log_level)
apps = [[n.strip() for n in sched_app.split(':')]
for sched_app in options.scheduler.split(',')]
if len(apps) == 1 and not options.with_scheduler:
apps = [ag.split(':') for ag in options.schedulers]
if not options.with_scheduler and len(apps) == 1:
app, code = get_code_for_scheduler(apps[0], options)
if not app:
return
@@ -968,7 +650,7 @@ def start():
""" Starts server and other services """
# get command line arguments
(options, args) = console()
options = console(version=ProgramVersion)
if options.gae:
# write app.yaml, gaehandler.py, and exit
@@ -990,7 +672,7 @@ def start():
return
logger = logging.getLogger("web2py")
logger.setLevel(options.debuglevel)
logger.setLevel(options.log_level)
# on new installation build the scaffolding app
create_welcome_w2p()
@@ -1027,36 +709,29 @@ def start():
from pydal.drivers import DRIVERS
print('Database drivers available: %s' % ', '.join(DRIVERS))
if options.test:
if options.run_doctests:
# run doctests and exit
test(options.test, verbose=options.verbose)
test(options.run_doctests, verbose=options.verbose)
return
if options.shell:
# run interactive shell and exit
sys.argv = [options.run] + options.args
sys.argv = [options.run or ''] + options.args
run(options.shell, plain=options.plain, bpython=options.bpython,
import_models=options.import_models, startfile=options.run,
cronjob=options.cronjob)
cron_job=options.cron_job)
return
if options.extcron:
if options.cron_run:
# run cron (extcron) and exit
logger.debug('Starting extcron...')
global_settings.web2py_crontype = 'external'
if options.scheduler:
# run cron for applications listed with --scheduler (-K)
apps = [app for app
in map(lambda ag : ag.split(':', 1)[0].strip(), options.scheduler.split(','))
if check_existent_app(options, app)]
else:
apps = None
extcron = newcron.extcron(options.folder, apps=apps)
extcron = newcron.extcron(options.folder, apps=options.crontabs)
extcron.start()
extcron.join()
return
if options.scheduler and not options.with_scheduler:
if not options.with_scheduler and options.schedulers:
# run schedulers and exit
try:
start_schedulers(options)
@@ -1064,22 +739,22 @@ def start():
pass
return
if options.runcron:
if options.softcron:
print('Using softcron (but this is not very efficient)')
if options.with_cron:
if options.soft_cron:
print('Using cron software emulation (but this is not very efficient)')
global_settings.web2py_crontype = 'soft'
else:
# start hardcron thread
logger.debug('Starting hardcron...')
global_settings.web2py_crontype = 'hard'
newcron.hardcron(options.folder).start()
newcron.hardcron(options.folder, apps=options.crontabs).start()
# if no password provided and have Tk library start GUI (when not
# explicitly disabled), we also need a GUI to put in taskbar (system tray)
# when requested
root = None
if (not options.nogui and options.password == '<ask>') or options.taskbar:
if (not options.no_gui and options.password == '<ask>') or options.taskbar:
try:
if PY2:
import Tkinter as tkinter
@@ -1089,10 +764,10 @@ def start():
except (ImportError, OSError):
logger.warn(
'GUI not available because Tk library is not installed')
options.nogui = True
options.no_gui = True
except:
logger.exception('cannot get Tk root window, GUI disabled')
options.nogui = True
options.no_gui = True
if root:
# run GUI and exit
@@ -1121,7 +796,7 @@ end tell
spt = None
if options.scheduler and options.with_scheduler:
if options.with_scheduler and options.schedulers:
# start schedulers in a separate thread
spt = threading.Thread(target=start_schedulers, args=(options,))
spt.start()
@@ -1144,7 +819,7 @@ end tell
ip = first_if[0]
port = first_if[1]
if options.ssl_certificate and options.ssl_private_key:
if options.server_key and options.server_cert:
proto = 'https'
else:
proto = 'http'
@@ -1186,11 +861,11 @@ end tell
pid_filename=options.pid_filename,
log_filename=options.log_filename,
profiler_dir=options.profiler_dir,
ssl_certificate=options.ssl_certificate,
ssl_private_key=options.ssl_private_key,
ssl_certificate=options.server_cert,
ssl_private_key=options.server_key,
ssl_ca_certificate=options.ca_cert,
min_threads=options.minthreads,
max_threads=options.maxthreads,
min_threads=options.min_threads,
max_threads=options.max_threads,
server_name=options.server_name,
request_queue_size=options.request_queue_size,
timeout=options.timeout,
+1 -1
View File
@@ -29,7 +29,7 @@ cd $DAEMON_DIR
start() {
echo -n $"Starting $DESC ($NAME): "
daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --nogui -a $ADMINPASS -d $PIDFILE -p $PORT &
daemon --check $NAME $PYTHON $DAEMON_DIR/web2py.py -Q --no_gui -a $ADMINPASS -d $PIDFILE -p $PORT &
RETVAL=$?
if [ $RETVAL -eq 0 ]; then
touch /var/lock/subsys/$NAME