Archive for GAE

在GAE上使用web.py模板出现 No module named templates 错误的解决方法

在 GAE 环境使用 web.py 的模板系统会出现错误。原因大概是web.py使用了一些GAE环境受限的功能(猜的,没看代码)。
解决办法是在手工编译模板。
执行
python /usr/lib/python2.5/site-packages/web/template.py –compile templates

templates 是项目的模板目录,如果你的不是请修改。如果有多个模板目录都需要执行一次,每次修改模板也需要执行一次。

下面是错误提示:

Python 2.5.2: /usr/bin/python
Mon Feb 23 08:28:24 2009

A problem occurred in a Python script. Here is the sequence of function calls leading up to the error, in the order they occurred.
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in _HandleRequest(self=)
2459 infile,
2460 outfile,
2461 base_env_dict=env_dict)
2462 finally:
2463 self.module_manager.UpdateModuleFileModificationTimes()
base_env_dict undefined, env_dict = {‘APPLICATION_ID’: ‘zcsino-dvd’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0′}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in Dispatch(self=, relative_url=’/’, path=None, headers=, infile=, outfile=, base_env_dict={‘APPLICATION_ID’: ‘zcsino-dvd’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0’})
352 infile,
353 outfile,
354 base_env_dict=base_env_dict)
355
356 return
base_env_dict = {‘APPLICATION_ID’: ‘zcsino-dvd’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0′}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in Dispatch(self=, relative_url=’/’, path=’gae.py’, headers=, infile=, outfile=, base_env_dict={‘APPLICATION_ID’: ‘zcsino-dvd’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘REMOTE_ADDR’: ‘127.0.0.1’, ‘REQUEST_METHOD’: ‘GET’, ‘SERVER_NAME’: ‘localhost’, ‘SERVER_PORT’: ‘8080’, ‘SERVER_PROTOCOL’: ‘HTTP/1.0’, ‘SERVER_SOFTWARE’: ‘Development/1.0’})
1869 infile,
1870 outfile,
1871 self._module_dict)
1872 handler.AddDebuggingConsole(relative_url, env, outfile)
1873 finally:
self = , self._module_dict = {‘Cookie’: , ‘StringIO’: , ‘UserDict’: , ‘__builtin__’: , ‘__future__’: , ‘__main__’: , ‘_bisect’: , ‘_hashlib’: , ‘_random’: , ‘_struct’: , …}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in ExecuteCGI(root_path=’/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd’, handler_path=’gae.py’, cgi_path=’/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py’, env={‘APPLICATION_ID’: ‘zcsino-dvd’, ‘AUTH_DOMAIN’: ‘gmail.com’, ‘CONTENT_LENGTH’: ”, ‘CONTENT_TYPE’: ‘application/x-www-form-urlencoded’, ‘CURRENT_VERSION_ID’: ‘1.1’, ‘GATEWAY_INTERFACE’: ‘CGI/1.1’, ‘HTTP_ACCEPT’: ‘text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8’, ‘HTTP_ACCEPT_CHARSET’: ‘GB2312,utf-8;q=0.7,*;q=0.7’, ‘HTTP_ACCEPT_LANGUAGE’: ‘zh-cn,zh;q=0.5’, ‘HTTP_CONNECTION’: ‘keep-alive’, …}, infile=, outfile=, module_dict={‘Cookie’: , ‘StringIO’: , ‘UserDict’: , ‘__builtin__’: , ‘__future__’: , ‘__main__’: , ‘_bisect’: , ‘_hashlib’: , ‘_random’: , ‘_struct’: , …}, exec_script=)
1787 logging.debug(‘Executing CGI with env:\n%s’, pprint.pformat(env))
1788 try:
1789 reset_modules = exec_script(handler_path, cgi_path, hook)
1790 except SystemExit, e:
1791 logging.debug(‘CGI exited with status: %s’, e)
reset_modules = True, exec_script = , handler_path = ‘gae.py’, cgi_path = ‘/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py’, hook =
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in ExecuteOrImportScript(handler_path=’gae.py’, cgi_path=’/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py’, import_hook=)
1683 try:
1684 if module_code:
1685 exec module_code in script_module.__dict__
1686 else:
1687 script_module.main()
module_code = <code object at 0x85e2380, file “/mnt/s…g/mpython/gae/project/zcsino-dvd/gae.py”, line 5>, script_module = , script_module.__dict__ = {‘__builtins__’: {‘ArithmeticError’: , ‘AssertionError’: , ‘AttributeError’: , ‘BaseException’: , ‘DeprecationWarning’: , ‘EOFError’: , ‘Ellipsis’: Ellipsis, ‘EnvironmentError’: , ‘Exception’: , ‘False’: False, …}, ‘__doc__’: None, ‘__file__’: ‘/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py’, ‘__loader__’: , ‘__name__’: ‘gae’, ‘import_wrapper’: }
/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py in ()
4 # 优先使用自带库
5 import import_wrapper
6 import views
7
8
views undefined
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in decorate(self=, *args=(‘views’,), **kwargs={})
847 self._indent_level += 1
848 try:
849 return func(self, *args, **kwargs)
850 finally:
851 self._indent_level -= 1
func = , self = , args = (‘views’,), kwargs = {}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in load_module(self=, fullname=’views’)
1443 search_path = parent_module.__path__
1444
1445 return self.FindAndLoadModule(submodule, fullname, search_path)
1446
1447 @Trace
self = , self.FindAndLoadModule = <bound method HardenedModulesHook.decorate of >, submodule = ‘views’, fullname = ‘views’, search_path = None
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in decorate(self=, *args=(‘views’, ‘views’, None), **kwargs={})
847 self._indent_level += 1
848 try:
849 return func(self, *args, **kwargs)
850 finally:
851 self._indent_level -= 1
func = , self = , args = (‘views’, ‘views’, None), kwargs = {}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in FindAndLoadModule(self=, submodule=’views’, submodule_fullname=’views’, search_path=None)
1351 source_file,
1352 pathname,
1353 description)
1354
1355 module.__loader__ = self
description = (‘.py’, ‘U’, 1)
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in decorate(self=, *args=(‘views’, , ‘/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/views.py’, (‘.py’, ‘U’, 1)), **kwargs={})
847 self._indent_level += 1
848 try:
849 return func(self, *args, **kwargs)
850 finally:
851 self._indent_level -= 1
func = , self = , args = (‘views’, , ‘/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/views.py’, (‘.py’, ‘U’, 1)), kwargs = {}
/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py in LoadModuleRestricted(self=, submodule_fullname=’views’, source_file=, pathname=’/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/views.py’, description=(‘.py’, ‘U’, 1))
1301 source_file,
1302 pathname,
1303 description)
1304 except:
1305 if submodule_fullname in self._module_dict:
description = (‘.py’, ‘U’, 1)
/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/views.py in ()
6 import config
7
8 render = web.template.render(‘templates/’, base=’base’)
9
10 urls = (
render undefined, web = None, web.template undefined, base undefined
/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/libs/web.zip/web/template.py in __init__(self=, loc=’templates/’, *a=(), **kw={‘base’: ‘base’})

: No module named templates
args = (‘No module named templates’,)
message = ‘No module named templates’

INFO 2009-02-23 08:28:24,069 dev_appserver.py] “GET / HTTP/1.1” 500 –
ERROR 2009-02-23 08:28:25,806 dev_appserver.py] Exception encountered handling request
Traceback (most recent call last):
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 2461, in _HandleRequest
base_env_dict=env_dict)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 354, in Dispatch
base_env_dict=base_env_dict)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1871, in Dispatch
self._module_dict)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1789, in ExecuteCGI
reset_modules = exec_script(handler_path, cgi_path, hook)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1685, in ExecuteOrImportScript
exec module_code in script_module.__dict__
File “/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/gae.py”, line 6, in
import views
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 849, in decorate
return func(self, *args, **kwargs)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1445, in load_module
return self.FindAndLoadModule(submodule, fullname, search_path)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 849, in decorate
return func(self, *args, **kwargs)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1353, in FindAndLoadModule
description)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 849, in decorate
return func(self, *args, **kwargs)
File “/mnt/source/home/gamexg/mpython/gae/google_appengine/google/appengine/tools/dev_appserver.py”, line 1303, in LoadModuleRestricted
description)
File “/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/views.py”, line 8, in
render = web.template.render(‘templates/’, base=’base’)
File “/mnt/source/home/gamexg/mpython/gae/project/zcsino-dvd/libs/web.zip/web/template.py”, line 995, in __init__
ImportError: No module named templates

