From 736311ff9a1c0dec94ee239bb2a7a13846291946 Mon Sep 17 00:00:00 2001 From: niphlod Date: Mon, 21 Oct 2013 12:52:43 -0700 Subject: [PATCH] fix building on windows. New handler for gevented webserver --- extras/build_web2py/setup_exe.conf | 5 +- extras/build_web2py/setup_exe.py | 76 ++++++++++--------- handlers/web2py_on_gevent.py | 113 +++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 34 deletions(-) create mode 100644 handlers/web2py_on_gevent.py diff --git a/extras/build_web2py/setup_exe.conf b/extras/build_web2py/setup_exe.conf index b4282a8d6..9996db06b 100644 --- a/extras/build_web2py/setup_exe.conf +++ b/extras/build_web2py/setup_exe.conf @@ -21,4 +21,7 @@ zip_filename = web2py_win #should the build, deposit & dist directories used by py2exe be removed? #if you created a zip file you likely don't need these directories anymore -remove_build_files = Yes \ No newline at end of file +remove_build_files = Yes + +#should the build include the gevented webserver (needs gevent) +include_gevent = Yes \ No newline at end of file diff --git a/extras/build_web2py/setup_exe.py b/extras/build_web2py/setup_exe.py index 1ca863278..19b2b2fa4 100755 --- a/extras/build_web2py/setup_exe.py +++ b/extras/build_web2py/setup_exe.py @@ -1,8 +1,8 @@ #!/usr/bin/env python # -*- coding: utf-8 -*- - + #Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py - + USAGE = """ Usage: Copy this and setup_exe.conf to web2py root folder @@ -13,7 +13,7 @@ Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/ run python setup_exe.py bbfreeze """ - + from distutils.core import setup from gluon.import_all import base_modules, contributed_modules from gluon.fileutils import readlines_file @@ -24,7 +24,7 @@ import sys import re import zipfile - + if len(sys.argv) != 2 or not os.path.isfile('web2py.py'): print USAGE sys.exit(1) @@ -32,11 +32,11 @@ if not BUILD_MODE in ('py2exe', 'bbfreeze'): print USAGE sys.exit(1) - + def unzip(source_filename, dest_dir): with zipfile.ZipFile(source_filename) as zf: zf.extractall(dest_dir) - + #borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile def recursive_zip(zipf, directory, folder=""): for item in os.listdir(directory): @@ -45,14 +45,14 @@ def recursive_zip(zipf, directory, folder=""): elif os.path.isdir(os.path.join(directory, item)): recursive_zip( zipf, os.path.join(directory, item), folder + os.sep + item) - - + + #read web2py version from VERSION file web2py_version_line = readlines_file('VERSION')[0] #use regular expression to get just the version number v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+') web2py_version = v_re.search(web2py_version_line).group(0) - + #pull in preferences from config file import ConfigParser Config = ConfigParser.ConfigParser() @@ -64,20 +64,16 @@ def recursive_zip(zipf, directory, folder=""): make_zip = Config.getboolean("Setup", "make_zip") zip_filename = Config.get("Setup", "zip_filename") remove_build_files = Config.getboolean("Setup", "remove_build_files") +include_gevent = Config.getboolean("Setup", "include_gevent") # Python base version python_version = sys.version_info[:3] - -if python_version > (2,5): - # Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52 - try: - shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/') - except: - print "You MUST copy Microsoft.VC90.CRT folder into the archive" - + + + if BUILD_MODE == 'py2exe': import py2exe - + setup( console=[{'script':'web2py.py', 'icon_resources': [(0, 'extras/icons/web2py.ico')] @@ -112,35 +108,49 @@ def recursive_zip(zipf, directory, folder=""): zipl.close() shutil.rmtree(library_temp_dir) print "web2py binary successfully built" - + elif BUILD_MODE == 'bbfreeze': modules = base_modules + contributed_modules from bbfreeze import Freezer f = Freezer(distdir="dist", includes=(modules)) - #f.addScript("web2py_gevent.py") f.addScript("web2py.py") #to make executable without GUI we need this trick shutil.copy("web2py.py", "web2py_no_console.py") f.addScript("web2py_no_console.py", gui_only=True) + if include_gevent: + #fetch the gevented webserver script and copy to root + gevented_webserver = os.path.join("handlers", "web2py_on_gevent.py") + shutil.copy(gevented_webserver, "web2py_on_gevent.py") + f.addScript("web2py_on_gevent.py") f.setIcon('extras/icons/web2py.ico') f() # starts the freezing process os.unlink("web2py_no_console.py") + if include_gevent: + os.unlink("web2py_on_gevent.py") #add data_files for req in ['ABOUT', 'LICENSE', 'VERSION']: shutil.copy(req, os.path.join('dist', req)) print "web2py binary successfully built" - + try: os.unlink('storage.sqlite') except: pass - + +#This need to happen after bbfreeze is run because Freezer() deletes distdir before starting! +if python_version > (2,5): + # Python26 compatibility: http://www.py2exe.org/index.cgi/Tutorial#Step52 + try: + shutil.copytree('C:\Bin\Microsoft.VC90.CRT', 'dist/Microsoft.VC90.CRT/') + except: + print "You MUST copy Microsoft.VC90.CRT folder into the archive" + def copy_folders(source, destination): """Copy files & folders from source to destination (within dist/)""" if os.path.exists(os.path.join('dist', destination)): shutil.rmtree(os.path.join('dist', destination)) shutil.copytree(os.path.join(source), os.path.join('dist', destination)) - + #should we remove Windows OS dlls user is unlikely to be able to distribute if remove_msft_dlls: print "Deleted Microsoft files not licensed for open source distribution" @@ -156,7 +166,7 @@ def copy_folders(source, destination): os.unlink(os.path.join('dist', f)) except: print "unable to delete dist/" + f - + #Should we include applications? if copy_apps: copy_folders('applications', 'applications') @@ -167,12 +177,12 @@ def copy_folders(source, destination): copy_folders('applications/welcome', 'applications/welcome') copy_folders('applications/examples', 'applications/examples') print "Only web2py's admin, examples & welcome applications have been added" - + copy_folders('extras', 'extras') copy_folders('examples', 'examples') copy_folders('handlers', 'handlers') - - + + #should we copy project's site-packages into dist/site-packages if copy_site_packages: #copy site-packages @@ -180,7 +190,7 @@ def copy_folders(source, destination): else: #no worries, web2py will create the (empty) folder first run print "Skipping site-packages" - + #should we copy project's scripts into dist/scripts if copy_scripts: #copy scripts @@ -188,7 +198,7 @@ def copy_folders(source, destination): else: #no worries, web2py will create the (empty) folder first run print "Skipping scripts" - + #should we create a zip file of the build? if make_zip: #create a web2py folder & copy dist's files into it @@ -205,7 +215,7 @@ def copy_folders(source, destination): print "Your Windows binary version of web2py can be found in " + \ zip_filename + ".zip" print "You may extract the archive anywhere and then run web2py/web2py.exe" - + #should py2exe build files be removed? if remove_build_files: if BUILD_MODE == 'py2exe': @@ -213,10 +223,10 @@ def copy_folders(source, destination): shutil.rmtree('deposit') shutil.rmtree('dist') print "build files removed" - + #final info if not make_zip and not remove_build_files: print "Your Windows binary & associated files can also be found in /dist" - + print "Finished!" -print "Enjoy web2py " + web2py_version_line +print "Enjoy web2py " + web2py_version_line \ No newline at end of file diff --git a/handlers/web2py_on_gevent.py b/handlers/web2py_on_gevent.py new file mode 100644 index 000000000..701e05e24 --- /dev/null +++ b/handlers/web2py_on_gevent.py @@ -0,0 +1,113 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import os +import optparse + +if '__file__' in globals(): + path = os.path.dirname(os.path.abspath(__file__)) +elif hasattr(sys, 'frozen'): + path = os.path.dirname(os.path.abspath(sys.executable)) +else: + path = os.getcwd() +os.chdir(path) + +sys.path = [path] + [p for p in sys.path if not p == path] + +from gevent import pywsgi +from gevent.pool import Pool +from gevent import monkey +monkey.patch_all() + +def run(options): + import gluon.main + if options.password != '': + gluon.main.save_password(options.password, int(options.port)) + if options.logging: + application = gluon.main.appfactory(wsgiapp=gluon.main.wsgibase, + logfilename='httpserver.log', + profiler_dir=profiler) + else: + application = gluon.main.wsgibase + address = (options.ip, int(options.port)) + workers = options.workers + spawn = workers and Pool(int(options.workers)) or 'default' + ssl_args = dict() + if options.ssl_private_key: + ssl_args['keyfile'] = options.ssl_private_key + if options.ssl_certificate: + ssl_args['certfile'] = options.ssl_certificate + server = pywsgi.WSGIServer( + address, application, + spawn=spawn, log=None, + **ssl_args + ) + server.serve_forever() + +def main(): + usage = 'python web2py_gevent.py -i 127.0.0.1 -p 8000 -a ""' + try: + version = open('VERSION','r') + except IOError: + version = '' + parser = optparse.OptionParser(usage, None, optparse.Option, version) + msg = ('password to be used for administration ' + '(use -a "" to reuse the last password))') + parser.add_option('-a', + '--password', + default='', + 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') + + parser.add_option('-l', + '--logging', + action='store_true', + default=False, + dest='logging', + help='log into httpserver.log') + + parser.add_option('-F', + '--profiler', + dest='profiler_dir', + default=None, + help='profiler dir') + + parser.add_option('-i', + '--ip', + default='127.0.0.1', + dest='ip', + help='ip address') + + parser.add_option('-p', + '--port', + default='8000', + dest='port', + help='port number') + + parser.add_option('-w', + '--workers', + default=None, + dest='workers', + help='number of workers') + + (options, args) = parser.parse_args() + print 'starting on %s:%s...' % ( + options.ip, options.port) + run(options) + +if __name__ == '__main__': + main() +