Web2py 这不适用于 Google App Engine (GAE)
Web2py this not working with Google App Engine (GAE)
我在 google 应用服务器 (gae) 上使用 web2py 时遇到问题。
当我在浏览器中插入 url localhost:8080
时出现的错误是:
rafael@rafael-debian:~/rafael/google_appengine$ python dev_appserver.py ../web2py/
INFO 2015-03-20 03:38:20,075 sdk_update_checker.py:229] Checking for updates to the SDK.
INFO 2015-03-20 03:38:20,520 sdk_update_checker.py:257] The SDK is up to date.
INFO 2015-03-20 03:38:21,109 api_server.py:172] Starting API server at: http://localhost:41836
INFO 2015-03-20 03:38:21,202 dispatcher.py:186] Starting module "default" running at: http://localhost:8080
INFO 2015-03-20 03:38:21,203 admin_server.py:118] Starting admin server at: http://localhost:8000
INFO 2015-03-20 03:38:32,968 module.py:737] default: "GET / HTTP/1.1" 404 -
INFO 2015-03-20 03:38:33,172 module.py:737] default: "GET /favicon.ico HTTP/1.1" 404 -
问题出在app.yaml
。我使用的是基于app.example.yaml
文件,我做了必要的修改,但还是不行
下面是这样的简化结构,目录 web2py
和 google_appengine
以及文件 app.yaml
:
web2py/
├── anyserver.py
├── applications
│ ├── admin
│ ├── core
│ ├── examples
│ └── welcome
├── app.yaml
├── handlers
│ ├── cgihandler.py
│ ├── fcgihandler.py
│ ├── gaehandler.py
│ ├── isapiwsgihandler.py
│ ├── modpythonhandler.py
│ ├── README
│ ├── scgihandler.py
│ ├── web2py_on_gevent.py
│ └── wsgihandler.py
├── web2py.py
└── welcome.w2p
│
google_appengine/
├── dev_appserver.py
app.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: core
version: 1
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: false # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/core/static/favicon.ico
upload: applications/core/static/favicon.ico
- url: /robots.txt
static_files: applications/core/static/robots.txt
upload: applications/welcome/static/robots.txt
- url: .*
script: handlers/gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
版本:
Python2.7.9;
Web2py 2.9.12;
GAE 1.9.18
有人可以帮助我 运行 带有 google 应用程序引擎的 web2py。我已经尝试了所有方法,我已经在论坛和群组中阅读了无数文章,在 youtube 上观看了视频,但没有任何效果。
谢谢关注
为了解决这个问题,我将参数 threadsafe 更改为 threadsafe: true
The script script: handlers/gaehandler.wsgiapp
for script: gaehandler.wsgiapp
。并将文件handlers/gaehandler.py
剪切到上面的目录
app.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: core
version: 1
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: true # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/core/static/favicon.ico
upload: applications/core/static/favicon.ico
- url: /robots.txt
static_files: applications/core/static/robots.txt
upload: applications/welcome/static/robots.txt
- url: .*
script: gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
目录
web2py/
├── anyserver.py
├── applications
│ ├── admin
│ ├── core
│ ├── examples
│ └── welcome
├── app.yaml
├── handlers
│ ├── cgihandler.py
│ ├── fcgihandler.py
│ ├──
│ ├── isapiwsgihandler.py
│ ├── modpythonhandler.py
│ ├── README
│ ├── scgihandler.py
│ ├── web2py_on_gevent.py
│ └── wsgihandler.py
├── web2py.py
├── gaehandler.py
└── welcome.w2p
│
google_appengine/
├── dev_appserver.py
404问题/归结为单行:
script: handlers/gaehandler.wsgiapp
这将目录符号(即“/”!)与 "Python module notation" 混合在一起,结果它将(线程安全为真)在目录 [=13] 中查找名为 gaehandler.wsgiapp
的文件=].
确保 handlers
目录是一个 Python 包 (即它包含一个文件名 __init__.py
,通常是空的)和然后将该行修复为
script: handlers.gaehandler.wsgiapp
看出区别了吗?一个点而不是一个斜线 -- 就是这样!-)
favicon.ico
上的 404 是一个单独的问题,应该与您的层次结构中名为 applications/core/static/favicon.ico
的缺失文件相关联——您确实说过您有 applications/core
但不是下面的文件它(事实上,你显示 nothing 在那个目录下?!)所以我想这是一种可能性(而且我想不出任何其他原因......)。
我是 运行 GAE 中的 Web2py:
您将需要,在 web2py.py 的同一目录中:
App.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: yourapplicaton
version: 22
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: true # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/init/static/favicon.ico
upload: applications/init/static/favicon.ico
- url: /robots.txt
static_files: applications/init/static/robots.txt
upload: applications/init/static/robots.txt
- url: .*
script: gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
# All URLs beginning with /stylesheets are treated as paths to static files in
# the stylesheets/ directory.
- url: /init/static/css/
static_dir: applications/init/static/css
mime_type: "text/css"
- url: /init/static/
static_dir: applications/init/static
mime_type: "text/css"
- url: /init/static/js/
static_dir: applications/init/static/js
mime_type: "text/javascript"
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
appengine_config.py
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Sample Appstats Configuration.
There are four sections:
0) WSGI middleware declaration.
1) Django version declaration.
2) Configuration constants.
3) Configuration functions.
Also a section at the end for the remote_api handler.
"""
import logging
import random
import re
# 0) WSGI middleware declaration.
# Only use this if you're not Django; with Django, it's easier to add
# 'google.appengine.ext.appstats.recording.AppstatsDjangoMiddleware',
# to your Django settings.py file.
def webapp_add_wsgi_middleware(app):
from google.appengine.ext.appstats import recording
app = recording.appstats_wsgi_middleware(app)
return app
# 1) Django version declaration.
# If your application uses Django and requires a specific version of
# Django, uncomment the following block of three lines. Currently
# supported values for the Django version are '0.96' (the default),
# '1.0', and '1.1'.
# # from google.appengine.dist import use_library
# # use_library('django', '1.0')
# # import django
# 2) Configuration constants.
# DEBUG: True of False. When True, verbose messages are logged at the
# DEBUG level. Also, this flag is causes tracebacks to be shown in
# the web UI when an exception occurs. (Tracebacks are always logged
# at the ERROR level as well.)
appstats_DEBUG = False
# DUMP_LEVEL: -1, 0, 1 or 2. Controls how much debug output is
# written to the logs by the internal dump() function during event
# recording. -1 dumps nothing; 0 dumps one line of information; 1
# dumps more informat and 2 dumps the maximum amount of information.
# You would only need to change this if you were debugging the
# recording implementation.
appstats_DUMP_LEVEL = -1
# The following constants control the resolution and range of the
# memcache keys used to record information about individual requests.
# Two requests that are closer than KEY_DISTANCE milliseconds will be
# mapped to the same key (thus losing all information about the
# earlier of the two requests). Up to KEY_MODULUS distinct keys are
# generated; after KEY_DISTANCE * KEY_MODULUS milliseconds the key
# values roll over. Increasing KEY_MODULUS causes a proportional
# increase of the amount of data saved in memcache. Increasing
# KEY_DISTANCE causes a requests during a larger timespan to be
# recorded, at the cost of increasing risk of assigning the same key
# to two adjacent requests.
appstats_KEY_DISTANCE = 100
appstats_KEY_MODULUS = 1000
# The following constants control the namespace and key values used to
# store information in memcache. You can safely leave this alone.
appstats_KEY_NAMESPACE = '__appstats__'
appstats_KEY_PREFIX = '__appstats__'
appstats_KEY_TEMPLATE = ':%06d'
appstats_PART_SUFFIX = ':part'
appstats_FULL_SUFFIX = ':full'
appstats_LOCK_SUFFIX = '<lock>'
# Numerical limits on how much information is saved for each event.
# MAX_STACK limits the number of stack frames saved; MAX_LOCALS limits
# the number of local variables saved per stack frame. MAX_REPR
# limits the length of the string representation of each variable
# saved; MAX_DEPTH limits the nesting depth used when computing the
# string representation of structured variables (e.g. lists of lists).
appstats_MAX_STACK = 10
appstats_MAX_LOCALS = 10
appstats_MAX_REPR = 100
appstats_MAX_DEPTH = 10
# Regular expressions. These are matched against the 'code key' of a
# stack frame, which is a string of the form
# '<filename>:<function>:<lineno>'. If the code key of a stack frame
# matches RE_STACK_BOTTOM, it and all remaining stack frames are
# skipped. If the code key matches RE_STACK_SKIP, that frame is not
# saved but subsequent frames may be saved.
appstats_RE_STACK_BOTTOM = r'dev_appserver\.py'
appstats_RE_STACK_SKIP = r'recording\.py|apiproxy_stub_map\.py'
# Timeout for memcache lock management, in seconds.
appstats_LOCK_TIMEOUT = 1
# Timezone offset. This is used to convert recorded times (which are
# all in UTC) to local time. The default is US/Pacific winter time. 8*3600
#Colombia es:
appstats_TZOFFSET = 5*3600
# URL path (sans host) leading to the stats UI. Should match app.yaml.
# If "builtins: - appstats: on" is used, the path should be /_ah/stats.
appstats_stats_url = '/_ah/stats'
# Fraction of requests to record. Set this to a float between 0.0
# and 1.0 to record that fraction of all requests.
appstats_RECORD_FRACTION = 1.0
# List of dicts mapping env vars to regular expressions. Each dict
# specifies a set of filters to be 'and'ed together. The keys are
# environment variables, the values are *match* regular expressions.
# A request is recorded if it matches all filters of at least one
# dict. If the FILTER_LIST variable is empty, all requests are
# recorded. Missing environment variables are considered to have
# the empty string as value. If a regular expression starts with
# '!', the sense of the match is negated (the value should *not*
# match the expression).
appstats_FILTER_LIST = []
# 3) Configuration functions.
# should_record() can be used to record a random percentage of calls.
# The argument is the CGI or WSGI environment dict. The default
# implementation returns True iff the request matches FILTER_LIST (see
# above) *and* random.random() < RECORD_FRACTION.
def appstats_should_record(env):
if appstats_FILTER_LIST:
logging.debug('FILTER_LIST: %r', appstats_FILTER_LIST)
for filter_dict in appstats_FILTER_LIST:
for key, regex in filter_dict.iteritems():
negated = isinstance(regex, str) and regex.startswith('!')
if negated:
regex = regex[1:]
value = env.get(key, '')
if bool(re.match(regex, value)) == negated:
logging.debug('No match on %r for %s=%r', regex, key, value)
break
else:
logging.debug('Match on %r', filter_dict)
break
else:
logging.debug('Non-empty FILTER_LIST, but no filter matches')
return False
if appstats_RECORD_FRACTION >= 1.0:
return True
return random.random() < appstats_RECORD_FRACTION
# The following functions are called by the UI code only; they don't
# affect the recorded information.
# normalize_path() takes a path and returns an 'path key'. The path
# key is used by the UI to compute statistics for similar URLs. If
# your application has a large or infinite URL space (e.g. each issue
# in an issue tracker might have its own numeric URL), this function
# can be used to produce more meaningful statistics.
def appstats_normalize_path(path):
return path
# extract_key() is a lower-level function with the same purpose as
# normalize_key(). It can be used to lump different request methods
# (e.g. GET and POST) together, or conversely to use other information
# on the request object (mostly the query string) to produce a more
# fine-grained path key. The argument is a StatsProto object; this is
# a class defined in recording.py. Useful methods are:
# # - http_method()
# - http_path()
# - http_query()
# - http_status()
# # Note that the StatsProto argument is loaded only with summary
# information; this means you cannot access the request headers.
def appstats_extract_key(request):
key = appstats_normalize_path(request.http_path())
if request.http_method() != 'GET':
key = '%s %s' % (request.http_method(), key)
return key
# ########################################
# Remote_API Authentication configuration.
# # See google/appengine/ext/remote_api/handler.py for more information.
# In most cases, you will not want to configure this.
# # remoteapi_CUSTOM_ENVIRONMENT_AUTHENTICATION = (
# 'HTTP_X_APPENGINE_INBOUND_APPID', ['a trusted appid here'])
gaehandler.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
"""
##############################################################################
# Configuration parameters for Google App Engine
##############################################################################
LOG_STATS = False # web2py level log statistics
APPSTATS = True # GAE level usage statistics and profiling
DEBUG = False # debug mode
#
# Read more about APPSTATS here
# http://googleappengine.blogspot.com/2010/03/easy-performance-profiling-with.html
# can be accessed from:
# http://localhost:8080/_ah/stats
##############################################################################
# All tricks in this file developed by Robin Bhattacharyya
##############################################################################
import time
import os
import sys
import logging
import cPickle
import pickle
import wsgiref.handlers
import datetime
path = os.path.dirname(os.path.abspath(__file__))
# os.chdir(path) ?
if not os.path.isdir('applications'):
raise RuntimeError('Running from the wrong folder')
sys.path = [path] + [p for p in sys.path if not p == path]
sys.modules['cPickle'] = sys.modules['pickle']
from gluon.settings import global_settings
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
global_settings.web2py_runtime_gae = True
global_settings.db_sessions = True
if os.environ.get('SERVER_SOFTWARE', '').startswith('Devel'):
(global_settings.web2py_runtime, DEBUG) = \
('gae:development', True)
else:
(global_settings.web2py_runtime, DEBUG) = \
('gae:production', False)
import gluon.main
def log_stats(fun):
"""Function that will act as a decorator to make logging"""
def newfun(env, res):
"""Log the execution time of the passed function"""
timer = lambda t: (t.time(), t.clock())
(t0, c0) = timer(time)
executed_function = fun(env, res)
(t1, c1) = timer(time)
log_info = """**** Request: %.2fms/%.2fms (real time/cpu time)"""
log_info = log_info % ((t1 - t0) * 1000, (c1 - c0) * 1000)
logging.info(log_info)
return executed_function
return newfun
logging.basicConfig(level=logging.INFO)
def wsgiapp(env, res):
"""Return the wsgiapp"""
env['PATH_INFO'] = env['PATH_INFO'].decode('latin1').encode('utf8')
#when using the blobstore image uploader GAE dev SDK passes these as unicode
# they should be regular strings as they are parts of URLs
env['wsgi.url_scheme'] = str(env['wsgi.url_scheme'])
env['QUERY_STRING'] = str(env['QUERY_STRING'])
env['SERVER_NAME'] = str(env['SERVER_NAME'])
#this deals with a problem where GAE development server seems to forget
# the path between requests
if global_settings.web2py_runtime == 'gae:development':
gluon.admin.create_missing_folders()
web2py_path = global_settings.applications_parent # backward compatibility
return gluon.main.wsgibase(env, res)
if LOG_STATS or DEBUG:
wsgiapp = log_stats(wsgiapp)
def main():
"""Run the wsgi app"""
run_wsgi_app(wsgiapp)
if __name__ == '__main__':
main()
我想,这个文件必须包含在web2py的主线程中。
我在 google 应用服务器 (gae) 上使用 web2py 时遇到问题。
当我在浏览器中插入 url localhost:8080
时出现的错误是:
rafael@rafael-debian:~/rafael/google_appengine$ python dev_appserver.py ../web2py/
INFO 2015-03-20 03:38:20,075 sdk_update_checker.py:229] Checking for updates to the SDK.
INFO 2015-03-20 03:38:20,520 sdk_update_checker.py:257] The SDK is up to date.
INFO 2015-03-20 03:38:21,109 api_server.py:172] Starting API server at: http://localhost:41836
INFO 2015-03-20 03:38:21,202 dispatcher.py:186] Starting module "default" running at: http://localhost:8080
INFO 2015-03-20 03:38:21,203 admin_server.py:118] Starting admin server at: http://localhost:8000
INFO 2015-03-20 03:38:32,968 module.py:737] default: "GET / HTTP/1.1" 404 -
INFO 2015-03-20 03:38:33,172 module.py:737] default: "GET /favicon.ico HTTP/1.1" 404 -
问题出在app.yaml
。我使用的是基于app.example.yaml
文件,我做了必要的修改,但还是不行
下面是这样的简化结构,目录 web2py
和 google_appengine
以及文件 app.yaml
:
web2py/
├── anyserver.py
├── applications
│ ├── admin
│ ├── core
│ ├── examples
│ └── welcome
├── app.yaml
├── handlers
│ ├── cgihandler.py
│ ├── fcgihandler.py
│ ├── gaehandler.py
│ ├── isapiwsgihandler.py
│ ├── modpythonhandler.py
│ ├── README
│ ├── scgihandler.py
│ ├── web2py_on_gevent.py
│ └── wsgihandler.py
├── web2py.py
└── welcome.w2p
│
google_appengine/
├── dev_appserver.py
app.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: core
version: 1
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: false # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/core/static/favicon.ico
upload: applications/core/static/favicon.ico
- url: /robots.txt
static_files: applications/core/static/robots.txt
upload: applications/welcome/static/robots.txt
- url: .*
script: handlers/gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
版本: Python2.7.9; Web2py 2.9.12; GAE 1.9.18
有人可以帮助我 运行 带有 google 应用程序引擎的 web2py。我已经尝试了所有方法,我已经在论坛和群组中阅读了无数文章,在 youtube 上观看了视频,但没有任何效果。 谢谢关注
为了解决这个问题,我将参数 threadsafe 更改为 threadsafe: true
The script script: handlers/gaehandler.wsgiapp
for script: gaehandler.wsgiapp
。并将文件handlers/gaehandler.py
剪切到上面的目录
app.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: core
version: 1
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: true # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/core/static/favicon.ico
upload: applications/core/static/favicon.ico
- url: /robots.txt
static_files: applications/core/static/robots.txt
upload: applications/welcome/static/robots.txt
- url: .*
script: gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
目录
web2py/
├── anyserver.py
├── applications
│ ├── admin
│ ├── core
│ ├── examples
│ └── welcome
├── app.yaml
├── handlers
│ ├── cgihandler.py
│ ├── fcgihandler.py
│ ├──
│ ├── isapiwsgihandler.py
│ ├── modpythonhandler.py
│ ├── README
│ ├── scgihandler.py
│ ├── web2py_on_gevent.py
│ └── wsgihandler.py
├── web2py.py
├── gaehandler.py
└── welcome.w2p
│
google_appengine/
├── dev_appserver.py
404问题/归结为单行:
script: handlers/gaehandler.wsgiapp
这将目录符号(即“/”!)与 "Python module notation" 混合在一起,结果它将(线程安全为真)在目录 [=13] 中查找名为 gaehandler.wsgiapp
的文件=].
确保 handlers
目录是一个 Python 包 (即它包含一个文件名 __init__.py
,通常是空的)和然后将该行修复为
script: handlers.gaehandler.wsgiapp
看出区别了吗?一个点而不是一个斜线 -- 就是这样!-)
favicon.ico
上的 404 是一个单独的问题,应该与您的层次结构中名为 applications/core/static/favicon.ico
的缺失文件相关联——您确实说过您有 applications/core
但不是下面的文件它(事实上,你显示 nothing 在那个目录下?!)所以我想这是一种可能性(而且我想不出任何其他原因......)。
我是 运行 GAE 中的 Web2py:
您将需要,在 web2py.py 的同一目录中:
App.yaml
# For Google App Engine deployment, copy this file to app.yaml
# and edit as required
# See http://code.google.com/appengine/docs/python/config/appconfig.html
# and http://web2py.com/book/default/chapter/11?search=app.yaml
application: yourapplicaton
version: 22
api_version: 1
# use these lines for Python 2.7
# upload app with: appcfg.py update web2py (where 'web2py' is web2py's root directory)
#
runtime: python27
threadsafe: true # true for WSGI & concurrent requests (Python 2.7 only)
default_expiration: "24h" # for static files
handlers:
# Warning! Static mapping - below - isn't compatible with
# the parametric router's language logic.
# You cannot use them together.
- url: /(.+?)/static/_\d.\d.\d\/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
expiration: "365d"
- url: /(.+?)/static/(.+)
static_files: applications//static/
upload: applications/(.+?)/static/(.+)
secure: optional
- url: /favicon.ico
static_files: applications/init/static/favicon.ico
upload: applications/init/static/favicon.ico
- url: /robots.txt
static_files: applications/init/static/robots.txt
upload: applications/init/static/robots.txt
- url: .*
script: gaehandler.wsgiapp # WSGI (Python 2.7 only)
secure: optional
# All URLs beginning with /stylesheets are treated as paths to static files in
# the stylesheets/ directory.
- url: /init/static/css/
static_dir: applications/init/static/css
mime_type: "text/css"
- url: /init/static/
static_dir: applications/init/static
mime_type: "text/css"
- url: /init/static/js/
static_dir: applications/init/static/js
mime_type: "text/javascript"
admin_console:
pages:
- name: Appstats
url: /_ah/stats
skip_files: |
^(.*/)?(
(app\.yaml)|
(app\.yml)|
(index\.yaml)|
(index\.yml)|
(#.*#)|
(.*~)|
(.*\.py[co])|
(.*/RCS/.*)|
(\..*)|
(applications/examples/.*)|
((examples|welcome)\.(w2p|tar))|
(applications/.*?/(cron|databases|errors|cache|sessions)/.*)|
((logs|scripts)/.*)|
(anyserver\.py)|
(web2py\.py)|
((cgi|fcgi|modpython|wsgi)handler\.py)|
(epydoc\.(conf|css))|
(httpserver\.log)|
(logging\.example\.conf)|
(route[rs]\.example\.py)|
(setup_(app|exe)\.py)|
(splashlogo\.gif)|
(parameters_\d+\.py)|
(options_std.py)|
(gluon/tests/.*)|
(gluon/rocket\.py)|
(contrib/(gateways|markdown|memcache|pymysql)/.*)|
(contrib/(populate|taskbar_widget)\.py)|
(google_appengine/.*)|
(.*\.(bak|orig))|
)$
builtins:
- remote_api: on
- appstats: on
- admin_redirect: on
- deferred: on
appengine_config.py
#!/usr/bin/env python
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Sample Appstats Configuration.
There are four sections:
0) WSGI middleware declaration.
1) Django version declaration.
2) Configuration constants.
3) Configuration functions.
Also a section at the end for the remote_api handler.
"""
import logging
import random
import re
# 0) WSGI middleware declaration.
# Only use this if you're not Django; with Django, it's easier to add
# 'google.appengine.ext.appstats.recording.AppstatsDjangoMiddleware',
# to your Django settings.py file.
def webapp_add_wsgi_middleware(app):
from google.appengine.ext.appstats import recording
app = recording.appstats_wsgi_middleware(app)
return app
# 1) Django version declaration.
# If your application uses Django and requires a specific version of
# Django, uncomment the following block of three lines. Currently
# supported values for the Django version are '0.96' (the default),
# '1.0', and '1.1'.
# # from google.appengine.dist import use_library
# # use_library('django', '1.0')
# # import django
# 2) Configuration constants.
# DEBUG: True of False. When True, verbose messages are logged at the
# DEBUG level. Also, this flag is causes tracebacks to be shown in
# the web UI when an exception occurs. (Tracebacks are always logged
# at the ERROR level as well.)
appstats_DEBUG = False
# DUMP_LEVEL: -1, 0, 1 or 2. Controls how much debug output is
# written to the logs by the internal dump() function during event
# recording. -1 dumps nothing; 0 dumps one line of information; 1
# dumps more informat and 2 dumps the maximum amount of information.
# You would only need to change this if you were debugging the
# recording implementation.
appstats_DUMP_LEVEL = -1
# The following constants control the resolution and range of the
# memcache keys used to record information about individual requests.
# Two requests that are closer than KEY_DISTANCE milliseconds will be
# mapped to the same key (thus losing all information about the
# earlier of the two requests). Up to KEY_MODULUS distinct keys are
# generated; after KEY_DISTANCE * KEY_MODULUS milliseconds the key
# values roll over. Increasing KEY_MODULUS causes a proportional
# increase of the amount of data saved in memcache. Increasing
# KEY_DISTANCE causes a requests during a larger timespan to be
# recorded, at the cost of increasing risk of assigning the same key
# to two adjacent requests.
appstats_KEY_DISTANCE = 100
appstats_KEY_MODULUS = 1000
# The following constants control the namespace and key values used to
# store information in memcache. You can safely leave this alone.
appstats_KEY_NAMESPACE = '__appstats__'
appstats_KEY_PREFIX = '__appstats__'
appstats_KEY_TEMPLATE = ':%06d'
appstats_PART_SUFFIX = ':part'
appstats_FULL_SUFFIX = ':full'
appstats_LOCK_SUFFIX = '<lock>'
# Numerical limits on how much information is saved for each event.
# MAX_STACK limits the number of stack frames saved; MAX_LOCALS limits
# the number of local variables saved per stack frame. MAX_REPR
# limits the length of the string representation of each variable
# saved; MAX_DEPTH limits the nesting depth used when computing the
# string representation of structured variables (e.g. lists of lists).
appstats_MAX_STACK = 10
appstats_MAX_LOCALS = 10
appstats_MAX_REPR = 100
appstats_MAX_DEPTH = 10
# Regular expressions. These are matched against the 'code key' of a
# stack frame, which is a string of the form
# '<filename>:<function>:<lineno>'. If the code key of a stack frame
# matches RE_STACK_BOTTOM, it and all remaining stack frames are
# skipped. If the code key matches RE_STACK_SKIP, that frame is not
# saved but subsequent frames may be saved.
appstats_RE_STACK_BOTTOM = r'dev_appserver\.py'
appstats_RE_STACK_SKIP = r'recording\.py|apiproxy_stub_map\.py'
# Timeout for memcache lock management, in seconds.
appstats_LOCK_TIMEOUT = 1
# Timezone offset. This is used to convert recorded times (which are
# all in UTC) to local time. The default is US/Pacific winter time. 8*3600
#Colombia es:
appstats_TZOFFSET = 5*3600
# URL path (sans host) leading to the stats UI. Should match app.yaml.
# If "builtins: - appstats: on" is used, the path should be /_ah/stats.
appstats_stats_url = '/_ah/stats'
# Fraction of requests to record. Set this to a float between 0.0
# and 1.0 to record that fraction of all requests.
appstats_RECORD_FRACTION = 1.0
# List of dicts mapping env vars to regular expressions. Each dict
# specifies a set of filters to be 'and'ed together. The keys are
# environment variables, the values are *match* regular expressions.
# A request is recorded if it matches all filters of at least one
# dict. If the FILTER_LIST variable is empty, all requests are
# recorded. Missing environment variables are considered to have
# the empty string as value. If a regular expression starts with
# '!', the sense of the match is negated (the value should *not*
# match the expression).
appstats_FILTER_LIST = []
# 3) Configuration functions.
# should_record() can be used to record a random percentage of calls.
# The argument is the CGI or WSGI environment dict. The default
# implementation returns True iff the request matches FILTER_LIST (see
# above) *and* random.random() < RECORD_FRACTION.
def appstats_should_record(env):
if appstats_FILTER_LIST:
logging.debug('FILTER_LIST: %r', appstats_FILTER_LIST)
for filter_dict in appstats_FILTER_LIST:
for key, regex in filter_dict.iteritems():
negated = isinstance(regex, str) and regex.startswith('!')
if negated:
regex = regex[1:]
value = env.get(key, '')
if bool(re.match(regex, value)) == negated:
logging.debug('No match on %r for %s=%r', regex, key, value)
break
else:
logging.debug('Match on %r', filter_dict)
break
else:
logging.debug('Non-empty FILTER_LIST, but no filter matches')
return False
if appstats_RECORD_FRACTION >= 1.0:
return True
return random.random() < appstats_RECORD_FRACTION
# The following functions are called by the UI code only; they don't
# affect the recorded information.
# normalize_path() takes a path and returns an 'path key'. The path
# key is used by the UI to compute statistics for similar URLs. If
# your application has a large or infinite URL space (e.g. each issue
# in an issue tracker might have its own numeric URL), this function
# can be used to produce more meaningful statistics.
def appstats_normalize_path(path):
return path
# extract_key() is a lower-level function with the same purpose as
# normalize_key(). It can be used to lump different request methods
# (e.g. GET and POST) together, or conversely to use other information
# on the request object (mostly the query string) to produce a more
# fine-grained path key. The argument is a StatsProto object; this is
# a class defined in recording.py. Useful methods are:
# # - http_method()
# - http_path()
# - http_query()
# - http_status()
# # Note that the StatsProto argument is loaded only with summary
# information; this means you cannot access the request headers.
def appstats_extract_key(request):
key = appstats_normalize_path(request.http_path())
if request.http_method() != 'GET':
key = '%s %s' % (request.http_method(), key)
return key
# ########################################
# Remote_API Authentication configuration.
# # See google/appengine/ext/remote_api/handler.py for more information.
# In most cases, you will not want to configure this.
# # remoteapi_CUSTOM_ENVIRONMENT_AUTHENTICATION = (
# 'HTTP_X_APPENGINE_INBOUND_APPID', ['a trusted appid here'])
gaehandler.py
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
This file is part of the web2py Web Framework
Copyrighted by Massimo Di Pierro <mdipierro@cs.depaul.edu>
License: LGPLv3 (http://www.gnu.org/licenses/lgpl.html)
"""
##############################################################################
# Configuration parameters for Google App Engine
##############################################################################
LOG_STATS = False # web2py level log statistics
APPSTATS = True # GAE level usage statistics and profiling
DEBUG = False # debug mode
#
# Read more about APPSTATS here
# http://googleappengine.blogspot.com/2010/03/easy-performance-profiling-with.html
# can be accessed from:
# http://localhost:8080/_ah/stats
##############################################################################
# All tricks in this file developed by Robin Bhattacharyya
##############################################################################
import time
import os
import sys
import logging
import cPickle
import pickle
import wsgiref.handlers
import datetime
path = os.path.dirname(os.path.abspath(__file__))
# os.chdir(path) ?
if not os.path.isdir('applications'):
raise RuntimeError('Running from the wrong folder')
sys.path = [path] + [p for p in sys.path if not p == path]
sys.modules['cPickle'] = sys.modules['pickle']
from gluon.settings import global_settings
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
global_settings.web2py_runtime_gae = True
global_settings.db_sessions = True
if os.environ.get('SERVER_SOFTWARE', '').startswith('Devel'):
(global_settings.web2py_runtime, DEBUG) = \
('gae:development', True)
else:
(global_settings.web2py_runtime, DEBUG) = \
('gae:production', False)
import gluon.main
def log_stats(fun):
"""Function that will act as a decorator to make logging"""
def newfun(env, res):
"""Log the execution time of the passed function"""
timer = lambda t: (t.time(), t.clock())
(t0, c0) = timer(time)
executed_function = fun(env, res)
(t1, c1) = timer(time)
log_info = """**** Request: %.2fms/%.2fms (real time/cpu time)"""
log_info = log_info % ((t1 - t0) * 1000, (c1 - c0) * 1000)
logging.info(log_info)
return executed_function
return newfun
logging.basicConfig(level=logging.INFO)
def wsgiapp(env, res):
"""Return the wsgiapp"""
env['PATH_INFO'] = env['PATH_INFO'].decode('latin1').encode('utf8')
#when using the blobstore image uploader GAE dev SDK passes these as unicode
# they should be regular strings as they are parts of URLs
env['wsgi.url_scheme'] = str(env['wsgi.url_scheme'])
env['QUERY_STRING'] = str(env['QUERY_STRING'])
env['SERVER_NAME'] = str(env['SERVER_NAME'])
#this deals with a problem where GAE development server seems to forget
# the path between requests
if global_settings.web2py_runtime == 'gae:development':
gluon.admin.create_missing_folders()
web2py_path = global_settings.applications_parent # backward compatibility
return gluon.main.wsgibase(env, res)
if LOG_STATS or DEBUG:
wsgiapp = log_stats(wsgiapp)
def main():
"""Run the wsgi app"""
run_wsgi_app(wsgiapp)
if __name__ == '__main__':
main()
我想,这个文件必须包含在web2py的主线程中。