Skip to content

Commit

Permalink
fix building on windows. New handler for gevented webserver
Browse files Browse the repository at this point in the history
  • Loading branch information
niphlod committed Oct 21, 2013
1 parent 665f728 commit 736311f
Show file tree
Hide file tree
Showing 3 changed files with 160 additions and 34 deletions.
5 changes: 4 additions & 1 deletion extras/build_web2py/setup_exe.conf
Expand Up @@ -21,4 +21,7 @@ zip_filename = web2py_win


#should the build, deposit & dist directories used by py2exe be removed? #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 #if you created a zip file you likely don't need these directories anymore
remove_build_files = Yes remove_build_files = Yes

#should the build include the gevented webserver (needs gevent)
include_gevent = Yes
76 changes: 43 additions & 33 deletions extras/build_web2py/setup_exe.py
@@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-

#Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py #Adapted from http://bazaar.launchpad.net/~flavour/sahana-eden/trunk/view/head:/static/scripts/tools/standalone_exe.py

USAGE = """ USAGE = """
Usage: Usage:
Copy this and setup_exe.conf to web2py root folder Copy this and setup_exe.conf to web2py root folder
Expand All @@ -13,7 +13,7 @@
Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/ Install bbfreeze: https://pypi.python.org/pypi/bbfreeze/
run python setup_exe.py bbfreeze run python setup_exe.py bbfreeze
""" """

from distutils.core import setup from distutils.core import setup
from gluon.import_all import base_modules, contributed_modules from gluon.import_all import base_modules, contributed_modules
from gluon.fileutils import readlines_file from gluon.fileutils import readlines_file
Expand All @@ -24,19 +24,19 @@
import sys import sys
import re import re
import zipfile import zipfile

if len(sys.argv) != 2 or not os.path.isfile('web2py.py'): if len(sys.argv) != 2 or not os.path.isfile('web2py.py'):
print USAGE print USAGE
sys.exit(1) sys.exit(1)
BUILD_MODE = sys.argv[1] BUILD_MODE = sys.argv[1]
if not BUILD_MODE in ('py2exe', 'bbfreeze'): if not BUILD_MODE in ('py2exe', 'bbfreeze'):
print USAGE print USAGE
sys.exit(1) sys.exit(1)

def unzip(source_filename, dest_dir): def unzip(source_filename, dest_dir):
with zipfile.ZipFile(source_filename) as zf: with zipfile.ZipFile(source_filename) as zf:
zf.extractall(dest_dir) zf.extractall(dest_dir)

#borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile #borrowed from http://bytes.com/topic/python/answers/851018-how-zip-directory-python-using-zipfile
def recursive_zip(zipf, directory, folder=""): def recursive_zip(zipf, directory, folder=""):
for item in os.listdir(directory): for item in os.listdir(directory):
Expand All @@ -45,14 +45,14 @@ def recursive_zip(zipf, directory, folder=""):
elif os.path.isdir(os.path.join(directory, item)): elif os.path.isdir(os.path.join(directory, item)):
recursive_zip( recursive_zip(
zipf, os.path.join(directory, item), folder + os.sep + item) zipf, os.path.join(directory, item), folder + os.sep + item)


#read web2py version from VERSION file #read web2py version from VERSION file
web2py_version_line = readlines_file('VERSION')[0] web2py_version_line = readlines_file('VERSION')[0]
#use regular expression to get just the version number #use regular expression to get just the version number
v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+') v_re = re.compile('[0-9]+\.[0-9]+\.[0-9]+')
web2py_version = v_re.search(web2py_version_line).group(0) web2py_version = v_re.search(web2py_version_line).group(0)

#pull in preferences from config file #pull in preferences from config file
import ConfigParser import ConfigParser
Config = ConfigParser.ConfigParser() Config = ConfigParser.ConfigParser()
Expand All @@ -64,20 +64,16 @@ def recursive_zip(zipf, directory, folder=""):
make_zip = Config.getboolean("Setup", "make_zip") make_zip = Config.getboolean("Setup", "make_zip")
zip_filename = Config.get("Setup", "zip_filename") zip_filename = Config.get("Setup", "zip_filename")
remove_build_files = Config.getboolean("Setup", "remove_build_files") remove_build_files = Config.getboolean("Setup", "remove_build_files")
include_gevent = Config.getboolean("Setup", "include_gevent")


# Python base version # Python base version
python_version = sys.version_info[:3] 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': if BUILD_MODE == 'py2exe':
import py2exe import py2exe

