def wsgibase(environ, responder): """ this is the gluon wsgi application. the first function called when a page is requested (static or dynamic). it can be called by paste.httpserver or by apache mod_wsgi. - fills request with info - the environment variables, replacing '.' with '_' - adds web2py path and version info - compensates for fcgi missing path_info and query_string - validates the path in url The url path must be either: 1. for static pages: - /<application>/static/<file> 2. for dynamic pages: - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>] - (sub may go several levels deep, currently 3 levels are supported: sub1/sub2/sub3) The naming conventions are: - application, controller, function and extension may only contain [a-zA-Z0-9_] - file and sub may also contain '-', '=', '.' and '/' """ eget = environ.get current.__dict__.clear() request = Request(environ) response = Response() session = Session() env = request.env #env.web2py_path = global_settings.applications_parent env.web2py_version = web2py_version #env.update(global_settings) static_file = False try: try: try: # ################################################## # handle fcgi missing path_info and query_string # select rewrite parameters # rewrite incoming URL # parse rewritten header variables # parse rewritten URL # serve file if static # ################################################## fixup_missing_path_info(environ) (static_file, version, environ) = url_in(request, environ) response.status = env.web2py_status_code or response.status if static_file: if eget('QUERY_STRING', '').startswith('attachment'): response.headers['Content-Disposition'] \ = 'attachment' if version: response.headers['Cache-Control'] = 'max-age=315360000' response.headers[ 'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT' response.stream(static_file, request=request) # ################################################## # fill in request items # ################################################## app = request.application # must go after url_in! if not global_settings.local_hosts: local_hosts = set(['127.0.0.1', '::ffff:127.0.0.1', '::1']) if not global_settings.web2py_runtime_gae: try: fqdn = socket.getfqdn() local_hosts.add(socket.gethostname()) local_hosts.add(fqdn) local_hosts.update([ addrinfo[4][0] for addrinfo in getipaddrinfo(fqdn)]) if env.server_name: local_hosts.add(env.server_name) local_hosts.update([ addrinfo[4][0] for addrinfo in getipaddrinfo(env.server_name)]) except (socket.gaierror, TypeError): pass global_settings.local_hosts = list(local_hosts) else: local_hosts = global_settings.local_hosts client = get_client(env) x_req_with = str(env.http_x_requested_with).lower() request.update( client = client, folder = abspath('applications', app) + os.sep, ajax = x_req_with == 'xmlhttprequest', cid = env.http_web2py_component_element, is_local = env.remote_addr in local_hosts, is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \ request.env.http_x_forwarded_proto in HTTPS_SCHEMES \ or env.https == 'on' ) request.compute_uuid() # requires client request.url = environ['PATH_INFO'] # ################################################## # access the requested application # ################################################## disabled = pjoin(request.folder, 'DISABLED') if not exists(request.folder): if app == rwthread.routes.default_application \ and app != 'welcome': redirect(URL('welcome', 'default', 'index')) elif rwthread.routes.error_handler: _handler = rwthread.routes.error_handler redirect(URL(_handler['application'], _handler['controller'], _handler['function'], args=app)) else: raise HTTP(404, rwthread.routes.error_message % 'invalid request', web2py_error='invalid application') elif not request.is_local and exists(disabled): raise HTTP(503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>") # ################################################## # build missing folders # ################################################## create_missing_app_folders(request) # ################################################## # get the GET and POST data # ################################################## #parse_get_post_vars(request, environ) # ################################################## # expose wsgi hooks for convenience # ################################################## request.wsgi = LazyWSGI(environ, request, response) # ################################################## # load cookies # ################################################## if env.http_cookie: try: request.cookies.load(env.http_cookie) except Cookie.CookieError, e: pass # invalid cookies # ################################################## # try load session or create new session file # ################################################## if not env.web2py_disable_session: session.connect(request, response) # ################################################## # run controller # ################################################## if global_settings.debugging and app != "admin": import gluon.debug # activate the debugger gluon.debug.dbg.do_debug(mainpyfile=request.folder) serve_controller(request, response, session) except HTTP, http_response: if static_file: return http_response.to(responder, env=env) if request.body: request.body.close() if hasattr(current,'request'): # ################################################## # on success, try store session in database # ################################################## session._try_store_in_db(request, response) # ################################################## # on success, commit database # ################################################## if response.do_not_commit is True: BaseAdapter.close_all_instances(None) elif response.custom_commit: BaseAdapter.close_all_instances(response.custom_commit) else: BaseAdapter.close_all_instances('commit') # ################################################## # if session not in db try store session on filesystem # this must be done after trying to commit database! # ################################################## session._try_store_in_cookie_or_file(request, response) # Set header so client can distinguish component requests. if request.cid: http_response.headers.setdefault( 'web2py-component-content', 'replace') if request.ajax: if response.flash: http_response.headers['web2py-component-flash'] = \ urllib2.quote(xmlescape(response.flash)\ .replace('\n','')) if response.js: http_response.headers['web2py-component-command'] = \ urllib2.quote(response.js.replace('\n','')) # ################################################## # store cookies in headers # ################################################## session._fixup_before_save() http_response.cookies2headers(response.cookies) ticket = None except RestrictedError, e: if request.body: request.body.close() # ################################################## # on application error, rollback database # ################################################## # log tickets before rollback if not in DB if not request.tickets_db: ticket = e.log(request) or 'unknown' # rollback if response._custom_rollback: response._custom_rollback() else: BaseAdapter.close_all_instances('rollback') # if tickets in db, reconnect and store it in db if request.tickets_db: ticket = e.log(request) or 'unknown' http_response = \ HTTP(500, rwthread.routes.error_message_ticket % dict(ticket=ticket), web2py_error='ticket %s' % ticket)
def __init__(self, root, options): """ web2pyDialog constructor """ if PY2: import Tkinter as tkinter import tkMessageBox as messagebox else: import tkinter from tkinter import messagebox root.withdraw() bg_color = 'white' self.root = tkinter.Toplevel(root, bg=bg_color) self.root.resizable(0, 0) self.root.title(ProgramName) self.options = options self.scheduler_processes_lock = threading.RLock() self.scheduler_processes = OrderedDict() iconphoto = os.path.join('extras', 'icons', 'web2py.gif') if os.path.exists(iconphoto): img = tkinter.PhotoImage(file=iconphoto) self.root.tk.call('wm', 'iconphoto', self.root._w, img) # Building the Menu self.menu = tkinter.Menu(self.root) servermenu = tkinter.Menu(self.menu, tearoff=0) httplog = os.path.join(options.folder, options.log_filename) item = lambda: start_browser(httplog) servermenu.add_command(label='View httpserver.log', command=item) servermenu.add_command(label='Quit (pid:%i)' % os.getpid(), command=self.quit) self.menu.add_cascade(label='Server', menu=servermenu) self.pagesmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Pages', menu=self.pagesmenu) self.schedmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Scheduler', menu=self.schedmenu) # register and start schedulers self.update_schedulers(start=True) helpmenu = tkinter.Menu(self.menu, tearoff=0) # Home Page item = lambda: start_browser('http://www.web2py.com/') helpmenu.add_command(label='Home Page', command=item) # About ProgramInfo = """%s %s %s""" % (ProgramName, ProgramAuthor, ProgramVersion) item = lambda: messagebox.showinfo('About web2py', ProgramInfo) helpmenu.add_command(label='About', command=item) self.menu.add_cascade(label='Info', menu=helpmenu) self.root.config(menu=self.menu) if options.taskbar: self.root.protocol('WM_DELETE_WINDOW', lambda: self.quit(True)) else: self.root.protocol('WM_DELETE_WINDOW', self.quit) sticky = tkinter.NW # Prepare the logo area self.logoarea = tkinter.Canvas(self.root, background=bg_color, width=300, height=300) self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky) self.logoarea.after(1000, self.update_canvas) logo = os.path.join('extras', 'icons', 'splashlogo.gif') if os.path.exists(logo): img = tkinter.PhotoImage(file=logo) pnl = tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0) pnl.pack(side='top', fill='both', expand='yes') # Prevent garbage collection of img pnl.image = img # Prepare the banner area self.bannerarea = tkinter.Canvas(self.root, bg=bg_color, width=300, height=300) self.bannerarea.grid(row=1, column=1, columnspan=2, sticky=sticky) tkinter.Label(self.bannerarea, anchor=tkinter.N, text=str(ProgramVersion + "\n" + ProgramAuthor), font=('Helvetica', 11), justify=tkinter.CENTER, foreground='#195866', background=bg_color, height=3).pack(side='top', fill='both', expand='yes') self.bannerarea.after(1000, self.update_canvas) # IP # retrieves the list of server IP addresses try: if_ips = list(set( # no duplicates [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo)])) except socket.gaierror: if_ips = [] tkinter.Label(self.root, text='Server IP:', bg=bg_color, justify=tkinter.RIGHT).grid(row=4, column=1, sticky=sticky) self.ips = {} self.selected_ip = tkinter.StringVar() row = 4 ips = [('127.0.0.1', 'Local (IPv4)')] + \ ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \ [(ip, 'Public') for ip in if_ips] + \ [('0.0.0.0', 'Public')] for ip, legend in ips: self.ips[ip] = tkinter.Radiobutton( self.root, bg=bg_color, highlightthickness=0, selectcolor='light grey', width=30, anchor=tkinter.W, text='%s (%s)' % (legend, ip), justify=tkinter.LEFT, variable=self.selected_ip, value=ip) self.ips[ip].grid(row=row, column=2, sticky=sticky) if row == 4: self.ips[ip].select() row += 1 shift = row # Port tkinter.Label(self.root, text='Server Port:', bg=bg_color, justify=tkinter.RIGHT).grid(row=shift, column=1, pady=10, sticky=sticky) self.port_number = tkinter.Entry(self.root) self.port_number.insert(tkinter.END, options.port) self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10) # Password tkinter.Label(self.root, text='Choose Password:'******'*') self.password.bind('<Return>', lambda e: self.start()) self.password.focus_force() self.password.grid(row=shift + 1, column=2, sticky=sticky) # Prepare the canvas self.canvas = tkinter.Canvas(self.root, width=400, height=100, bg='black') self.canvas.grid(row=shift + 2, column=1, columnspan=2, pady=5, sticky=sticky) self.canvas.after(1000, self.update_canvas) # Prepare the frame frame = tkinter.Frame(self.root) frame.grid(row=shift + 3, column=1, columnspan=2, pady=5, sticky=sticky) # Start button self.button_start = tkinter.Button(frame, text='start server', command=self.start) self.button_start.grid(row=0, column=0, sticky=sticky) # Stop button self.button_stop = tkinter.Button(frame, text='stop server', command=self.stop) self.button_stop.grid(row=0, column=1, sticky=sticky) self.button_stop.configure(state='disabled') if options.taskbar: import gluon.contrib.taskbar_widget self.tb = gluon.contrib.taskbar_widget.TaskBarIcon() self.checkTaskBar() if options.password != '<ask>': self.password.insert(0, options.password) self.start() self.root.withdraw() else: self.tb = None
def console(): """ Defines the behavior of the console web2py execution """ import optparse import textwrap usage = "python web2py.py" description = """\ web2py Web Framework startup script. ATTENTION: unless a password is specified (-a 'passwd') web2py will attempt to run a GUI. In this case command line options are ignored.""" description = textwrap.dedent(description) parser = optparse.OptionParser( usage, None, optparse.Option, ProgramVersion) parser.description = description msg = ('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('-i', '--ip', default='127.0.0.1', dest='ip', help=msg) parser.add_option('-p', '--port', default='8000', dest='port', type='int', help='port of server (8000)') msg = ('password to be used for administration ' '(use -a "<recycle>" to reuse the last password))') parser.add_option('-a', '--password', default='<ask>', dest='password', help=msg) parser.add_option('-c', '--ssl_certificate', default='', dest='ssl_certificate', help='file that contains ssl certificate') parser.add_option('-k', '--ssl_private_key', default='', dest='ssl_private_key', help='file that contains ssl private key') msg = ('Use this file containing the CA certificate to validate X509 ' 'certificates from clients') parser.add_option('--ca-cert', action='store', dest='ssl_ca_certificate', default=None, help=msg) parser.add_option('-d', '--pid_filename', default='httpserver.pid', dest='pid_filename', help='file to store the pid of the server') parser.add_option('-l', '--log_filename', default='httpserver.log', dest='log_filename', help='file to log connections') parser.add_option('-n', '--numthreads', default=None, type='int', dest='numthreads', help='number of threads (deprecated)') parser.add_option('--minthreads', default=None, type='int', dest='minthreads', help='minimum number of server threads') parser.add_option('--maxthreads', default=None, type='int', dest='maxthreads', help='maximum number of server threads') parser.add_option('-s', '--server_name', default=socket.gethostname(), dest='server_name', help='server name for the web server') msg = 'max number of queued requests when server unavailable' parser.add_option('-q', '--request_queue_size', default='5', type='int', dest='request_queue_size', help=msg) parser.add_option('-o', '--timeout', default='10', type='int', dest='timeout', help='timeout for individual request (10 seconds)') parser.add_option('-z', '--shutdown_timeout', default='5', type='int', dest='shutdown_timeout', help='timeout on shutdown of server (5 seconds)') parser.add_option('--socket-timeout', default=5, type='int', dest='socket_timeout', help='timeout for socket (5 second)') parser.add_option('-f', '--folder', default=os.getcwd(), dest='folder', help='folder from which to run web2py') parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='increase --test verbosity') parser.add_option('-Q', '--quiet', action='store_true', dest='quiet', default=False, help='disable all output') msg = ('set debug output level (0-100, 0 means all, 100 means none; ' 'default is 30)') parser.add_option('-D', '--debug', dest='debuglevel', default=30, type='int', help=msg) msg = ('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 (c,f optional)') parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME', help=msg) msg = ('run web2py in interactive shell or bpython (if installed) with ' 'specified appname (if app does not exist it will be created).\n' 'Use combined with --shell') parser.add_option('-B', '--bpython', action='store_true', default=False, dest='bpython', help=msg) msg = 'only use plain python shell; should be used with --shell option' parser.add_option('-P', '--plain', action='store_true', default=False, dest='plain', help=msg) msg = ('auto import model files; default is False; should be used ' 'with --shell option') parser.add_option('-M', '--import_models', action='store_true', default=False, dest='import_models', help=msg) msg = ('run PYTHON_FILE in web2py environment; ' 'should be used with --shell option') parser.add_option('-R', '--run', dest='run', metavar='PYTHON_FILE', default='', help=msg) msg = ('run scheduled tasks for the specified apps: expects a list of ' 'app names as -K app1,app2,app3 ' 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' 'to override specific group_names. (only strings, no spaces ' 'allowed. Requires a scheduler defined in the models') parser.add_option('-K', '--scheduler', dest='scheduler', default=None, help=msg) msg = 'run schedulers alongside webserver, needs -K app1 and -a too' parser.add_option('-X', '--with-scheduler', action='store_true', default=False, dest='with_scheduler', help=msg) msg = ('run doctests in web2py environment; ' 'TEST_PATH like a/c/f (c,f optional)') parser.add_option('-T', '--test', dest='test', metavar='TEST_PATH', default=None, help=msg) parser.add_option('-W', '--winservice', dest='winservice', default='', help='-W install|start|stop as Windows service') msg = 'trigger a cron run manually; usually invoked from a system crontab' parser.add_option('-C', '--cron', action='store_true', dest='extcron', default=False, help=msg) msg = 'triggers the use of softcron' parser.add_option('--softcron', action='store_true', dest='softcron', default=False, help=msg) parser.add_option('-Y', '--run-cron', action='store_true', dest='runcron', default=False, help='start the background cron process') parser.add_option('-J', '--cronjob', action='store_true', dest='cronjob', default=False, help='identify cron-initiated command') parser.add_option('-L', '--config', dest='config', default='', help='config file') parser.add_option('-F', '--profiler', dest='profiler_dir', default=None, help='profiler dir') parser.add_option('-t', '--taskbar', action='store_true', dest='taskbar', default=False, help='use web2py gui and run in taskbar (system tray)') parser.add_option('', '--nogui', action='store_true', default=False, dest='nogui', help='text-only, no GUI') msg = ('should be followed by a list of arguments to be passed to script, ' 'to be used with -S, -A must be the last option') parser.add_option('-A', '--args', action='store', dest='args', default=None, help=msg) parser.add_option('--no-banner', action='store_true', default=False, dest='nobanner', help='Do not print header banner') msg = ('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('--interfaces', action='store', dest='interfaces', default=None, help=msg) msg = 'runs web2py tests' parser.add_option('--run_system_tests', action='store_true', dest='run_system_tests', default=False, help=msg) msg = ('adds coverage reporting (needs --run_system_tests), ' 'python 2.7 and the coverage module installed. ' 'You can alter the default path setting the environmental ' 'var "COVERAGE_PROCESS_START". ' 'By default it takes gluon/tests/coverage.ini') parser.add_option('--with_coverage', action='store_true', dest='with_coverage', default=False, help=msg) 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() options.args = [options.run] + other_args global_settings.cmd_options = options global_settings.cmd_args = args try: options.ips = list(set( # no duplicates [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo)])) except socket.gaierror: options.ips = [] if options.run_system_tests: run_system_tests(options) if options.quiet: capture = cStringIO.StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.config[-3:] == '.py': options.config = options.config[:-3] if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): interfaces = options.interfaces.split(';') options.interfaces = [] for interface in interfaces: if interface.startswith('['): # IPv6 ip, if_remainder = interface.split(']', 1) ip = ip[1:] if_remainder = if_remainder[1:].split(':') if_remainder[0] = int(if_remainder[0]) # numeric port options.interfaces.append(tuple([ip] + if_remainder)) else: # IPv4 interface = interface.split(':') interface[1] = int(interface[1]) # numeric port options.interfaces.append(tuple(interface)) # accepts --scheduler in the form # "app:group1,group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ':' in options.scheduler: for opt in options.scheduler.split(','): scheduler.append(opt.split(':')) options.scheduler = ','.join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists('applications/__init__.py'): write_file('applications/__init__.py', '') return options, args
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 (if not disabled with --nogui).''') parser.add_option('-i', '--ip', default='127.0.0.1', 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', help='port of server (%default)') parser.add_option('-G', '--GAE', dest='gae', default=None, metavar='APP_NAME', help=\ "will create app.yaml and gaehandler.py") parser.add_option('-a', '--password', default='<ask>', help=\ 'password to be used for administration ' \ '(use -a "<recycle>" to reuse the last password))') parser.add_option('-c', '--ssl_certificate', default='', help='file that contains ssl certificate') parser.add_option('-k', '--ssl_private_key', default='', help='file that contains ssl private key') parser.add_option('--ca-cert', dest='ssl_ca_certificate', default=None, help=\ 'use this file containing the CA certificate to validate X509 ' \ 'certificates from clients') parser.add_option('-d', '--pid_filename', default='httpserver.pid', help='file to store the pid of the server') parser.add_option('-l', '--log_filename', default='httpserver.log', help='name for the server log file') parser.add_option('-n', '--numthreads', default=None, type='int', help='number of threads (deprecated)') parser.add_option('--minthreads', default=None, type='int', help='minimum number of server threads') parser.add_option('--maxthreads', default=None, type='int', 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', help=\ 'max number of queued requests when server unavailable') parser.add_option('-o', '--timeout', default='10', type='int', help='timeout for individual request (%default seconds)') parser.add_option('-z', '--shutdown_timeout', default='5', type='int', help='timeout on shutdown of server (%default seconds)') parser.add_option( '--socket-timeout', dest='socket_timeout', # not needed default=5, type='int', help='timeout for socket (%default seconds)') parser.add_option('-f', '--folder', default=os.getcwd(), 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', dest='print_errors', default=False, action='store_true', help='log all errors to console') parser.add_option('-D', '--debug', dest='debuglevel', default=30, type='int', help=\ 'set debug output 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 x,y 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, help=\ 'run scheduled tasks for the specified apps: expects a list of ' \ 'app names as -K app1,app2,app3 ' \ 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' \ 'to override specific group_names. (only strings, no spaces ' \ 'allowed. 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 app1 and -a too') 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 manually; usually invoked from a system crontab') parser.add_option('--softcron', default=False, action='store_true', help='triggers the use of softcron') 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', help='identify cron-initiated command') 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, -A must be the last option') parser.add_option('--no-banner', dest='nobanner', 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=\ 'adds coverage reporting (needs --run_system_tests), ' \ 'python 2.7 and the coverage module installed. ' \ 'You can alter the default path setting the environment ' \ 'variable "COVERAGE_PROCESS_START" ' \ '(by default it takes gluon/tests/coverage.ini)') 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() options.args = other_args if options.config.endswith('.py'): options.config = options.config[:-3] # TODO: process --config here; now is done in start function, too late copy_options = copy.deepcopy(options) copy_options.password = '******' global_settings.cmd_options = copy_options global_settings.cmd_args = args if options.gae: if not os.path.exists('app.yaml'): name = options.gae # for backward compatibility if name == 'configure': name = input("Your GAE app name: ") content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() open('app.yaml', 'wb').write(content.replace("yourappname", name)) else: print("app.yaml alreday exists in the web2py folder") if not os.path.exists('gaehandler.py'): content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() open('gaehandler.py', 'wb').write(content) else: print("gaehandler.py alreday exists in the web2py folder") sys.exit(0) try: options.ips = list( set( # no duplicates [ addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo) ])) except socket.gaierror: options.ips = [] # FIXME: this should be done after create_welcome_w2p if options.run_system_tests: # run system test and exit run_system_tests(options) if options.quiet: capture = StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): 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)) # accepts --scheduler in the form # "app:group1:group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ':' in options.scheduler: for opt in options.scheduler.split(','): scheduler.append(opt.split(':')) options.scheduler = ','.join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() # FIXME: do we still really need this? if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists('applications/__init__.py'): write_file('applications/__init__.py', '') return options, args
def wsgibase(environ, responder): """ The gluon wsgi application. The first function called when a page is requested (static or dynamic). It can be called by paste.httpserver or by apache mod_wsgi (or any WSGI-compatible server). - fills request with info - the environment variables, replacing '.' with '_' - adds web2py path and version info - compensates for fcgi missing path_info and query_string - validates the path in url The url path must be either: 1. for static pages: - /<application>/static/<file> 2. for dynamic pages: - /<application>[/<controller>[/<function>[/<sub>]]][.<extension>] The naming conventions are: - application, controller, function and extension may only contain `[a-zA-Z0-9_]` - file and sub may also contain '-', '=', '.' and '/' """ eget = environ.get current.__dict__.clear() request = Request(environ) response = Response() session = Session() env = request.env #env.web2py_path = global_settings.applications_parent env.web2py_version = web2py_version #env.update(global_settings) static_file = False http_response = None try: try: try: # ################################################## # handle fcgi missing path_info and query_string # select rewrite parameters # rewrite incoming URL # parse rewritten header variables # parse rewritten URL # serve file if static # ################################################## fixup_missing_path_info(environ) (static_file, version, environ) = url_in(request, environ) response.status = env.web2py_status_code or response.status if static_file: if eget('QUERY_STRING', '').startswith('attachment'): response.headers['Content-Disposition'] \ = 'attachment' if version: response.headers['Cache-Control'] = 'max-age=315360000' response.headers[ 'Expires'] = 'Thu, 31 Dec 2037 23:59:59 GMT' response.stream(static_file, request=request) # ################################################## # fill in request items # ################################################## app = request.application # must go after url_in! if not global_settings.local_hosts: local_hosts = set(['127.0.0.1', '::ffff:127.0.0.1', '::1']) if not global_settings.web2py_runtime_gae: try: fqdn = socket.getfqdn() local_hosts.add(socket.gethostname()) local_hosts.add(fqdn) local_hosts.update([ addrinfo[4][0] for addrinfo in getipaddrinfo(fqdn) ]) if env.server_name: local_hosts.add(env.server_name) local_hosts.update([ addrinfo[4][0] for addrinfo in getipaddrinfo(env.server_name) ]) except (socket.gaierror, TypeError): pass global_settings.local_hosts = list(local_hosts) else: local_hosts = global_settings.local_hosts client = get_client(env) x_req_with = str(env.http_x_requested_with).lower() cmd_opts = global_settings.cmd_options request.update( client = client, folder = abspath('applications', app) + os.sep, ajax = x_req_with == 'xmlhttprequest', cid = env.http_web2py_component_element, is_local = (env.remote_addr in local_hosts and client == env.remote_addr), is_shell = False, is_scheduler = False, is_https = env.wsgi_url_scheme in HTTPS_SCHEMES or \ request.env.http_x_forwarded_proto in HTTPS_SCHEMES \ or env.https == 'on' ) request.url = environ['PATH_INFO'] # ################################################## # access the requested application # ################################################## disabled = pjoin(request.folder, 'DISABLED') if not exists(request.folder): if app == rwthread.routes.default_application \ and app != 'welcome': redirect(URL('welcome', 'default', 'index')) elif rwthread.routes.error_handler: _handler = rwthread.routes.error_handler redirect( URL(_handler['application'], _handler['controller'], _handler['function'], args=app)) else: raise HTTP(404, rwthread.routes.error_message % 'invalid request', web2py_error='invalid application') elif not request.is_local and exists(disabled): five0three = os.path.join(request.folder, 'static', '503.html') if os.path.exists(five0three): raise HTTP(503, file(five0three, 'r').read()) else: raise HTTP( 503, "<html><body><h1>Temporarily down for maintenance</h1></body></html>" ) # ################################################## # build missing folders # ################################################## create_missing_app_folders(request) # ################################################## # get the GET and POST data # ################################################## #parse_get_post_vars(request, environ) # ################################################## # expose wsgi hooks for convenience # ################################################## request.wsgi = LazyWSGI(environ, request, response) # ################################################## # load cookies # ################################################## if env.http_cookie: for single_cookie in env.http_cookie.split(';'): single_cookie = single_cookie.strip() if single_cookie: try: request.cookies.load(single_cookie) except Cookie.CookieError: pass # single invalid cookie ignore # ################################################## # try load session or create new session file # ################################################## if not env.web2py_disable_session: session.connect(request, response) # ################################################## # run controller # ################################################## if global_settings.debugging and app != "admin": import gluon.debug # activate the debugger gluon.debug.dbg.do_debug(mainpyfile=request.folder) serve_controller(request, response, session) except HTTP as hr: http_response = hr if static_file: return http_response.to(responder, env=env) if request.body: request.body.close() if hasattr(current, 'request'): # ################################################## # on success, try store session in database # ################################################## if not env.web2py_disable_session: session._try_store_in_db(request, response) # ################################################## # on success, commit database # ################################################## if response.do_not_commit is True: BaseAdapter.close_all_instances(None) elif response.custom_commit: BaseAdapter.close_all_instances(response.custom_commit) else: BaseAdapter.close_all_instances('commit') # ################################################## # if session not in db try store session on filesystem # this must be done after trying to commit database! # ################################################## if not env.web2py_disable_session: session._try_store_in_cookie_or_file(request, response) # Set header so client can distinguish component requests. if request.cid: http_response.headers.setdefault( 'web2py-component-content', 'replace') if request.ajax: if response.flash: http_response.headers['web2py-component-flash'] = \ urllib2.quote(xmlescape(response.flash).replace(b'\n', b'')) if response.js: http_response.headers['web2py-component-command'] = \ urllib2.quote(response.js.replace('\n', '')) # ################################################## # store cookies in headers # ################################################## session._fixup_before_save() http_response.cookies2headers(response.cookies) ticket = None except RestrictedError as e: if request.body: request.body.close() # ################################################## # on application error, rollback database # ################################################## # log tickets before rollback if not in DB if not request.tickets_db: ticket = e.log(request) or 'unknown' # rollback if response._custom_rollback: response._custom_rollback() else: BaseAdapter.close_all_instances('rollback') # if tickets in db, reconnect and store it in db if request.tickets_db: ticket = e.log(request) or 'unknown' http_response = \ HTTP(500, rwthread.routes.error_message_ticket % dict(ticket=ticket), web2py_error='ticket %s' % ticket) except: if request.body: request.body.close() # ################################################## # on application error, rollback database # ################################################## try: if response._custom_rollback: response._custom_rollback() else: BaseAdapter.close_all_instances('rollback') except: pass e = RestrictedError('Framework', '', '', locals()) ticket = e.log(request) or 'unrecoverable' http_response = \ HTTP(500, rwthread.routes.error_message_ticket % dict(ticket=ticket), web2py_error='ticket %s' % ticket) finally: if response and hasattr(response, 'session_file') \ and response.session_file: response.session_file.close() session._unlock(response) http_response, new_environ = try_rewrite_on_error(http_response, request, environ, ticket) if not http_response: return wsgibase(new_environ, responder) if global_settings.web2py_crontype == 'soft': newcron.softcron(global_settings.applications_parent).start() return http_response.to(responder, env=env)
def console(): """ Defines the behavior of the console web2py execution """ import optparse import textwrap usage = "python web2py.py" description = """\ web2py Web Framework startup script. ATTENTION: unless a password is specified (-a 'passwd') web2py will attempt to run a GUI. In this case command line options are ignored.""" description = textwrap.dedent(description) parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion) parser.description = description msg = ('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('-i', '--ip', default='127.0.0.1', dest='ip', help=msg) parser.add_option('-p', '--port', default='8000', dest='port', type='int', help='port of server (8000)') msg = ('password to be used for administration ' '(use -a "<recycle>" to reuse the last password))') parser.add_option('-a', '--password', default='<ask>', dest='password', help=msg) parser.add_option('-c', '--ssl_certificate', default='', dest='ssl_certificate', help='file that contains ssl certificate') parser.add_option('-k', '--ssl_private_key', default='', dest='ssl_private_key', help='file that contains ssl private key') msg = ('Use this file containing the CA certificate to validate X509 ' 'certificates from clients') parser.add_option('--ca-cert', action='store', dest='ssl_ca_certificate', default=None, help=msg) parser.add_option('-d', '--pid_filename', default='httpserver.pid', dest='pid_filename', help='file to store the pid of the server') parser.add_option('-l', '--log_filename', default='httpserver.log', dest='log_filename', help='file to log connections') parser.add_option('-n', '--numthreads', default=None, type='int', dest='numthreads', help='number of threads (deprecated)') parser.add_option('--minthreads', default=None, type='int', dest='minthreads', help='minimum number of server threads') parser.add_option('--maxthreads', default=None, type='int', dest='maxthreads', help='maximum number of server threads') parser.add_option('-s', '--server_name', default=socket.gethostname(), dest='server_name', help='server name for the web server') msg = 'max number of queued requests when server unavailable' parser.add_option('-q', '--request_queue_size', default='5', type='int', dest='request_queue_size', help=msg) parser.add_option('-o', '--timeout', default='10', type='int', dest='timeout', help='timeout for individual request (10 seconds)') parser.add_option('-z', '--shutdown_timeout', default='5', type='int', dest='shutdown_timeout', help='timeout on shutdown of server (5 seconds)') parser.add_option('--socket-timeout', default=5, type='int', dest='socket_timeout', help='timeout for socket (5 second)') parser.add_option('-f', '--folder', default=os.getcwd(), dest='folder', help='folder from which to run web2py') parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='increase --test verbosity') parser.add_option('-Q', '--quiet', action='store_true', dest='quiet', default=False, help='disable all output') msg = ('set debug output level (0-100, 0 means all, 100 means none; ' 'default is 30)') parser.add_option('-D', '--debug', dest='debuglevel', default=30, type='int', help=msg) msg = ('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 (c,f optional)') parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME', help=msg) msg = ('run web2py in interactive shell or bpython (if installed) with ' 'specified appname (if app does not exist it will be created).\n' 'Use combined with --shell') parser.add_option('-B', '--bpython', action='store_true', default=False, dest='bpython', help=msg) msg = 'only use plain python shell; should be used with --shell option' parser.add_option('-P', '--plain', action='store_true', default=False, dest='plain', help=msg) msg = ('auto import model files; default is False; should be used ' 'with --shell option') parser.add_option('-M', '--import_models', action='store_true', default=False, dest='import_models', help=msg) msg = ('run PYTHON_FILE in web2py environment; ' 'should be used with --shell option') parser.add_option('-R', '--run', dest='run', metavar='PYTHON_FILE', default='', help=msg) msg = ('run scheduled tasks for the specified apps: expects a list of ' 'app names as -K app1,app2,app3 ' 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' 'to override specific group_names. (only strings, no spaces ' 'allowed. Requires a scheduler defined in the models') parser.add_option('-K', '--scheduler', dest='scheduler', default=None, help=msg) msg = 'run schedulers alongside webserver, needs -K app1 and -a too' parser.add_option('-X', '--with-scheduler', action='store_true', default=False, dest='with_scheduler', help=msg) msg = ('run doctests in web2py environment; ' 'TEST_PATH like a/c/f (c,f optional)') parser.add_option('-T', '--test', dest='test', metavar='TEST_PATH', default=None, help=msg) msg = 'trigger a cron run manually; usually invoked from a system crontab' parser.add_option('-C', '--cron', action='store_true', dest='extcron', default=False, help=msg) msg = 'triggers the use of softcron' parser.add_option('--softcron', action='store_true', dest='softcron', default=False, help=msg) parser.add_option('-Y', '--run-cron', action='store_true', dest='runcron', default=False, help='start the background cron process') parser.add_option('-J', '--cronjob', action='store_true', dest='cronjob', default=False, help='identify cron-initiated command') parser.add_option('-L', '--config', dest='config', default='', help='config file') parser.add_option('-F', '--profiler', dest='profiler_dir', default=None, help='profiler dir') parser.add_option('-t', '--taskbar', action='store_true', dest='taskbar', default=False, help='use web2py gui and run in taskbar (system tray)') parser.add_option('', '--nogui', action='store_true', default=False, dest='nogui', help='text-only, no GUI') msg = ('should be followed by a list of arguments to be passed to script, ' 'to be used with -S, -A must be the last option') parser.add_option('-A', '--args', action='store', dest='args', default=None, help=msg) parser.add_option('--no-banner', action='store_true', default=False, dest='nobanner', help='Do not print header banner') msg = ('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('--interfaces', action='store', dest='interfaces', default=None, help=msg) msg = 'runs web2py tests' parser.add_option('--run_system_tests', action='store_true', dest='run_system_tests', default=False, help=msg) msg = ('adds coverage reporting (needs --run_system_tests), ' 'python 2.7 and the coverage module installed. ' 'You can alter the default path setting the environmental ' 'var "COVERAGE_PROCESS_START". ' 'By default it takes gluon/tests/coverage.ini') parser.add_option('--with_coverage', action='store_true', dest='with_coverage', default=False, help=msg) 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() options.args = [options.run] + other_args global_settings.cmd_options = options global_settings.cmd_args = args try: options.ips = list( set( # no duplicates [ addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo) ])) except socket.gaierror: options.ips = [] if options.run_system_tests: run_system_tests(options) if options.quiet: capture = cStringIO.StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.config[-3:] == '.py': options.config = options.config[:-3] if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): interfaces = options.interfaces.split(';') options.interfaces = [] for interface in interfaces: if interface.startswith('['): # IPv6 ip, if_remainder = interface.split(']', 1) ip = ip[1:] if_remainder = if_remainder[1:].split(':') if_remainder[0] = int(if_remainder[0]) # numeric port options.interfaces.append(tuple([ip] + if_remainder)) else: # IPv4 interface = interface.split(':') interface[1] = int(interface[1]) # numeric port options.interfaces.append(tuple(interface)) # accepts --scheduler in the form # "app:group1,group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ':' in options.scheduler: for opt in options.scheduler.split(','): scheduler.append(opt.split(':')) options.scheduler = ','.join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists('applications/__init__.py'): write_file('applications/__init__.py', '') return options, args
def __init__(self, root, options): """ web2pyDialog constructor """ if PY2: import Tkinter as tkinter import tkMessageBox as messagebox else: import tkinter from tkinter import messagebox bg_color = 'white' root.withdraw() self.root = tkinter.Toplevel(root, bg=bg_color) self.root.resizable(0, 0) self.root.title(ProgramName) self.options = options self.scheduler_processes = {} self.menu = tkinter.Menu(self.root) servermenu = tkinter.Menu(self.menu, tearoff=0) httplog = os.path.join(self.options.folder, self.options.log_filename) iconphoto = os.path.join('extras', 'icons', 'web2py.gif') if os.path.exists(iconphoto): img = tkinter.PhotoImage(file=iconphoto) self.root.tk.call('wm', 'iconphoto', self.root._w, img) # Building the Menu item = lambda: start_browser(httplog) servermenu.add_command(label='View httpserver.log', command=item) servermenu.add_command(label='Quit (pid:%i)' % os.getpid(), command=self.quit) self.menu.add_cascade(label='Server', menu=servermenu) self.pagesmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Pages', menu=self.pagesmenu) #scheduler menu self.schedmenu = tkinter.Menu(self.menu, tearoff=0) self.menu.add_cascade(label='Scheduler', menu=self.schedmenu) #start and register schedulers from options self.update_schedulers(start=True) helpmenu = tkinter.Menu(self.menu, tearoff=0) # Home Page item = lambda: start_browser('http://www.web2py.com/') helpmenu.add_command(label='Home Page', command=item) # About ProgramInfo = """%s %s %s""" % (ProgramName, ProgramAuthor, ProgramVersion) item = lambda: messagebox.showinfo('About web2py', ProgramInfo) helpmenu.add_command(label='About', command=item) self.menu.add_cascade(label='Info', menu=helpmenu) self.root.config(menu=self.menu) if options.taskbar: self.root.protocol('WM_DELETE_WINDOW', lambda: self.quit(True)) else: self.root.protocol('WM_DELETE_WINDOW', self.quit) sticky = tkinter.NW # Prepare the logo area self.logoarea = tkinter.Canvas(self.root, background=bg_color, width=300, height=300) self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky) self.logoarea.after(1000, self.update_canvas) logo = os.path.join('extras', 'icons', 'splashlogo.gif') if os.path.exists(logo): img = tkinter.PhotoImage(file=logo) pnl = tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0) pnl.pack(side='top', fill='both', expand='yes') # Prevent garbage collection of img pnl.image = img # Prepare the banner area self.bannerarea = tkinter.Canvas(self.root, bg=bg_color, width=300, height=300) self.bannerarea.grid(row=1, column=1, columnspan=2, sticky=sticky) tkinter.Label(self.bannerarea, anchor=tkinter.N, text=str(ProgramVersion + "\n" + ProgramAuthor), font=('Helvetica', 11), justify=tkinter.CENTER, foreground='#195866', background=bg_color, height=3).pack(side='top', fill='both', expand='yes') self.bannerarea.after(1000, self.update_canvas) # IP # retrieves the list of server IP addresses try: if_ips = list(set( # no duplicates [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo)])) except socket.gaierror: if_ips = [] tkinter.Label(self.root, text='Server IP:', bg=bg_color, justify=tkinter.RIGHT).grid(row=4, column=1, sticky=sticky) self.ips = {} self.selected_ip = tkinter.StringVar() row = 4 ips = [('127.0.0.1', 'Local (IPv4)')] + \ ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \ [(ip, 'Public') for ip in if_ips] + \ [('0.0.0.0', 'Public')] for ip, legend in ips: self.ips[ip] = tkinter.Radiobutton( self.root, bg=bg_color, highlightthickness=0, selectcolor='light grey', width=30, anchor=tkinter.W, text='%s (%s)' % (legend, ip), justify=tkinter.LEFT, variable=self.selected_ip, value=ip) self.ips[ip].grid(row=row, column=2, sticky=sticky) if row == 4: self.ips[ip].select() row += 1 shift = row # Port tkinter.Label(self.root, text='Server Port:', bg=bg_color, justify=tkinter.RIGHT).grid(row=shift, column=1, pady=10, sticky=sticky) self.port_number = tkinter.Entry(self.root) self.port_number.insert(tkinter.END, self.options.port) self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10) # Password tkinter.Label(self.root, text='Choose Password:'******'*') self.password.bind('<Return>', lambda e: self.start()) self.password.focus_force() self.password.grid(row=shift + 1, column=2, sticky=sticky) # Prepare the canvas self.canvas = tkinter.Canvas(self.root, width=400, height=100, bg='black') self.canvas.grid(row=shift + 2, column=1, columnspan=2, pady=5, sticky=sticky) self.canvas.after(1000, self.update_canvas) # Prepare the frame frame = tkinter.Frame(self.root) frame.grid(row=shift + 3, column=1, columnspan=2, pady=5, sticky=sticky) # Start button self.button_start = tkinter.Button(frame, text='start server', command=self.start) self.button_start.grid(row=0, column=0, sticky=sticky) # Stop button self.button_stop = tkinter.Button(frame, text='stop server', command=self.stop) self.button_stop.grid(row=0, column=1, sticky=sticky) self.button_stop.configure(state='disabled') if options.taskbar: import gluon.contrib.taskbar_widget self.tb = gluon.contrib.taskbar_widget.TaskBarIcon() self.checkTaskBar() if options.password != '<ask>': self.password.insert(0, options.password) self.start() self.root.withdraw() else: self.tb = None
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 (if not disabled with --nogui).''') parser.add_option('-i', '--ip', default='127.0.0.1', 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', help='port of server (%default)') parser.add_option('-G', '--GAE', dest='gae', default=None, metavar='APP_NAME', help=\ "will create app.yaml and gaehandler.py") parser.add_option('-a', '--password', default='<ask>', help=\ 'password to be used for administration ' \ '(use -a "<recycle>" to reuse the last password))') parser.add_option('-c', '--ssl_certificate', default='', help='file that contains ssl certificate') parser.add_option('-k', '--ssl_private_key', default='', help='file that contains ssl private key') parser.add_option('--ca-cert', dest='ssl_ca_certificate', default=None, help=\ 'use this file containing the CA certificate to validate X509 ' \ 'certificates from clients') parser.add_option('-d', '--pid_filename', default='httpserver.pid', help='file to store the pid of the server') parser.add_option('-l', '--log_filename', default='httpserver.log', help='name for the server log file') parser.add_option('-n', '--numthreads', default=None, type='int', help='number of threads (deprecated)') parser.add_option('--minthreads', default=None, type='int', help='minimum number of server threads') parser.add_option('--maxthreads', default=None, type='int', 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', help=\ 'max number of queued requests when server unavailable') parser.add_option('-o', '--timeout', default='10', type='int', help='timeout for individual request (%default seconds)') parser.add_option('-z', '--shutdown_timeout', default='5', type='int', help='timeout on shutdown of server (%default seconds)') parser.add_option('--socket-timeout', dest='socket_timeout', # not needed default=5, type='int', help='timeout for socket (%default seconds)') parser.add_option('-f', '--folder', default=os.getcwd(), 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', dest='print_errors', default=False, action='store_true', help='log all errors to console') parser.add_option('-D', '--debug', dest='debuglevel', default=30, type='int', help=\ 'set debug output 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 x,y 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, help=\ 'run scheduled tasks for the specified apps: expects a list of ' \ 'app names as -K app1,app2,app3 ' \ 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' \ 'to override specific group_names. (only strings, no spaces ' \ 'allowed. 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 app1 and -a too') 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 manually; usually invoked from a system crontab') parser.add_option('--softcron', default=False, action='store_true', help='triggers the use of softcron') 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', help='identify cron-initiated command') 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, -A must be the last option') parser.add_option('--no-banner', dest='nobanner', 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=\ 'adds coverage reporting (needs --run_system_tests), ' \ 'python 2.7 and the coverage module installed. ' \ 'You can alter the default path setting the environment ' \ 'variable "COVERAGE_PROCESS_START" ' \ '(by default it takes gluon/tests/coverage.ini)') 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() options.args = other_args if options.config.endswith('.py'): options.config = options.config[:-3] # TODO: process --config here; now is done in start function, too late copy_options = copy.deepcopy(options) copy_options.password = '******' global_settings.cmd_options = copy_options global_settings.cmd_args = args if options.gae: if not os.path.exists('app.yaml'): name = options.gae # for backward compatibility if name == 'configure': name = input("Your GAE app name: ") content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() open('app.yaml', 'wb').write(content.replace("yourappname", name)) else: print("app.yaml alreday exists in the web2py folder") if not os.path.exists('gaehandler.py'): content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() open('gaehandler.py', 'wb').write(content) else: print("gaehandler.py alreday exists in the web2py folder") sys.exit(0) try: options.ips = list(set( # no duplicates [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo)])) except socket.gaierror: options.ips = [] # FIXME: this should be done after create_welcome_w2p if options.run_system_tests: # run system test and exit run_system_tests(options) if options.quiet: capture = StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): 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)) # accepts --scheduler in the form # "app:group1:group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ':' in options.scheduler: for opt in options.scheduler.split(','): scheduler.append(opt.split(':')) options.scheduler = ','.join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() # FIXME: do we still really need this? if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists('applications/__init__.py'): write_file('applications/__init__.py', '') return options, args
def console(): """ Defines the behavior of the console web2py execution """ import optparse import textwrap usage = "python web2py.py" description = """\ web2py Web Framework startup script. ATTENTION: unless a password is specified (-a 'passwd') web2py will attempt to run a GUI. In this case command line options are ignored.""" description = textwrap.dedent(description) parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion) parser.description = description msg = ( "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("-i", "--ip", default="127.0.0.1", dest="ip", help=msg) parser.add_option("-p", "--port", default="8000", dest="port", type="int", help="port of server (8000)") parser.add_option( "-G", "--GAE", default=None, dest="gae", help="'-G configure' will create app.yaml and gaehandler.py" ) msg = "password to be used for administration " '(use -a "<recycle>" to reuse the last password))' parser.add_option("-a", "--password", default="<ask>", dest="password", help=msg) parser.add_option( "-c", "--ssl_certificate", default="", dest="ssl_certificate", help="file that contains ssl certificate" ) parser.add_option( "-k", "--ssl_private_key", default="", dest="ssl_private_key", help="file that contains ssl private key" ) msg = "Use this file containing the CA certificate to validate X509 " "certificates from clients" parser.add_option("--ca-cert", action="store", dest="ssl_ca_certificate", default=None, help=msg) parser.add_option( "-d", "--pid_filename", default="httpserver.pid", dest="pid_filename", help="file to store the pid of the server", ) parser.add_option( "-l", "--log_filename", default="httpserver.log", dest="log_filename", help="file to log connections" ) parser.add_option( "-n", "--numthreads", default=None, type="int", dest="numthreads", help="number of threads (deprecated)" ) parser.add_option( "--minthreads", default=None, type="int", dest="minthreads", help="minimum number of server threads" ) parser.add_option( "--maxthreads", default=None, type="int", dest="maxthreads", help="maximum number of server threads" ) parser.add_option( "-s", "--server_name", default=socket.gethostname(), dest="server_name", help="server name for the web server" ) msg = "max number of queued requests when server unavailable" parser.add_option("-q", "--request_queue_size", default="5", type="int", dest="request_queue_size", help=msg) parser.add_option( "-o", "--timeout", default="10", type="int", dest="timeout", help="timeout for individual request (10 seconds)" ) parser.add_option( "-z", "--shutdown_timeout", default="5", type="int", dest="shutdown_timeout", help="timeout on shutdown of server (5 seconds)", ) parser.add_option( "--socket-timeout", default=5, type="int", dest="socket_timeout", help="timeout for socket (5 second)" ) parser.add_option("-f", "--folder", default=os.getcwd(), dest="folder", help="folder from which to run web2py") parser.add_option( "-v", "--verbose", action="store_true", dest="verbose", default=False, help="increase --test verbosity" ) parser.add_option("-Q", "--quiet", action="store_true", dest="quiet", default=False, help="disable all output") parser.add_option( "-e", "--errors_to_console", action="store_true", dest="print_errors", default=False, help="log all errors to console", ) msg = "set debug output level (0-100, 0 means all, 100 means none; " "default is 30)" parser.add_option("-D", "--debug", dest="debuglevel", default=30, type="int", help=msg) msg = ( "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 (c,f optional)" ) parser.add_option("-S", "--shell", dest="shell", metavar="APPNAME", help=msg) msg = ( "run web2py in interactive shell or bpython (if installed) with " "specified appname (if app does not exist it will be created).\n" "Use combined with --shell" ) parser.add_option("-B", "--bpython", action="store_true", default=False, dest="bpython", help=msg) msg = "only use plain python shell; should be used with --shell option" parser.add_option("-P", "--plain", action="store_true", default=False, dest="plain", help=msg) msg = "auto import model files; default is False; should be used " "with --shell option" parser.add_option("-M", "--import_models", action="store_true", default=False, dest="import_models", help=msg) msg = "run PYTHON_FILE in web2py environment; " "should be used with --shell option" parser.add_option("-R", "--run", dest="run", metavar="PYTHON_FILE", default="", help=msg) msg = ( "run scheduled tasks for the specified apps: expects a list of " "app names as -K app1,app2,app3 " "or a list of app:groups as -K app1:group1:group2,app2:group1 " "to override specific group_names. (only strings, no spaces " "allowed. Requires a scheduler defined in the models" ) parser.add_option("-K", "--scheduler", dest="scheduler", default=None, help=msg) msg = "run schedulers alongside webserver, needs -K app1 and -a too" parser.add_option("-X", "--with-scheduler", action="store_true", default=False, dest="with_scheduler", help=msg) msg = "run doctests in web2py environment; " "TEST_PATH like a/c/f (c,f optional)" parser.add_option("-T", "--test", dest="test", metavar="TEST_PATH", default=None, help=msg) msg = "trigger a cron run manually; usually invoked from a system crontab" parser.add_option("-C", "--cron", action="store_true", dest="extcron", default=False, help=msg) msg = "triggers the use of softcron" parser.add_option("--softcron", action="store_true", dest="softcron", default=False, help=msg) parser.add_option( "-Y", "--run-cron", action="store_true", dest="runcron", default=False, help="start the background cron process" ) parser.add_option( "-J", "--cronjob", action="store_true", dest="cronjob", default=False, help="identify cron-initiated command" ) parser.add_option("-L", "--config", dest="config", default="", help="config file") parser.add_option("-F", "--profiler", dest="profiler_dir", default=None, help="profiler dir") parser.add_option( "-t", "--taskbar", action="store_true", dest="taskbar", default=False, help="use web2py gui and run in taskbar (system tray)", ) parser.add_option("", "--nogui", action="store_true", default=False, dest="nogui", help="text-only, no GUI") msg = ( "should be followed by a list of arguments to be passed to script, " "to be used with -S, -A must be the last option" ) parser.add_option("-A", "--args", action="store", dest="args", default=None, help=msg) parser.add_option( "--no-banner", action="store_true", default=False, dest="nobanner", help="Do not print header banner" ) msg = ( "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("--interfaces", action="store", dest="interfaces", default=None, help=msg) msg = "runs web2py tests" parser.add_option("--run_system_tests", action="store_true", dest="run_system_tests", default=False, help=msg) msg = ( "adds coverage reporting (needs --run_system_tests), " "python 2.7 and the coverage module installed. " "You can alter the default path setting the environmental " 'var "COVERAGE_PROCESS_START". ' "By default it takes gluon/tests/coverage.ini" ) parser.add_option("--with_coverage", action="store_true", dest="with_coverage", default=False, help=msg) 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() options.args = [options.run] + other_args global_settings.cmd_options = options global_settings.cmd_args = args if options.gae: if not os.path.exists("app.yaml"): name = raw_input("Your GAE app name: ") content = open(os.path.join("examples", "app.example.yaml"), "rb").read() open("app.yaml", "wb").write(content.replace("yourappname", name)) else: print "app.yaml alreday exists in the web2py folder" if not os.path.exists("gaehandler.py"): content = open(os.path.join("handlers", "gaehandler.py"), "rb").read() open("gaehandler.py", "wb").write(content) else: print "gaehandler.py alreday exists in the web2py folder" sys.exit(0) try: options.ips = list( set( # no duplicates [ addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo) ] ) ) except socket.gaierror: options.ips = [] if options.run_system_tests: run_system_tests(options) if options.quiet: capture = cStringIO.StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.config[-3:] == ".py": options.config = options.config[:-3] if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): interfaces = options.interfaces.split(";") options.interfaces = [] for interface in interfaces: if interface.startswith("["): # IPv6 ip, if_remainder = interface.split("]", 1) ip = ip[1:] if_remainder = if_remainder[1:].split(":") if_remainder[0] = int(if_remainder[0]) # numeric port options.interfaces.append(tuple([ip] + if_remainder)) else: # IPv4 interface = interface.split(":") interface[1] = int(interface[1]) # numeric port options.interfaces.append(tuple(interface)) # accepts --scheduler in the form # "app:group1,group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ":" in options.scheduler: for opt in options.scheduler.split(","): scheduler.append(opt.split(":")) options.scheduler = ",".join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists("applications/__init__.py"): write_file("applications/__init__.py", "") return options, args
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='ssl_ca_certificate', 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', dest='print_errors', 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 x,y 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', help='identify cron-initiated command') 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='nobanner', 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=\ 'adds coverage reporting (should be used with --run_system_tests), ' \ 'needs Python 2.7+ and the coverage module installed. ' \ 'You can alter the default path setting the environment ' \ 'variable "COVERAGE_PROCESS_START" ' \ '(by default it takes gluon/tests/coverage.ini)') 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.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)) # store in options.ips the list of server IP addresses try: options.ips = list(set( # no duplicates [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo)])) except socket.gaierror: options.ips = [] if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True # 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)) # strip group infos from options.scheduler, in the form # "app:group1:group2,app2:group1", and put into a list of lists # in options.scheduler_groups if options.scheduler and ':' in options.scheduler: sg = options.scheduler_groups = [] for awg in options.scheduler.split(','): sg.append(awg.split(':')) options.scheduler = ','.join([app[0] for app in sg]) else: options.scheduler_groups = None 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