跳转到主要内容

来自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 (23.3 kB 查看散列值)

上传时间

支持