setup( setup(
console=[{'script':'web2py.py', console=[{'script':'web2py.py',
'icon_resources': [(0, 'extras/icons/web2py.ico')] 'icon_resources': [(0, 'extras/icons/web2py.ico')]
Expand Down Expand Up @@ -112,35 +108,49 @@ def recursive_zip(zipf, directory, folder=""):
zipl.close() zipl.close()
shutil.rmtree(library_temp_dir) shutil.rmtree(library_temp_dir)
print "web2py binary successfully built" print "web2py binary successfully built"

elif BUILD_MODE == 'bbfreeze': elif BUILD_MODE == 'bbfreeze':
modules = base_modules + contributed_modules modules = base_modules + contributed_modules
from bbfreeze import Freezer from bbfreeze import Freezer
f = Freezer(distdir="dist", includes=(modules)) f = Freezer(distdir="dist", includes=(modules))
#f.addScript("web2py_gevent.py")
f.addScript("web2py.py") f.addScript("web2py.py")
#to make executable without GUI we need this trick #to make executable without GUI we need this trick
shutil.copy("web2py.py", "web2py_no_console.py") shutil.copy("web2py.py", "web2py_no_console.py")
f.addScript("web2py_no_console.py", gui_only=True) 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.setIcon('extras/icons/web2py.ico')
f() # starts the freezing process f() # starts the freezing process
os.unlink("web2py_no_console.py") os.unlink("web2py_no_console.py")
if include_gevent:
os.unlink("web2py_on_gevent.py")
#add data_files #add data_files
for req in ['ABOUT', 'LICENSE', 'VERSION']: for req in ['ABOUT', 'LICENSE', 'VERSION']:
shutil.copy(req, os.path.join('dist', req)) shutil.copy(req, os.path.join('dist', req))
print "web2py binary successfully built" print "web2py binary successfully built"

try: try:
os.unlink('storage.sqlite') os.unlink('storage.sqlite')
except: except:
pass 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): def copy_folders(source, destination):
"""Copy files & folders from source to destination (within dist/)""" """Copy files & folders from source to destination (within dist/)"""
if os.path.exists(os.path.join('dist', destination)): if os.path.exists(os.path.join('dist', destination)):
shutil.rmtree(os.path.join('dist', destination)) shutil.rmtree(os.path.join('dist', destination))
shutil.copytree(os.path.join(source), 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 #should we remove Windows OS dlls user is unlikely to be able to distribute
if remove_msft_dlls: if remove_msft_dlls:
print "Deleted Microsoft files not licensed for open source distribution" print "Deleted Microsoft files not licensed for open source distribution"
Expand All @@ -156,7 +166,7 @@ def copy_folders(source, destination):
os.unlink(os.path.join('dist', f)) os.unlink(os.path.join('dist', f))
except: except:
print "unable to delete dist/" + f print "unable to delete dist/" + f

#Should we include applications? #Should we include applications?
if copy_apps: if copy_apps:
copy_folders('applications', 'applications') copy_folders('applications', 'applications')
Expand All @@ -167,28 +177,28 @@ def copy_folders(source, destination):
copy_folders('applications/welcome', 'applications/welcome') copy_folders('applications/welcome', 'applications/welcome')
copy_folders('applications/examples', 'applications/examples') copy_folders('applications/examples', 'applications/examples')
print "Only web2py's admin, examples & welcome applications have been added" print "Only web2py's admin, examples & welcome applications have been added"

copy_folders('extras', 'extras') copy_folders('extras', 'extras')
copy_folders('examples', 'examples') copy_folders('examples', 'examples')
copy_folders('handlers', 'handlers') copy_folders('handlers', 'handlers')


#should we copy project's site-packages into dist/site-packages #should we copy project's site-packages into dist/site-packages
if copy_site_packages: if copy_site_packages:
#copy site-packages #copy site-packages
copy_folders('site-packages', 'site-packages') copy_folders('site-packages', 'site-packages')
else: else:
#no worries, web2py will create the (empty) folder first run #no worries, web2py will create the (empty) folder first run
print "Skipping site-packages" print "Skipping site-packages"

#should we copy project's scripts into dist/scripts #should we copy project's scripts into dist/scripts
if copy_scripts: if copy_scripts:
#copy scripts #copy scripts
copy_folders('scripts', 'scripts') copy_folders('scripts', 'scripts')
else: else:
#no worries, web2py will create the (empty) folder first run #no worries, web2py will create the (empty) folder first run
print "Skipping scripts" print "Skipping scripts"

#should we create a zip file of the build? #should we create a zip file of the build?
if make_zip: if make_zip:
#create a web2py folder & copy dist's files into it #create a web2py folder & copy dist's files into it
Expand All @@ -205,18 +215,18 @@ def copy_folders(source, destination):
print "Your Windows binary version of web2py can be found in " + \ print "Your Windows binary version of web2py can be found in " + \
zip_filename + ".zip" zip_filename + ".zip"
print "You may extract the archive anywhere and then run web2py/web2py.exe" print "You may extract the archive anywhere and then run web2py/web2py.exe"

#should py2exe build files be removed? #should py2exe build files be removed?
if remove_build_files: if remove_build_files:
if BUILD_MODE == 'py2exe': if BUILD_MODE == 'py2exe':
shutil.rmtree('build') shutil.rmtree('build')
shutil.rmtree('deposit') shutil.rmtree('deposit')
shutil.rmtree('dist') shutil.rmtree('dist')
print "build files removed" print "build files removed"

#final info #final info
if not make_zip and not remove_build_files: if not make_zip and not remove_build_files:
print "Your Windows binary & associated files can also be found in /dist" print "Your Windows binary & associated files can also be found in /dist"

print "Finished!" print "Finished!"
print "Enjoy web2py " + web2py_version_line print "Enjoy web2py " + web2py_version_line
113 changes: 113 additions & 0 deletions 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 != '<recycle>':
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 "<recycle>"'
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 "<recycle>" to reuse the last password))')
parser.add_option('-a',
'--password',
default='<recycle>',
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()

0 comments on commit 736311f

Please sign in to comment.