来自Lovely Systems的Appengine相关Python包
项目描述
lovely.gae.async
此包异步执行任务,它使用appengine taskqueue来执行任务。
>>> from lovely.gae.async import defer, get_tasks
defer函数异步执行处理程序作为任务。我们创建了3个具有相同签名的工作。
>>> import time >>> for i in range(3): ... print defer(time.sleep, [0.3]) <google.appengine.api.labs.taskqueue.taskqueue.Task object at ...> None None
让我们看看有哪些工作。注意,只有一个,因为我们添加的3个工作都是相同的。
>>> len(get_tasks()) 1
如果我们更改任务的签名,将会添加一个新的。
>>> added = defer(time.sleep, [0.4]) >>> len(get_tasks()) 2
通常作业会由taskqueueapi自动执行,我们有一个测试方法,它可以执行作业并返回完成的作业数量。
>>> run_tasks() 2
现在我们可以再次添加相同的签名。
>>> added = defer(time.sleep, [0.4]) >>> run_tasks() 1
我们还可以将only_once设置为false,以便使用相同的签名多次执行工作器。
>>> from pprint import pprint >>> defer(pprint, ['hello'], once_only=False) <google.appengine.api.labs.taskqueue.taskqueue.Task object at ...> >>> defer(pprint, ['hello'], once_only=False) <google.appengine.api.labs.taskqueue.taskqueue.Task object at ...> >>> run_tasks() 'hello' 'hello' 2
数据库自定义属性类
类型化列表
此属性将模型实例转换为键并确保长度。
>>> from lovely.gae.db.property import TypedListProperty >>> from google.appengine.ext import db
让我们创建一个模型
>>> class Yummy(db.Model): pass >>> class Bummy(db.Model): pass
现在我们可以使用我们的属性引用Yummy实例。请注意,我们还可以将类型名称用作类型的参数。
>>> class Dummy(db.Model): ... yummies = TypedListProperty(Yummy) ... bummies = TypedListProperty('Bummy', length=3)
类型参数需要是子类类型名称或db.Model。
>>> TypedListProperty(object) Traceback (most recent call last): ... ValueError: Kind needs to be a subclass of db.Model>>> dummy = Dummy() >>> dummy_key = dummy.put() >>> yummy1 = Yummy() >>> yummy1_key = yummy1.put() >>> dummy.yummies = [yummy1]
我们无法设置任何其他类型。
>>> bummy1 = Bummy() >>> bummy1_key = bummy1.put() >>> dummy.yummies = [bummy1] Traceback (most recent call last): ... BadValueError: Wrong kind u'Bummy'
如果已定义,长度需要匹配(见上文)。
>>> dummy.bummies = [bummy1] Traceback (most recent call last): ... BadValueError: Wrong length need 3 got 1>>> dummy.bummies = [bummy1, bummy1, bummy1] >>> dummy_key == dummy.put() True
不区分大小写的字符串属性
此属性允许以不区分大小写的方式搜索小写前缀。这对于我们不希望为搜索单独创建属性的自动完成实现非常有用。
>>> from lovely.gae.db.property import IStringProperty >>> class Foo(db.Model): ... title = IStringProperty()>>> f1 = Foo(title='Foo 1') >>> kf1 = f1.put() >>> f2 = Foo(title='Foo 2') >>> kf2 = f2.put() >>> f3 = Foo(title='foo 3') >>> kf3 = f3.put() >>> f4 = Foo(title=None) >>> kf4 = f4.put()
该属性不允许使用特殊的分隔符字符,它正好位于最高Unicode字符之下。
>>> f3 = Foo(title='Foo 3' + IStringProperty.SEPERATOR) Traceback (most recent call last): ... BadValueError: Not all characters in property title
请注意,如果我们想要进行精确搜索,我们必须使用由属性实例创建的特殊过滤器。
>>> [f.title for f in Foo.all().filter('title =', 'foo 1')] []
“等于”过滤器参数可以通过属性上的特殊方法计算得出。
>>> ef = Foo.title.equals_filter('Foo 1') >>> ef ('title =', u'foo 1\xef\xbf\xbcFoo 1')>>> [f.title for f in Foo.all().filter(*ef)] [u'Foo 1']
让我们尝试不等式,例如前缀搜索。前缀搜索通常使用两个过滤器,使用最高Unicode字符来完成。
搜索所有以“fo”开头的条目,不区分大小写。
>>> q = Foo.all() >>> q = q.filter('title >=', 'fo') >>> q = q.filter('title <', 'fo' + u'\xEF\xBF\xBD') >>> [f.title for f in q] [u'Foo 1', u'Foo 2', u'foo 3']
搜索以“foo 1”开头的所有条目。
>>> q = Foo.all() >>> q = q.filter('title >=', 'foo 1') >>> q = q.filter('title <', 'foo 1' + u'\xEF\xBF\xBD') >>> [f.title for f in q] [u'Foo 1']>>> q = Foo.all() >>> q = q.filter('title >=', 'foo 2') >>> q = q.filter('title <=', 'foo 2' + u'\xEF\xBF\xBD') >>> [f.title for f in q] [u'Foo 2']>>> q = Foo.all() >>> q = q.filter('title >=', 'foo 3') >>> q = q.filter('title <=', 'foo 3' + u'\xEF\xBF\xBD') >>> [f.title for f in q] [u'foo 3']
可序列化属性
可序列化属性可以存储任何可序列化的Python对象。
>>> from lovely.gae.db.property import PickleProperty>>> class Pickie(db.Model): ... data = PickleProperty()>>> pickie = Pickie(data={}) >>> pickie.data {} >>> kp = pickie.put() >>> pickie.data {} >>> pickie = db.get(kp) >>> pickie.data {} >>> pickie.data = {'key':501*"x"} >>> kp = pickie.put() >>> pickie.data {'key': 'xx...xx'}
如果值不可序列化,我们将得到一个验证错误。
>>> pickie = Pickie(data=dict(y=lambda x:x)) Traceback (most recent call last): BadValueError: Property 'data' must be pickleable: (Can't pickle <function <lambda> at ...>: it's not found as __main__.<lambda>)
安全引用属性
>>> from lovely.gae.db.property import SafeReferenceProperty
我们使用一个新的模型,具有gae引用和我们的安全引用。
>>> class Refie(db.Model): ... ref = db.ReferenceProperty(Yummy, collection_name='ref_ref') ... sfref = SafeReferenceProperty(Yummy, collection_name='sfref_ref')>>> refie = Refie() >>> refie.sfref is None True >>> refie.ref is None True
要引用的对象。
>>> refyummy1 = Yummy() >>> ignore = refyummy1.put()
将引用设置为我们的yummy对象。
>>> refie.sfref = refyummy1 >>> refie.sfref <Yummy object at ...> >>> refie.ref = refyummy1 >>> refie.ref <Yummy object at ...>>>> refieKey = refie.put()
现在我们删除引用的对象。
>>> refyummy1.delete()
并重新加载我们的引用对象。
>>> refie = db.get(refieKey)
gae引用会抛出异常。
>>> refie.ref Traceback (most recent call last): Error: ReferenceProperty failed to be resolved
我们在此捕获日志。
>>> import logging >>> from StringIO import StringIO >>> log = StringIO() >>> handler = logging.StreamHandler(log) >>> logger = logging.getLogger('lovely.gae.db') >>> logger.setLevel(logging.INFO) >>> logger.addHandler(handler)
我们的安全引用返回None。
>>> pos = log.tell() >>> refie.sfref is None True
让我们看看日志包含什么。
>>> log.seek(pos) >>> print log.read() Unresolved Reference for "Refie._sfref" set to None
再次访问过时的属性,我们将看到它已重置为None
>>> pos = log.tell() >>> refie.sfref is None True >>> log.seek(pos) >>> print log.read() == '' True
如果引用指向的对象已死亡,则属性将设置为None,但这仅当属性不是必需时才发生
>>> class Requy(db.Model): ... sfref = SafeReferenceProperty(Yummy, collection_name='req_sfref_ref', ... required=True) >>> refyummy1 = Yummy() >>> ignore = refyummy1.put() >>> requy = Requy(sfref = refyummy1) >>> requyKey = requy.put() >>> requy.sfref <Yummy object at ...> >>> refyummy1.delete() >>> requy = db.get(requyKey) >>> pos = log.tell() >>> requy.sfref is None True >>> log.seek(pos) >>> print log.read() Unresolved Reference for "Requy._sfref" will remain because it is required
批量标记创建
此包提供了为给定查询的每个N个对象创建标记的可能性。这对于创建分批HTML页面或为每个N个对象生成作业非常有用。
创建了一个表示给定查询中任何给定位置批处理结束的属性值列表。结果存储在memcache中,并提供了密钥给回调函数。
>>> from lovely.gae import batch
让我们创建一些测试对象。
>>> from google.appengine.ext import db >>> class Stub(db.Model): ... c_time = db.DateTimeProperty(auto_now_add=True, required=True) ... name = db.StringProperty() ... age = db.IntegerProperty() ... state = db.IntegerProperty() ... def __repr__(self): ... return '<Stub %s>' % self.name>>> for i in range(1,13): ... s = Stub(name=str(i), age=i, state=i%2) ... sk = s.put()>>> Stub.all().count() 12
首先,我们确保测试中没有队列任务。
>>> from lovely.gae.async import get_tasks >>> len(get_tasks()) 0
例如,如果我们想了解给定类型的任何第100个键,我们可以像下面这样计算。请注意,我们提供pprint函数作为回调,因此我们在输出中获得了memcache键。
calculate_markers函数返回在计算完成后将用于存储结果的memcache键。
>>> from pprint import pprint >>> mc_key = batch.create_markers('Stub', callback=pprint) >>> mc_key 'create_markers:...-...-...'
创建了一个任务。
>>> tasks = get_tasks() >>> len(tasks) 1
让我们运行任务。
>>> run_tasks(1) 1
我们现在还有一个任务留给回调,实际上就是pprint函数。
>>> run_tasks() 'create_markers:...-...-...' 1
我们现在应该有一个结果。结果显示,我们不需要为给定的批量大小创建批处理(因为我们只有12个对象)。
>>> from google.appengine.api import memcache >>> memcache.get(mc_key) []
让我们使用另一个批量大小。这次没有回调。
>>> mc_key = batch.create_markers('Stub', batchsize=1) >>> run_tasks() 1
现在我们有确切的12个键,因为批量大小是1。
>>> len(memcache.get(mc_key)) 12
默认返回的属性是键。
>>> memcache.get(mc_key) [datastore_types.Key.fro...
我们还可以使用其他属性。让我们按c_time降序批量获取项目。请注意,如果没有检查值是否唯一,所以如果使用非唯一属性,批量范围可能包含重复的对象。
>>> mc_key = batch.create_markers('Stub', ... attribute='c_time', ... order='desc', ... batchsize=3) >>> run_tasks() 1 >>> markers = memcache.get(mc_key) >>> markers [datetime.datetime(... >>> len(markers) 4 >>> sorted(markers, reverse=True) == markers True>>> mc_key = batch.create_markers('Stub', ... attribute='c_time', ... order='asc', ... batchsize=3) >>> run_tasks() 1 >>> markers = memcache.get(mc_key) >>> markers [datetime.datetime(... >>> len(markers) 4 >>> sorted(markers) == markers True
我们还可以像这样向查询传递应用于批次的过滤器
>>> mc_key = batch.create_markers('Stub', ... filters=[('state', 0)], ... attribute='c_time', ... order='asc', ... batchsize=3) >>> run_tasks() 1 >>> markers = memcache.get(mc_key) >>> len(markers) 2
项目详情
lovely.gae-1.0.0a2.tar.gz的散列值
算法 | 散列摘要 | |
---|---|---|
SHA256 | 44768188e14cef5967700491ad141bfccdfb4a57cf784132a68194863ed38cdd |
|
MD5 | 59f5858de18b1f7cf0cadb7a67a844ea |
|
BLAKE2b-256 | 70f73085010612aab92592163b7d7323029f0fc4cb8316bffe6684f48f429ae7 |