Comments (4) »

Google App Engine (GAE) 翻译 – Transactions(事务)

Transactions

事务

The App Engine datastore supports transactions. A transaction is an operation or set of operations that either succeeds completely, or fails completely. An application can perform multiple operations in a single transaction using Python function objects and the db.run_in_transaction() function.
应用程序数据库支持事务。事务是一些操作同时成功或同时失败。应用程序可以使用 Python 程序对象和 db.run_in_transaction() 程序在一个事务内执行多个操作。

* Using Transactions(使用事务)
* What Can Be Done In a Transaction(什么可以在一个事务内完成)
* Uses For Transactions(事务的例子)

Using Transactions

使用事务

A transaction is a datastore operation or a set of datastore operations that either succeed completely, or fail completely. If the transaction succeeds, then all of its intended effects are applied to the datastore. If the transaction fails, then none of the effects are applied.
事务是一些操作同时成功或同时失败。如果事务成功,则所有操作应用到数据库。如果事务失败,则没有操作被应用到数据库。

Every datastore write operation is atomic. A put() or delete() either happens, or it doesn’t. An operation may fail due to a high rate of contention, with too many users trying to modify an entity at the same time. Or an operation may fail due to the application reaching a quota limit. Or there may be an internal error with the datastore. In all cases, the operation’s effects are not applied, and the datastore API raises an exception.
数据库写操作是原子的,要么成功要么失败。一个操作可能因为链接过多而超时,或者多个用户同时对一个实体进行修改。或者操作失败的原因是超出配额限制。也有可能是数据库内部错误。在所有情况下,这个操作不会被应用,并且数据库API引发一个异常。

