source: OpenRLabs-Git/deploy/rlabs-docker/web2py-rlabs/gluon/widget.py @ 42095c5

mainqndtest v1.1.1
Last change on this file since 42095c5 was 42bd667, checked in by David Fuertes <dfuertes@…>, 4 years ago

Historial Limpio

  • Property mode set to 100755
File size: 32.0 KB
Line 
1# -*- coding: utf-8 -*-
2# vim: set ts=4 sw=4 et ai:
3
4"""
5| This file is part of the web2py Web Framework
6| Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
7| License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
8
9GUI widget and services start function
10--------------------------------------
11"""
12
13from __future__ import print_function
14
15import time
16import sys
17import os
18from collections import OrderedDict
19import socket
20import threading
21import math
22import logging
23import signal
24import getpass
25
26from gluon.fileutils import read_file, create_welcome_w2p
27from gluon.shell import die, run, test
28from gluon._compat import PY2, xrange
29from gluon.utils import (getipaddrinfo, is_loopback_ip_address,
30    is_valid_ip_address)
31from gluon.console import is_appdir, console
32from gluon import newcron
33from gluon import main
34from gluon.settings import global_settings
35
36
37ProgramName = 'web2py Web Framework'
38ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str(
39    time.localtime().tm_year)
40ProgramVersion = read_file('VERSION').rstrip()
41
42if sys.version_info < (2, 7) or (3, 0) < sys.version_info < (3, 5):
43    from platform import python_version
44    sys.stderr.write("Warning: web2py requires at least Python 2.7/3.5"
45        " but you are running %s\n" % python_version())
46
47
48def run_system_tests(options):
49    """
50    Runs unittests for gluon.tests
51    """
52    # see "python -m unittest -h" for unittest options help
53    # NOTE: someone might be interested either in using the
54    #       -f (--failfast) option to stop testing on first failure, or
55    #       in customizing the test selection, for example to run only
56    #       'gluon.tests.<module>', 'gluon.tests.<module>.<class>' (this
57    #       could be shortened as 'gluon.tests.<class>'), or even
58    #       'gluon.tests.<module>.<class>.<method>' (or
59    #       the shorter 'gluon.tests.<class>.<method>')
60    call_args = ['-m', 'unittest', '-c', 'gluon.tests']
61    if options.verbose:
62        call_args.insert(-1, '-v')
63    if options.with_coverage:
64        try:
65            import coverage
66        except:
67            die('Coverage not installed')
68    if not PY2:
69        sys.stderr.write('Experimental ')
70    sys.stderr.write("Python %s\n" % sys.version)
71    if options.with_coverage:
72        coverage_exec = 'coverage2' if PY2 else 'coverage3'
73        coverage_config_file = os.path.join('gluon', 'tests', 'coverage.ini')
74        coverage_config = os.environ.setdefault("COVERAGE_PROCESS_START",
75                                                coverage_config_file)
76        run_args = [coverage_exec, 'run', '--rcfile=%s' % coverage_config]
77        # replace the current process
78        os.execvpe(run_args[0], run_args + call_args, os.environ)
79    else:
80        run_args = [sys.executable]
81        # replace the current process
82        os.execv(run_args[0], run_args + call_args)
83
84
85def get_url(host, path='/', proto='http', port=80):
86    if ':' in host:
87        host = '[%s]' % host
88    elif host == '0.0.0.0':
89        host = '127.0.0.1'
90    if path.startswith('/'):
91        path = path[1:]
92    if proto.endswith(':'):
93        proto = proto[:-1]
94    if not port or port == 80:
95        port = ''
96    else:
97        port = ':%s' % port
98    return '%s://%s%s/%s' % (proto, host, port, path)
99
100
101def start_browser(url, startup=False):
102    if startup:
103        print('please visit:')
104        print('\t' + url)
105        print('starting browser...')
106    try:
107        import webbrowser
108        webbrowser.open(url)
109    except:
110        print('warning: unable to detect your browser')
111
112
113class web2pyDialog(object):
114    """ Main window dialog """
115
116    def __init__(self, root, options):
117        """ web2pyDialog constructor  """
118
119        if PY2:
120            import Tkinter as tkinter
121            import tkMessageBox as messagebox
122        else:
123            import tkinter
124            from tkinter import messagebox
125
126        root.withdraw()
127
128        bg_color = 'white'
129        self.root = tkinter.Toplevel(root, bg=bg_color)
130        self.root.resizable(0, 0)
131        self.root.title(ProgramName)
132
133        self.options = options
134        self.scheduler_processes_lock = threading.RLock()
135        self.scheduler_processes = OrderedDict()
136
137        iconphoto = os.path.join('extras', 'icons', 'web2py.gif')
138        if os.path.exists(iconphoto):
139            img = tkinter.PhotoImage(file=iconphoto)
140            self.root.tk.call('wm', 'iconphoto', self.root._w, img)
141
142        # Building the Menu
143        self.menu = tkinter.Menu(self.root)
144        servermenu = tkinter.Menu(self.menu, tearoff=0)
145
146        httplog = os.path.join(options.folder, options.log_filename)
147        item = lambda: start_browser(httplog)
148        servermenu.add_command(label='View httpserver.log',
149                               command=item)
150
151        servermenu.add_command(label='Quit (pid:%i)' % os.getpid(),
152                               command=self.quit)
153
154        self.menu.add_cascade(label='Server', menu=servermenu)
155
156        self.pagesmenu = tkinter.Menu(self.menu, tearoff=0)
157        self.menu.add_cascade(label='Pages', menu=self.pagesmenu)
158
159        self.schedmenu = tkinter.Menu(self.menu, tearoff=0)
160        self.menu.add_cascade(label='Scheduler', menu=self.schedmenu)
161        # register and start schedulers
162        self.update_schedulers(start=True)
163
164        helpmenu = tkinter.Menu(self.menu, tearoff=0)
165
166        # Home Page
167        item = lambda: start_browser('http://www.web2py.com/')
168        helpmenu.add_command(label='Home Page',
169                             command=item)
170
171        # About
172        ProgramInfo = """%s
173                 %s
174                 %s""" % (ProgramName, ProgramAuthor, ProgramVersion)
175        item = lambda: messagebox.showinfo('About web2py', ProgramInfo)
176        helpmenu.add_command(label='About',
177                             command=item)
178
179        self.menu.add_cascade(label='Info', menu=helpmenu)
180
181        self.root.config(menu=self.menu)
182
183        if options.taskbar:
184            self.root.protocol('WM_DELETE_WINDOW',
185                               lambda: self.quit(True))
186        else:
187            self.root.protocol('WM_DELETE_WINDOW', self.quit)
188
189        sticky = tkinter.NW
190
191        # Prepare the logo area
192        self.logoarea = tkinter.Canvas(self.root,
193                                background=bg_color,
194                                width=300,
195                                height=300)
196        self.logoarea.grid(row=0, column=0, columnspan=4, sticky=sticky)
197        self.logoarea.after(1000, self.update_canvas)
198
199        logo = os.path.join('extras', 'icons', 'splashlogo.gif')
200        if os.path.exists(logo):
201            img = tkinter.PhotoImage(file=logo)
202            pnl = tkinter.Label(self.logoarea, image=img, background=bg_color, bd=0)
203            pnl.pack(side='top', fill='both', expand='yes')
204            # Prevent garbage collection of img
205            pnl.image = img
206
207        # Prepare the banner area
208        self.bannerarea = tkinter.Canvas(self.root,
209                                bg=bg_color,
210                                width=300,
211                                height=300)
212        self.bannerarea.grid(row=1, column=1, columnspan=2, sticky=sticky)
213
214        tkinter.Label(self.bannerarea, anchor=tkinter.N,
215                      text=str(ProgramVersion + "\n" + ProgramAuthor),
216                      font=('Helvetica', 11), justify=tkinter.CENTER,
217                      foreground='#195866', background=bg_color,
218                      height=3).pack(side='top',
219                                     fill='both',
220                                     expand='yes')
221
222        self.bannerarea.after(1000, self.update_canvas)
223
224        # IP
225        # retrieves the list of server IP addresses
226        try:
227            if_ips = list(set(  # no duplicates
228                [addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn())
229                 if not is_loopback_ip_address(addrinfo=addrinfo)]))
230        except socket.gaierror:
231            if_ips = []
232
233        tkinter.Label(self.root,
234                      text='Server IP:', bg=bg_color,
235                      justify=tkinter.RIGHT).grid(row=4,
236                                                  column=1,
237                                                  sticky=sticky)
238        self.ips = {}
239        self.selected_ip = tkinter.StringVar()
240        row = 4
241        ips = [('127.0.0.1', 'Local (IPv4)')] + \
242            ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \
243            [(ip, 'Public') for ip in if_ips] + \
244            [('0.0.0.0', 'Public')]
245        for ip, legend in ips:
246            self.ips[ip] = tkinter.Radiobutton(
247                self.root, bg=bg_color, highlightthickness=0,
248                selectcolor='light grey', width=30,
249                anchor=tkinter.W, text='%s (%s)' % (legend, ip),
250                justify=tkinter.LEFT,
251                variable=self.selected_ip, value=ip)
252            self.ips[ip].grid(row=row, column=2, sticky=sticky)
253            if row == 4:
254                self.ips[ip].select()
255            row += 1
256        shift = row
257
258        # Port
259        tkinter.Label(self.root,
260                      text='Server Port:', bg=bg_color,
261                      justify=tkinter.RIGHT).grid(row=shift,
262                                                  column=1, pady=10,
263                                                  sticky=sticky)
264
265        self.port_number = tkinter.Entry(self.root)
266        self.port_number.insert(tkinter.END, options.port)
267        self.port_number.grid(row=shift, column=2, sticky=sticky, pady=10)
268
269        # Password
270        tkinter.Label(self.root,
271                      text='Choose Password:', bg=bg_color,
272                      justify=tkinter.RIGHT).grid(row=shift + 1,
273                                                  column=1,
274                                                  sticky=sticky)
275
276        self.password = tkinter.Entry(self.root, show='*')
277        self.password.bind('<Return>', lambda e: self.start())
278        self.password.focus_force()
279        self.password.grid(row=shift + 1, column=2, sticky=sticky)
280
281        # Prepare the canvas
282        self.canvas = tkinter.Canvas(self.root,
283                                     width=400,
284                                     height=100,
285                                     bg='black')
286        self.canvas.grid(row=shift + 2, column=1, columnspan=2, pady=5,
287                         sticky=sticky)
288        self.canvas.after(1000, self.update_canvas)
289
290        # Prepare the frame
291        frame = tkinter.Frame(self.root)
292        frame.grid(row=shift + 3, column=1, columnspan=2, pady=5,
293                   sticky=sticky)
294
295        # Start button
296        self.button_start = tkinter.Button(frame,
297                                           text='start server',
298                                           command=self.start)
299
300        self.button_start.grid(row=0, column=0, sticky=sticky)
301
302        # Stop button
303        self.button_stop = tkinter.Button(frame,
304                                          text='stop server',
305                                          command=self.stop)
306
307        self.button_stop.grid(row=0, column=1,  sticky=sticky)
308        self.button_stop.configure(state='disabled')
309
310        if options.taskbar:
311            import gluon.contrib.taskbar_widget
312            self.tb = gluon.contrib.taskbar_widget.TaskBarIcon()
313            self.checkTaskBar()
314
315            if options.password != '<ask>':
316                self.password.insert(0, options.password)
317                self.start()
318                self.root.withdraw()
319        else:
320            self.tb = None
321
322    def update_schedulers(self, start=False):
323        applications_folder = os.path.join(self.options.folder, 'applications')
324        available_apps = [
325            arq for arq in os.listdir(applications_folder)
326            if os.path.isdir(os.path.join(applications_folder, arq))
327        ]
328        with self.scheduler_processes_lock:
329            # reset the menu
330            # since applications can disappear (be disinstalled) must
331            # clear the menu (should use tkinter.END or tkinter.LAST)
332            self.schedmenu.delete(0, 'end')
333            for arq in available_apps:
334                if arq not in self.scheduler_processes:
335                    item = lambda a = arq: self.try_start_scheduler(a)
336                    self.schedmenu.add_command(label="start %s" % arq,
337                                               command=item)
338                if arq in self.scheduler_processes:
339                    item = lambda a = arq: self.try_stop_scheduler(a)
340                    self.schedmenu.add_command(label="stop %s" % arq,
341                                               command=item)
342
343        if start and self.options.with_scheduler and self.options.schedulers:
344            # the widget takes care of starting the schedulers
345            apps = [ag.split(':', 1)[0] for ag in self.options.schedulers]
346        else:
347            apps = []
348        for app in apps:
349            self.try_start_scheduler(app)
350
351    def start_schedulers(self, app):
352        from multiprocessing import Process
353        code = "from gluon.globals import current;current._scheduler.loop()"
354        print('starting scheduler from widget for "%s"...' % app)
355        args = (app, True, True, None, False, code, False, True)
356        p = Process(target=run, args=args)
357        with self.scheduler_processes_lock:
358            self.scheduler_processes[app] = p
359            self.update_schedulers()
360            print("Currently running %s scheduler processes" % (
361                len(self.scheduler_processes)))
362        p.start()
363        print("Processes started")
364
365    def try_stop_scheduler(self, app, skip_update=False):
366        p = None
367        with self.scheduler_processes_lock:
368            if app in self.scheduler_processes:
369                p = self.scheduler_processes[app]
370                del self.scheduler_processes[app]
371        if p is not None:
372            p.terminate()
373            p.join()
374        if not skip_update:
375            self.update_schedulers()
376
377    def try_start_scheduler(self, app):
378        t = None
379        with self.scheduler_processes_lock:
380            if not is_appdir(self.options.folder, app):
381                self.schedmenu.delete("start %s" % app)
382                return
383            if app not in self.scheduler_processes:
384                t = threading.Thread(target=self.start_schedulers, args=(app,))
385        if t is not None:
386            t.start()
387
388    def checkTaskBar(self):
389        """ Checks taskbar status """
390        tb = self.tb
391        if tb.status:
392            st0 = tb.status[0]
393            EnumStatus = tb.EnumStatus
394            if st0 == EnumStatus.QUIT:
395                self.quit()
396            elif st0 == EnumStatus.TOGGLE:
397                if self.root.state() == 'withdrawn':
398                    self.root.deiconify()
399                else:
400                    self.root.withdraw()
401            elif st0 == EnumStatus.STOP:
402                self.stop()
403            elif st0 == EnumStatus.START:
404                self.start()
405            elif st0 == EnumStatus.RESTART:
406                self.stop()
407                self.start()
408            del tb.status[0]
409
410        self.root.after(1000, self.checkTaskBar)
411
412    def connect_pages(self):
413        """ Connects pages """
414        # reset the menu,
415        # since applications can disappear (be disinstalled) must
416        # clear the menu (should use tkinter.END or tkinter.LAST)
417        self.pagesmenu.delete(0, 'end')
418        applications_folder = os.path.join(self.options.folder, 'applications')
419        available_apps = [
420            arq for arq in os.listdir(applications_folder)
421            if os.path.exists(os.path.join(applications_folder, arq, '__init__.py'))
422        ]
423        for arq in available_apps:
424            url = self.url + arq
425            item = lambda a = arq: self.try_start_browser(a)
426            self.pagesmenu.add_command(
427                label=url, command=item)
428
429    def try_start_browser(self, app):
430        url = self.url + app
431        if not is_appdir(self.options.folder, app):
432            self.pagesmenu.delete(url)
433            return
434        start_browser(url)
435
436    def quit(self, justHide=False):
437        """ Finishes the program execution """
438        if justHide:
439            self.root.withdraw()
440        else:
441            try:
442                with self.scheduler_processes_lock:
443                    scheds = list(self.scheduler_processes.keys())
444                for t in scheds:
445                    self.try_stop_scheduler(t, skip_update=True)
446            except:
447                pass
448            if self.options.with_cron and not self.options.soft_cron:
449                # shutting down hardcron
450                try:
451                    newcron.stopcron()
452                except:
453                    pass
454            try:
455                # HttpServer.stop takes care of stopping softcron
456                self.server.stop()
457            except:
458                pass
459            try:
460                self.tb.Destroy()
461            except:
462                pass
463
464            self.root.destroy()
465            sys.exit(0)
466
467    def error(self, message):
468        """ Shows error message """
469        if PY2:
470            import tkMessageBox as messagebox
471        else:
472            from tkinter import messagebox
473        messagebox.showerror('web2py start server', message)
474
475    def start(self):
476        """ Starts web2py server """
477        password = self.password.get()
478        if not password:
479            self.error('no password, no web admin interface')
480
481        ip = self.selected_ip.get()
482        if not is_valid_ip_address(ip):
483            return self.error('invalid host ip address')
484        try:
485            port = int(self.port_number.get())
486        except ValueError:
487            return self.error('invalid port number')
488
489        if self.options.server_key and self.options.server_cert:
490            proto = 'https'
491        else:
492            proto = 'http'
493        self.url = get_url(ip, proto=proto, port=port)
494
495        self.connect_pages()
496        self.update_schedulers()
497
498        # softcron is stopped with HttpServer, thus if starting again
499        # need to reset newcron._stopping to re-enable cron
500        if self.options.soft_cron:
501            newcron.reset()
502
503        # FIXME: if the HttpServer is stopped, then started again,
504        #        does not start because of following error:
505        # WARNING:Rocket.Errors.Port8000:Listener started when not ready.
506
507        self.button_start.configure(state='disabled')
508
509        try:
510            options = self.options
511            req_queue_size = options.request_queue_size
512            self.server = main.HttpServer(
513                ip,
514                port,
515                password,
516                pid_filename=options.pid_filename,
517                log_filename=options.log_filename,
518                profiler_dir=options.profiler_dir,
519                ssl_certificate=options.server_cert,
520                ssl_private_key=options.server_key,
521                ssl_ca_certificate=options.ca_cert,
522                min_threads=options.min_threads,
523                max_threads=options.max_threads,
524                server_name=options.server_name,
525                request_queue_size=req_queue_size,
526                timeout=options.timeout,
527                shutdown_timeout=options.shutdown_timeout,
528                path=options.folder,
529                interfaces=options.interfaces)
530
531            threading.Thread(target=self.server.start).start()
532        except Exception as e:
533            self.button_start.configure(state='normal')
534            return self.error(str(e))
535
536        if not self.server_ready():
537            self.button_start.configure(state='normal')
538            return
539
540        self.button_stop.configure(state='normal')
541
542        if not options.taskbar:
543            cpt = threading.Thread(target=start_browser,
544                args=(get_url(ip, proto=proto, port=port), True))
545            cpt.setDaemon(True)
546            cpt.start()
547
548        self.password.configure(state='readonly')
549        for ip in self.ips.values():
550            ip.configure(state='disabled')
551        self.port_number.configure(state='readonly')
552
553        if self.tb:
554            self.tb.SetServerRunning()
555
556    def server_ready(self):
557        for listener in self.server.server.listeners:
558            if listener.ready:
559                return True
560        return False
561
562    def stop(self):
563        """ Stops web2py server """
564        self.button_start.configure(state='normal')
565        self.button_stop.configure(state='disabled')
566        self.password.configure(state='normal')
567        for ip in self.ips.values():
568            ip.configure(state='normal')
569        self.port_number.configure(state='normal')
570        self.server.stop()
571
572        if self.tb:
573            self.tb.SetServerStopped()
574
575    def update_canvas(self):
576        """ Updates canvas """
577        httplog = os.path.join(self.options.folder, self.options.log_filename)
578        canvas = self.canvas
579        try:
580            t1 = os.path.getsize(httplog)
581        except OSError:
582            canvas.after(1000, self.update_canvas)
583            return
584
585        points = 400
586        try:
587            pvalues = self.p0[1:]
588            with open(httplog, 'r') as fp:
589                fp.seek(self.t0)
590                data = fp.read(t1 - self.t0)
591            self.p0 = pvalues + [10 + 90.0 / math.sqrt(1 + data.count('\n'))]
592
593            for i in xrange(points - 1):
594                c = canvas.coords(self.q0[i])
595                canvas.coords(self.q0[i],
596                                   (c[0], self.p0[i],
597                                    c[2], self.p0[i + 1]))
598            self.t0 = t1
599        except AttributeError:
600            self.t0 = time.time()
601            self.t0 = t1
602            self.p0 = [100] * points
603            self.q0 = [canvas.create_line(i, 100, i + 1, 100,
604                       fill='green') for i in xrange(points - 1)]
605
606        canvas.after(1000, self.update_canvas)
607
608
609def get_code_for_scheduler(applications_parent, app_groups):
610    app = app_groups[0]
611    if not is_appdir(applications_parent, app):
612        print("Application '%s' doesn't exist, skipping" % app)
613        return None, None
614    code = 'from gluon.globals import current;'
615    if len(app_groups) > 1:
616        code += "current._scheduler.group_names=['%s'];" % "','".join(
617            app_groups[1:])
618    code += "current._scheduler.loop()"
619    return app, code
620
621
622def start_schedulers(options):
623    from multiprocessing import Process
624    apps = [ag.split(':') for ag in options.schedulers]
625    if not options.with_scheduler and len(apps) == 1:
626        app, code = get_code_for_scheduler(options.folder, apps[0])
627        if not app:
628            return
629        print('starting single-scheduler for "%s"...' % app)
630        run(app, True, True, None, False, code, False, True)
631        return
632
633    # Work around OS X problem: http://bugs.python.org/issue9405
634    if PY2:
635        import urllib
636    else:
637        import urllib.request as urllib
638    urllib.getproxies()
639
640    processes = []
641    for app_groups in apps:
642        app, code = get_code_for_scheduler(options.folder, app_groups)
643        if not app:
644            continue
645        print('starting scheduler for "%s"...' % app)
646        args = (app, True, True, None, False, code, False, True)
647        p = Process(target=run, args=args)
648        processes.append(p)
649        print("Currently running %s scheduler processes" % (len(processes)))
650        p.start()
651        ##to avoid bashing the db at the same time
652        time.sleep(0.7)
653        print("Processes started")
654    for p in processes:
655        try:
656            p.join()
657        except (KeyboardInterrupt, SystemExit):
658            print("Processes stopped")
659        except:
660            p.terminate()
661            p.join()
662
663
664def start():
665    """ Starts server and other services """
666
667    # get command line arguments
668    options = console(version=ProgramVersion)
669
670    if options.with_scheduler or len(options.schedulers) > 1:
671        try:
672            from multiprocessing import Process
673        except:
674            die('Sorry, -K/--scheduler only supported for Python 2.6+')
675
676    if options.gae:
677        # write app.yaml, gaehandler.py, and exit
678        if not os.path.exists('app.yaml'):
679            name = options.gae
680            # for backward compatibility
681            if name == 'configure':
682                if PY2: input = raw_input
683                name = input("Your GAE app name: ")
684            content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read()
685            open('app.yaml', 'wb').write(content.replace("yourappname", name))
686        else:
687            print("app.yaml alreday exists in the web2py folder")
688        if not os.path.exists('gaehandler.py'):
689            content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read()
690            open('gaehandler.py', 'wb').write(content)
691        else:
692            print("gaehandler.py alreday exists in the web2py folder")
693        return
694
695    logger = logging.getLogger("web2py")
696    logger.setLevel(options.log_level)
697    logging.getLogger().setLevel(options.log_level) # root logger
698
699    # on new installation build the scaffolding app
700    create_welcome_w2p()
701
702    if options.run_system_tests:
703        # run system test and exit
704        run_system_tests(options)
705
706    if options.quiet:
707        # to prevent writes on stdout set a null stream
708        class NullFile(object):
709            def write(self, x):
710                pass
711        sys.stdout = NullFile()
712        # but still has to mute existing loggers, to do that iterate
713        # over all existing loggers (root logger included) and remove
714        # all attached logging.StreamHandler instances currently
715        # streaming on sys.stdout or sys.stderr
716        loggers = [logging.getLogger()]
717        loggers.extend(logging.Logger.manager.loggerDict.values())
718        for l in loggers:
719            if isinstance(l, logging.PlaceHolder): continue
720            for h in l.handlers[:]:
721                if isinstance(h, logging.StreamHandler) and \
722                    h.stream in (sys.stdout, sys.stderr):
723                    l.removeHandler(h)
724        # NOTE: stderr.write() is still working
725
726    if not options.no_banner:
727        # banner
728        print(ProgramName)
729        print(ProgramAuthor)
730        print(ProgramVersion)
731        from pydal.drivers import DRIVERS
732        print('Database drivers available: %s' % ', '.join(DRIVERS))
733
734    if options.run_doctests:
735        # run doctests and exit
736        test(options.run_doctests, verbose=options.verbose)
737        return
738
739    if options.shell:
740        # run interactive shell and exit
741        sys.argv = [options.run or ''] + options.args
742        run(options.shell, plain=options.plain, bpython=options.bpython,
743            import_models=options.import_models, startfile=options.run,
744            cron_job=options.cron_job, force_migrate=options.force_migrate,
745            fake_migrate=options.fake_migrate)
746        return
747
748    # set size of cron thread pools
749    newcron.dancer_size(options.min_threads)
750    newcron.launcher_size(options.cron_threads)
751
752    if options.cron_run:
753        # run cron (extcron) and exit
754        logger.debug('Running extcron...')
755        global_settings.web2py_crontype = 'external'
756        newcron.extcron(options.folder, apps=options.crontabs)
757        return
758
759    if not options.with_scheduler and options.schedulers:
760        # run schedulers and exit
761        try:
762            start_schedulers(options)
763        except KeyboardInterrupt:
764            pass
765        return
766
767    if options.with_cron:
768        if options.soft_cron:
769            print('Using cron software emulation (but this is not very efficient)')
770            global_settings.web2py_crontype = 'soft'
771        else:
772            # start hardcron thread
773            logger.debug('Starting hardcron...')
774            global_settings.web2py_crontype = 'hard'
775            newcron.hardcron(options.folder, apps=options.crontabs).start()
776
777    # if no password provided and have Tk library start GUI (when not
778    # explicitly disabled), we also need a GUI to put in taskbar (system tray)
779    # when requested
780    root = None
781
782    if (not options.no_gui and options.password == '<ask>') or options.taskbar:
783        try:
784            if PY2:
785                import Tkinter as tkinter
786            else:
787                import tkinter
788            root = tkinter.Tk()
789        except (ImportError, OSError):
790            logger.warn(
791                'GUI not available because Tk library is not installed')
792            options.no_gui = True
793        except:
794            logger.exception('cannot get Tk root window, GUI disabled')
795            options.no_gui = True
796
797    if root:
798        # run GUI and exit
799        root.focus_force()
800
801        # Mac OS X - make the GUI window rise to the top
802        if os.path.exists("/usr/bin/osascript"):
803            applescript = """
804tell application "System Events"
805    set proc to first process whose unix id is %d
806    set frontmost of proc to true
807end tell
808""" % (os.getpid())
809            os.system("/usr/bin/osascript -e '%s'" % applescript)
810
811        # web2pyDialog takes care of schedulers
812        master = web2pyDialog(root, options)
813        signal.signal(signal.SIGTERM, lambda a, b: master.quit())
814
815        try:
816            root.mainloop()
817        except:
818            master.quit()
819
820        sys.exit()
821
822    spt = None
823
824    if options.with_scheduler and options.schedulers:
825        # start schedulers in a separate thread
826        spt = threading.Thread(target=start_schedulers, args=(options,))
827        spt.start()
828
829    # start server
830
831    if options.password == '<ask>':
832        options.password = getpass.getpass('choose a password:')
833
834    if not options.password and not options.no_banner:
835        print('no password, no web admin interface')
836
837    # Use first interface IP and port if interfaces specified, since the
838    # interfaces option overrides the IP (and related) options.
839    if not options.interfaces:
840        ip = options.ip
841        port = options.port
842    else:
843        first_if = options.interfaces[0]
844        ip = first_if[0]
845        port = first_if[1]
846
847    if options.server_key and options.server_cert:
848        proto = 'https'
849    else:
850        proto = 'http'
851
852    url = get_url(ip, proto=proto, port=port)
853
854    if not options.no_banner:
855        message = '\nplease visit:\n\t%s\n'
856        if sys.platform.startswith('win'):
857            message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n'
858        else:
859            message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n'
860        print(message % (url, os.getpid()))
861
862    # enhance linecache.getline (used by debugger) to look at the source file
863    # if the line was not found (under py2exe & when file was modified)
864    import linecache
865    py2exe_getline = linecache.getline
866
867    def getline(filename, lineno, *args, **kwargs):
868        line = py2exe_getline(filename, lineno, *args, **kwargs)
869        if not line:
870            try:
871                with open(filename, "rb") as f:
872                    for i, line in enumerate(f):
873                        line = line.decode('utf-8')
874                        if lineno == i + 1:
875                            break
876                    else:
877                        line = ''
878            except (IOError, OSError):
879                line = ''
880        return line
881    linecache.getline = getline
882
883    server = main.HttpServer(ip=ip,
884                             port=port,
885                             password=options.password,
886                             pid_filename=options.pid_filename,
887                             log_filename=options.log_filename,
888                             profiler_dir=options.profiler_dir,
889                             ssl_certificate=options.server_cert,
890                             ssl_private_key=options.server_key,
891                             ssl_ca_certificate=options.ca_cert,
892                             min_threads=options.min_threads,
893                             max_threads=options.max_threads,
894                             server_name=options.server_name,
895                             request_queue_size=options.request_queue_size,
896                             timeout=options.timeout,
897                             socket_timeout=options.socket_timeout,
898                             shutdown_timeout=options.shutdown_timeout,
899                             path=options.folder,
900                             interfaces=options.interfaces)
901
902    try:
903        server.start()
904    except KeyboardInterrupt:
905        server.stop()
906        if spt is not None:
907            try:
908                spt.join()
909            except:
910                logger.exception('error terminating schedulers')
911    logging.shutdown()
Note: See TracBrowser for help on using the repository browser.