|
Package Dropbox ::
Package web2py ::
Package gluon ::
Module widget
|
|
1
2
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
9 The widget is called from web2py.
10 """
11
12 import datetime
13 import sys
14 import cStringIO
15 import time
16 import thread
17 import threading
18 import re
19 import os
20 import socket
21 import signal
22 import math
23 import logging
24 import newcron
25 import getpass
26 import main
27
28 from fileutils import w2p_pack, read_file, write_file, create_welcome_w2p
29 from settings import global_settings
30 from shell import run, test
31 from utils import is_valid_ip_address, is_loopback_ip_address
32
33 try:
34 import Tkinter
35 import tkMessageBox
36 import contrib.taskbar_widget
37 from winservice import register_service_handler, Web2pyService
38 have_winservice = True
39 except:
40 have_winservice = False
41
42
43 try:
44 BaseException
45 except NameError:
46 BaseException = Exception
47
48 ProgramName = 'web2py Web Framework'
49 ProgramAuthor = 'Created by Massimo Di Pierro, Copyright 2007-' + str(
50 datetime.datetime.now().year)
51 ProgramVersion = read_file('VERSION').strip()
52
53 ProgramInfo = '''%s
54 %s
55 %s''' % (ProgramName, ProgramAuthor, ProgramVersion)
56
57 if not sys.version[:3] in ['2.4', '2.5', '2.6', '2.7']:
58 msg = 'Warning: web2py requires Python 2.4, 2.5 (recommended), 2.6 or 2.7 but you are running:\n%s'
59 msg = msg % sys.version
60 sys.stderr.write(msg)
61
62 logger = logging.getLogger("web2py")
63
64
66 major_version = sys.version_info[0]
67 minor_version = sys.version_info[1]
68 print "minor_version = %r" % minor_version
69 if major_version == 2:
70 if minor_version in (5, 6):
71 print "Python 2.5 or 2.6"
72 ret = os.system("PYTHONPATH=. unit2 -v gluon.tests")
73 elif minor_version in (7,):
74 print "Python 2.7"
75 ret = os.system("PYTHONPATH=. python -m unittest -v gluon.tests")
76 else:
77 print "unknown python 2.x version"
78 ret = 256
79 else:
80 print "Only Python 2.x supported."
81 ret = 256
82 sys.exit(ret and 1)
83
84
86 """ """
87
89 """ """
90
91 self.buffer = cStringIO.StringIO()
92
94 """ """
95
96 sys.__stdout__.write(data)
97 if hasattr(self, 'callback'):
98 self.callback(data)
99 else:
100 self.buffer.write(data)
101
102
103 -def get_url(host, path='/', proto='http', port=80):
104 if ':' in host:
105 host = '[%s]' % host
106 else:
107 host = host.replace('0.0.0.0', '127.0.0.1')
108 if path.startswith('/'):
109 path = path[1:]
110 if proto.endswith(':'):
111 proto = proto[:-1]
112 if not port or port == 80:
113 port = ''
114 else:
115 port = ':%s' % port
116 return '%s://%s%s/%s' % (proto, host, port, path)
117
118
120 if startup:
121 print 'please visit:'
122 print '\t', url
123 print 'starting browser...'
124 try:
125 import webbrowser
126 webbrowser.open(url)
127 except:
128 print 'warning: unable to detect your browser'
129
130
132 """ Draw the splash screen """
133
134 root.withdraw()
135
136 dx = root.winfo_screenwidth()
137 dy = root.winfo_screenheight()
138
139 dialog = Tkinter.Toplevel(root, bg='white')
140 dialog.geometry('%ix%i+%i+%i' % (500, 300, dx / 2 - 200, dy / 2 - 150))
141
142 dialog.overrideredirect(1)
143 dialog.focus_force()
144
145 canvas = Tkinter.Canvas(dialog,
146 background='white',
147 width=500,
148 height=300)
149 canvas.pack()
150 root.update()
151
152 logo = 'splashlogo.gif'
153 if os.path.exists(logo):
154 img = Tkinter.PhotoImage(file=logo)
155 pnl = Tkinter.Label(canvas, image=img, background='white', bd=0)
156 pnl.pack(side='top', fill='both', expand='yes')
157
158 pnl.image = img
159
160 def add_label(text='Change Me', font_size=12, foreground='#195866', height=1):
161 return Tkinter.Label(
162 master=canvas,
163 width=250,
164 height=height,
165 text=text,
166 font=('Helvetica', font_size),
167 anchor=Tkinter.CENTER,
168 foreground=foreground,
169 background='white'
170 )
171
172 add_label('Welcome to...').pack(side='top')
173 add_label(ProgramName, 18, '#FF5C1F', 2).pack()
174 add_label(ProgramAuthor).pack()
175 add_label(ProgramVersion).pack()
176
177 root.update()
178 time.sleep(5)
179 dialog.destroy()
180 return
181
182
184 """ Main window dialog """
185
187 """ web2pyDialog constructor """
188
189 root.title('web2py server')
190 self.root = Tkinter.Toplevel(root)
191 self.options = options
192 self.scheduler_processes = {}
193 self.menu = Tkinter.Menu(self.root)
194 servermenu = Tkinter.Menu(self.menu, tearoff=0)
195 httplog = os.path.join(self.options.folder, 'httpserver.log')
196
197
198 item = lambda: start_browser(httplog)
199 servermenu.add_command(label='View httpserver.log',
200 command=item)
201
202 servermenu.add_command(label='Quit (pid:%i)' % os.getpid(),
203 command=self.quit)
204
205 self.menu.add_cascade(label='Server', menu=servermenu)
206
207 self.pagesmenu = Tkinter.Menu(self.menu, tearoff=0)
208 self.menu.add_cascade(label='Pages', menu=self.pagesmenu)
209
210
211 self.schedmenu = Tkinter.Menu(self.menu, tearoff=0)
212 self.menu.add_cascade(label='Scheduler', menu=self.schedmenu)
213
214 self.update_schedulers(start=True)
215
216 helpmenu = Tkinter.Menu(self.menu, tearoff=0)
217
218
219 item = lambda: start_browser('http://www.web2py.com/')
220 helpmenu.add_command(label='Home Page',
221 command=item)
222
223
224 item = lambda: tkMessageBox.showinfo('About web2py', ProgramInfo)
225 helpmenu.add_command(label='About',
226 command=item)
227
228 self.menu.add_cascade(label='Info', menu=helpmenu)
229
230 self.root.config(menu=self.menu)
231
232 if options.taskbar:
233 self.root.protocol('WM_DELETE_WINDOW',
234 lambda: self.quit(True))
235 else:
236 self.root.protocol('WM_DELETE_WINDOW', self.quit)
237
238 sticky = Tkinter.NW
239
240
241 Tkinter.Label(self.root,
242 text='Server IP:',
243 justify=Tkinter.LEFT).grid(row=0,
244 column=0,
245 sticky=sticky)
246 self.ips = {}
247 self.selected_ip = Tkinter.StringVar()
248 row = 0
249 ips = [('127.0.0.1', 'Local (IPv4)')] + \
250 ([('::1', 'Local (IPv6)')] if socket.has_ipv6 else []) + \
251 [(ip, 'Public') for ip in options.ips] + \
252 [('0.0.0.0', 'Public')]
253 for ip, legend in ips:
254 self.ips[ip] = Tkinter.Radiobutton(
255 self.root, text='%s (%s)' % (legend, ip),
256 variable=self.selected_ip, value=ip)
257 self.ips[ip].grid(row=row, column=1, sticky=sticky)
258 if row == 0:
259 self.ips[ip].select()
260 row += 1
261 shift = row
262
263 Tkinter.Label(self.root,
264 text='Server Port:',
265 justify=Tkinter.LEFT).grid(row=shift,
266 column=0,
267 sticky=sticky)
268
269 self.port_number = Tkinter.Entry(self.root)
270 self.port_number.insert(Tkinter.END, self.options.port)
271 self.port_number.grid(row=shift, column=1, sticky=sticky)
272
273
274 Tkinter.Label(self.root,
275 text='Choose Password:',
276 justify=Tkinter.LEFT).grid(row=shift + 1,
277 column=0,
278 sticky=sticky)
279
280 self.password = Tkinter.Entry(self.root, show='*')
281 self.password.bind('<Return>', lambda e: self.start())
282 self.password.focus_force()
283 self.password.grid(row=shift + 1, column=1, sticky=sticky)
284
285
286 self.canvas = Tkinter.Canvas(self.root,
287 width=300,
288 height=100,
289 bg='black')
290 self.canvas.grid(row=shift + 2, column=0, columnspan=2)
291 self.canvas.after(1000, self.update_canvas)
292
293
294 frame = Tkinter.Frame(self.root)
295 frame.grid(row=shift + 3, column=0, columnspan=2)
296
297
298 self.button_start = Tkinter.Button(frame,
299 text='start server',
300 command=self.start)
301
302 self.button_start.grid(row=0, column=0)
303
304
305 self.button_stop = Tkinter.Button(frame,
306 text='stop server',
307 command=self.stop)
308
309 self.button_stop.grid(row=0, column=1)
310 self.button_stop.configure(state='disabled')
311
312 if options.taskbar:
313 self.tb = contrib.taskbar_widget.TaskBarIcon()
314 self.checkTaskBar()
315
316 if options.password != '<ask>':
317 self.password.insert(0, options.password)
318 self.start()
319 self.root.withdraw()
320 else:
321 self.tb = None
322
324 x = 0
325 apps = []
326 available_apps = [arq for arq in os.listdir('applications/')]
327 available_apps = [arq for arq in available_apps
328 if os.path.exists('applications/%s/models/scheduler.py' % arq)]
329 if start:
330
331 if self.options.scheduler and self.options.with_scheduler:
332 apps = [app.strip() for app in self.options.scheduler.split(',')
333 if app in available_apps]
334 for app in apps:
335 self.try_start_scheduler(app)
336
337
338 self.schedmenu.delete(0, len(available_apps))
339 for arq in available_apps:
340 if arq not in self.scheduler_processes:
341 item = lambda u = arq: self.try_start_scheduler(u)
342 self.schedmenu.add_command(label="start %s" % arq,
343 command=item)
344 if arq in self.scheduler_processes:
345 item = lambda u = arq: self.try_stop_scheduler(u)
346 self.schedmenu.add_command(label="stop %s" % arq,
347 command=item)
348
350 try:
351 from multiprocessing import Process
352 except:
353 sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n')
354 return
355 code = "from gluon import current;current._scheduler.loop()"
356 print 'starting scheduler from widget for "%s"...' % app
357 args = (app, True, True, None, False, code)
358 logging.getLogger().setLevel(self.options.debuglevel)
359 p = Process(target=run, args=args)
360 self.scheduler_processes[app] = p
361 self.update_schedulers()
362 print "Currently running %s scheduler processes" % (
363 len(self.scheduler_processes))
364 p.start()
365 print "Processes started"
366
368 if app in self.scheduler_processes:
369 p = self.scheduler_processes[app]
370 del self.scheduler_processes[app]
371 p.terminate()
372 p.join()
373 self.update_schedulers()
374
376 if app not in self.scheduler_processes:
377 t = threading.Thread(target=self.start_schedulers, args=(app,))
378 t.start()
379
381 """ Check taskbar status """
382
383 if self.tb.status:
384 if self.tb.status[0] == self.tb.EnumStatus.QUIT:
385 self.quit()
386 elif self.tb.status[0] == self.tb.EnumStatus.TOGGLE:
387 if self.root.state() == 'withdrawn':
388 self.root.deiconify()
389 else:
390 self.root.withdraw()
391 elif self.tb.status[0] == self.tb.EnumStatus.STOP:
392 self.stop()
393 elif self.tb.status[0] == self.tb.EnumStatus.START:
394 self.start()
395 elif self.tb.status[0] == self.tb.EnumStatus.RESTART:
396 self.stop()
397 self.start()
398 del self.tb.status[0]
399
400 self.root.after(1000, self.checkTaskBar)
401
403 """ Update app text """
404
405 try:
406 self.text.configure(state='normal')
407 self.text.insert('end', text)
408 self.text.configure(state='disabled')
409 except:
410 pass
411
412 - def connect_pages(self):
413 """ Connect pages """
414
415 available_apps = [arq for arq in os.listdir('applications/')
416 if os.path.exists('applications/%s/__init__.py' % arq)]
417 self.pagesmenu.delete(0, len(available_apps))
418 for arq in available_apps:
419 url = self.url + arq
420 self.pagesmenu.add_command(label=url,
421 command=lambda u=url: start_browser(u))
422
423 - def quit(self, justHide=False):
424 """ Finish the program execution """
425 if justHide:
426 self.root.withdraw()
427 else:
428 try:
429 scheds = self.scheduler_processes.keys()
430 for t in scheds:
431 self.try_stop_scheduler(t)
432 except:
433 pass
434 try:
435 self.server.stop()
436 except:
437 pass
438 try:
439 self.tb.Destroy()
440 except:
441 pass
442
443 self.root.destroy()
444 sys.exit(0)
445
446 - def error(self, message):
447 """ Show error message """
448
449 tkMessageBox.showerror('web2py start server', message)
450
452 """ Start web2py server """
453
454 password = self.password.get()
455
456 if not password:
457 self.error('no password, no web admin interface')
458
459 ip = self.selected_ip.get()
460
461 if not is_valid_ip_address(ip):
462 return self.error('invalid host ip address')
463
464 try:
465 port = int(self.port_number.get())
466 except:
467 return self.error('invalid port number')
468
469
470 if (len(self.options.ssl_certificate) > 0) or (len(self.options.ssl_private_key) > 0):
471 proto = 'https'
472 else:
473 proto = 'http'
474
475 self.url = get_url(ip, proto=proto, port=port)
476 self.connect_pages()
477 self.button_start.configure(state='disabled')
478
479 try:
480 options = self.options
481 req_queue_size = options.request_queue_size
482 self.server = main.HttpServer(
483 ip,
484 port,
485 password,
486 pid_filename=options.pid_filename,
487 log_filename=options.log_filename,
488 profiler_filename=options.profiler_filename,
489 ssl_certificate=options.ssl_certificate,
490 ssl_private_key=options.ssl_private_key,
491 min_threads=options.minthreads,
492 max_threads=options.maxthreads,
493 server_name=options.server_name,
494 request_queue_size=req_queue_size,
495 timeout=options.timeout,
496 shutdown_timeout=options.shutdown_timeout,
497 path=options.folder,
498 interfaces=options.interfaces)
499
500 thread.start_new_thread(self.server.start, ())
501 except Exception, e:
502 self.button_start.configure(state='normal')
503 return self.error(str(e))
504
505 if not self.server_ready():
506 self.button_start.configure(state='normal')
507 return
508
509 self.button_stop.configure(state='normal')
510
511 if not options.taskbar:
512 thread.start_new_thread(start_browser,
513 (get_url(ip, proto=proto, port=port),),
514 dict(startup=True))
515
516 self.password.configure(state='readonly')
517 [ip.configure(state='disabled') for ip in self.ips.values()]
518 self.port_number.configure(state='readonly')
519
520 if self.tb:
521 self.tb.SetServerRunning()
522
524 for listener in self.server.server.listeners:
525 if listener.ready:
526 return True
527
528 return False
529
531 """ Stop web2py server """
532
533 self.button_start.configure(state='normal')
534 self.button_stop.configure(state='disabled')
535 self.password.configure(state='normal')
536 [ip.configure(state='normal') for ip in self.ips.values()]
537 self.port_number.configure(state='normal')
538 self.server.stop()
539
540 if self.tb:
541 self.tb.SetServerStopped()
542
544 """ Update canvas """
545
546 try:
547 t1 = os.path.getsize('httpserver.log')
548 except:
549 self.canvas.after(1000, self.update_canvas)
550 return
551
552 try:
553 fp = open('httpserver.log', 'r')
554 fp.seek(self.t0)
555 data = fp.read(t1 - self.t0)
556 fp.close()
557 value = self.p0[1:] + [10 + 90.0 / math.sqrt(1 + data.count('\n'))]
558 self.p0 = value
559
560 for i in xrange(len(self.p0) - 1):
561 c = self.canvas.coords(self.q0[i])
562 self.canvas.coords(self.q0[i],
563 (c[0],
564 self.p0[i],
565 c[2],
566 self.p0[i + 1]))
567 self.t0 = t1
568 except BaseException:
569 self.t0 = time.time()
570 self.t0 = t1
571 self.p0 = [100] * 300
572 self.q0 = [self.canvas.create_line(i, 100, i + 1, 100,
573 fill='green') for i in xrange(len(self.p0) - 1)]
574
575 self.canvas.after(1000, self.update_canvas)
576
577
579 """ Defines the behavior of the console web2py execution """
580 import optparse
581 import textwrap
582
583 usage = "python web2py.py"
584
585 description = """\
586 web2py Web Framework startup script.
587 ATTENTION: unless a password is specified (-a 'passwd') web2py will
588 attempt to run a GUI. In this case command line options are ignored."""
589
590 description = textwrap.dedent(description)
591
592 parser = optparse.OptionParser(
593 usage, None, optparse.Option, ProgramVersion)
594
595 parser.description = description
596
597 msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); '
598 'Note: This value is ignored when using the \'interfaces\' option.')
599 parser.add_option('-i',
600 '--ip',
601 default='127.0.0.1',
602 dest='ip',
603 help=msg)
604
605 parser.add_option('-p',
606 '--port',
607 default='8000',
608 dest='port',
609 type='int',
610 help='port of server (8000)')
611
612 msg = ('password to be used for administration '
613 '(use -a "<recycle>" to reuse the last password))')
614 parser.add_option('-a',
615 '--password',
616 default='<ask>',
617 dest='password',
618 help=msg)
619
620 parser.add_option('-c',
621 '--ssl_certificate',
622 default='',
623 dest='ssl_certificate',
624 help='file that contains ssl certificate')
625
626 parser.add_option('-k',
627 '--ssl_private_key',
628 default='',
629 dest='ssl_private_key',
630 help='file that contains ssl private key')
631
632 msg = ('Use this file containing the CA certificate to validate X509 '
633 'certificates from clients')
634 parser.add_option('--ca-cert',
635 action='store',
636 dest='ssl_ca_certificate',
637 default=None,
638 help=msg)
639
640 parser.add_option('-d',
641 '--pid_filename',
642 default='httpserver.pid',
643 dest='pid_filename',
644 help='file to store the pid of the server')
645
646 parser.add_option('-l',
647 '--log_filename',
648 default='httpserver.log',
649 dest='log_filename',
650 help='file to log connections')
651
652 parser.add_option('-n',
653 '--numthreads',
654 default=None,
655 type='int',
656 dest='numthreads',
657 help='number of threads (deprecated)')
658
659 parser.add_option('--minthreads',
660 default=None,
661 type='int',
662 dest='minthreads',
663 help='minimum number of server threads')
664
665 parser.add_option('--maxthreads',
666 default=None,
667 type='int',
668 dest='maxthreads',
669 help='maximum number of server threads')
670
671 parser.add_option('-s',
672 '--server_name',
673 default=socket.gethostname(),
674 dest='server_name',
675 help='server name for the web server')
676
677 msg = 'max number of queued requests when server unavailable'
678 parser.add_option('-q',
679 '--request_queue_size',
680 default='5',
681 type='int',
682 dest='request_queue_size',
683 help=msg)
684
685 parser.add_option('-o',
686 '--timeout',
687 default='10',
688 type='int',
689 dest='timeout',
690 help='timeout for individual request (10 seconds)')
691
692 parser.add_option('-z',
693 '--shutdown_timeout',
694 default='5',
695 type='int',
696 dest='shutdown_timeout',
697 help='timeout on shutdown of server (5 seconds)')
698
699 parser.add_option('--socket-timeout',
700 default=5,
701 type='int',
702 dest='socket_timeout',
703 help='timeout for socket (5 second)')
704
705 parser.add_option('-f',
706 '--folder',
707 default=os.getcwd(),
708 dest='folder',
709 help='folder from which to run web2py')
710
711 parser.add_option('-v',
712 '--verbose',
713 action='store_true',
714 dest='verbose',
715 default=False,
716 help='increase --test verbosity')
717
718 parser.add_option('-Q',
719 '--quiet',
720 action='store_true',
721 dest='quiet',
722 default=False,
723 help='disable all output')
724
725 msg = ('set debug output level (0-100, 0 means all, 100 means none; '
726 'default is 30)')
727 parser.add_option('-D',
728 '--debug',
729 dest='debuglevel',
730 default=30,
731 type='int',
732 help=msg)
733
734 msg = ('run web2py in interactive shell or IPython (if installed) with '
735 'specified appname (if app does not exist it will be created). '
736 'APPNAME like a/c/f (c,f optional)')
737 parser.add_option('-S',
738 '--shell',
739 dest='shell',
740 metavar='APPNAME',
741 help=msg)
742
743 msg = ('run web2py in interactive shell or bpython (if installed) with '
744 'specified appname (if app does not exist it will be created).\n'
745 'Use combined with --shell')
746 parser.add_option('-B',
747 '--bpython',
748 action='store_true',
749 default=False,
750 dest='bpython',
751 help=msg)
752
753 msg = 'only use plain python shell; should be used with --shell option'
754 parser.add_option('-P',
755 '--plain',
756 action='store_true',
757 default=False,
758 dest='plain',
759 help=msg)
760
761 msg = ('auto import model files; default is False; should be used '
762 'with --shell option')
763 parser.add_option('-M',
764 '--import_models',
765 action='store_true',
766 default=False,
767 dest='import_models',
768 help=msg)
769
770 msg = ('run PYTHON_FILE in web2py environment; '
771 'should be used with --shell option')
772 parser.add_option('-R',
773 '--run',
774 dest='run',
775 metavar='PYTHON_FILE',
776 default='',
777 help=msg)
778
779 msg = ('run scheduled tasks for the specified apps: expects a list of '
780 'app names as -K app1,app2,app3 '
781 'or a list of app:groups as -K app1:group1:group2,app2:group1 '
782 'to override specific group_names. (only strings, no spaces '
783 'allowed. Requires a scheduler defined in the models')
784 parser.add_option('-K',
785 '--scheduler',
786 dest='scheduler',
787 default=None,
788 help=msg)
789
790 msg = 'run schedulers alongside webserver'
791 parser.add_option('-X',
792 '--with-scheduler',
793 action='store_true',
794 default=False,
795 dest='with_scheduler',
796 help=msg)
797
798 msg = ('run doctests in web2py environment; '
799 'TEST_PATH like a/c/f (c,f optional)')
800 parser.add_option('-T',
801 '--test',
802 dest='test',
803 metavar='TEST_PATH',
804 default=None,
805 help=msg)
806
807 parser.add_option('-W',
808 '--winservice',
809 dest='winservice',
810 default='',
811 help='-W install|start|stop as Windows service')
812
813 msg = 'trigger a cron run manually; usually invoked from a system crontab'
814 parser.add_option('-C',
815 '--cron',
816 action='store_true',
817 dest='extcron',
818 default=False,
819 help=msg)
820
821 msg = 'triggers the use of softcron'
822 parser.add_option('--softcron',
823 action='store_true',
824 dest='softcron',
825 default=False,
826 help=msg)
827
828 parser.add_option('-Y',
829 '--run-cron',
830 action='store_true',
831 dest='runcron',
832 default=False,
833 help='start the background cron process')
834
835 parser.add_option('-J',
836 '--cronjob',
837 action='store_true',
838 dest='cronjob',
839 default=False,
840 help='identify cron-initiated command')
841
842 parser.add_option('-L',
843 '--config',
844 dest='config',
845 default='',
846 help='config file')
847
848 parser.add_option('-F',
849 '--profiler',
850 dest='profiler_filename',
851 default=None,
852 help='profiler filename')
853
854 parser.add_option('-t',
855 '--taskbar',
856 action='store_true',
857 dest='taskbar',
858 default=False,
859 help='use web2py gui and run in taskbar (system tray)')
860
861 parser.add_option('',
862 '--nogui',
863 action='store_true',
864 default=False,
865 dest='nogui',
866 help='text-only, no GUI')
867
868 msg = ('should be followed by a list of arguments to be passed to script, '
869 'to be used with -S, -A must be the last option')
870 parser.add_option('-A',
871 '--args',
872 action='store',
873 dest='args',
874 default=None,
875 help=msg)
876
877 parser.add_option('--no-banner',
878 action='store_true',
879 default=False,
880 dest='nobanner',
881 help='Do not print header banner')
882
883 msg = ('listen on multiple addresses: '
884 '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." '
885 '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in '
886 'square [] brackets)')
887 parser.add_option('--interfaces',
888 action='store',
889 dest='interfaces',
890 default=None,
891 help=msg)
892
893 msg = 'runs web2py tests'
894 parser.add_option('--run_system_tests',
895 action='store_true',
896 dest='run_system_tests',
897 default=False,
898 help=msg)
899
900 if '-A' in sys.argv:
901 k = sys.argv.index('-A')
902 elif '--args' in sys.argv:
903 k = sys.argv.index('--args')
904 else:
905 k = len(sys.argv)
906 sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:]
907 (options, args) = parser.parse_args()
908 options.args = [options.run] + other_args
909 global_settings.cmd_options = options
910 global_settings.cmd_args = args
911
912 try:
913 options.ips = list(set([
914 ip[4][0] for ip in socket.getaddrinfo(socket.getfqdn(), 0)
915 if not is_loopback_ip_address(ip[4][0])]))
916 except socket.gaierror:
917 options.ips = []
918
919 if options.run_system_tests:
920 run_system_tests()
921
922 if options.quiet:
923 capture = cStringIO.StringIO()
924 sys.stdout = capture
925 logger.setLevel(logging.CRITICAL + 1)
926 else:
927 logger.setLevel(options.debuglevel)
928
929 if options.config[-3:] == '.py':
930 options.config = options.config[:-3]
931
932 if options.cronjob:
933 global_settings.cronjob = True
934 options.plain = True
935 options.nobanner = True
936 options.nogui = True
937
938 options.folder = os.path.abspath(options.folder)
939
940
941
942
943 if isinstance(options.interfaces, str):
944 interfaces = options.interfaces.split(';')
945 options.interfaces = []
946 for interface in interfaces:
947 if interface.startswith('['):
948 ip, if_remainder = interface.split(']', 1)
949 ip = ip[1:]
950 if_remainder = if_remainder[1:].split(':')
951 if_remainder[0] = int(if_remainder[0])
952 options.interfaces.append(tuple([ip] + if_remainder))
953 else:
954 interface = interface.split(':')
955 interface[1] = int(interface[1])
956 options.interfaces.append(tuple(interface))
957
958
959
960 scheduler = []
961 options.scheduler_groups = None
962 if isinstance(options.scheduler, str):
963 if ':' in options.scheduler:
964 for opt in options.scheduler.split(','):
965 scheduler.append(opt.split(':'))
966 options.scheduler = ','.join([app[0] for app in scheduler])
967 options.scheduler_groups = scheduler
968
969 if options.numthreads is not None and options.minthreads is None:
970 options.minthreads = options.numthreads
971
972 create_welcome_w2p()
973
974 if not options.cronjob:
975
976 if not os.path.exists('applications/__init__.py'):
977 write_file('applications/__init__.py', '')
978
979 return (options, args)
980
981
983 if os.path.isdir(os.path.join(options.folder, 'applications', appname)):
984 return True
985
986
988 if len(app) == 1 or app[1] is None:
989 code = "from gluon import current;current._scheduler.loop()"
990 else:
991 code = "from gluon import current;current._scheduler.group_names = ['%s'];"
992 code += "current._scheduler.loop()"
993 code = code % ("','".join(app[1:]))
994 app_ = app[0]
995 if not check_existent_app(options, app_):
996 print "Application '%s' doesn't exist, skipping" % (app_)
997 return None, None
998 return app_, code
999
1000
1002 try:
1003 from multiprocessing import Process
1004 except:
1005 sys.stderr.write('Sorry, -K only supported for python 2.6-2.7\n')
1006 return
1007 processes = []
1008 apps = [(app.strip(), None) for app in options.scheduler.split(',')]
1009 if options.scheduler_groups:
1010 apps = options.scheduler_groups
1011 code = "from gluon import current;current._scheduler.loop()"
1012 logging.getLogger().setLevel(options.debuglevel)
1013 if len(apps) == 1 and not options.with_scheduler:
1014 app_, code = get_code_for_scheduler(apps[0], options)
1015 if not app_:
1016 return
1017 print 'starting single-scheduler for "%s"...' % app_
1018 run(app_, True, True, None, False, code)
1019 return
1020 for app in apps:
1021 app_, code = get_code_for_scheduler(app, options)
1022 if not app_:
1023 continue
1024 print 'starting scheduler for "%s"...' % app_
1025 args = (app_, True, True, None, False, code)
1026 p = Process(target=run, args=args)
1027 processes.append(p)
1028 print "Currently running %s scheduler processes" % (len(processes))
1029 p.start()
1030
1031 time.sleep(0.7)
1032 print "Processes started"
1033 for p in processes:
1034 try:
1035 p.join()
1036 except (KeyboardInterrupt, SystemExit):
1037 print "Processes stopped"
1038 except:
1039 p.terminate()
1040 p.join()
1041
1042
1044 """ Start server """
1045
1046
1047
1048 (options, args) = console()
1049
1050 if not options.nobanner:
1051 print ProgramName
1052 print ProgramAuthor
1053 print ProgramVersion
1054
1055 from dal import DRIVERS
1056 if not options.nobanner:
1057 print 'Database drivers available: %s' % ', '.join(DRIVERS)
1058
1059
1060 if options.config:
1061 try:
1062 options2 = __import__(options.config, {}, {}, '')
1063 except Exception:
1064 try:
1065
1066 options2 = __import__(options.config)
1067 except Exception:
1068 print 'Cannot import config file [%s]' % options.config
1069 sys.exit(1)
1070 for key in dir(options2):
1071 if hasattr(options, key):
1072 setattr(options, key, getattr(options2, key))
1073
1074 if False and not os.path.exists('logging.conf') and \
1075 os.path.exists('logging.example.conf'):
1076 import shutil
1077 sys.stdout.write("Copying logging.conf.example to logging.conf ... ")
1078 shutil.copyfile('logging.example.conf', 'logging.conf')
1079 sys.stdout.write("OK\n")
1080
1081
1082 if hasattr(options, 'test') and options.test:
1083 test(options.test, verbose=options.verbose)
1084 return
1085
1086
1087 if options.shell:
1088 if not options.args is None:
1089 sys.argv[:] = options.args
1090 run(options.shell, plain=options.plain, bpython=options.bpython,
1091 import_models=options.import_models, startfile=options.run)
1092 return
1093
1094
1095
1096 if options.extcron:
1097 logger.debug('Starting extcron...')
1098 global_settings.web2py_crontype = 'external'
1099 if options.scheduler:
1100 apps = [app.strip() for app in options.scheduler.split(
1101 ',') if check_existent_app(options, app.strip())]
1102 else:
1103 apps = None
1104 extcron = newcron.extcron(options.folder, apps=apps)
1105 extcron.start()
1106 extcron.join()
1107 return
1108
1109
1110 if options.scheduler and not options.with_scheduler:
1111 try:
1112 start_schedulers(options)
1113 except KeyboardInterrupt:
1114 pass
1115 return
1116
1117
1118 if options.winservice:
1119 if os.name == 'nt':
1120 if have_winservice:
1121 register_service_handler(
1122 argv=['', options.winservice],
1123 opt_file=options.config,
1124 cls=Web2pyService)
1125 else:
1126 print 'Error: Missing python module winservice'
1127 sys.exit(1)
1128 else:
1129 print 'Error: Windows services not supported on this platform'
1130 sys.exit(1)
1131 return
1132
1133
1134
1135
1136 if cron and options.runcron and options.softcron:
1137 print 'Using softcron (but this is not very efficient)'
1138 global_settings.web2py_crontype = 'soft'
1139 elif cron and options.runcron:
1140 logger.debug('Starting hardcron...')
1141 global_settings.web2py_crontype = 'hard'
1142 newcron.hardcron(options.folder).start()
1143
1144
1145
1146
1147 try:
1148 options.taskbar
1149 except:
1150 options.taskbar = False
1151
1152 if options.taskbar and os.name != 'nt':
1153 print 'Error: taskbar not supported on this platform'
1154 sys.exit(1)
1155
1156 root = None
1157
1158 if not options.nogui:
1159 try:
1160 import Tkinter
1161 havetk = True
1162 except ImportError:
1163 logger.warn(
1164 'GUI not available because Tk library is not installed')
1165 havetk = False
1166
1167 if options.password == '<ask>' and havetk or options.taskbar and havetk:
1168 try:
1169 root = Tkinter.Tk()
1170 except:
1171 pass
1172
1173 if root:
1174 root.focus_force()
1175
1176
1177 if os.path.exists("/usr/bin/osascript"):
1178 applescript = """
1179 tell application "System Events"
1180 set proc to first process whose unix id is %d
1181 set frontmost of proc to true
1182 end tell
1183 """ % (os.getpid())
1184 os.system("/usr/bin/osascript -e '%s'" % applescript)
1185
1186 if not options.quiet:
1187 presentation(root)
1188 master = web2pyDialog(root, options)
1189 signal.signal(signal.SIGTERM, lambda a, b: master.quit())
1190
1191 try:
1192 root.mainloop()
1193 except:
1194 master.quit()
1195
1196 sys.exit()
1197
1198
1199
1200 if not root and options.password == '<ask>':
1201 options.password = getpass.getpass('choose a password:')
1202
1203 if not options.password and not options.nobanner:
1204 print 'no password, no admin interface'
1205
1206
1207 if not root and options.scheduler and options.with_scheduler:
1208 t = threading.Thread(target=start_schedulers, args=(options,))
1209 t.start()
1210
1211
1212
1213
1214
1215 if not options.interfaces:
1216 (ip, port) = (options.ip, int(options.port))
1217 else:
1218 first_if = options.interfaces[0]
1219 (ip, port) = first_if[0], first_if[1]
1220
1221
1222 if (len(options.ssl_certificate) > 0) or (len(options.ssl_private_key) > 0):
1223 proto = 'https'
1224 else:
1225 proto = 'http'
1226
1227 url = get_url(ip, proto=proto, port=port)
1228
1229 if not options.nobanner:
1230 print 'please visit:'
1231 print '\t', url
1232 print 'use "kill -SIGTERM %i" to shutdown the web2py server' % os.getpid()
1233
1234 server = main.HttpServer(ip=ip,
1235 port=port,
1236 password=options.password,
1237 pid_filename=options.pid_filename,
1238 log_filename=options.log_filename,
1239 profiler_filename=options.profiler_filename,
1240 ssl_certificate=options.ssl_certificate,
1241 ssl_private_key=options.ssl_private_key,
1242 ssl_ca_certificate=options.ssl_ca_certificate,
1243 min_threads=options.minthreads,
1244 max_threads=options.maxthreads,
1245 server_name=options.server_name,
1246 request_queue_size=options.request_queue_size,
1247 timeout=options.timeout,
1248 socket_timeout=options.socket_timeout,
1249 shutdown_timeout=options.shutdown_timeout,
1250 path=options.folder,
1251 interfaces=options.interfaces)
1252
1253 try:
1254 server.start()
1255 except KeyboardInterrupt:
1256 server.stop()
1257 try:
1258 t.join()
1259 except:
1260 pass
1261 logging.shutdown()
1262