An application can execute a set of statements and datastore operations in a single transaction, such that if any statement or operation raises an exception, none of the datastore operations in the set are applied. The application defines the actions to perform in the transaction using a Python function, then calls db.run_in_transaction() with the function as an argument:
应用程序可以在一个事务内执行一个设置指令和数据库操作,如果设置指令或数据库操作引发异常,那么没有任何操作被应用到数据库。应用程序把需要在事务内执行的操作放到一个 Python 函数里,然后把函数作为参数条用 db.run_in_transaction() .

from google.appengine.ext import db

class Accumulator(db.Model):
counter = db.IntegerProperty()

def increment_counter(key, amount):
obj = db.get(key)
obj.counter += amount
obj.put()

q = db.GqlQuery(“SELECT * FROM Accumulator”)
acc = q.get()

db.run_in_transaction(increment_counter, acc.key(), 5)

db.run_in_transaction() takes the function object, and positional and keyword arguments to pass to the function. If the function returns a value, db.run_in_transaction() will return the value.
db.run_in_transaction() 取得函数对象,并把附加参数传递到这个函数。如果函数有返回值,那么db.run_in_transaction() 将返回该值。

If the function returns, the transaction is committed, and all effects of datastore operations are applied. If the function raises an exception, the transaction is “rolled back,” and the effects are not applied.
如果程序返回,那么事务是成功的,所有的数据库操作引起的变更被应用。如果程序引发异常,那么事务将回滚,并且所有的变更不会被应用。

If the function raises the Rollback exception, db.run_in_transaction() returns None. For any other exception, db.run_in_transaction() re-raises the exception.
如果程序引发 Rollback 异常,db.run_in_transaction() 返回 None 。如果是其它异常,db.run_in_transaction() 重新引发这个异常。

What Can Be Done In a Transaction

什么可以在一个事务内完成

The datastore imposes several restrictions on what can be done inside a single transaction.
数据库对在一个事务内可完成的操作有一些限制。

All datastore operations in a transaction must operate on entities in the same entity group. This includes db.get(), put() and delete(). Notice that each root entity belongs to a separate entity group, so a single transaction cannot create or operate on more than one root entity. For an explanation of entity groups, see Keys and Entity Groups.
所有在同一个事务内执行的操作必须属于同一组。包含 db.get(),put() 和 delete()。注意每个根实体分别属于一个组,所以在单个事务内无法创建或操作一个以上的根实体。想要了解实体组,请看 键和实体组 。

A transaction cannot perform queries using Query or GqlQuery. However, a transaction can retrieve datastore entities using keys and db.get(). Keys can be passed to the transaction function, or built inside the function with key names or IDs and Key.from_path(), Model.get_by_key_name() or Model.get_by_id().
事务内不能使用 Query 或 GqlQuery 执行查询。但是在事务内可以使用 Keys 和 db.get() 从数据库获得实体。Keys 可以使传递进事务函数或者是在函数内使用 key names or IDs and Key.from_path(), Model.get_by_key_name() or Model.get_by_id() 生成。

An application cannot create or update an entity more than once in a single transaction.
应用程序不能在一个事务内多次建立或修改同一个实体。

All other Python code is allowed inside a transaction function. The transaction function should not have side effects other than the datastore operations. The transaction function may be called multiple times if a datastore operation fails due to another user updating entities in the entity group at the same time. When this happens, the datastore API retries the transaction a fixed number of times. If they all fail, db.run_in_transaction() raises a TransactionFailedError.
允许在事务函数内包含其它的Python代码。事务函数不能有其他的对数据库造成修改的操作。如果另外一个用户同一时间更新了同一组的的实体那么事务函数可能被多次调用。当发生这种情况时,数据库 API 将在一定时间内尝试多次。如果全部失败,db.run_in_transaction() 引发 TransactionFailedError 异常。

Similarly, the transaction function should not have side effects that depend on the success of the transaction, unless the code that calls the transaction function knows to undo those effects. For example, if the transaction stores a new datastore entity, saves the created entity’s ID for later use, then the transaction fails, the saved ID does not refer to the intended entity because the entity’s creation was rolled back. The calling code would have to be careful not to use the saved ID in this case.
同样,在事务函数的事务内应该不存在成功的变更,除非代码通知回滚这些变更。例子:如果这个事务储存一个新的实体,保存创建的实体 ID 备用,如果事务失败,那么保存的ID不会指向有效的实体,原因实体建立操作被回滚。调用代码需要谨慎防止使用这种情况下保存的ID。

Uses For Transactions

事务的例子

The example above demonstrates one use of transactions: updating an entity with a new property value relative to its current value.
一个关于事务的例子:对更新一个实体由于一个新的属性和当前值有关联。

def increment_counter(key, amount):
obj = db.get(key)
obj.counter += amount
obj.put()

This requires a transaction because the value may be updated by another user after this user’s request calls db.get(key) but before it calls obj.put(). Without a transaction, the user’s request will use the value of obj.counter prior to the update, and obj.put() will overwrite the update. With a transaction, the entity is guaranteed not to change between the two calls. If the entity is updated during the transaction, then the transaction is retried until all steps are completed without interruption.
这里需要事务的原因是另一个用户可能在调用 db.get(key) 之后并在调用 obj.put() 之前更新该值。没有事务时,用户必须使用 obj.counter 更新,并且为了更新将写很多代码。有事务时,担保实体不会在两次操作之间被更改。如果实体在事务中被更改,则事务会重试直到所有的操作不被打断的完成。

Another common use for transactions is to update an entity with a named key, or create it if it doesn’t yet exist:
另外一个经常使用事务的地方是更新指定键名称的实体,或者在它不存在时创建它。

class SalesAccount(db.Model):
address = db.PostalAddressProperty()
phone_number = db.PhoneNumberProperty()

def create_or_update(parent_obj, account_id, address, phone_number):
obj = db.get(Key.from_path(“SalesAccount”, account_id, parent=parent_obj))
if not obj:
obj = SalesAccount(key_name=account_id,
parent=parent_obj,
address=address,
phone_number=phone_number)
else:
obj.address = address
obj.phone_number = phone_number

obj.put()

As before, a transaction is necessary to handle the case where another user is attempting to create or update an entity with the same account_id. Without a transaction, if the entity does not exist and two users attempt to create it, the second will fail. With a transaction, the second attempt will retry, notice that the entity now exists, and update the entity instead.
前面例子,一个事务是必须处理另外一个用户试图创建或更新同一 account_id 的实体 的情况。没有事务时,如果实体不存在并且两个用户试图创建它,那么第二个将会失败。有事务时,第二个操作将会重试,注意实体现在已经存在,将使用更新代替。

Create-or-update is so useful that there is a built-in method for it: Model.get_or_insert() takes a key name, an optional parent, and arguments to pass to the model constructor if an entity of that name and path does not exist. The get attempt and the create happen in one transaction, so (if the transaction is successful) the method always returns a model instance that represents an actual entity.
因为 创建或更新经常使用,所以有一个内置方法:Model.get_or_insert() 接受 键名称 和可选的父实体,指定名称和路径的实体不存在时参数将传递给模型的构造函数。它在一个事务内执行尝试获得和创建操作,所以(如果事务成功)这个方法总是返回一个真实存在的模板的实例。

Tip: A transaction should happen as quickly as possible to reduce the likelihood that the entities used by the transaction will change, requiring the transaction be retried. As much as possible, prepare data outside of the transaction, then execute the transaction to perform datastore operations that depend on a consistent state. The application should prepare Keys for objects used inside the transaction, then use db.get() to fetch the entities inside the transaction.
注意:事务应该尽快完成或尽量少的使用,原因是事务使用的实体被改变时需要重试。大多数场合,在事务外准备数据,在事务内执行数据库操作获得一致性保证。应用程序应该在事务内部为准备了 key 的实体执行插入,然后在事务内部使用 db.get() 获得实体。

No comment »